Change interface to support newer hoauth2

This adds support for `ghc-9.12` / `hoauth2-2.15` and drops support for
`ghc < 9.4` / `hoauth2 < 2.8`.

Since this would be a major version bump no matter what, I've changed
the interface we present to align with `hoauth2-2.15`. This means using
the newer `fetch` functions, and `TokenResponse{,Error}` type names.

I've maintained our own `OAuth2` type so that the redirect-uri can
remain a `Maybe` field. The way plugins are constructed, we need to
build an `OAuth2` value in a pure context without one, which is then
supplied later, when we have `MonadHandler` and so can render URLs.
This commit is contained in:
patrick brisbin 2026-01-05 15:01:16 -05:00 committed by Pat Brisbin
parent a179049522
commit a8de561848
52 changed files with 134 additions and 448 deletions

2
.stack-all Normal file
View File

@ -0,0 +1,2 @@
[versions]
oldest = lts-21

View File

@ -29,7 +29,7 @@ library:
- bytestring >=0.9.1.4 - bytestring >=0.9.1.4
- crypton - crypton
- errors - errors
- hoauth2 >=1.11.0 - hoauth2 >=2.8.0 # TokenRequestError
- http-client >=0.4.0 - http-client >=0.4.0
- http-conduit >=2.0 - http-conduit >=2.0
- http-types >=0.8 - http-types >=0.8

View File

