diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,17 @@
-## [_Unreleased_](https://github.com/thoughtbot/yesod-auth-oauth2/compare/v0.7.4.0...main)
+## [_Unreleased_](https://github.com/thoughtbot/yesod-auth-oauth2/compare/v0.8.0.0...main)
+
+## [v0.8.0.0](https://github.com/thoughtbot/yesod-auth-oauth2/compare/v0.7.4.0...v0.8.0.0)
+
+- Drop support for GHC < 9.4 and hoauth2 < 2.8
+- Add support for GHC 9.12 and hoauth2-2.15
+- To align our interfaces with hoauth2-2.15:
+  - Make `OAuth2 {clientSecret}` non-`Maybe`
+  - Replace `OAuthToken` with `TokenResponse`
+  - Replace `Errors` with `TokenResponseError`
+  - Replace `fetchAccessToken{,2}` with `fetchAccessToken{Basic,Post}`
+
+While technically a major version bump, this change should only affect those
+users that maintain their own plugins.
 
 ## [v0.7.4.0](https://github.com/thoughtbot/yesod-auth-oauth2/compare/v0.7.3.0...v0.7.4.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,170 +2,117 @@
 
 module Network.OAuth.OAuth2.Compat
   ( OAuth2 (..)
-  , OAuth2Result
-  , Errors
   , authorizationUrl
-  , fetchAccessToken
-  , fetchAccessToken2
+  , fetchAccessTokenBasic
+  , fetchAccessTokenPost
   , authGetBS
 
     -- * Re-exports
-  , module Network.OAuth.OAuth2
+  , AccessToken (..)
+  , ExchangeToken (..)
+  , RefreshToken (..)
+  , TokenResponse
+  , accessToken
+  , refreshToken
+  , expiresIn
+  , tokenType
+  , idToken
+  , TokenResponseError
   ) where
 
+import Control.Monad.IO.Class (MonadIO)
+import Control.Monad.Trans.Except (runExceptT)
 import Data.ByteString.Lazy (ByteString)
 import Data.Text (Text)
 import Network.HTTP.Conduit (Manager)
+import URI.ByteString
+
+#if MIN_VERSION_hoauth2(2,15,0)
+import Network.OAuth2
+  ( AccessToken (..)
+  , ExchangeToken (..)
+  , RefreshToken (..)
+  , TokenResponse (..)
+  , TokenResponseError
+  )
+import qualified Network.OAuth2 as OAuth2
+
+#elif MIN_VERSION_hoauth2(2,9,0)
 import Network.OAuth.OAuth2
   ( AccessToken (..)
   , ExchangeToken (..)
-  , OAuth2Token (..)
   , RefreshToken (..)
+  , OAuth2Token (..)
+  , TokenResponseError
   )
 import qualified Network.OAuth.OAuth2 as OAuth2
-import URI.ByteString
 
-#if MIN_VERSION_hoauth2(2,2,0)
-import Control.Monad.Trans.Except (ExceptT, runExceptT)
-import Data.Maybe (fromMaybe)
-#endif
+type TokenResponse = OAuth2Token
 
-#if MIN_VERSION_hoauth2(2,9,0)
-import Network.OAuth.OAuth2.TokenRequest (TokenResponseError)
-type Errors = TokenResponseError
-#elif MIN_VERSION_hoauth2(2,7,0)
-import Network.OAuth.OAuth2.TokenRequest (TokenRequestError)
-type Errors = TokenRequestError
 #else
-import qualified Network.OAuth.OAuth2.TokenRequest as LegacyTokenRequest
-import Network.OAuth.OAuth2 (OAuth2Error)
-type Errors = OAuth2Error LegacyTokenRequest.Errors
-#endif
+-- hoauth2-2.8
+import Network.OAuth.OAuth2
+  ( AccessToken (..)
+  , ExchangeToken (..)
+  , RefreshToken (..)
+  , OAuth2Token (..)
+  )
+import Network.OAuth.OAuth2.TokenRequest (TokenRequestError)
+import qualified Network.OAuth.OAuth2 as OAuth2
 
