diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,10 @@
-## [_Unreleased_](https://github.com/thoughtbot/yesod-auth-oauth2/compare/v0.7.0.0...main)
+## [_Unreleased_](https://github.com/thoughtbot/yesod-auth-oauth2/compare/v0.7.0.1...main)
 
 None
+
+## [v0.7.0.1](https://github.com/thoughtbot/yesod-auth-oauth2/compare/v0.7.0.0...v0.7.0.1)
+
+- Support `hoauth-2.2` and `2.3`
 
 ## [v0.7.0.0](https://github.com/thoughtbot/yesod-auth-oauth2/compare/v0.6.3.4...v0.7.0.0)
 
diff --git a/src/Network/OAuth/OAuth2/Compat.hs b/src/Network/OAuth/OAuth2/Compat.hs
--- a/src/Network/OAuth/OAuth2/Compat.hs
+++ b/src/Network/OAuth/OAuth2/Compat.hs
@@ -2,27 +2,34 @@
 
 module Network.OAuth.OAuth2.Compat
     ( OAuth2(..)
+    , OAuth2Result
     , authorizationUrl
     , fetchAccessToken
     , fetchAccessToken2
+    , authGetBS
+
+    -- * Re-exports
     , module Network.OAuth.OAuth2
     ) where
 
+import Data.ByteString.Lazy (ByteString)
+import Data.Text (Text)
 import Network.HTTP.Conduit (Manager)
-import Network.OAuth.OAuth2 hiding
-    (OAuth2(..), authorizationUrl, fetchAccessToken, fetchAccessToken2)
+import Network.OAuth.OAuth2
+    ( AccessToken(..)
+    , ExchangeToken(..)
+    , OAuth2Error
+    , OAuth2Token(..)
+    , RefreshToken(..)
+    )
 import qualified Network.OAuth.OAuth2 as OAuth2
 import Network.OAuth.OAuth2.TokenRequest (Errors)
 import URI.ByteString
 
-#if MIN_VERSION_hoauth2(2,0,0)
-import Network.OAuth.OAuth2 (OAuth2(..))
-
-getOAuth2 :: OAuth2 -> OAuth2
-getOAuth2 = id
-
-#else
-import Data.Text (Text)
+#if MIN_VERSION_hoauth2(2,2,0)
+import Control.Monad.Trans.Except (ExceptT, runExceptT)
+import Data.Maybe (fromMaybe)
+#endif
 
 data OAuth2 = OAuth2
     { oauth2ClientId :: Text
@@ -32,30 +39,116 @@
     , oauth2RedirectUri :: Maybe (URIRef Absolute)
     }
 
+type OAuth2Result err a = Either (OAuth2Error err) a
+
+authorizationUrl :: OAuth2 -> URI
+authorizationUrl = OAuth2.authorizationUrl . getOAuth2
+
+fetchAccessToken
+    :: Manager
+    -> OAuth2
+    -> ExchangeToken
+    -> IO (OAuth2Result Errors OAuth2Token)
+fetchAccessToken = fetchAccessTokenBasic
+
+fetchAccessToken2
+    :: Manager
+    -> OAuth2
+    -> ExchangeToken
+    -> IO (OAuth2Result Errors OAuth2Token)
+fetchAccessToken2 = fetchAccessTokenPost
+
+authGetBS :: Manager -> AccessToken -> URI -> IO (Either ByteString ByteString)
+authGetBS m a u = runOAuth2 $ OAuth2.authGetBS m a u
+
+-- Normalize the rename of record fields at hoauth2-2.0. Our type is the newer
+-- names and we up-convert if hoauth2-1.x is in use. getClientSecret and
+-- getRedirectUri handle the differences in hoauth2-2.2 and 2.3.
+
+#if MIN_VERSION_hoauth2(2,0,0)
 getOAuth2 :: OAuth2 -> OAuth2.OAuth2
 getOAuth2 o = OAuth2.OAuth2
+    { OAuth2.oauth2ClientId = oauth2ClientId o
+    , OAuth2.oauth2ClientSecret = getClientSecret $ oauth2ClientSecret o
+    , OAuth2.oauth2AuthorizeEndpoint = oauth2AuthorizeEndpoint o
+    , OAuth2.oauth2TokenEndpoint = oauth2TokenEndpoint o
+    , OAuth2.oauth2RedirectUri = getRedirectUri $ oauth2RedirectUri o
+    }
+#else
+getOAuth2 :: OAuth2 -> OAuth2.OAuth2
+getOAuth2 o = OAuth2.OAuth2
     { OAuth2.oauthClientId = oauth2ClientId o
-    , OAuth2.oauthClientSecret = oauth2ClientSecret o
+    , OAuth2.oauthClientSecret = getClientSecret $ oauth2ClientSecret o
     , OAuth2.oauthOAuthorizeEndpoint = oauth2AuthorizeEndpoint o
     , OAuth2.oauthAccessTokenEndpoint = oauth2TokenEndpoint o
-    , OAuth2.oauthCallback = oauth2RedirectUri o
+    , OAuth2.oauthCallback = getRedirectUri $ oauth2RedirectUri o
     }
+#endif
 