@ -2,170 +2,117 @@
module Network.OAuth.OAuth2.Compat module Network.OAuth.OAuth2.Compat
( OAuth2 (..) ( OAuth2 (..)
, OAuth2Result
, Errors
, authorizationUrl , authorizationUrl
, fetchAccessToken , fetchAccessTokenBasic
, fetchAccessToken2 , fetchAccessTokenPost
, authGetBS , authGetBS
-- * Re-exports -- * Re-exports
, module Network.OAuth.OAuth2 , AccessToken (..)
, ExchangeToken (..)
, RefreshToken (..)
, TokenResponse
, accessToken
, refreshToken
, expiresIn
, tokenType
, idToken
, TokenResponseError
) where ) where
import Control.Monad.IO.Class (MonadIO)
import Control.Monad.Trans.Except (runExceptT)
import Data.ByteString.Lazy (ByteString) import Data.ByteString.Lazy (ByteString)
import Data.Text (Text) import Data.Text (Text)
import Network.HTTP.Conduit (Manager) 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 import Network.OAuth.OAuth2
( AccessToken (..) ( AccessToken (..)
, ExchangeToken (..) , ExchangeToken (..)
, OAuth2Token (..)
, RefreshToken (..) , RefreshToken (..)
, OAuth2Token (..)
, TokenResponseError
) )
import qualified Network.OAuth.OAuth2 as OAuth2 import qualified Network.OAuth.OAuth2 as OAuth2
import URI.ByteString
#if MIN_VERSION_hoauth2(2,2,0) type TokenResponse = OAuth2Token
import Control.Monad.Trans.Except (ExceptT, runExceptT)
import Data.Maybe (fromMaybe)
#endif
#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 #else
import qualified Network.OAuth.OAuth2.TokenRequest as LegacyTokenRequest -- hoauth2-2.8
import Network.OAuth.OAuth2 (OAuth2Error) import Network.OAuth.OAuth2
type Errors = OAuth2Error LegacyTokenRequest.Errors ( AccessToken (..)
#endif , 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 data OAuth2 = OAuth2
{ oauth2ClientId :: Text { oauth2ClientId :: Text
, oauth2ClientSecret :: Maybe Text , oauth2ClientSecret :: Text
, oauth2AuthorizeEndpoint :: URIRef Absolute , oauth2AuthorizeEndpoint :: URIRef Absolute
, oauth2TokenEndpoint :: URIRef Absolute , oauth2TokenEndpoint :: URIRef Absolute
, oauth2RedirectUri :: Maybe (URIRef Absolute) , oauth2RedirectUri :: Maybe (URIRef Absolute)
} }
type OAuth2Result err a = Either err a
authorizationUrl :: OAuth2 -> URI authorizationUrl :: OAuth2 -> URI
authorizationUrl = OAuth2.authorizationUrl . getOAuth2 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 = getClientSecret $ oauth2ClientSecret o
, OAuth2.oauthOAuthorizeEndpoint = oauth2AuthorizeEndpoint o
, OAuth2.oauthAccessTokenEndpoint = oauth2TokenEndpoint 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
-- 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 fetchAccessTokenBasic
:: Manager :: Manager
-> OAuth2 -> OAuth2
-> ExchangeToken -> ExchangeToken
-> IO (OAuth2Result Errors OAuth2Token) -> IO (Either TokenResponseError TokenResponse)
fetchAccessTokenBasic m o e = runOAuth2 $ f m (getOAuth2 o) e fetchAccessTokenBasic =
where runFetchAccessToken OAuth2.ClientSecretBasic
#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 fetchAccessTokenPost
:: Manager :: Manager
-> OAuth2 -> OAuth2
-> ExchangeToken -> ExchangeToken
-> IO (OAuth2Result Errors OAuth2Token) -> IO (Either TokenResponseError TokenResponse)
fetchAccessTokenPost m o e = runOAuth2 $ f m (getOAuth2 o) e fetchAccessTokenPost =
where runFetchAccessToken OAuth2.ClientSecretPost
#if MIN_VERSION_hoauth2(2, 6, 0)
f = OAuth2.fetchAccessTokenWithAuthMethod OAuth2.ClientSecretPost authGetBS :: Manager -> AccessToken -> URI -> IO (Either ByteString ByteString)
#elif MIN_VERSION_hoauth2(2,3,0) authGetBS m a u = runExceptT $ OAuth2.authGetBS m a u
f = OAuth2.fetchAccessTokenInternal OAuth2.ClientSecretPost
#else getOAuth2 :: OAuth2 -> OAuth2.OAuth2
f = OAuth2.fetchAccessToken2 getOAuth2 o =
#endif OAuth2.OAuth2
{ OAuth2.oauth2ClientId = oauth2ClientId o
, OAuth2.oauth2ClientSecret = oauth2ClientSecret o
, OAuth2.oauth2AuthorizeEndpoint = oauth2AuthorizeEndpoint o
, OAuth2.oauth2TokenEndpoint = oauth2TokenEndpoint 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
}
runFetchAccessToken
:: MonadIO m
=> OAuth2.ClientAuthenticationMethod
-> Manager
-> OAuth2
-> ExchangeToken
-> m (Either TokenResponseError TokenResponse)
runFetchAccessToken am m o e = runExceptT $ OAuth2.fetchAccessTokenWithAuthMethod am m (getOAuth2 o) e

View File

@ -11,13 +11,13 @@ module Yesod.Auth.OAuth2
( OAuth2 (..) ( OAuth2 (..)
, FetchCreds , FetchCreds
, Manager , Manager
, OAuth2Token (..) , TokenResponse
, Creds (..) , Creds (..)
, oauth2Url , oauth2Url
, authOAuth2 , authOAuth2
, authOAuth2Widget , authOAuth2Widget
-- * Alternatives that use 'fetchAccessToken2' -- * Alternatives that use 'fetchAccessTokenPost'
, authOAuth2' , authOAuth2'
, authOAuth2Widget' , authOAuth2Widget'
@ -49,7 +49,7 @@ oauth2Url name = PluginR name ["forward"]
authOAuth2 :: YesodAuth m => Text -> OAuth2 -> FetchCreds m -> AuthPlugin m authOAuth2 :: YesodAuth m => Text -> OAuth2 -> FetchCreds m -> AuthPlugin m
authOAuth2 name = authOAuth2Widget [whamlet|Login via #{name}|] name 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> -- See <https://github.com/thoughtbot/yesod-auth-oauth2/pull/129>
authOAuth2' :: YesodAuth m => Text -> OAuth2 -> FetchCreds m -> AuthPlugin m authOAuth2' :: YesodAuth m => Text -> OAuth2 -> FetchCreds m -> AuthPlugin m
@ -66,9 +66,9 @@ authOAuth2Widget
-> OAuth2 -> OAuth2
-> FetchCreds m -> FetchCreds m
-> AuthPlugin 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> -- See <https://github.com/thoughtbot/yesod-auth-oauth2/pull/129>
authOAuth2Widget' authOAuth2Widget'
@ -78,7 +78,7 @@ authOAuth2Widget'
-> OAuth2 -> OAuth2
-> FetchCreds m -> FetchCreds m
-> AuthPlugin m -> AuthPlugin m
authOAuth2Widget' = buildPlugin fetchAccessToken2 authOAuth2Widget' = buildPlugin fetchAccessTokenPost
buildPlugin buildPlugin
:: YesodAuth m :: YesodAuth m

View File

@ -52,7 +52,7 @@ oauth2Auth0HostScopes host scopes clientId clientSecret =
oauth2 = oauth2 =
OAuth2 OAuth2
{ oauth2ClientId = clientId { oauth2ClientId = clientId
, oauth2ClientSecret = Just clientSecret , oauth2ClientSecret = clientSecret
, oauth2AuthorizeEndpoint = , oauth2AuthorizeEndpoint =
host `withPath` "/authorize" `withQuery` [scopeParam " " scopes] host `withPath` "/authorize" `withQuery` [scopeParam " " scopes]
, oauth2TokenEndpoint = host `withPath` "/oauth/token" , oauth2TokenEndpoint = host `withPath` "/oauth/token"

View File

@ -48,7 +48,7 @@ oauth2AzureADScoped scopes clientId clientSecret =
oauth2 = oauth2 =
OAuth2 OAuth2
{ oauth2ClientId = clientId { oauth2ClientId = clientId
, oauth2ClientSecret = Just clientSecret , oauth2ClientSecret = clientSecret
, oauth2AuthorizeEndpoint = , oauth2AuthorizeEndpoint =
"https://login.windows.net/common/oauth2/authorize" "https://login.windows.net/common/oauth2/authorize"
`withQuery` [ scopeParam "," scopes `withQuery` [ scopeParam "," scopes

View File

@ -89,7 +89,7 @@ oauth2AzureADv2ScopedWidget widget scopes tenantId clientId clientSecret =
oauth2 = oauth2 =
OAuth2 OAuth2
{ oauth2ClientId = clientId { oauth2ClientId = clientId
, oauth2ClientSecret = Just clientSecret , oauth2ClientSecret = clientSecret
, oauth2AuthorizeEndpoint = , oauth2AuthorizeEndpoint =
tenantUrl "/authorize" `withQuery` [scopeParam " " scopes] tenantUrl "/authorize" `withQuery` [scopeParam " " scopes]
, oauth2TokenEndpoint = tenantUrl "/token" , oauth2TokenEndpoint = tenantUrl "/token"

View File

@ -53,7 +53,7 @@ oauth2BattleNet widget region clientId clientSecret =
oauth2 = oauth2 =
OAuth2 OAuth2
{ oauth2ClientId = clientId { oauth2ClientId = clientId
, oauth2ClientSecret = Just clientSecret , oauth2ClientSecret = clientSecret
, oauth2AuthorizeEndpoint = fromRelative "https" host "/oauth/authorize" , oauth2AuthorizeEndpoint = fromRelative "https" host "/oauth/authorize"
, oauth2TokenEndpoint = fromRelative "https" host "/oauth/token" , oauth2TokenEndpoint = fromRelative "https" host "/oauth/token"
, oauth2RedirectUri = Nothing , oauth2RedirectUri = Nothing

View File

@ -55,7 +55,7 @@ oauth2BitbucketScoped scopes clientId clientSecret =
oauth2 = oauth2 =
OAuth2 OAuth2
{ oauth2ClientId = clientId { oauth2ClientId = clientId
, oauth2ClientSecret = Just clientSecret , oauth2ClientSecret = clientSecret
, oauth2AuthorizeEndpoint = , oauth2AuthorizeEndpoint =
"https://bitbucket.com/site/oauth2/authorize" "https://bitbucket.com/site/oauth2/authorize"
`withQuery` [scopeParam "," scopes] `withQuery` [scopeParam "," scopes]

View File

@ -43,7 +43,7 @@ oauth2ClassLinkScoped scopes clientId clientSecret =
oauth2 = oauth2 =
OAuth2 OAuth2
{ oauth2ClientId = clientId { oauth2ClientId = clientId
, oauth2ClientSecret = Just clientSecret , oauth2ClientSecret = clientSecret
, oauth2AuthorizeEndpoint = , oauth2AuthorizeEndpoint =
"https://launchpad.classlink.com/oauth2/v2/auth" "https://launchpad.classlink.com/oauth2/v2/auth"
`withQuery` [scopeParam "," scopes] `withQuery` [scopeParam "," scopes]

View File

@ -6,8 +6,8 @@
module Yesod.Auth.OAuth2.Dispatch module Yesod.Auth.OAuth2.Dispatch
( FetchToken ( FetchToken
, fetchAccessToken , fetchAccessTokenBasic
, fetchAccessToken2 , fetchAccessTokenPost
, FetchCreds , FetchCreds
, dispatchAuthRequest , dispatchAuthRequest
) where ) where
@ -31,10 +31,13 @@ import Yesod.Core hiding (ErrorResponse)
-- --
-- This will be 'fetchAccessToken' or 'fetchAccessToken2' -- This will be 'fetchAccessToken' or 'fetchAccessToken2'
type FetchToken = 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 -- | 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 -- | Dispatch the various OAuth2 handshake routes
dispatchAuthRequest dispatchAuthRequest

View File

@ -16,7 +16,7 @@ module Yesod.Auth.OAuth2.DispatchError
import Control.Monad.Except import Control.Monad.Except
import Data.Text (Text, pack) import Data.Text (Text, pack)
import Network.OAuth.OAuth2.Compat (Errors) import Network.OAuth.OAuth2.Compat (TokenResponseError)
import UnliftIO.Except () import UnliftIO.Except ()
import UnliftIO.Exception import UnliftIO.Exception
import Yesod.Auth hiding (ServerError) import Yesod.Auth hiding (ServerError)
@ -30,7 +30,7 @@ data DispatchError
| InvalidStateToken (Maybe Text) Text | InvalidStateToken (Maybe Text) Text
| InvalidCallbackUri Text | InvalidCallbackUri Text
| OAuth2HandshakeError ErrorResponse | OAuth2HandshakeError ErrorResponse
| OAuth2ResultError Errors | OAuth2ResultError TokenResponseError
| FetchCredsIOException IOException | FetchCredsIOException IOException
| FetchCredsYesodOAuth2Exception YesodOAuth2Exception | FetchCredsYesodOAuth2Exception YesodOAuth2Exception
| OtherDispatchError Text | OtherDispatchError Text

View File

@ -76,7 +76,7 @@ oauth2EveScoped scopes widgetType clientId clientSecret =
oauth2 = oauth2 =
OAuth2 OAuth2
{ oauth2ClientId = clientId { oauth2ClientId = clientId
, oauth2ClientSecret = Just clientSecret , oauth2ClientSecret = clientSecret
, oauth2AuthorizeEndpoint = , oauth2AuthorizeEndpoint =
"https://login.eveonline.com/oauth/authorize" "https://login.eveonline.com/oauth/authorize"
`withQuery` [("response_type", "code"), scopeParam " " scopes] `withQuery` [("response_type", "code"), scopeParam " " scopes]

View File

@ -1,5 +1,3 @@
{-# LANGUAGE DeriveDataTypeable #-}
module Yesod.Auth.OAuth2.Exception module Yesod.Auth.OAuth2.Exception
( YesodOAuth2Exception (..) ( YesodOAuth2Exception (..)
) where ) where
@ -21,6 +19,6 @@ data YesodOAuth2Exception
-- --
-- Plugin name and error message. -- Plugin name and error message.
GenericError Text String GenericError Text String
deriving (Show, Typeable) deriving (Show)
instance Exception YesodOAuth2Exception instance Exception YesodOAuth2Exception

View File

@ -61,7 +61,7 @@ oauth2GitHubScopedWidget widget scopes clientId clientSecret =
oauth2 = oauth2 =
OAuth2 OAuth2
{ oauth2ClientId = clientId { oauth2ClientId = clientId
, oauth2ClientSecret = Just clientSecret , oauth2ClientSecret = clientSecret
, oauth2AuthorizeEndpoint = , oauth2AuthorizeEndpoint =
"https://github.com/login/oauth/authorize" "https://github.com/login/oauth/authorize"
`withQuery` [scopeParam "," scopes] `withQuery` [scopeParam "," scopes]

View File

@ -53,7 +53,7 @@ oauth2GitLabHostScopes host scopes clientId clientSecret =
oauth2 = oauth2 =
OAuth2 OAuth2
{ oauth2ClientId = clientId { oauth2ClientId = clientId
, oauth2ClientSecret = Just clientSecret , oauth2ClientSecret = clientSecret
, oauth2AuthorizeEndpoint = , oauth2AuthorizeEndpoint =
host `withPath` "/oauth/authorize" `withQuery` [scopeParam " " scopes] host `withPath` "/oauth/authorize" `withQuery` [scopeParam " " scopes]
, oauth2TokenEndpoint = host `withPath` "/oauth/token" , oauth2TokenEndpoint = host `withPath` "/oauth/token"

View File

@ -80,7 +80,7 @@ oauth2GoogleScopedWidget widget scopes clientId clientSecret =
oauth2 = oauth2 =
OAuth2 OAuth2
{ oauth2ClientId = clientId { oauth2ClientId = clientId
, oauth2ClientSecret = Just clientSecret , oauth2ClientSecret = clientSecret
, oauth2AuthorizeEndpoint = , oauth2AuthorizeEndpoint =
"https://accounts.google.com/o/oauth2/auth" "https://accounts.google.com/o/oauth2/auth"
`withQuery` [scopeParam " " scopes] `withQuery` [scopeParam " " scopes]

View File

@ -55,7 +55,7 @@ oauth2Nylas clientId clientSecret =
oauth = oauth =
OAuth2 OAuth2
{ oauth2ClientId = clientId { oauth2ClientId = clientId
, oauth2ClientSecret = Just clientSecret , oauth2ClientSecret = clientSecret
, oauth2AuthorizeEndpoint = , oauth2AuthorizeEndpoint =
"https://api.nylas.com/oauth/authorize" "https://api.nylas.com/oauth/authorize"
`withQuery` [ ("response_type", "code") `withQuery` [ ("response_type", "code")

View File

@ -41,7 +41,7 @@ oauth2ORCID clientId clientSecret =
oauth2 = oauth2 =
OAuth2 OAuth2
{ oauth2ClientId = clientId { oauth2ClientId = clientId
, oauth2ClientSecret = Just clientSecret , oauth2ClientSecret = clientSecret
, oauth2AuthorizeEndpoint = , oauth2AuthorizeEndpoint =
"https://orcid.org/oauth/authorize" "https://orcid.org/oauth/authorize"
`withQuery` [scopeParam " " ["openid"]] `withQuery` [scopeParam " " ["openid"]]

View File

@ -30,7 +30,12 @@ module Yesod.Auth.OAuth2.Prelude
-- * OAuth2 -- * OAuth2
, OAuth2 (..) , OAuth2 (..)
, OAuth2Token (..) , TokenResponse
, accessToken
, refreshToken
, expiresIn
, tokenType
, idToken
, AccessToken (..) , AccessToken (..)
, RefreshToken (..) , RefreshToken (..)
@ -78,7 +83,7 @@ authGetProfile
:: FromJSON a :: FromJSON a
=> Text => Text
-> Manager -> Manager
-> OAuth2Token -> TokenResponse
-> URI -> URI
-> IO (a, BL.ByteString) -> IO (a, BL.ByteString)
authGetProfile name manager token url = do authGetProfile name manager token url = do
@ -114,7 +119,7 @@ scopeParam d = ("scope",) . encodeUtf8 . T.intercalate d
-- May set the following keys: -- May set the following keys:
-- --
-- - @refreshToken@: if the provider supports refreshing the @accessToken@ -- - @refreshToken@: if the provider supports refreshing the @accessToken@
setExtra :: OAuth2Token -> BL.ByteString -> [(Text, Text)] setExtra :: TokenResponse -> BL.ByteString -> [(Text, Text)]
setExtra token userResponse = setExtra token userResponse =
[ ("accessToken", atoken $ accessToken token) [ ("accessToken", atoken $ accessToken token)
, ("userResponse", decodeUtf8 $ BL.toStrict userResponse) , ("userResponse", decodeUtf8 $ BL.toStrict userResponse)

View File

@ -76,7 +76,7 @@ salesforceHelper name profileUri authorizeUri tokenUri scopes clientId clientSec
oauth2 = oauth2 =
OAuth2 OAuth2
{ oauth2ClientId = clientId { oauth2ClientId = clientId
, oauth2ClientSecret = Just clientSecret , oauth2ClientSecret = clientSecret
, oauth2AuthorizeEndpoint = authorizeUri `withQuery` [scopeParam " " scopes] , oauth2AuthorizeEndpoint = authorizeUri `withQuery` [scopeParam " " scopes]
, oauth2TokenEndpoint = tokenUri , oauth2TokenEndpoint = tokenUri
, oauth2RedirectUri = Nothing , oauth2RedirectUri = Nothing

View File

@ -74,7 +74,7 @@ oauth2SlackScoped scopes clientId clientSecret =
oauth2 = oauth2 =
OAuth2 OAuth2
{ oauth2ClientId = clientId { oauth2ClientId = clientId
, oauth2ClientSecret = Just clientSecret , oauth2ClientSecret = clientSecret
, oauth2AuthorizeEndpoint = , oauth2AuthorizeEndpoint =
"https://slack.com/oauth/authorize" "https://slack.com/oauth/authorize"
`withQuery` [scopeParam "," $ map scopeText scopes] `withQuery` [scopeParam "," $ map scopeText scopes]

View File

@ -37,7 +37,7 @@ oauth2Spotify scopes clientId clientSecret =
oauth2 = oauth2 =
OAuth2 OAuth2
{ oauth2ClientId = clientId { oauth2ClientId = clientId
, oauth2ClientSecret = Just clientSecret , oauth2ClientSecret = clientSecret
, oauth2AuthorizeEndpoint = , oauth2AuthorizeEndpoint =
"https://accounts.spotify.com/authorize" "https://accounts.spotify.com/authorize"
`withQuery` [scopeParam " " scopes] `withQuery` [scopeParam " " scopes]

View File

@ -49,7 +49,7 @@ oauth2TwitchScoped scopes clientId clientSecret =
oauth2 = oauth2 =
OAuth2 OAuth2
{ oauth2ClientId = clientId { oauth2ClientId = clientId
, oauth2ClientSecret = Just clientSecret , oauth2ClientSecret = clientSecret
, oauth2AuthorizeEndpoint = , oauth2AuthorizeEndpoint =
"https://id.twitch.tv/oauth2/authorize" "https://id.twitch.tv/oauth2/authorize"
`withQuery` [scopeParam " " scopes] `withQuery` [scopeParam " " scopes]

View File

@ -44,7 +44,7 @@ oauth2Upcase clientId clientSecret =
oauth2 = oauth2 =
OAuth2 OAuth2
{ oauth2ClientId = clientId { oauth2ClientId = clientId
, oauth2ClientSecret = Just clientSecret , oauth2ClientSecret = clientSecret
, oauth2AuthorizeEndpoint = "http://upcase.com/oauth/authorize" , oauth2AuthorizeEndpoint = "http://upcase.com/oauth/authorize"
, oauth2TokenEndpoint = "http://upcase.com/oauth/token" , oauth2TokenEndpoint = "http://upcase.com/oauth/token"
, oauth2RedirectUri = Nothing , oauth2RedirectUri = Nothing

View File

@ -41,7 +41,7 @@ oauth2WordPressDotCom clientId clientSecret =
oauth2 = oauth2 =
OAuth2 OAuth2
{ oauth2ClientId = clientId { oauth2ClientId = clientId
, oauth2ClientSecret = Just clientSecret , oauth2ClientSecret = clientSecret
, oauth2AuthorizeEndpoint = , oauth2AuthorizeEndpoint =
"https://public-api.wordpress.com/oauth2/authorize" "https://public-api.wordpress.com/oauth2/authorize"
`withQuery` [scopeParam "," ["auth"]] `withQuery` [scopeParam "," ["auth"]]

View File

@ -1,4 +0,0 @@
resolver: lts-18.28
extra-deps:
- crypton-1.0.0
- hoauth2-2.0.0

View File

@ -1,26 +0,0 @@
# This file was autogenerated by Stack.
# You should not edit this file by hand.
# For more information, please see the documentation at:
# https://docs.haskellstack.org/en/stable/lock_files
packages:
- completed:
hackage: crypton-1.0.0@sha256:637e58581978c84ef1288d14fa9cac1d2905ef60e319924293bc11250aca882d,14527
pantry-tree:
sha256: 4b5e5511567c0fe735a224cb8b2b278e1caa79344f2940d030d169e69b1b81e1
size: 23275
original:
hackage: crypton-1.0.0
- completed:
hackage: hoauth2-2.0.0@sha256:4686d776272d4c57d3c8dbeb9e58b04afe4d2b410382011bd78a3d2bfb08a3fe,5662
pantry-tree:
sha256: 291b3dd90854ef44f270519ec17e34b6778f8430f6d6517bd67b0128bd549553
size: 2171
original:
hackage: hoauth2-2.0.0
snapshots:
- completed:
sha256: 428ec8d5ce932190d3cbe266b9eb3c175cd81e984babf876b64019e2cbe4ea68
size: 590100
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/28.yaml
original: lts-18.28

View File

@ -1,4 +0,0 @@
resolver: nightly-2022-02-25
extra-deps:
- crypton-1.0.0
- hoauth2-2.2.0

View File

@ -1,26 +0,0 @@
# This file was autogenerated by Stack.
# You should not edit this file by hand.
# For more information, please see the documentation at:
# https://docs.haskellstack.org/en/stable/lock_files
packages:
- completed:
hackage: crypton-1.0.0@sha256:637e58581978c84ef1288d14fa9cac1d2905ef60e319924293bc11250aca882d,14527
pantry-tree:
sha256: 4b5e5511567c0fe735a224cb8b2b278e1caa79344f2940d030d169e69b1b81e1
size: 23275
original:
hackage: crypton-1.0.0
- completed:
hackage: hoauth2-2.2.0@sha256:83a96156717d9e2c93394b35bef4151f82b90dc88b83d0e35c0bf1158bd41c6c,2801
pantry-tree:
sha256: d6e2d12e0e66eb9392301ec97d50677afb71608568f3664eb466a4451c66ba59
size: 593
original:
hackage: hoauth2-2.2.0
snapshots:
- completed:
sha256: b18614ab8986a4ba6d469921a2c18decab244af78309effa3d2dab85dbdfef80
size: 611886
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2022/2/25.yaml
original: nightly-2022-02-25

View File

@ -1,4 +0,0 @@
resolver: nightly-2022-02-25
extra-deps:
- crypton-1.0.0
- hoauth2-2.3.0

View File

@ -1,26 +0,0 @@
# This file was autogenerated by Stack.
# You should not edit this file by hand.
# For more information, please see the documentation at:
# https://docs.haskellstack.org/en/stable/lock_files
packages:
- completed:
hackage: crypton-1.0.0@sha256:637e58581978c84ef1288d14fa9cac1d2905ef60e319924293bc11250aca882d,14527
pantry-tree:
sha256: 4b5e5511567c0fe735a224cb8b2b278e1caa79344f2940d030d169e69b1b81e1
size: 23275
original:
hackage: crypton-1.0.0
- completed:
hackage: hoauth2-2.3.0@sha256:213744356007a4686ff3bb72105843d478bc0ba6229659429cbe241a99f55095,2816
pantry-tree:
sha256: e559c811165a2e75cfe649b68396466b3bd0b6a5353a9d6476605e6a40e0eb37
size: 594
original:
hackage: hoauth2-2.3.0
snapshots:
- completed:
sha256: b18614ab8986a4ba6d469921a2c18decab244af78309effa3d2dab85dbdfef80
size: 611886
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2022/2/25.yaml
original: nightly-2022-02-25

View File

@ -1,7 +0,0 @@
resolver: nightly-2022-12-09
extra-deps:
- crypton-1.0.0
- hoauth2-2.6.0
allow-newer: true
allow-newer-deps:
- hoauth2 # allow newer memory and text

View File

@ -1,26 +0,0 @@
# This file was autogenerated by Stack.
# You should not edit this file by hand.
# For more information, please see the documentation at:
# https://docs.haskellstack.org/en/stable/lock_files
packages:
- completed:
hackage: crypton-1.0.0@sha256:637e58581978c84ef1288d14fa9cac1d2905ef60e319924293bc11250aca882d,14527
pantry-tree:
sha256: 4b5e5511567c0fe735a224cb8b2b278e1caa79344f2940d030d169e69b1b81e1
size: 23275
original:
hackage: crypton-1.0.0
- completed:
hackage: hoauth2-2.6.0@sha256:168321df73bf75dc7cdda8e72725e9f3f624a9776b1fe59ae46c29c45029dc5d,2262
pantry-tree:
sha256: 5d39759b171cfaaf5842069a5548c2c1a41c0978645d018da7df713a186192b5
size: 859
original:
hackage: hoauth2-2.6.0
snapshots:
- completed:
sha256: b77e2c2b7988ed34cd317c01eb1958a8b8c234c3cc17e44077616c212959bed0
size: 558756
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2022/12/9.yaml
original: nightly-2022-12-09

View File

@ -1,3 +0,0 @@
resolver: lts-16.31
extra-deps:
- crypton-1.0.0

View File

@ -1,19 +0,0 @@
# This file was autogenerated by Stack.
# You should not edit this file by hand.
# For more information, please see the documentation at:
# https://docs.haskellstack.org/en/stable/lock_files
packages:
- completed:
hackage: crypton-1.0.0@sha256:637e58581978c84ef1288d14fa9cac1d2905ef60e319924293bc11250aca882d,14527
pantry-tree:
sha256: 4b5e5511567c0fe735a224cb8b2b278e1caa79344f2940d030d169e69b1b81e1
size: 23275
original:
hackage: crypton-1.0.0
snapshots:
- completed:
sha256: 637fb77049b25560622a224845b7acfe81a09fdb6a96a3c75997a10b651667f6
size: 534126
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/16/31.yaml
original: lts-16.31

View File

@ -1,3 +0,0 @@
resolver: lts-18.28
extra-deps:
- crypton-1.0.0

View File

@ -1,19 +0,0 @@
# This file was autogenerated by Stack.
# You should not edit this file by hand.
# For more information, please see the documentation at:
# https://docs.haskellstack.org/en/stable/lock_files
packages:
- completed:
hackage: crypton-1.0.0@sha256:637e58581978c84ef1288d14fa9cac1d2905ef60e319924293bc11250aca882d,14527
pantry-tree:
sha256: 4b5e5511567c0fe735a224cb8b2b278e1caa79344f2940d030d169e69b1b81e1
size: 23275
original:
hackage: crypton-1.0.0
snapshots:
- completed:
sha256: 428ec8d5ce932190d3cbe266b9eb3c175cd81e984babf876b64019e2cbe4ea68
size: 590100
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/28.yaml
original: lts-18.28

View File

@ -1,3 +0,0 @@
resolver: lts-19.33
extra-deps:
- crypton-1.0.0

View File

@ -1,19 +0,0 @@
# This file was autogenerated by Stack.
# You should not edit this file by hand.
# For more information, please see the documentation at:
# https://docs.haskellstack.org/en/stable/lock_files
packages:
- completed:
hackage: crypton-1.0.0@sha256:637e58581978c84ef1288d14fa9cac1d2905ef60e319924293bc11250aca882d,14527
pantry-tree:
sha256: 4b5e5511567c0fe735a224cb8b2b278e1caa79344f2940d030d169e69b1b81e1
size: 23275
original:
hackage: crypton-1.0.0
snapshots:
- completed:
sha256: 6d1532d40621957a25bad5195bfca7938e8a06d923c91bc52aa0f3c41181f2d4
size: 619204
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/19/33.yaml
original: lts-19.33

View File

@ -1,3 +0,0 @@
resolver: lts-20.26
extra-deps:
- crypton-1.0.0

View File

@ -1,19 +0,0 @@
# This file was autogenerated by Stack.
# You should not edit this file by hand.
# For more information, please see the documentation at:
# https://docs.haskellstack.org/en/stable/lock_files
packages:
- completed:
hackage: crypton-1.0.0@sha256:637e58581978c84ef1288d14fa9cac1d2905ef60e319924293bc11250aca882d,14527
pantry-tree:
sha256: 4b5e5511567c0fe735a224cb8b2b278e1caa79344f2940d030d169e69b1b81e1
size: 23275
original:
hackage: crypton-1.0.0
snapshots:
- completed:
sha256: 5a59b2a405b3aba3c00188453be172b85893cab8ebc352b1ef58b0eae5d248a2
size: 650475
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/26.yaml
original: lts-20.26

View File

@ -1,12 +0,0 @@
# This file was autogenerated by Stack.
# You should not edit this file by hand.
# For more information, please see the documentation at:
# https://docs.haskellstack.org/en/stable/lock_files
packages: []
snapshots:
- completed:
sha256: a81fb3877c4f9031e1325eb3935122e608d80715dc16b586eb11ddbff8671ecd
size: 640086
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/21/25.yaml
original: lts-21.25

1
stack-lts22.yaml Normal file
View File

@ -0,0 +1 @@
resolver: lts-22.44

1
stack-lts23.yaml Normal file
View File

@ -0,0 +1 @@
resolver: lts-23.28

1
stack-lts24.yaml Normal file
View File

@ -0,0 +1 @@
resolver: lts-24.26

View File

@ -1,13 +1,4 @@
resolver: nightly-2024-02-27 resolver: nightly-2026-01-05
extra-deps: extra-deps:
- yesod-auth-1.6.11.2
# For yesod-auth
- email-validate-2.3.2.19
- yesod-form-1.7.6
- yesod-1.6.2.1
- cryptonite-0.30 - cryptonite-0.30
- yesod-auth-1.6.11.3
allow-newer: true
allow-newer-deps:
- email-validate

View File

@ -1,40 +0,0 @@
# This file was autogenerated by Stack.
# You should not edit this file by hand.
# For more information, please see the documentation at:
# https://docs.haskellstack.org/en/stable/lock_files
packages:
- completed:
hackage: yesod-auth-1.6.11.2@sha256:c580afaccc311ad90fc8a90210b5cf998d95350ddffb622e45282d9b640cf519,3108
pantry-tree:
sha256: 58e36ca675fc01109915342d4df9cb2680fd37ff20919d84694cdd1d52eb7dc3
size: 1013
original:
hackage: yesod-auth-1.6.11.2
- completed:
hackage: email-validate-2.3.2.19@sha256:206a835fe99a79f2360c576e3594d8add82d2f1e001d58b49a60f60233da2bf5,1380
pantry-tree:
sha256: 391475ca7c84d59b1d8de362856acd2f56321d29d54ffcd05ed2a170d4e62692
size: 419
original:
hackage: email-validate-2.3.2.19
- completed:
hackage: yesod-form-1.7.6@sha256:42219f2c4feaa2de32280c0b8f98db818f993152693a79960808fd3001a4c0e2,3434
pantry-tree:
sha256: 8a48fc861796a61cf6abd2087fedd86844ba5c3e11c89c74d2d04c4656d69997
size: 1797
original:
hackage: yesod-form-1.7.6
- completed:
hackage: yesod-1.6.2.1@sha256:504bc888257dae9bb40f4cd1e1110c4e80dfe7b867e8e207b22c4ca4dbd87f9a,2030
pantry-tree:
sha256: 5a78fa0c6e7dcb46c9acd687ab9409c8fcda0d59b930e2a93e5dc44128c740ec
size: 618
original:
hackage: yesod-1.6.2.1
snapshots:
- completed:
sha256: e596f5467d31095fd7b9f750e82aeffe012e38e795c13e1cdc945c9cab928085
size: 604415
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2024/2/27.yaml
original: nightly-2024-02-27

View File

@ -1 +0,0 @@
resolver: lts-22.12

1
stack.yaml Symbolic link
View File

@ -0,0 +1 @@
stack-lts24.yaml

View File

@ -1,12 +1,12 @@
# This file was autogenerated by Stack. # This file was autogenerated by Stack.
# You should not edit this file by hand. # You should not edit this file by hand.
# For more information, please see the documentation at: # For more information, please see the documentation at:
# https://docs.haskellstack.org/en/stable/lock_files # https://docs.haskellstack.org/en/stable/topics/lock_files
packages: [] packages: []
snapshots: snapshots:
- completed: - completed:
sha256: e2c529ccfb21501f98f639e056cbde50470b86256d9849d7a82d414ca23e4276 sha256: d90eb1418667a225998b173817300e5ae2e1500ed03c0a9457cc2a0e78a0122a
size: 712898 size: 726337
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/22/12.yaml url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/24/26.yaml
original: lts-22.12 original: lts-24.26

View File

@ -1,10 +1,10 @@
cabal-version: 1.18 cabal-version: 1.18
-- This file has been generated from package.yaml by hpack version 0.37.0. -- This file has been generated from package.yaml by hpack version 0.38.1.
-- --
-- see: https://github.com/sol/hpack -- see: https://github.com/sol/hpack
-- --
-- hash: 6df1b551d88fc83903790620d83c1f4f549b14ab976cd46cb6bfb1e26722cf4f -- hash: dc142db361fed3689429889b831a2a76c4bb29da073c24340afacf62d3c14faa
name: yesod-auth-oauth2 name: yesod-auth-oauth2
version: 0.7.4.0 version: 0.7.4.0
@ -74,7 +74,7 @@ library
, bytestring >=0.9.1.4 , bytestring >=0.9.1.4
, crypton , crypton
, errors , errors
, hoauth2 >=1.11.0 , hoauth2 >=2.8.0
, http-client >=0.4.0 , http-client >=0.4.0
, http-conduit >=2.0 , http-conduit >=2.0
, http-types >=0.8 , http-types >=0.8