-{-# ANN module ("HLint: ignore Use fewer imports" :: String) #-}
+type TokenResponse = OAuth2Token
+type TokenResponseError = TokenRequestError
+#endif
 
 data OAuth2 = OAuth2
   { oauth2ClientId :: Text
-  , oauth2ClientSecret :: Maybe Text
+  , oauth2ClientSecret :: Text
   , oauth2AuthorizeEndpoint :: URIRef Absolute
   , oauth2TokenEndpoint :: URIRef Absolute
   , oauth2RedirectUri :: Maybe (URIRef Absolute)
   }
 
-type OAuth2Result err a = Either err a
-
 authorizationUrl :: OAuth2 -> URI
 authorizationUrl = OAuth2.authorizationUrl . getOAuth2
 
-fetchAccessToken
+fetchAccessTokenBasic
   :: Manager
   -> OAuth2
   -> ExchangeToken
-  -> IO (OAuth2Result Errors OAuth2Token)
-fetchAccessToken = fetchAccessTokenBasic
+  -> IO (Either TokenResponseError TokenResponse)
+fetchAccessTokenBasic =
+  runFetchAccessToken OAuth2.ClientSecretBasic
 
-fetchAccessToken2
+fetchAccessTokenPost
   :: Manager
   -> OAuth2
   -> ExchangeToken
-  -> IO (OAuth2Result Errors OAuth2Token)
-fetchAccessToken2 = fetchAccessTokenPost
+  -> IO (Either TokenResponseError TokenResponse)
+fetchAccessTokenPost =
+  runFetchAccessToken OAuth2.ClientSecretPost
 
 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.
+authGetBS m a u = runExceptT $ OAuth2.authGetBS m a u
 
-#if MIN_VERSION_hoauth2(2,0,0)
 getOAuth2 :: OAuth2 -> OAuth2.OAuth2
-getOAuth2 o = OAuth2.OAuth2
+getOAuth2 o =
+  OAuth2.OAuth2
     { OAuth2.oauth2ClientId = oauth2ClientId o
-    , OAuth2.oauth2ClientSecret = getClientSecret $ oauth2ClientSecret o
+    , OAuth2.oauth2ClientSecret = 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 = getClientSecret $ oauth2ClientSecret o
-    , OAuth2.oauthOAuthorizeEndpoint = oauth2AuthorizeEndpoint o
-    , OAuth2.oauthAccessTokenEndpoint = oauth2TokenEndpoint o
-    , OAuth2.oauthCallback = getRedirectUri $ oauth2RedirectUri o
+    , OAuth2.oauth2RedirectUri = case oauth2RedirectUri o of
+        Nothing ->
+          error
+            "programmer error: yesod-auth-oauth2:OAuth2 must have a Just value set as oauth2RedirectUri before using as an hauth2:OAuth2 value"
+        Just uri -> uri
     }
-#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
-
--- 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.
-
-#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)
-fetchAccessTokenBasic m o e = runOAuth2 $ f m (getOAuth2 o) e
- where
-#if MIN_VERSION_hoauth2(2,6,0)
-    f = OAuth2.fetchAccessTokenWithAuthMethod OAuth2.ClientSecretBasic
-#elif MIN_VERSION_hoauth2(2,3,0)
-    f = OAuth2.fetchAccessTokenInternal OAuth2.ClientSecretBasic
-#else
-    f = OAuth2.fetchAccessToken
-#endif
-
-fetchAccessTokenPost
-  :: Manager
+runFetchAccessToken
+  :: MonadIO m
+  => OAuth2.ClientAuthenticationMethod
+  -> Manager
   -> OAuth2
   -> ExchangeToken
-  -> IO (OAuth2Result Errors OAuth2Token)
-fetchAccessTokenPost m o e = runOAuth2 $ f m (getOAuth2 o) e
- where
-#if MIN_VERSION_hoauth2(2, 6, 0)
-    f = OAuth2.fetchAccessTokenWithAuthMethod OAuth2.ClientSecretPost
-#elif MIN_VERSION_hoauth2(2,3,0)
-    f = OAuth2.fetchAccessTokenInternal OAuth2.ClientSecretPost
-#else
-    f = OAuth2.fetchAccessToken2
-#endif
+  -> m (Either TokenResponseError TokenResponse)
+runFetchAccessToken am m o e = runExceptT $ OAuth2.fetchAccessTokenWithAuthMethod am m (getOAuth2 o) e
diff --git a/src/Yesod/Auth/OAuth2.hs b/src/Yesod/Auth/OAuth2.hs
--- a/src/Yesod/Auth/OAuth2.hs
+++ b/src/Yesod/Auth/OAuth2.hs
@@ -11,13 +11,13 @@
   ( OAuth2 (..)
   , FetchCreds
   , Manager
-  , OAuth2Token (..)
+  , TokenResponse
   , Creds (..)
   , oauth2Url
   , authOAuth2
   , authOAuth2Widget
 
-    -- * Alternatives that use 'fetchAccessToken2'
+    -- * Alternatives that use 'fetchAccessTokenPost'
   , authOAuth2'
   , authOAuth2Widget'
 
@@ -49,7 +49,7 @@
 authOAuth2 :: YesodAuth m => Text -> OAuth2 -> FetchCreds m -> AuthPlugin m
 authOAuth2 name = authOAuth2Widget [whamlet|Login via #{name}|] name
 
--- | A version of 'authOAuth2' that uses 'fetchAccessToken2'
+-- | A version of 'authOAuth2' that uses 'fetchAccessTokenPost'
 --
 -- See <https://github.com/thoughtbot/yesod-auth-oauth2/pull/129>
 authOAuth2' :: YesodAuth m => Text -> OAuth2 -> FetchCreds m -> AuthPlugin m
@@ -66,9 +66,9 @@
   -> OAuth2
   -> FetchCreds m
   -> AuthPlugin m
-authOAuth2Widget = buildPlugin fetchAccessToken
+authOAuth2Widget = buildPlugin fetchAccessTokenBasic
 
--- | A version of 'authOAuth2Widget' that uses 'fetchAccessToken2'
+-- | A version of 'authOAuth2Widget' that uses 'fetchAccessTokenPost'
 --
 -- See <https://github.com/thoughtbot/yesod-auth-oauth2/pull/129>
 authOAuth2Widget'
@@ -78,7 +78,7 @@
   -> OAuth2
   -> FetchCreds m
   -> AuthPlugin m
-authOAuth2Widget' = buildPlugin fetchAccessToken2
+authOAuth2Widget' = buildPlugin fetchAccessTokenPost
 
 buildPlugin
   :: YesodAuth m
diff --git a/src/Yesod/Auth/OAuth2/Auth0.hs b/src/Yesod/Auth/OAuth2/Auth0.hs
--- a/src/Yesod/Auth/OAuth2/Auth0.hs
+++ b/src/Yesod/Auth/OAuth2/Auth0.hs
@@ -52,7 +52,7 @@
   oauth2 =
     OAuth2
       { oauth2ClientId = clientId
-      , oauth2ClientSecret = Just clientSecret
+      , oauth2ClientSecret = clientSecret
       , oauth2AuthorizeEndpoint =
           host `withPath` "/authorize" `withQuery` [scopeParam " " scopes]
       , oauth2TokenEndpoint = host `withPath` "/oauth/token"
diff --git a/src/Yesod/Auth/OAuth2/AzureAD.hs b/src/Yesod/Auth/OAuth2/AzureAD.hs
--- a/src/Yesod/Auth/OAuth2/AzureAD.hs
+++ b/src/Yesod/Auth/OAuth2/AzureAD.hs
@@ -48,7 +48,7 @@
   oauth2 =
     OAuth2
       { oauth2ClientId = clientId
-      , oauth2ClientSecret = Just clientSecret
+      , oauth2ClientSecret = clientSecret
       , oauth2AuthorizeEndpoint =
           "https://login.windows.net/common/oauth2/authorize"
             `withQuery` [ scopeParam "," scopes
diff --git a/src/Yesod/Auth/OAuth2/AzureADv2.hs b/src/Yesod/Auth/OAuth2/AzureADv2.hs
--- a/src/Yesod/Auth/OAuth2/AzureADv2.hs
+++ b/src/Yesod/Auth/OAuth2/AzureADv2.hs
@@ -89,7 +89,7 @@
   oauth2 =
     OAuth2
       { oauth2ClientId = clientId
-      , oauth2ClientSecret = Just clientSecret
+      , oauth2ClientSecret = clientSecret
       , oauth2AuthorizeEndpoint =
           tenantUrl "/authorize" `withQuery` [scopeParam " " scopes]
       , oauth2TokenEndpoint = tenantUrl "/token"
diff --git a/src/Yesod/Auth/OAuth2/BattleNet.hs b/src/Yesod/Auth/OAuth2/BattleNet.hs
--- a/src/Yesod/Auth/OAuth2/BattleNet.hs
+++ b/src/Yesod/Auth/OAuth2/BattleNet.hs
@@ -53,7 +53,7 @@
   oauth2 =
     OAuth2
       { oauth2ClientId = clientId
-      , oauth2ClientSecret = Just clientSecret
+      , oauth2ClientSecret = clientSecret
       , oauth2AuthorizeEndpoint = fromRelative "https" host "/oauth/authorize"
       , oauth2TokenEndpoint = fromRelative "https" host "/oauth/token"
       , oauth2RedirectUri = Nothing
diff --git a/src/Yesod/Auth/OAuth2/Bitbucket.hs b/src/Yesod/Auth/OAuth2/Bitbucket.hs
--- a/src/Yesod/Auth/OAuth2/Bitbucket.hs
+++ b/src/Yesod/Auth/OAuth2/Bitbucket.hs
@@ -55,7 +55,7 @@
   oauth2 =
     OAuth2
       { oauth2ClientId = clientId
-      , oauth2ClientSecret = Just clientSecret
+      , oauth2ClientSecret = clientSecret
       , oauth2AuthorizeEndpoint =
           "https://bitbucket.com/site/oauth2/authorize"
             `withQuery` [scopeParam "," scopes]
diff --git a/src/Yesod/Auth/OAuth2/ClassLink.hs b/src/Yesod/Auth/OAuth2/ClassLink.hs
--- a/src/Yesod/Auth/OAuth2/ClassLink.hs
+++ b/src/Yesod/Auth/OAuth2/ClassLink.hs
@@ -43,7 +43,7 @@
   oauth2 =
     OAuth2
       { oauth2ClientId = clientId
-      , oauth2ClientSecret = Just clientSecret
+      , oauth2ClientSecret = clientSecret
       , oauth2AuthorizeEndpoint =
           "https://launchpad.classlink.com/oauth2/v2/auth"
             `withQuery` [scopeParam "," scopes]
diff --git a/src/Yesod/Auth/OAuth2/Dispatch.hs b/src/Yesod/Auth/OAuth2/Dispatch.hs
--- a/src/Yesod/Auth/OAuth2/Dispatch.hs
+++ b/src/Yesod/Auth/OAuth2/Dispatch.hs
@@ -6,8 +6,8 @@
 
 module Yesod.Auth.OAuth2.Dispatch
   ( FetchToken
-  , fetchAccessToken
-  , fetchAccessToken2
+  , fetchAccessTokenBasic
+  , fetchAccessTokenPost
   , FetchCreds
   , dispatchAuthRequest
   ) where
@@ -31,10 +31,13 @@
 --
 -- This will be 'fetchAccessToken' or 'fetchAccessToken2'
 type FetchToken =
-  Manager -> OAuth2 -> ExchangeToken -> IO (OAuth2Result Errors OAuth2Token)
+  Manager
+  -> OAuth2
+  -> ExchangeToken
+  -> IO (Either TokenResponseError TokenResponse)
 
 -- | How to take an @'OAuth2Token'@ and retrieve user credentials
-type FetchCreds m = Manager -> OAuth2Token -> IO (Creds m)
+type FetchCreds m = Manager -> TokenResponse -> IO (Creds m)
 
 -- | Dispatch the various OAuth2 handshake routes
 dispatchAuthRequest
diff --git a/src/Yesod/Auth/OAuth2/DispatchError.hs b/src/Yesod/Auth/OAuth2/DispatchError.hs
--- a/src/Yesod/Auth/OAuth2/DispatchError.hs
+++ b/src/Yesod/Auth/OAuth2/DispatchError.hs
@@ -16,7 +16,7 @@
 
 import Control.Monad.Except
 import Data.Text (Text, pack)
-import Network.OAuth.OAuth2.Compat (Errors)
+import Network.OAuth.OAuth2.Compat (TokenResponseError)
 import UnliftIO.Except ()
 import UnliftIO.Exception
 import Yesod.Auth hiding (ServerError)
@@ -30,7 +30,7 @@
   | InvalidStateToken (Maybe Text) Text
   | InvalidCallbackUri Text
   | OAuth2HandshakeError ErrorResponse
-  | OAuth2ResultError Errors
+  | OAuth2ResultError TokenResponseError
   | FetchCredsIOException IOException
   | FetchCredsYesodOAuth2Exception YesodOAuth2Exception
   | OtherDispatchError Text
diff --git a/src/Yesod/Auth/OAuth2/EveOnline.hs b/src/Yesod/Auth/OAuth2/EveOnline.hs
--- a/src/Yesod/Auth/OAuth2/EveOnline.hs
+++ b/src/Yesod/Auth/OAuth2/EveOnline.hs
@@ -76,7 +76,7 @@
   oauth2 =
     OAuth2
       { oauth2ClientId = clientId
-      , oauth2ClientSecret = Just clientSecret
+      , oauth2ClientSecret = clientSecret
       , oauth2AuthorizeEndpoint =
           "https://login.eveonline.com/oauth/authorize"
             `withQuery` [("response_type", "code"), scopeParam " " scopes]
diff --git a/src/Yesod/Auth/OAuth2/Exception.hs b/src/Yesod/Auth/OAuth2/Exception.hs
--- a/src/Yesod/Auth/OAuth2/Exception.hs
+++ b/src/Yesod/Auth/OAuth2/Exception.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE DeriveDataTypeable #-}
-
 module Yesod.Auth.OAuth2.Exception
   ( YesodOAuth2Exception (..)
   ) where
@@ -21,6 +19,6 @@
     --
     -- Plugin name and error message.
     GenericError Text String
-  deriving (Show, Typeable)
+  deriving (Show)
 
 instance Exception YesodOAuth2Exception
diff --git a/src/Yesod/Auth/OAuth2/GitHub.hs b/src/Yesod/Auth/OAuth2/GitHub.hs
--- a/src/Yesod/Auth/OAuth2/GitHub.hs
+++ b/src/Yesod/Auth/OAuth2/GitHub.hs
@@ -61,7 +61,7 @@
   oauth2 =
     OAuth2
       { oauth2ClientId = clientId
-      , oauth2ClientSecret = Just clientSecret
+      , oauth2ClientSecret = clientSecret
       , oauth2AuthorizeEndpoint =
           "https://github.com/login/oauth/authorize"
             `withQuery` [scopeParam "," scopes]
diff --git a/src/Yesod/Auth/OAuth2/GitLab.hs b/src/Yesod/Auth/OAuth2/GitLab.hs
--- a/src/Yesod/Auth/OAuth2/GitLab.hs
+++ b/src/Yesod/Auth/OAuth2/GitLab.hs
@@ -53,7 +53,7 @@
   oauth2 =
     OAuth2
       { oauth2ClientId = clientId
-      , oauth2ClientSecret = Just clientSecret
+      , oauth2ClientSecret = clientSecret
       , oauth2AuthorizeEndpoint =
           host `withPath` "/oauth/authorize" `withQuery` [scopeParam " " scopes]
       , oauth2TokenEndpoint = host `withPath` "/oauth/token"
diff --git a/src/Yesod/Auth/OAuth2/Google.hs b/src/Yesod/Auth/OAuth2/Google.hs
--- a/src/Yesod/Auth/OAuth2/Google.hs
+++ b/src/Yesod/Auth/OAuth2/Google.hs
@@ -80,7 +80,7 @@
   oauth2 =
     OAuth2
       { oauth2ClientId = clientId
-      , oauth2ClientSecret = Just clientSecret
+      , oauth2ClientSecret = clientSecret
       , oauth2AuthorizeEndpoint =
           "https://accounts.google.com/o/oauth2/auth"
             `withQuery` [scopeParam " " scopes]
diff --git a/src/Yesod/Auth/OAuth2/Nylas.hs b/src/Yesod/Auth/OAuth2/Nylas.hs
--- a/src/Yesod/Auth/OAuth2/Nylas.hs
+++ b/src/Yesod/Auth/OAuth2/Nylas.hs
@@ -55,7 +55,7 @@
   oauth =
     OAuth2
       { oauth2ClientId = clientId
-      , oauth2ClientSecret = Just clientSecret
+      , oauth2ClientSecret = clientSecret
       , oauth2AuthorizeEndpoint =
           "https://api.nylas.com/oauth/authorize"
             `withQuery` [ ("response_type", "code")
diff --git a/src/Yesod/Auth/OAuth2/ORCID.hs b/src/Yesod/Auth/OAuth2/ORCID.hs
--- a/src/Yesod/Auth/OAuth2/ORCID.hs
+++ b/src/Yesod/Auth/OAuth2/ORCID.hs
@@ -41,7 +41,7 @@
   oauth2 =
     OAuth2
       { oauth2ClientId = clientId
-      , oauth2ClientSecret = Just clientSecret
+      , oauth2ClientSecret = clientSecret
       , oauth2AuthorizeEndpoint =
           "https://orcid.org/oauth/authorize"
             `withQuery` [scopeParam " " ["openid"]]
diff --git a/src/Yesod/Auth/OAuth2/Prelude.hs b/src/Yesod/Auth/OAuth2/Prelude.hs
--- a/src/Yesod/Auth/OAuth2/Prelude.hs
+++ b/src/Yesod/Auth/OAuth2/Prelude.hs
@@ -30,7 +30,12 @@
 
     -- * OAuth2
   , OAuth2 (..)
-  , OAuth2Token (..)
+  , TokenResponse
+  , accessToken
+  , refreshToken
+  , expiresIn
+  , tokenType
+  , idToken
   , AccessToken (..)
   , RefreshToken (..)
 
@@ -78,7 +83,7 @@
   :: FromJSON a
   => Text
   -> Manager
-  -> OAuth2Token
+  -> TokenResponse
   -> URI
   -> IO (a, BL.ByteString)
 authGetProfile name manager token url = do
@@ -114,7 +119,7 @@
 -- May set the following keys:
 --
 -- - @refreshToken@: if the provider supports refreshing the @accessToken@
-setExtra :: OAuth2Token -> BL.ByteString -> [(Text, Text)]
+setExtra :: TokenResponse -> BL.ByteString -> [(Text, Text)]
 setExtra token userResponse =
   [ ("accessToken", atoken $ accessToken token)
   , ("userResponse", decodeUtf8 $ BL.toStrict userResponse)
diff --git a/src/Yesod/Auth/OAuth2/Salesforce.hs b/src/Yesod/Auth/OAuth2/Salesforce.hs
--- a/src/Yesod/Auth/OAuth2/Salesforce.hs
+++ b/src/Yesod/Auth/OAuth2/Salesforce.hs
@@ -76,7 +76,7 @@
   oauth2 =
     OAuth2
       { oauth2ClientId = clientId
-      , oauth2ClientSecret = Just clientSecret
+      , oauth2ClientSecret = clientSecret
       , oauth2AuthorizeEndpoint = authorizeUri `withQuery` [scopeParam " " scopes]
       , oauth2TokenEndpoint = tokenUri
       , oauth2RedirectUri = Nothing
diff --git a/src/Yesod/Auth/OAuth2/Slack.hs b/src/Yesod/Auth/OAuth2/Slack.hs
--- a/src/Yesod/Auth/OAuth2/Slack.hs
+++ b/src/Yesod/Auth/OAuth2/Slack.hs
@@ -74,7 +74,7 @@
   oauth2 =
     OAuth2
       { oauth2ClientId = clientId
-      , oauth2ClientSecret = Just clientSecret
+      , oauth2ClientSecret = clientSecret
       , oauth2AuthorizeEndpoint =
           "https://slack.com/oauth/authorize"
             `withQuery` [scopeParam "," $ map scopeText scopes]
diff --git a/src/Yesod/Auth/OAuth2/Spotify.hs b/src/Yesod/Auth/OAuth2/Spotify.hs
--- a/src/Yesod/Auth/OAuth2/Spotify.hs
+++ b/src/Yesod/Auth/OAuth2/Spotify.hs
@@ -37,7 +37,7 @@
   oauth2 =
     OAuth2
       { oauth2ClientId = clientId
-      , oauth2ClientSecret = Just clientSecret
+      , oauth2ClientSecret = clientSecret
       , oauth2AuthorizeEndpoint =
           "https://accounts.spotify.com/authorize"
             `withQuery` [scopeParam " " scopes]
diff --git a/src/Yesod/Auth/OAuth2/Twitch.hs b/src/Yesod/Auth/OAuth2/Twitch.hs
--- a/src/Yesod/Auth/OAuth2/Twitch.hs
+++ b/src/Yesod/Auth/OAuth2/Twitch.hs
@@ -49,7 +49,7 @@
   oauth2 =
     OAuth2
       { oauth2ClientId = clientId
-      , oauth2ClientSecret = Just clientSecret
+      , oauth2ClientSecret = clientSecret
       , oauth2AuthorizeEndpoint =
           "https://id.twitch.tv/oauth2/authorize"
             `withQuery` [scopeParam " " scopes]
diff --git a/src/Yesod/Auth/OAuth2/Upcase.hs b/src/Yesod/Auth/OAuth2/Upcase.hs
--- a/src/Yesod/Auth/OAuth2/Upcase.hs
+++ b/src/Yesod/Auth/OAuth2/Upcase.hs
@@ -44,7 +44,7 @@
   oauth2 =
     OAuth2
       { oauth2ClientId = clientId
-      , oauth2ClientSecret = Just clientSecret
+      , oauth2ClientSecret = clientSecret
       , oauth2AuthorizeEndpoint = "http://upcase.com/oauth/authorize"
       , oauth2TokenEndpoint = "http://upcase.com/oauth/token"
       , oauth2RedirectUri = Nothing
diff --git a/src/Yesod/Auth/OAuth2/WordPressDotCom.hs b/src/Yesod/Auth/OAuth2/WordPressDotCom.hs
--- a/src/Yesod/Auth/OAuth2/WordPressDotCom.hs
+++ b/src/Yesod/Auth/OAuth2/WordPressDotCom.hs
@@ -41,7 +41,7 @@
   oauth2 =
     OAuth2
       { oauth2ClientId = clientId
-      , oauth2ClientSecret = Just clientSecret
+      , oauth2ClientSecret = clientSecret
       , oauth2AuthorizeEndpoint =
           "https://public-api.wordpress.com/oauth2/authorize"
             `withQuery` [scopeParam "," ["auth"]]
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.18
 name:            yesod-auth-oauth2
-version:         0.7.4.0
+version:         0.8.0.0
 license:         MIT
 license-file:    LICENSE
 maintainer:      engineering@freckle.com
@@ -68,20 +68,20 @@
         aeson >=0.6,
         base >=4.9.0.0 && <5,
         bytestring >=0.9.1.4,
-        crypton >=1.0.0,
+        crypton >=0.32,
         errors >=2.3.0,
-        hoauth2 >=1.11.0,
+        hoauth2 >=2.8.0,
         http-client >=0.4.0,
         http-conduit >=2.0,
         http-types >=0.8,
-        memory >=0.15.0,
-        microlens >=0.4.11.2,
+        memory >=0.18.0,
+        microlens >=0.4.13.1,
         mtl >=2.2.2,
-        safe-exceptions >=0.1.7.1,
+        safe-exceptions >=0.1.7.4,
         text >=0.7,
         transformers >=0.5.6.2,
-        unliftio >=0.2.13.1,
-        uri-bytestring >=0.3.3.0,
+        unliftio >=0.2.25.0,
+        uri-bytestring >=0.3.3.1,
         yesod-auth >=1.6.0,
         yesod-core >=1.6.0
 
@@ -93,15 +93,15 @@
     ghc-options:      -Wall -threaded -rtsopts -with-rtsopts=-N
     build-depends:
         aeson >=0.6,
-        aeson-pretty >=0.8.8,
+        aeson-pretty >=0.8.9,
         base >=4.9.0.0 && <5,
         bytestring >=0.9.1.4,
         containers >=0.6.0.1,
         http-conduit >=2.0,
         load-env >=0.2.1.0,
         text >=0.7,
-        warp >=3.3.13,
-        yesod >=1.6.1.0,
+        warp >=3.3.25,
+        yesod >=1.6.2.1,
         yesod-auth >=1.6.0,
         yesod-auth-oauth2
 
@@ -120,6 +120,6 @@
     ghc-options:      -Wall
     build-depends:
         base >=4.9.0.0 && <5,
-        hspec >=2.7.6,
-        uri-bytestring >=0.3.3.0,
+        hspec >=2.10.10,
+        uri-bytestring >=0.3.3.1,
         yesod-auth-oauth2