+-- hoauth2-2.2 made oauth2ClientSecret non-Maybe, after 2.0 had just made it
+-- Maybe so we have to adjust, twice. TODO: change ours type to non-Maybe (major
+-- bump) and reverse this to up-convert with Just in pre-2.2.
+
+#if MIN_VERSION_hoauth2(2,2,0)
+getClientSecret :: Maybe Text -> Text
+getClientSecret =
+    fromMaybe $ error "Cannot use OAuth2.oauth2ClientSecret with Nothing"
+#else
+getClientSecret :: Maybe Text -> Maybe Text
+getClientSecret = id
 #endif
 
-authorizationUrl :: OAuth2 -> URI
-authorizationUrl = OAuth2.authorizationUrl . getOAuth2
+-- hoauth2-2.3 then made oauth2RedirectUri non-Maybe too. We logically rely on
+-- instantiating with Nothing at definition-time, then setting it to the
+-- callback at use-time, which means we can't just change our type and invert
+-- this shim; we'll have to do something much more pervasive to avoid this
+-- fromMaybe.
 
-fetchAccessToken
+#if MIN_VERSION_hoauth2(2,3,0)
+getRedirectUri :: Maybe (URIRef Absolute) -> (URIRef Absolute)
+getRedirectUri =
+    fromMaybe $ error "Cannot use OAuth2.oauth2RedirectUri with Nothing"
+#else
+getRedirectUri :: Maybe (URIRef Absolute) -> Maybe (URIRef Absolute)
+getRedirectUri = id
+#endif
+
+-- hoauth-2.2 moved most IO-Either functions to ExceptT. This reverses that.
+
+#if MIN_VERSION_hoauth2(2,2,0)
+runOAuth2 :: ExceptT e m a -> m (Either e a)
+runOAuth2 = runExceptT
+#else
+runOAuth2 :: IO (Either e a) -> IO (Either e a)
+runOAuth2 = id
+#endif
+
+-- The fetchAccessToken functions grew a nicer interface in hoauth2-2.3. This
+-- up-converts the older ones. We should update our code to use these functions
+-- directly.
+
+fetchAccessTokenBasic
     :: Manager
     -> OAuth2
     -> ExchangeToken
     -> IO (OAuth2Result Errors OAuth2Token)
-fetchAccessToken m = OAuth2.fetchAccessToken m . getOAuth2
+fetchAccessTokenBasic m o e = runOAuth2 $ f m (getOAuth2 o) e
+  where
+#if MIN_VERSION_hoauth2(2,3,0)
+    f = OAuth2.fetchAccessTokenInternal OAuth2.ClientSecretBasic
+#else
+    f = OAuth2.fetchAccessToken
+#endif
 
-fetchAccessToken2
+fetchAccessTokenPost
     :: Manager
     -> OAuth2
     -> ExchangeToken
     -> IO (OAuth2Result Errors OAuth2Token)
-fetchAccessToken2 m = OAuth2.fetchAccessToken2 m . getOAuth2
+fetchAccessTokenPost m o e = runOAuth2 $ f m (getOAuth2 o) e
+  where
+#if MIN_VERSION_hoauth2(2,3,0)
+    f = OAuth2.fetchAccessTokenInternal OAuth2.ClientSecretPost
+#else
+    f = OAuth2.fetchAccessToken2
+#endif
diff --git a/yesod-auth-oauth2.cabal b/yesod-auth-oauth2.cabal
--- a/yesod-auth-oauth2.cabal
+++ b/yesod-auth-oauth2.cabal
@@ -1,6 +1,6 @@
 cabal-version:      1.12
 name:               yesod-auth-oauth2
-version:            0.7.0.0
+version:            0.7.0.1
 license:            MIT
 license-file:       LICENSE
 maintainer:         engineering@freckle.com
@@ -70,13 +70,14 @@
         http-client >=0.4.0,
         http-conduit >=2.0,
         http-types >=0.8,
-        memory >=0.15.0,
-        microlens >=0.4.12.0,
+        memory >=0.14.18,
+        microlens >=0.4.10,
         mtl >=2.2.2,
-        safe-exceptions >=0.1.7.2,
+        safe-exceptions >=0.1.7.0,
         text >=0.7,
-        unliftio >=0.2.20,
-        uri-bytestring >=0.3.3.1,
+        transformers >=0.5.5.0,
+        unliftio >=0.2.10,
+        uri-bytestring >=0.3.2.1,
         yesod-auth >=1.6.0,
         yesod-core >=1.6.0
 
@@ -88,15 +89,15 @@
     ghc-options:      -Wall -threaded -rtsopts -with-rtsopts=-N
     build-depends:
         aeson >=0.6,
-        aeson-pretty >=0.8.9,
+        aeson-pretty >=0.8.7,
         base >=4.9.0.0 && <5,
         bytestring >=0.9.1.4,
-        containers >=0.6.5.1,
+        containers >=0.6.0.1,
         http-conduit >=2.0,
-        load-env >=0.2.1.0,
+        load-env >=0.2.0.2,
         text >=0.7,
-        warp >=3.3.18,
-        yesod >=1.6.1.2,
+        warp >=3.2.25,
+        yesod >=1.6.0,
         yesod-auth >=1.6.0,
         yesod-auth-oauth2 -any
 
@@ -115,6 +116,6 @@
     ghc-options:      -Wall
     build-depends:
         base >=4.9.0.0 && <5,
-        hspec >=2.7.10,
-        uri-bytestring >=0.3.3.1,
+        hspec >=2.6.1,
+        uri-bytestring >=0.3.2.1,
         yesod-auth-oauth2 -any
