-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Haskell OAuth2 authentication client -- -- This is Haskell binding of OAuth2 Authorization framework and Bearer -- Token Usage framework. @package hoauth2 @version 2.12.0 module Network.OAuth2.Experiment.Pkce mkPkceParam :: MonadIO m => m PkceRequestParam newtype CodeChallenge CodeChallenge :: Text -> CodeChallenge [unCodeChallenge] :: CodeChallenge -> Text newtype CodeVerifier CodeVerifier :: Text -> CodeVerifier [unCodeVerifier] :: CodeVerifier -> Text data CodeChallengeMethod S256 :: CodeChallengeMethod data PkceRequestParam PkceRequestParam :: CodeVerifier -> CodeChallenge -> CodeChallengeMethod -> PkceRequestParam [codeVerifier] :: PkceRequestParam -> CodeVerifier [codeChallenge] :: PkceRequestParam -> CodeChallenge -- | spec says optional but in practice it is S256 -- https://datatracker.ietf.org/doc/html/rfc7636#section-4.3 [codeChallengeMethod] :: PkceRequestParam -> CodeChallengeMethod instance GHC.Show.Show Network.OAuth2.Experiment.Pkce.CodeChallengeMethod -- | Bindings Access Token and Refresh Token part of The OAuth 2.0 -- Authorization Framework RFC6749 -- https://www.rfc-editor.org/rfc/rfc6749 module Network.OAuth.OAuth2.TokenRequest data TokenResponseError TokenResponseError :: TokenResponseErrorCode -> Maybe Text -> Maybe (URIRef Absolute) -> TokenResponseError [tokenResponseError] :: TokenResponseError -> TokenResponseErrorCode [tokenResponseErrorDescription] :: TokenResponseError -> Maybe Text [tokenResponseErrorUri] :: TokenResponseError -> Maybe (URIRef Absolute) -- | Token Error Responses -- https://tools.ietf.org/html/rfc6749#section-5.2 data TokenResponseErrorCode InvalidRequest :: TokenResponseErrorCode InvalidClient :: TokenResponseErrorCode InvalidGrant :: TokenResponseErrorCode UnauthorizedClient :: TokenResponseErrorCode UnsupportedGrantType :: TokenResponseErrorCode InvalidScope :: TokenResponseErrorCode UnknownErrorCode :: Text -> TokenResponseErrorCode parseTokeResponseError :: ByteString -> TokenResponseError -- | Prepare the URL and the request body query for fetching an access -- token. accessTokenUrl :: OAuth2 -> ExchangeToken -> (URI, PostBody) -- | Obtain a new access token by sending a Refresh Token to the -- Authorization server. refreshAccessTokenUrl :: OAuth2 -> RefreshToken -> (URI, PostBody) -- | Exchange code for an Access Token with authenticate in -- request header. fetchAccessToken :: MonadIO m => Manager -> OAuth2 -> ExchangeToken -> ExceptT TokenResponseError m OAuth2Token -- | Deprecated: use fetchAccessTokenWithAuthMethod fetchAccessToken2 :: MonadIO m => Manager -> OAuth2 -> ExchangeToken -> ExceptT TokenResponseError m OAuth2Token -- | Deprecated: use fetchAccessTokenWithAuthMethod fetchAccessTokenInternal :: MonadIO m => ClientAuthenticationMethod -> Manager -> OAuth2 -> ExchangeToken -> ExceptT TokenResponseError m OAuth2Token -- | Exchange code for an Access Token -- -- OAuth2 spec allows credential (client_id, -- client_secret) to be sent either in the header (a.k.a -- ClientSecretBasic). or as form/url params (a.k.a -- ClientSecretPost). -- -- The OAuth provider can choose to implement only one, or both. Look for -- API document from the OAuth provider you're dealing with. If you`re -- uncertain, try fetchAccessToken which sends credential in -- authorization http header, which is common case. fetchAccessTokenWithAuthMethod :: MonadIO m => ClientAuthenticationMethod -> Manager -> OAuth2 -> ExchangeToken -> ExceptT TokenResponseError m OAuth2Token -- | Fetch a new AccessToken using the Refresh Token with authentication in -- request header. refreshAccessToken :: MonadIO m => Manager -> OAuth2 -> RefreshToken -> ExceptT TokenResponseError m OAuth2Token -- | Deprecated: use refreshAccessTokenWithAuthMethod refreshAccessToken2 :: MonadIO m => Manager -> OAuth2 -> RefreshToken -> ExceptT TokenResponseError m OAuth2Token -- | Deprecated: use refreshAccessTokenWithAuthMethod refreshAccessTokenInternal :: MonadIO m => ClientAuthenticationMethod -> Manager -> OAuth2 -> RefreshToken -> ExceptT TokenResponseError m OAuth2Token -- | Fetch a new AccessToken using the Refresh Token. -- -- OAuth2 spec allows credential ("client_id", "client_secret") to be -- sent either in the header (a.k.a ClientSecretBasic). or as -- form/url params (a.k.a ClientSecretPost). -- -- The OAuth provider can choose to implement only one, or both. Look for -- API document from the OAuth provider you're dealing with. If you're -- uncertain, try refreshAccessToken which sends credential in -- authorization http header, which is common case. refreshAccessTokenWithAuthMethod :: MonadIO m => ClientAuthenticationMethod -> Manager -> OAuth2 -> RefreshToken -> ExceptT TokenResponseError m OAuth2Token -- | Conduct post request and return response as JSON. doJSONPostRequest :: (MonadIO m, FromJSON a) => Manager -> OAuth2 -> URI -> PostBody -> ExceptT TokenResponseError m a -- | Conduct post request. doSimplePostRequest :: MonadIO m => Manager -> OAuth2 -> URI -> PostBody -> ExceptT TokenResponseError m ByteString -- | Gets response body from a Response if 200 otherwise assume -- TokenResponseError handleOAuth2TokenResponse :: Response ByteString -> Either TokenResponseError ByteString -- | Try to parses response as JSON, if failed, try to parse as like query -- string. parseResponseFlexible :: FromJSON a => ByteString -> Either TokenResponseError a -- | Parses the response that contains not JSON but a Query String parseResponseString :: FromJSON a => ByteString -> Either TokenResponseError a -- | Add Basic Authentication header using client_id and client_secret. addBasicAuth :: OAuth2 -> Request -> Request -- | Set several header values: + userAgennt : "hoauth2" + accept : -- "application/json" addDefaultRequestHeaders :: Request -> Request -- | Add Credential (client_id, client_secret) to the request post body. clientSecretPost :: OAuth2 -> PostBody instance GHC.Classes.Eq Network.OAuth.OAuth2.TokenRequest.TokenResponseErrorCode instance GHC.Show.Show Network.OAuth.OAuth2.TokenRequest.TokenResponseErrorCode instance GHC.Classes.Eq Network.OAuth.OAuth2.TokenRequest.TokenResponseError instance GHC.Show.Show Network.OAuth.OAuth2.TokenRequest.TokenResponseError instance Data.Aeson.Types.FromJSON.FromJSON Network.OAuth.OAuth2.TokenRequest.TokenResponseError instance Data.Aeson.Types.FromJSON.FromJSON Network.OAuth.OAuth2.TokenRequest.TokenResponseErrorCode -- | Bindings for The OAuth 2.0 Authorization Framework: Bearer Token Usage -- RFC6750 https://www.rfc-editor.org/rfc/rfc6750 module Network.OAuth.OAuth2.HttpClient -- | Conduct an authorized GET request and return response as JSON. Inject -- Access Token to Authorization Header. authGetJSON :: (MonadIO m, FromJSON a) => Manager -> AccessToken -> URI -> ExceptT ByteString m a -- | Conduct an authorized GET request. Inject Access Token to -- Authorization Header. authGetBS :: MonadIO m => Manager -> AccessToken -> URI -> ExceptT ByteString m ByteString -- | Same to authGetBS but set access token to query parameter -- rather than header -- | Deprecated: use authGetBSWithAuthMethod authGetBS2 :: MonadIO m => Manager -> AccessToken -> URI -> ExceptT ByteString m ByteString -- | Conduct an authorized GET request and return response as JSON. Allow -- to specify how to append AccessToken. authGetJSONWithAuthMethod :: (MonadIO m, FromJSON a) => APIAuthenticationMethod -> Manager -> AccessToken -> URI -> ExceptT ByteString m a -- | Deprecated. Use authGetJSONWithAuthMethod instead. -- | Deprecated: use authGetJSONWithAuthMethod authGetJSONInternal :: (MonadIO m, FromJSON a) => APIAuthenticationMethod -> Manager -> AccessToken -> URI -> ExceptT ByteString m a -- | Conduct an authorized GET request and return response as ByteString. -- Allow to specify how to append AccessToken. authGetBSWithAuthMethod :: MonadIO m => APIAuthenticationMethod -> Manager -> AccessToken -> URI -> ExceptT ByteString m ByteString -- | Deprecated: use authGetBSWithAuthMethod authGetBSInternal :: MonadIO m => APIAuthenticationMethod -> Manager -> AccessToken -> URI -> ExceptT ByteString m ByteString -- | Conduct POST request and return response as JSON. Inject Access Token -- to Authorization Header. authPostJSON :: (MonadIO m, FromJSON a) => Manager -> AccessToken -> URI -> PostBody -> ExceptT ByteString m a -- | Conduct POST request. Inject Access Token to http header -- (Authorization) authPostBS :: MonadIO m => Manager -> AccessToken -> URI -> PostBody -> ExceptT ByteString m ByteString -- | Conduct POST request with access token only in the request body but -- header. -- | Deprecated: use authPostBSWithAuthMethod authPostBS2 :: MonadIO m => Manager -> AccessToken -> URI -> PostBody -> ExceptT ByteString m ByteString -- | Conduct POST request with access token only in the header and not in -- body -- | Deprecated: use authPostBSWithAuthMethod authPostBS3 :: MonadIO m => Manager -> AccessToken -> URI -> PostBody -> ExceptT ByteString m ByteString -- | Conduct POST request and return response as JSON. Allow to specify how -- to append AccessToken. authPostJSONWithAuthMethod :: (MonadIO m, FromJSON a) => APIAuthenticationMethod -> Manager -> AccessToken -> URI -> PostBody -> ExceptT ByteString m a -- | Deprecated: use authPostJSONWithAuthMethod authPostJSONInternal :: (MonadIO m, FromJSON a) => APIAuthenticationMethod -> Manager -> AccessToken -> URI -> PostBody -> ExceptT ByteString m a -- | Conduct POST request and return response as ByteString. Allow to -- specify how to append AccessToken. authPostBSWithAuthMethod :: MonadIO m => APIAuthenticationMethod -> Manager -> AccessToken -> URI -> PostBody -> ExceptT ByteString m ByteString -- | Deprecated: use authPostBSWithAuthMethod authPostBSInternal :: MonadIO m => APIAuthenticationMethod -> Manager -> AccessToken -> URI -> PostBody -> ExceptT ByteString m ByteString -- | https://www.rfc-editor.org/rfc/rfc6750#section-2 data APIAuthenticationMethod -- | Provides in Authorization header AuthInRequestHeader :: APIAuthenticationMethod -- | Provides in request body AuthInRequestBody :: APIAuthenticationMethod -- | Provides in request query parameter AuthInRequestQuery :: APIAuthenticationMethod instance GHC.Classes.Ord Network.OAuth.OAuth2.HttpClient.APIAuthenticationMethod instance GHC.Classes.Eq Network.OAuth.OAuth2.HttpClient.APIAuthenticationMethod -- | Bindings Authorization part of The OAuth 2.0 Authorization Framework -- RFC6749 https://www.rfc-editor.org/rfc/rfc6749 module Network.OAuth.OAuth2.AuthorizationRequest -- | Authorization Code Grant Error Responses -- https://tools.ietf.org/html/rfc6749#section-4.1.2.1 -- -- I found hard time to figure a way to test the authorization error flow -- When anything wrong in /authorize request, it will stuck at -- the Provider page hence no way for this library to parse error -- response. In other words, /authorize ends up with 4xx or 5xx. -- Revisit this whenever find a case OAuth2 provider redirects back to -- Relying party with errors. data AuthorizationResponseError AuthorizationResponseError :: AuthorizationResponseErrorCode -> Maybe Text -> Maybe (URIRef Absolute) -> AuthorizationResponseError [authorizationResponseError] :: AuthorizationResponseError -> AuthorizationResponseErrorCode [authorizationResponseErrorDescription] :: AuthorizationResponseError -> Maybe Text [authorizationResponseErrorUri] :: AuthorizationResponseError -> Maybe (URIRef Absolute) data AuthorizationResponseErrorCode InvalidRequest :: AuthorizationResponseErrorCode UnauthorizedClient :: AuthorizationResponseErrorCode AccessDenied :: AuthorizationResponseErrorCode UnsupportedResponseType :: AuthorizationResponseErrorCode InvalidScope :: AuthorizationResponseErrorCode ServerError :: AuthorizationResponseErrorCode TemporarilyUnavailable :: AuthorizationResponseErrorCode UnknownErrorCode :: Text -> AuthorizationResponseErrorCode -- | See authorizationUrlWithParams authorizationUrl :: OAuth2 -> URI -- | Prepare the authorization URL. Redirect to this URL asking for user -- interactive authentication. authorizationUrlWithParams :: QueryParams -> OAuth2 -> URI instance GHC.Classes.Eq Network.OAuth.OAuth2.AuthorizationRequest.AuthorizationResponseErrorCode instance GHC.Show.Show Network.OAuth.OAuth2.AuthorizationRequest.AuthorizationResponseErrorCode instance GHC.Classes.Eq Network.OAuth.OAuth2.AuthorizationRequest.AuthorizationResponseError instance GHC.Show.Show Network.OAuth.OAuth2.AuthorizationRequest.AuthorizationResponseError instance Data.Aeson.Types.FromJSON.FromJSON Network.OAuth.OAuth2.AuthorizationRequest.AuthorizationResponseError instance Data.Aeson.Types.FromJSON.FromJSON Network.OAuth.OAuth2.AuthorizationRequest.AuthorizationResponseErrorCode -- | A lightweight oauth2 Haskell binding. See Readme for more details module Network.OAuth.OAuth2 -- | Query Parameter Representation data OAuth2 OAuth2 :: Text -> Text -> URIRef Absolute -> URIRef Absolute -> URIRef Absolute -> OAuth2 [oauth2ClientId] :: OAuth2 -> Text [oauth2ClientSecret] :: OAuth2 -> Text [oauth2AuthorizeEndpoint] :: OAuth2 -> URIRef Absolute [oauth2TokenEndpoint] :: OAuth2 -> URIRef Absolute [oauth2RedirectUri] :: OAuth2 -> URIRef Absolute newtype AccessToken AccessToken :: Text -> AccessToken [atoken] :: AccessToken -> Text newtype RefreshToken RefreshToken :: Text -> RefreshToken [rtoken] :: RefreshToken -> Text newtype IdToken IdToken :: Text -> IdToken [idtoken] :: IdToken -> Text -- | Authorization Code newtype ExchangeToken ExchangeToken :: Text -> ExchangeToken [extoken] :: ExchangeToken -> Text -- | https://www.rfc-editor.org/rfc/rfc6749#section-4.1.4 data OAuth2Token OAuth2Token :: AccessToken -> Maybe RefreshToken -> Maybe Int -> Maybe Text -> Maybe IdToken -> OAuth2Token [accessToken] :: OAuth2Token -> AccessToken -- | Exists when offline_access scope is in the Authorization -- Request and the provider supports Refresh Access Token. [refreshToken] :: OAuth2Token -> Maybe RefreshToken [expiresIn] :: OAuth2Token -> Maybe Int -- | See https://www.rfc-editor.org/rfc/rfc6749#section-5.1. It's -- required per spec. But OAuth2 provider implementation are vary. Maybe -- will remove Maybe in future release. [tokenType] :: OAuth2Token -> Maybe Text -- | Exists when openid scope is in the Authorization Request and -- the provider supports OpenID protocol. [idToken] :: OAuth2Token -> Maybe IdToken -- | https://www.rfc-editor.org/rfc/rfc6749#section-2.3 According to -- spec: -- -- The client MUST NOT use more than one authentication method in each -- request. -- -- Which means use Authorization header or Post body. -- -- However, I found I have to include authentication in the header all -- the time in real world. -- -- In other words, ClientSecretBasic is always assured. -- ClientSecretPost is optional. -- -- Maybe consider an alternative implementation that boolean kind of data -- type is good enough. data ClientAuthenticationMethod ClientSecretBasic :: ClientAuthenticationMethod ClientSecretPost :: ClientAuthenticationMethod ClientAssertionJwt :: ClientAuthenticationMethod -- | Type synonym of post body content type PostBody = [(ByteString, ByteString)] -- | Type sysnonym of request query params type QueryParams = [(ByteString, ByteString)] defaultRequestHeaders :: [(HeaderName, ByteString)] appendQueryParams :: [(ByteString, ByteString)] -> URIRef a -> URIRef a uriToRequest :: MonadThrow m => URI -> m Request requestToUri :: Request -> URI hostLens :: Lens' Request ByteString portLens :: Lens' Request Int -- | See authorizationUrlWithParams authorizationUrl :: OAuth2 -> URI -- | Prepare the authorization URL. Redirect to this URL asking for user -- interactive authentication. authorizationUrlWithParams :: QueryParams -> OAuth2 -> URI module Network.OAuth2.Experiment.Types -- | Idp i consists various endpoints endpoints. -- -- The i is actually phantom type for information only (Idp -- name) at this moment. And it is PolyKinds. -- -- Hence whenever Idp i or IdpApplication i a is used -- as function parameter, PolyKinds need to be enabled. data Idp (i :: k) Idp :: URI -> URI -> URI -> Maybe URI -> Idp (i :: k) -- | Userinfo Endpoint [idpUserInfoEndpoint] :: Idp (i :: k) -> URI -- | Authorization Endpoint [idpAuthorizeEndpoint] :: Idp (i :: k) -> URI -- | Token Endpoint [idpTokenEndpoint] :: Idp (i :: k) -> URI -- | Apparently not all IdP support device code flow [idpDeviceAuthorizationEndpoint] :: Idp (i :: k) -> Maybe URI -- | An OAuth2 Application "a" of IdP "i". "a" can be one of following -- type: -- --
-- import Network.OAuth2.Experiment
-- import URI.ByteString.QQ
--
-- data Google = Google deriving (Eq, Show)
--
-- googleIdp :: Idp Google
-- googleIdp =
-- Idp
-- { idpAuthorizeEndpoint = [uri|https://accounts.google.com/o/oauth2/v2/auth|]
-- , idpTokenEndpoint = [uri|https://oauth2.googleapis.com/token|]
-- , idpUserInfoEndpoint = [uri|https://www.googleapis.com/oauth2/v2/userinfo|]
-- , idpDeviceAuthorizationEndpoint = Just [uri|https://oauth2.googleapis.com/device/code|]
-- }
--
-- fooApp :: AuthorizationCodeApplication
-- fooApp =
-- AuthorizationCodeApplication
-- { acClientId = "xxxxx",
-- acClientSecret = "xxxxx",
-- acScope =
-- Set.fromList
-- [ "https://www.googleapis.com/auth/userinfo.email",
-- "https://www.googleapis.com/auth/userinfo.profile"
-- ],
-- acAuthorizeState = "CHANGE_ME",
-- acAuthorizeRequestExtraParams = Map.empty,
-- acRedirectUri = [uri|http://localhost/oauth2/callback|],
-- acName = "sample-google-authorization-code-app",
-- acTokenRequestAuthenticationMethod = ClientSecretBasic,
-- }
--
-- fooIdpApplication :: IdpApplication AuthorizationCodeApplication Google
-- fooIdpApplication = IdpApplication fooApp googleIdp
--
--
-- Secondly, construct the authorize URL.
--
-- -- authorizeUrl = mkAuthorizationRequest fooIdpApplication ---- -- Thirdly, after a successful redirect with authorize code, you could -- exchange for access token -- --
-- mgr <- liftIO $ newManager tlsManagerSettings -- tokenResp <- conduitTokenRequest fooIdpApplication mgr authorizeCode ---- -- If you'd like to fetch user info, uses this method -- --
-- conduitUserInfoRequest fooIdpApplication mgr (accessToken tokenResp) ---- -- You could also find example from hoauth2-providers-tutorials -- module. module Network.OAuth2.Experiment -- | An Application that supports "Authorization code" flow -- -- https://www.rfc-editor.org/rfc/rfc6749#section-4.1 data AuthorizationCodeApplication AuthorizationCodeApplication :: Text -> ClientId -> ClientSecret -> Set Scope -> URI -> AuthorizeState -> Map Text Text -> ClientAuthenticationMethod -> AuthorizationCodeApplication [acName] :: AuthorizationCodeApplication -> Text [acClientId] :: AuthorizationCodeApplication -> ClientId [acClientSecret] :: AuthorizationCodeApplication -> ClientSecret [acScope] :: AuthorizationCodeApplication -> Set Scope [acRedirectUri] :: AuthorizationCodeApplication -> URI [acAuthorizeState] :: AuthorizationCodeApplication -> AuthorizeState [acAuthorizeRequestExtraParams] :: AuthorizationCodeApplication -> Map Text Text [acTokenRequestAuthenticationMethod] :: AuthorizationCodeApplication -> ClientAuthenticationMethod -- | An Application that supports "Device Authorization Grant" -- -- https://www.rfc-editor.org/rfc/rfc8628#section-3.1 data DeviceAuthorizationApplication DeviceAuthorizationApplication :: Text -> ClientId -> ClientSecret -> Set Scope -> Map Text Text -> Maybe ClientAuthenticationMethod -> DeviceAuthorizationApplication [daName] :: DeviceAuthorizationApplication -> Text [daClientId] :: DeviceAuthorizationApplication -> ClientId [daClientSecret] :: DeviceAuthorizationApplication -> ClientSecret [daScope] :: DeviceAuthorizationApplication -> Set Scope -- | Additional parameters to the device authorization request. Most of -- identity providers follow the spec strictly but AzureAD requires -- "tenant" parameter. [daAuthorizationRequestExtraParam] :: DeviceAuthorizationApplication -> Map Text Text -- | The spec requires similar authentication method as /token request. -- Most of identity providers doesn't required it but some does like -- Okta. [daAuthorizationRequestAuthenticationMethod] :: DeviceAuthorizationApplication -> Maybe ClientAuthenticationMethod pollDeviceTokenRequest :: MonadIO m => IdpApplication i DeviceAuthorizationApplication -> Manager -> DeviceAuthorizationResponse -> ExceptT TokenResponseError m OAuth2Token -- | An Application that supports "Client Credentials" flow -- -- https://www.rfc-editor.org/rfc/rfc6749#section-4.4 data ClientCredentialsApplication ClientCredentialsApplication :: ClientId -> ClientSecret -> Text -> Set Scope -> Map Text Text -> ClientAuthenticationMethod -> ClientCredentialsApplication [ccClientId] :: ClientCredentialsApplication -> ClientId [ccClientSecret] :: ClientCredentialsApplication -> ClientSecret [ccName] :: ClientCredentialsApplication -> Text [ccScope] :: ClientCredentialsApplication -> Set Scope [ccTokenRequestExtraParams] :: ClientCredentialsApplication -> Map Text Text [ccTokenRequestAuthenticationMethod] :: ClientCredentialsApplication -> ClientAuthenticationMethod -- | An Application that supports "Resource Owner Password" flow -- -- https://www.rfc-editor.org/rfc/rfc6749#section-4.3 data ResourceOwnerPasswordApplication ResourceOwnerPasswordApplication :: ClientId -> ClientSecret -> Text -> Set Scope -> Username -> Password -> Map Text Text -> ResourceOwnerPasswordApplication [ropClientId] :: ResourceOwnerPasswordApplication -> ClientId [ropClientSecret] :: ResourceOwnerPasswordApplication -> ClientSecret [ropName] :: ResourceOwnerPasswordApplication -> Text [ropScope] :: ResourceOwnerPasswordApplication -> Set Scope [ropUserName] :: ResourceOwnerPasswordApplication -> Username [ropPassword] :: ResourceOwnerPasswordApplication -> Password [ropTokenRequestExtraParams] :: ResourceOwnerPasswordApplication -> Map Text Text -- | An Application that supports "JWT Bearer" flow -- -- https://datatracker.ietf.org/doc/html/rfc7523 data JwtBearerApplication JwtBearerApplication :: Text -> ByteString -> JwtBearerApplication [jbName] :: JwtBearerApplication -> Text [jbJwtAssertion] :: JwtBearerApplication -> ByteString class HasAuthorizeRequest a -- | Constructs Authorization Code request URI -- https://www.rfc-editor.org/rfc/rfc6749#section-4.1.1 mkAuthorizationRequest :: HasAuthorizeRequest a => IdpApplication i a -> URI -- | Constructs Authorization Code (PKCE) request URI and the Code -- Verifier. https://datatracker.ietf.org/doc/html/rfc7636 mkPkceAuthorizeRequest :: (MonadIO m, HasPkceAuthorizeRequest a) => IdpApplication i a -> m (URI, CodeVerifier) -- | https://www.rfc-editor.org/rfc/rfc8628#section-3.2 data DeviceAuthorizationResponse DeviceAuthorizationResponse :: DeviceCode -> Text -> URI -> Maybe URI -> Integer -> Maybe Int -> DeviceAuthorizationResponse [deviceCode] :: DeviceAuthorizationResponse -> DeviceCode [userCode] :: DeviceAuthorizationResponse -> Text [verificationUri] :: DeviceAuthorizationResponse -> URI [verificationUriComplete] :: DeviceAuthorizationResponse -> Maybe URI [expiresIn] :: DeviceAuthorizationResponse -> Integer [interval] :: DeviceAuthorizationResponse -> Maybe Int class HasOAuth2Key a => HasDeviceAuthorizationRequest a -- | Makes Device Authorization Request -- https://www.rfc-editor.org/rfc/rfc8628#section-3.1 conduitDeviceAuthorizationRequest :: (MonadIO m, HasDeviceAuthorizationRequest a) => IdpApplication i a -> Manager -> ExceptT ByteString m DeviceAuthorizationResponse class (HasOAuth2Key a, HasTokenRequestClientAuthenticationMethod a) => HasTokenRequest a where { data TokenRequest a; type ExchangeTokenInfo a; } -- | Only Authorization Code Grant involves a Exchange Token (Authorization -- Code). ResourceOwnerPassword and Client Credentials make token request -- directly. data NoNeedExchangeToken NoNeedExchangeToken :: NoNeedExchangeToken -- | Make Token Request (PKCE) -- https://datatracker.ietf.org/doc/html/rfc7636#section-4.5 conduitPkceTokenRequest :: (HasTokenRequest a, ToQueryParam (TokenRequest a), MonadIO m) => IdpApplication i a -> Manager -> (ExchangeTokenInfo a, CodeVerifier) -> ExceptT TokenResponseError m OAuth2Token -- | Make Token Request -- https://www.rfc-editor.org/rfc/rfc6749#section-4.1.3 conduitTokenRequest :: (HasTokenRequest a, ToQueryParam (TokenRequest a), MonadIO m) => IdpApplication i a -> Manager -> ExchangeTokenInfo a -> ExceptT TokenResponseError m OAuth2Token class (HasOAuth2Key a, HasTokenRequestClientAuthenticationMethod a) => HasRefreshTokenRequest a -- | Make Refresh Token Request -- https://www.rfc-editor.org/rfc/rfc6749#section-6 conduitRefreshTokenRequest :: (MonadIO m, HasRefreshTokenRequest a) => IdpApplication i a -> Manager -> RefreshToken -> ExceptT TokenResponseError m OAuth2Token class HasUserInfoRequest a -- | Standard approach of fetching /userinfo conduitUserInfoRequest :: (MonadIO m, HasUserInfoRequest a, FromJSON b) => IdpApplication i a -> Manager -> AccessToken -> ExceptT ByteString m b -- | Usually conduitUserInfoRequest is good enough. But some IdP has -- different approach to fetch user information rather than GET. This -- method gives the flexiblity. conduitUserInfoRequestWithCustomMethod :: (MonadIO m, HasUserInfoRequest a, FromJSON b) => (Manager -> AccessToken -> URI -> ExceptT ByteString m b) -> IdpApplication i a -> Manager -> AccessToken -> ExceptT ByteString m b newtype AuthorizeState AuthorizeState :: Text -> AuthorizeState [unAuthorizeState] :: AuthorizeState -> Text newtype ClientId ClientId :: Text -> ClientId [unClientId] :: ClientId -> Text -- | Can be either "Client Secret" or JWT base on client authentication -- method newtype ClientSecret ClientSecret :: Text -> ClientSecret [unClientSecret] :: ClientSecret -> Text class HasOAuth2Key a -- | Idp i consists various endpoints endpoints. -- -- The i is actually phantom type for information only (Idp -- name) at this moment. And it is PolyKinds. -- -- Hence whenever Idp i or IdpApplication i a is used -- as function parameter, PolyKinds need to be enabled. data Idp (i :: k) Idp :: URI -> URI -> URI -> Maybe URI -> Idp (i :: k) -- | Userinfo Endpoint [idpUserInfoEndpoint] :: Idp (i :: k) -> URI -- | Authorization Endpoint [idpAuthorizeEndpoint] :: Idp (i :: k) -> URI -- | Token Endpoint [idpTokenEndpoint] :: Idp (i :: k) -> URI -- | Apparently not all IdP support device code flow [idpDeviceAuthorizationEndpoint] :: Idp (i :: k) -> Maybe URI -- | An OAuth2 Application "a" of IdP "i". "a" can be one of following -- type: -- -- data IdpApplication (i :: k) a IdpApplication :: Idp i -> a -> IdpApplication (i :: k) a [idp] :: IdpApplication (i :: k) a -> Idp i [application] :: IdpApplication (i :: k) a -> a newtype Password Password :: Text -> Password [unPassword] :: Password -> Text newtype RedirectUri RedirectUri :: URI -> RedirectUri [unRedirectUri] :: RedirectUri -> URI newtype Scope Scope :: Text -> Scope [unScope] :: Scope -> Text newtype Username Username :: Text -> Username [unUsername] :: Username -> Text newtype CodeVerifier CodeVerifier :: Text -> CodeVerifier [unCodeVerifier] :: CodeVerifier -> Text -- | https://www.rfc-editor.org/rfc/rfc6749#section-2.3 According to -- spec: -- -- The client MUST NOT use more than one authentication method in each -- request. -- -- Which means use Authorization header or Post body. -- -- However, I found I have to include authentication in the header all -- the time in real world. -- -- In other words, ClientSecretBasic is always assured. -- ClientSecretPost is optional. -- -- Maybe consider an alternative implementation that boolean kind of data -- type is good enough. data ClientAuthenticationMethod ClientSecretBasic :: ClientAuthenticationMethod ClientSecretPost :: ClientAuthenticationMethod ClientAssertionJwt :: ClientAuthenticationMethod uriToText :: URI -> Text