packages feed

hoauth2 2.8.1 → 2.9.0

raw patch · 23 files changed

+1250/−787 lines, 23 filesdep ~transformersPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: transformers

API changes (from Hackage documentation)

- Network.OAuth.OAuth2.AuthorizationRequest: data Errors
- Network.OAuth.OAuth2.AuthorizationRequest: instance Data.Aeson.Types.FromJSON.FromJSON Network.OAuth.OAuth2.AuthorizationRequest.Errors
- Network.OAuth.OAuth2.AuthorizationRequest: instance GHC.Classes.Eq Network.OAuth.OAuth2.AuthorizationRequest.Errors
- Network.OAuth.OAuth2.AuthorizationRequest: instance GHC.Generics.Generic Network.OAuth.OAuth2.AuthorizationRequest.Errors
- Network.OAuth.OAuth2.AuthorizationRequest: instance GHC.Show.Show Network.OAuth.OAuth2.AuthorizationRequest.Errors
- Network.OAuth.OAuth2.TokenRequest: TokenRequestError :: TokenRequestErrorCode -> Maybe Text -> Maybe (URIRef Absolute) -> TokenRequestError
- Network.OAuth.OAuth2.TokenRequest: [errorDescription] :: TokenRequestError -> Maybe Text
- Network.OAuth.OAuth2.TokenRequest: [errorUri] :: TokenRequestError -> Maybe (URIRef Absolute)
- Network.OAuth.OAuth2.TokenRequest: [error] :: TokenRequestError -> TokenRequestErrorCode
- Network.OAuth.OAuth2.TokenRequest: data TokenRequestError
- Network.OAuth.OAuth2.TokenRequest: data TokenRequestErrorCode
- Network.OAuth.OAuth2.TokenRequest: instance Data.Aeson.Types.FromJSON.FromJSON Network.OAuth.OAuth2.TokenRequest.TokenRequestError
- Network.OAuth.OAuth2.TokenRequest: instance Data.Aeson.Types.FromJSON.FromJSON Network.OAuth.OAuth2.TokenRequest.TokenRequestErrorCode
- Network.OAuth.OAuth2.TokenRequest: instance GHC.Classes.Eq Network.OAuth.OAuth2.TokenRequest.TokenRequestError
- Network.OAuth.OAuth2.TokenRequest: instance GHC.Classes.Eq Network.OAuth.OAuth2.TokenRequest.TokenRequestErrorCode
- Network.OAuth.OAuth2.TokenRequest: instance GHC.Generics.Generic Network.OAuth.OAuth2.TokenRequest.TokenRequestError
- Network.OAuth.OAuth2.TokenRequest: instance GHC.Show.Show Network.OAuth.OAuth2.TokenRequest.TokenRequestError
- Network.OAuth.OAuth2.TokenRequest: instance GHC.Show.Show Network.OAuth.OAuth2.TokenRequest.TokenRequestErrorCode
- Network.OAuth.OAuth2.TokenRequest: parseTokeRequestError :: ByteString -> TokenRequestError
- Network.OAuth2.Experiment: -- WithExchangeToken a b = b' implies no exchange token v.s. 'type
- Network.OAuth2.Experiment: -- client credentials) will use <a>ExchangeToken</a> in the token request
- Network.OAuth2.Experiment: -- create type family to be explicit on it. with 'type instance
- Network.OAuth2.Experiment: -- endpoint.
- Network.OAuth2.Experiment: -- instance WithExchangeToken a b = ExchangeToken -&gt; b' implies
- Network.OAuth2.Experiment: -- needing an exchange token
- Network.OAuth2.Experiment: -- | <a>https://www.rfc-editor.org/rfc/rfc6749#page-47</a>
- Network.OAuth2.Experiment: AuthorizationCode :: GrantTypeFlow
- Network.OAuth2.Experiment: ClientCredentials :: GrantTypeFlow
- Network.OAuth2.Experiment: CodeChallenge :: Text -> CodeChallenge
- Network.OAuth2.Experiment: GTAuthorizationCode :: GrantTypeValue
- Network.OAuth2.Experiment: GTClientCredentials :: GrantTypeValue
- Network.OAuth2.Experiment: GTJwtBearer :: GrantTypeValue
- Network.OAuth2.Experiment: GTPassword :: GrantTypeValue
- Network.OAuth2.Experiment: GTRefreshToken :: GrantTypeValue
- Network.OAuth2.Experiment: JwtBearer :: GrantTypeFlow
- Network.OAuth2.Experiment: PkceRequestParam :: CodeVerifier -> CodeChallenge -> CodeChallengeMethod -> PkceRequestParam
- Network.OAuth2.Experiment: ResourceOwnerPassword :: GrantTypeFlow
- Network.OAuth2.Experiment: S256 :: CodeChallengeMethod
- Network.OAuth2.Experiment: [$sel:idpAuthorizeEndpoint:Idp] :: Idp a -> URI
- Network.OAuth2.Experiment: [$sel:idpFetchUserInfo:Idp] :: Idp a -> forall m. (FromJSON (IdpUserInfo a), MonadIO m) => Manager -> AccessToken -> URI -> ExceptT ByteString m (IdpUserInfo a)
- Network.OAuth2.Experiment: [$sel:idpTokenEndpoint:Idp] :: Idp a -> URI
- Network.OAuth2.Experiment: [$sel:idpUserInfoEndpoint:Idp] :: Idp a -> URI
- Network.OAuth2.Experiment: [$sel:unAuthorizeState:AuthorizeState] :: AuthorizeState -> Text
- Network.OAuth2.Experiment: [$sel:unClientId:ClientId] :: ClientId -> Text
- Network.OAuth2.Experiment: [$sel:unClientSecret:ClientSecret] :: ClientSecret -> Text
- Network.OAuth2.Experiment: [$sel:unPassword:Password] :: Password -> Text
- Network.OAuth2.Experiment: [$sel:unRedirectUri:RedirectUri] :: RedirectUri -> URI
- Network.OAuth2.Experiment: [$sel:unScope:Scope] :: Scope -> Text
- Network.OAuth2.Experiment: [$sel:unUsername:Username] :: Username -> Text
- Network.OAuth2.Experiment: [codeChallengeMethod] :: PkceRequestParam -> CodeChallengeMethod
- Network.OAuth2.Experiment: [codeChallenge] :: PkceRequestParam -> CodeChallenge
- Network.OAuth2.Experiment: [codeVerifier] :: PkceRequestParam -> CodeVerifier
- Network.OAuth2.Experiment: [unCodeChallenge] :: CodeChallenge -> Text
- Network.OAuth2.Experiment: class HasPkceAuthorizeRequest (a :: GrantTypeFlow)
- Network.OAuth2.Experiment: class HasPkceTokenRequest (b :: GrantTypeFlow)
- Network.OAuth2.Experiment: class ToQueryParam a
- Network.OAuth2.Experiment: class ToResponseTypeValue (a :: GrantTypeFlow)
- Network.OAuth2.Experiment: data AuthorizationRequest a;
- Network.OAuth2.Experiment: data CodeChallengeMethod
- Network.OAuth2.Experiment: data GrantTypeFlow
- Network.OAuth2.Experiment: data GrantTypeValue
- Network.OAuth2.Experiment: data PkceRequestParam
- Network.OAuth2.Experiment: data RefreshTokenRequest a;
- Network.OAuth2.Experiment: data family IdpApplication (a :: GrantTypeFlow) (i :: Type)
- Network.OAuth2.Experiment: mkAuthorizeRequest :: HasAuthorizeRequest a => IdpApplication a i -> MkAuthorizationRequestResponse a
- Network.OAuth2.Experiment: mkAuthorizeRequestParameter :: HasAuthorizeRequest a => IdpApplication a i -> AuthorizationRequest a
- Network.OAuth2.Experiment: mkPkceParam :: MonadIO m => m PkceRequestParam
- Network.OAuth2.Experiment: mkRefreshTokenRequest :: HasRefreshTokenRequest a => IdpApplication a i -> RefreshToken -> RefreshTokenRequest a
- Network.OAuth2.Experiment: mkTokenRequest :: HasTokenRequest a => IdpApplication a i -> WithExchangeToken a (TokenRequest a)
- Network.OAuth2.Experiment: newtype CodeChallenge
- Network.OAuth2.Experiment: toOAuth2Key :: ClientId -> ClientSecret -> OAuth2
- Network.OAuth2.Experiment: toQueryParam :: ToQueryParam a => a -> Map Text Text
- Network.OAuth2.Experiment: toResponseTypeParam :: forall a b req. (ToResponseTypeValue a, IsString b) => req a -> Map b b
- Network.OAuth2.Experiment: toResponseTypeValue :: (ToResponseTypeValue a, IsString b) => b
- Network.OAuth2.Experiment: type MkAuthorizationRequestResponse a;
- Network.OAuth2.Experiment: type WithExchangeToken a b;
- Network.OAuth2.Experiment: type family IdpUserInfo a
+ Network.OAuth.OAuth2.AuthorizationRequest: AuthorizationResponseError :: AuthorizationResponseErrorCode -> Maybe Text -> Maybe (URIRef Absolute) -> AuthorizationResponseError
+ Network.OAuth.OAuth2.AuthorizationRequest: UnknownErrorCode :: Text -> AuthorizationResponseErrorCode
+ Network.OAuth.OAuth2.AuthorizationRequest: [authorizationResponseErrorDescription] :: AuthorizationResponseError -> Maybe Text
+ Network.OAuth.OAuth2.AuthorizationRequest: [authorizationResponseErrorUri] :: AuthorizationResponseError -> Maybe (URIRef Absolute)
+ Network.OAuth.OAuth2.AuthorizationRequest: [authorizationResponseError] :: AuthorizationResponseError -> AuthorizationResponseErrorCode
+ Network.OAuth.OAuth2.AuthorizationRequest: data AuthorizationResponseError
+ Network.OAuth.OAuth2.AuthorizationRequest: data AuthorizationResponseErrorCode
+ Network.OAuth.OAuth2.AuthorizationRequest: instance Data.Aeson.Types.FromJSON.FromJSON Network.OAuth.OAuth2.AuthorizationRequest.AuthorizationResponseError
+ Network.OAuth.OAuth2.AuthorizationRequest: instance Data.Aeson.Types.FromJSON.FromJSON Network.OAuth.OAuth2.AuthorizationRequest.AuthorizationResponseErrorCode
+ Network.OAuth.OAuth2.AuthorizationRequest: instance GHC.Classes.Eq Network.OAuth.OAuth2.AuthorizationRequest.AuthorizationResponseError
+ Network.OAuth.OAuth2.AuthorizationRequest: instance GHC.Classes.Eq Network.OAuth.OAuth2.AuthorizationRequest.AuthorizationResponseErrorCode
+ Network.OAuth.OAuth2.AuthorizationRequest: instance GHC.Show.Show Network.OAuth.OAuth2.AuthorizationRequest.AuthorizationResponseError
+ Network.OAuth.OAuth2.AuthorizationRequest: instance GHC.Show.Show Network.OAuth.OAuth2.AuthorizationRequest.AuthorizationResponseErrorCode
+ Network.OAuth.OAuth2.TokenRequest: TokenResponseError :: TokenResponseErrorCode -> Maybe Text -> Maybe (URIRef Absolute) -> TokenResponseError
+ Network.OAuth.OAuth2.TokenRequest: [tokenResponseErrorDescription] :: TokenResponseError -> Maybe Text
+ Network.OAuth.OAuth2.TokenRequest: [tokenResponseErrorUri] :: TokenResponseError -> Maybe (URIRef Absolute)
+ Network.OAuth.OAuth2.TokenRequest: [tokenResponseError] :: TokenResponseError -> TokenResponseErrorCode
+ Network.OAuth.OAuth2.TokenRequest: addBasicAuth :: OAuth2 -> Request -> Request
+ Network.OAuth.OAuth2.TokenRequest: data TokenResponseError
+ Network.OAuth.OAuth2.TokenRequest: data TokenResponseErrorCode
+ Network.OAuth.OAuth2.TokenRequest: instance Data.Aeson.Types.FromJSON.FromJSON Network.OAuth.OAuth2.TokenRequest.TokenResponseError
+ Network.OAuth.OAuth2.TokenRequest: instance Data.Aeson.Types.FromJSON.FromJSON Network.OAuth.OAuth2.TokenRequest.TokenResponseErrorCode
+ Network.OAuth.OAuth2.TokenRequest: instance GHC.Classes.Eq Network.OAuth.OAuth2.TokenRequest.TokenResponseError
+ Network.OAuth.OAuth2.TokenRequest: instance GHC.Classes.Eq Network.OAuth.OAuth2.TokenRequest.TokenResponseErrorCode
+ Network.OAuth.OAuth2.TokenRequest: instance GHC.Show.Show Network.OAuth.OAuth2.TokenRequest.TokenResponseError
+ Network.OAuth.OAuth2.TokenRequest: instance GHC.Show.Show Network.OAuth.OAuth2.TokenRequest.TokenResponseErrorCode
+ Network.OAuth.OAuth2.TokenRequest: parseTokeResponseError :: ByteString -> TokenResponseError
+ Network.OAuth2.Experiment: AuthorizationCodeApplication :: Text -> ClientId -> ClientSecret -> Set Scope -> URI -> AuthorizeState -> Map Text Text -> ClientAuthenticationMethod -> AuthorizationCodeApplication
+ Network.OAuth2.Experiment: ClientAssertionJwt :: ClientAuthenticationMethod
+ Network.OAuth2.Experiment: ClientCredentialsApplication :: ClientId -> ClientSecret -> Text -> Set Scope -> Map Text Text -> ClientAuthenticationMethod -> ClientCredentialsApplication
+ Network.OAuth2.Experiment: ClientSecretBasic :: ClientAuthenticationMethod
+ Network.OAuth2.Experiment: ClientSecretPost :: ClientAuthenticationMethod
+ Network.OAuth2.Experiment: DeviceAuthorizationApplication :: Text -> ClientId -> ClientSecret -> Set Scope -> Map Text Text -> Maybe ClientAuthenticationMethod -> DeviceAuthorizationApplication
+ Network.OAuth2.Experiment: DeviceAuthorizationResponse :: DeviceCode -> Text -> URI -> Maybe URI -> Integer -> Maybe Int -> DeviceAuthorizationResponse
+ Network.OAuth2.Experiment: IdpApplication :: Idp i -> a -> IdpApplication (i :: k) a
+ Network.OAuth2.Experiment: JwtBearerApplication :: Text -> ByteString -> JwtBearerApplication
+ Network.OAuth2.Experiment: NoNeedExchangeToken :: NoNeedExchangeToken
+ Network.OAuth2.Experiment: ResourceOwnerPasswordApplication :: ClientId -> ClientSecret -> Text -> Set Scope -> Username -> Password -> Map Text Text -> ResourceOwnerPasswordApplication
+ Network.OAuth2.Experiment: [acAuthorizeRequestExtraParams] :: AuthorizationCodeApplication -> Map Text Text
+ Network.OAuth2.Experiment: [acAuthorizeState] :: AuthorizationCodeApplication -> AuthorizeState
+ Network.OAuth2.Experiment: [acClientId] :: AuthorizationCodeApplication -> ClientId
+ Network.OAuth2.Experiment: [acClientSecret] :: AuthorizationCodeApplication -> ClientSecret
+ Network.OAuth2.Experiment: [acName] :: AuthorizationCodeApplication -> Text
+ Network.OAuth2.Experiment: [acRedirectUri] :: AuthorizationCodeApplication -> URI
+ Network.OAuth2.Experiment: [acScope] :: AuthorizationCodeApplication -> Set Scope
+ Network.OAuth2.Experiment: [acTokenRequestAuthenticationMethod] :: AuthorizationCodeApplication -> ClientAuthenticationMethod
+ Network.OAuth2.Experiment: [application] :: IdpApplication (i :: k) a -> a
+ Network.OAuth2.Experiment: [ccClientId] :: ClientCredentialsApplication -> ClientId
+ Network.OAuth2.Experiment: [ccClientSecret] :: ClientCredentialsApplication -> ClientSecret
+ Network.OAuth2.Experiment: [ccName] :: ClientCredentialsApplication -> Text
+ Network.OAuth2.Experiment: [ccScope] :: ClientCredentialsApplication -> Set Scope
+ Network.OAuth2.Experiment: [ccTokenRequestAuthenticationMethod] :: ClientCredentialsApplication -> ClientAuthenticationMethod
+ Network.OAuth2.Experiment: [ccTokenRequestExtraParams] :: ClientCredentialsApplication -> Map Text Text
+ Network.OAuth2.Experiment: [daAuthorizationRequestAuthenticationMethod] :: DeviceAuthorizationApplication -> Maybe ClientAuthenticationMethod
+ Network.OAuth2.Experiment: [daAuthorizationRequestExtraParam] :: DeviceAuthorizationApplication -> Map Text Text
+ Network.OAuth2.Experiment: [daClientId] :: DeviceAuthorizationApplication -> ClientId
+ Network.OAuth2.Experiment: [daClientSecret] :: DeviceAuthorizationApplication -> ClientSecret
+ Network.OAuth2.Experiment: [daName] :: DeviceAuthorizationApplication -> Text
+ Network.OAuth2.Experiment: [daScope] :: DeviceAuthorizationApplication -> Set Scope
+ Network.OAuth2.Experiment: [deviceCode] :: DeviceAuthorizationResponse -> DeviceCode
+ Network.OAuth2.Experiment: [expiresIn] :: DeviceAuthorizationResponse -> Integer
+ Network.OAuth2.Experiment: [idpAuthorizeEndpoint] :: Idp (i :: k) -> URI
+ Network.OAuth2.Experiment: [idpDeviceAuthorizationEndpoint] :: Idp (i :: k) -> Maybe URI
+ Network.OAuth2.Experiment: [idpTokenEndpoint] :: Idp (i :: k) -> URI
+ Network.OAuth2.Experiment: [idpUserInfoEndpoint] :: Idp (i :: k) -> URI
+ Network.OAuth2.Experiment: [idp] :: IdpApplication (i :: k) a -> Idp i
+ Network.OAuth2.Experiment: [interval] :: DeviceAuthorizationResponse -> Maybe Int
+ Network.OAuth2.Experiment: [jbJwtAssertion] :: JwtBearerApplication -> ByteString
+ Network.OAuth2.Experiment: [jbName] :: JwtBearerApplication -> Text
+ Network.OAuth2.Experiment: [ropClientId] :: ResourceOwnerPasswordApplication -> ClientId
+ Network.OAuth2.Experiment: [ropClientSecret] :: ResourceOwnerPasswordApplication -> ClientSecret
+ Network.OAuth2.Experiment: [ropName] :: ResourceOwnerPasswordApplication -> Text
+ Network.OAuth2.Experiment: [ropPassword] :: ResourceOwnerPasswordApplication -> Password
+ Network.OAuth2.Experiment: [ropScope] :: ResourceOwnerPasswordApplication -> Set Scope
+ Network.OAuth2.Experiment: [ropTokenRequestExtraParams] :: ResourceOwnerPasswordApplication -> Map Text Text
+ Network.OAuth2.Experiment: [ropUserName] :: ResourceOwnerPasswordApplication -> Username
+ Network.OAuth2.Experiment: [unAuthorizeState] :: AuthorizeState -> Text
+ Network.OAuth2.Experiment: [unClientId] :: ClientId -> Text
+ Network.OAuth2.Experiment: [unClientSecret] :: ClientSecret -> Text
+ Network.OAuth2.Experiment: [unPassword] :: Password -> Text
+ Network.OAuth2.Experiment: [unRedirectUri] :: RedirectUri -> URI
+ Network.OAuth2.Experiment: [unScope] :: Scope -> Text
+ Network.OAuth2.Experiment: [unUsername] :: Username -> Text
+ Network.OAuth2.Experiment: [userCode] :: DeviceAuthorizationResponse -> Text
+ Network.OAuth2.Experiment: [verificationUriComplete] :: DeviceAuthorizationResponse -> Maybe URI
+ Network.OAuth2.Experiment: [verificationUri] :: DeviceAuthorizationResponse -> URI
+ Network.OAuth2.Experiment: class HasOAuth2Key a => HasDeviceAuthorizationRequest a
+ Network.OAuth2.Experiment: class HasOAuth2Key a
+ Network.OAuth2.Experiment: conduitDeviceAuthorizationRequest :: (MonadIO m, HasDeviceAuthorizationRequest a) => IdpApplication i a -> Manager -> ExceptT ByteString m DeviceAuthorizationResponse
+ Network.OAuth2.Experiment: conduitUserInfoRequestWithCustomMethod :: (MonadIO m, HasUserInfoRequest a, FromJSON b) => (Manager -> AccessToken -> URI -> ExceptT ByteString m b) -> IdpApplication i a -> Manager -> AccessToken -> ExceptT ByteString m b
+ Network.OAuth2.Experiment: data AuthorizationCodeApplication
+ Network.OAuth2.Experiment: data ClientAuthenticationMethod
+ Network.OAuth2.Experiment: data ClientCredentialsApplication
+ Network.OAuth2.Experiment: data DeviceAuthorizationApplication
+ Network.OAuth2.Experiment: data DeviceAuthorizationResponse
+ Network.OAuth2.Experiment: data IdpApplication (i :: k) a
+ Network.OAuth2.Experiment: data JwtBearerApplication
+ Network.OAuth2.Experiment: data NoNeedExchangeToken
+ Network.OAuth2.Experiment: data ResourceOwnerPasswordApplication
+ Network.OAuth2.Experiment: mkAuthorizationRequest :: HasAuthorizeRequest a => IdpApplication i a -> URI
+ Network.OAuth2.Experiment: pollDeviceTokenRequest :: MonadIO m => IdpApplication i DeviceAuthorizationApplication -> Manager -> DeviceAuthorizationResponse -> ExceptT TokenResponseError m OAuth2Token
+ Network.OAuth2.Experiment: type ExchangeTokenInfo a;
+ Network.OAuth2.Experiment: uriToText :: URI -> Text
+ Network.OAuth2.Experiment.Flows.AuthorizationRequest: AuthorizationRequestParam :: Set Scope -> AuthorizeState -> ClientId -> Maybe RedirectUri -> ResponseType -> Map Text Text -> AuthorizationRequestParam
+ Network.OAuth2.Experiment.Flows.AuthorizationRequest: [arClientId] :: AuthorizationRequestParam -> ClientId
+ Network.OAuth2.Experiment.Flows.AuthorizationRequest: [arExtraParams] :: AuthorizationRequestParam -> Map Text Text
+ Network.OAuth2.Experiment.Flows.AuthorizationRequest: [arRedirectUri] :: AuthorizationRequestParam -> Maybe RedirectUri
+ Network.OAuth2.Experiment.Flows.AuthorizationRequest: [arResponseType] :: AuthorizationRequestParam -> ResponseType
+ Network.OAuth2.Experiment.Flows.AuthorizationRequest: [arScope] :: AuthorizationRequestParam -> Set Scope
+ Network.OAuth2.Experiment.Flows.AuthorizationRequest: [arState] :: AuthorizationRequestParam -> AuthorizeState
+ Network.OAuth2.Experiment.Flows.AuthorizationRequest: class HasAuthorizeRequest a
+ Network.OAuth2.Experiment.Flows.AuthorizationRequest: class HasAuthorizeRequest a => HasPkceAuthorizeRequest a
+ Network.OAuth2.Experiment.Flows.AuthorizationRequest: data AuthorizationRequestParam
+ Network.OAuth2.Experiment.Flows.AuthorizationRequest: instance Network.OAuth2.Experiment.Types.ToQueryParam Network.OAuth2.Experiment.Flows.AuthorizationRequest.AuthorizationRequestParam
+ Network.OAuth2.Experiment.Flows.AuthorizationRequest: mkAuthorizationRequest :: HasAuthorizeRequest a => IdpApplication i a -> URI
+ Network.OAuth2.Experiment.Flows.AuthorizationRequest: mkAuthorizationRequestParam :: HasAuthorizeRequest a => a -> AuthorizationRequestParam
+ Network.OAuth2.Experiment.Flows.AuthorizationRequest: mkPkceAuthorizeRequest :: (MonadIO m, HasPkceAuthorizeRequest a) => IdpApplication i a -> m (URI, CodeVerifier)
+ Network.OAuth2.Experiment.Flows.AuthorizationRequest: mkPkceAuthorizeRequestParam :: (HasPkceAuthorizeRequest a, MonadIO m) => a -> m (AuthorizationRequestParam, CodeVerifier)
+ Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest: DeviceAuthorizationRequestParam :: Set Scope -> Maybe ClientId -> Map Text Text -> DeviceAuthorizationRequestParam
+ Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest: DeviceAuthorizationResponse :: DeviceCode -> Text -> URI -> Maybe URI -> Integer -> Maybe Int -> DeviceAuthorizationResponse
+ Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest: DeviceCode :: Text -> DeviceCode
+ Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest: [arClientId] :: DeviceAuthorizationRequestParam -> Maybe ClientId
+ Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest: [arExtraParams] :: DeviceAuthorizationRequestParam -> Map Text Text
+ Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest: [arScope] :: DeviceAuthorizationRequestParam -> Set Scope
+ Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest: [deviceCode] :: DeviceAuthorizationResponse -> DeviceCode
+ Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest: [expiresIn] :: DeviceAuthorizationResponse -> Integer
+ Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest: [interval] :: DeviceAuthorizationResponse -> Maybe Int
+ Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest: [userCode] :: DeviceAuthorizationResponse -> Text
+ Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest: [verificationUriComplete] :: DeviceAuthorizationResponse -> Maybe URI
+ Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest: [verificationUri] :: DeviceAuthorizationResponse -> URI
+ Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest: class HasOAuth2Key a => HasDeviceAuthorizationRequest a
+ Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest: conduitDeviceAuthorizationRequest :: (MonadIO m, HasDeviceAuthorizationRequest a) => IdpApplication i a -> Manager -> ExceptT ByteString m DeviceAuthorizationResponse
+ Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest: data DeviceAuthorizationRequestParam
+ Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest: data DeviceAuthorizationResponse
+ Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest: instance Data.Aeson.Types.FromJSON.FromJSON Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest.DeviceAuthorizationResponse
+ Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest: instance Data.Aeson.Types.FromJSON.FromJSON Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest.DeviceCode
+ Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest: instance Network.OAuth2.Experiment.Types.ToQueryParam Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest.DeviceAuthorizationRequestParam
+ Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest: instance Network.OAuth2.Experiment.Types.ToQueryParam Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest.DeviceCode
+ Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest: mkDeviceAuthorizationRequestParam :: HasDeviceAuthorizationRequest a => a -> DeviceAuthorizationRequestParam
+ Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest: newtype DeviceCode
+ Network.OAuth2.Experiment.Flows.RefreshTokenRequest: RefreshTokenRequest :: RefreshToken -> GrantTypeValue -> Set Scope -> RefreshTokenRequest
+ Network.OAuth2.Experiment.Flows.RefreshTokenRequest: [rrGrantType] :: RefreshTokenRequest -> GrantTypeValue
+ Network.OAuth2.Experiment.Flows.RefreshTokenRequest: [rrRefreshToken] :: RefreshTokenRequest -> RefreshToken
+ Network.OAuth2.Experiment.Flows.RefreshTokenRequest: [rrScope] :: RefreshTokenRequest -> Set Scope
+ Network.OAuth2.Experiment.Flows.RefreshTokenRequest: class (HasOAuth2Key a, HasTokenRequestClientAuthenticationMethod a) => HasRefreshTokenRequest a
+ Network.OAuth2.Experiment.Flows.RefreshTokenRequest: conduitRefreshTokenRequest :: (MonadIO m, HasRefreshTokenRequest a) => IdpApplication i a -> Manager -> RefreshToken -> ExceptT TokenResponseError m OAuth2Token
+ Network.OAuth2.Experiment.Flows.RefreshTokenRequest: data RefreshTokenRequest
+ Network.OAuth2.Experiment.Flows.RefreshTokenRequest: instance Network.OAuth2.Experiment.Types.ToQueryParam Network.OAuth2.Experiment.Flows.RefreshTokenRequest.RefreshTokenRequest
+ Network.OAuth2.Experiment.Flows.RefreshTokenRequest: mkRefreshTokenRequestParam :: HasRefreshTokenRequest a => a -> RefreshToken -> RefreshTokenRequest
+ Network.OAuth2.Experiment.Flows.TokenRequest: NoNeedExchangeToken :: NoNeedExchangeToken
+ Network.OAuth2.Experiment.Flows.TokenRequest: class (HasOAuth2Key a, HasTokenRequestClientAuthenticationMethod a) => HasTokenRequest a where {
+ Network.OAuth2.Experiment.Flows.TokenRequest: class HasTokenRequestClientAuthenticationMethod a
+ Network.OAuth2.Experiment.Flows.TokenRequest: conduitPkceTokenRequest :: (HasTokenRequest a, ToQueryParam (TokenRequest a), MonadIO m) => IdpApplication i a -> Manager -> (ExchangeTokenInfo a, CodeVerifier) -> ExceptT TokenResponseError m OAuth2Token
+ Network.OAuth2.Experiment.Flows.TokenRequest: conduitTokenRequest :: (HasTokenRequest a, ToQueryParam (TokenRequest a), MonadIO m) => IdpApplication i a -> Manager -> ExchangeTokenInfo a -> ExceptT TokenResponseError m OAuth2Token
+ Network.OAuth2.Experiment.Flows.TokenRequest: data NoNeedExchangeToken
+ Network.OAuth2.Experiment.Flows.TokenRequest: data TokenRequest a;
+ Network.OAuth2.Experiment.Flows.TokenRequest: getClientAuthenticationMethod :: HasTokenRequestClientAuthenticationMethod a => a -> ClientAuthenticationMethod
+ Network.OAuth2.Experiment.Flows.TokenRequest: mkTokenRequestParam :: HasTokenRequest a => a -> ExchangeTokenInfo a -> TokenRequest a
+ Network.OAuth2.Experiment.Flows.TokenRequest: type ExchangeTokenInfo a;
+ Network.OAuth2.Experiment.Flows.TokenRequest: }
+ Network.OAuth2.Experiment.Flows.UserInfoRequest: class HasUserInfoRequest a
+ Network.OAuth2.Experiment.Flows.UserInfoRequest: conduitUserInfoRequest :: (MonadIO m, HasUserInfoRequest a, FromJSON b) => IdpApplication i a -> Manager -> AccessToken -> ExceptT ByteString m b
+ Network.OAuth2.Experiment.Flows.UserInfoRequest: conduitUserInfoRequestWithCustomMethod :: (MonadIO m, HasUserInfoRequest a, FromJSON b) => (Manager -> AccessToken -> URI -> ExceptT ByteString m b) -> IdpApplication i a -> Manager -> AccessToken -> ExceptT ByteString m b
+ Network.OAuth2.Experiment.Grants.AuthorizationCode: AuthorizationCodeApplication :: Text -> ClientId -> ClientSecret -> Set Scope -> URI -> AuthorizeState -> Map Text Text -> ClientAuthenticationMethod -> AuthorizationCodeApplication
+ Network.OAuth2.Experiment.Grants.AuthorizationCode: [acAuthorizeRequestExtraParams] :: AuthorizationCodeApplication -> Map Text Text
+ Network.OAuth2.Experiment.Grants.AuthorizationCode: [acAuthorizeState] :: AuthorizationCodeApplication -> AuthorizeState
+ Network.OAuth2.Experiment.Grants.AuthorizationCode: [acClientId] :: AuthorizationCodeApplication -> ClientId
+ Network.OAuth2.Experiment.Grants.AuthorizationCode: [acClientSecret] :: AuthorizationCodeApplication -> ClientSecret
+ Network.OAuth2.Experiment.Grants.AuthorizationCode: [acName] :: AuthorizationCodeApplication -> Text
+ Network.OAuth2.Experiment.Grants.AuthorizationCode: [acRedirectUri] :: AuthorizationCodeApplication -> URI
+ Network.OAuth2.Experiment.Grants.AuthorizationCode: [acScope] :: AuthorizationCodeApplication -> Set Scope
+ Network.OAuth2.Experiment.Grants.AuthorizationCode: [acTokenRequestAuthenticationMethod] :: AuthorizationCodeApplication -> ClientAuthenticationMethod
+ Network.OAuth2.Experiment.Grants.AuthorizationCode: data AuthorizationCodeApplication
+ Network.OAuth2.Experiment.Grants.AuthorizationCode: instance Network.OAuth2.Experiment.Flows.AuthorizationRequest.HasAuthorizeRequest Network.OAuth2.Experiment.Grants.AuthorizationCode.AuthorizationCodeApplication
+ Network.OAuth2.Experiment.Grants.AuthorizationCode: instance Network.OAuth2.Experiment.Flows.AuthorizationRequest.HasPkceAuthorizeRequest Network.OAuth2.Experiment.Grants.AuthorizationCode.AuthorizationCodeApplication
+ Network.OAuth2.Experiment.Grants.AuthorizationCode: instance Network.OAuth2.Experiment.Flows.RefreshTokenRequest.HasRefreshTokenRequest Network.OAuth2.Experiment.Grants.AuthorizationCode.AuthorizationCodeApplication
+ Network.OAuth2.Experiment.Grants.AuthorizationCode: instance Network.OAuth2.Experiment.Flows.TokenRequest.HasTokenRequest Network.OAuth2.Experiment.Grants.AuthorizationCode.AuthorizationCodeApplication
+ Network.OAuth2.Experiment.Grants.AuthorizationCode: instance Network.OAuth2.Experiment.Flows.TokenRequest.HasTokenRequestClientAuthenticationMethod Network.OAuth2.Experiment.Grants.AuthorizationCode.AuthorizationCodeApplication
+ Network.OAuth2.Experiment.Grants.AuthorizationCode: instance Network.OAuth2.Experiment.Flows.UserInfoRequest.HasUserInfoRequest Network.OAuth2.Experiment.Grants.AuthorizationCode.AuthorizationCodeApplication
+ Network.OAuth2.Experiment.Grants.AuthorizationCode: instance Network.OAuth2.Experiment.Types.HasOAuth2Key Network.OAuth2.Experiment.Grants.AuthorizationCode.AuthorizationCodeApplication
+ Network.OAuth2.Experiment.Grants.AuthorizationCode: instance Network.OAuth2.Experiment.Types.ToQueryParam (Network.OAuth2.Experiment.Flows.TokenRequest.TokenRequest Network.OAuth2.Experiment.Grants.AuthorizationCode.AuthorizationCodeApplication)
+ Network.OAuth2.Experiment.Grants.ClientCredentials: ClientCredentialsApplication :: ClientId -> ClientSecret -> Text -> Set Scope -> Map Text Text -> ClientAuthenticationMethod -> ClientCredentialsApplication
+ Network.OAuth2.Experiment.Grants.ClientCredentials: [ccClientId] :: ClientCredentialsApplication -> ClientId
+ Network.OAuth2.Experiment.Grants.ClientCredentials: [ccClientSecret] :: ClientCredentialsApplication -> ClientSecret
+ Network.OAuth2.Experiment.Grants.ClientCredentials: [ccName] :: ClientCredentialsApplication -> Text
+ Network.OAuth2.Experiment.Grants.ClientCredentials: [ccScope] :: ClientCredentialsApplication -> Set Scope
+ Network.OAuth2.Experiment.Grants.ClientCredentials: [ccTokenRequestAuthenticationMethod] :: ClientCredentialsApplication -> ClientAuthenticationMethod
+ Network.OAuth2.Experiment.Grants.ClientCredentials: [ccTokenRequestExtraParams] :: ClientCredentialsApplication -> Map Text Text
+ Network.OAuth2.Experiment.Grants.ClientCredentials: data ClientCredentialsApplication
+ Network.OAuth2.Experiment.Grants.ClientCredentials: instance Network.OAuth2.Experiment.Flows.TokenRequest.HasTokenRequest Network.OAuth2.Experiment.Grants.ClientCredentials.ClientCredentialsApplication
+ Network.OAuth2.Experiment.Grants.ClientCredentials: instance Network.OAuth2.Experiment.Flows.TokenRequest.HasTokenRequestClientAuthenticationMethod Network.OAuth2.Experiment.Grants.ClientCredentials.ClientCredentialsApplication
+ Network.OAuth2.Experiment.Grants.ClientCredentials: instance Network.OAuth2.Experiment.Types.HasOAuth2Key Network.OAuth2.Experiment.Grants.ClientCredentials.ClientCredentialsApplication
+ Network.OAuth2.Experiment.Grants.ClientCredentials: instance Network.OAuth2.Experiment.Types.ToQueryParam (Network.OAuth2.Experiment.Flows.TokenRequest.TokenRequest Network.OAuth2.Experiment.Grants.ClientCredentials.ClientCredentialsApplication)
+ Network.OAuth2.Experiment.Grants.DeviceAuthorization: DeviceAuthorizationApplication :: Text -> ClientId -> ClientSecret -> Set Scope -> Map Text Text -> Maybe ClientAuthenticationMethod -> DeviceAuthorizationApplication
+ Network.OAuth2.Experiment.Grants.DeviceAuthorization: [daAuthorizationRequestAuthenticationMethod] :: DeviceAuthorizationApplication -> Maybe ClientAuthenticationMethod
+ Network.OAuth2.Experiment.Grants.DeviceAuthorization: [daAuthorizationRequestExtraParam] :: DeviceAuthorizationApplication -> Map Text Text
+ Network.OAuth2.Experiment.Grants.DeviceAuthorization: [daClientId] :: DeviceAuthorizationApplication -> ClientId
+ Network.OAuth2.Experiment.Grants.DeviceAuthorization: [daClientSecret] :: DeviceAuthorizationApplication -> ClientSecret
+ Network.OAuth2.Experiment.Grants.DeviceAuthorization: [daName] :: DeviceAuthorizationApplication -> Text
+ Network.OAuth2.Experiment.Grants.DeviceAuthorization: [daScope] :: DeviceAuthorizationApplication -> Set Scope
+ Network.OAuth2.Experiment.Grants.DeviceAuthorization: data DeviceAuthorizationApplication
+ Network.OAuth2.Experiment.Grants.DeviceAuthorization: instance Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest.HasDeviceAuthorizationRequest Network.OAuth2.Experiment.Grants.DeviceAuthorization.DeviceAuthorizationApplication
+ Network.OAuth2.Experiment.Grants.DeviceAuthorization: instance Network.OAuth2.Experiment.Flows.TokenRequest.HasTokenRequest Network.OAuth2.Experiment.Grants.DeviceAuthorization.DeviceAuthorizationApplication
+ Network.OAuth2.Experiment.Grants.DeviceAuthorization: instance Network.OAuth2.Experiment.Flows.TokenRequest.HasTokenRequestClientAuthenticationMethod Network.OAuth2.Experiment.Grants.DeviceAuthorization.DeviceAuthorizationApplication
+ Network.OAuth2.Experiment.Grants.DeviceAuthorization: instance Network.OAuth2.Experiment.Flows.UserInfoRequest.HasUserInfoRequest Network.OAuth2.Experiment.Grants.DeviceAuthorization.DeviceAuthorizationApplication
+ Network.OAuth2.Experiment.Grants.DeviceAuthorization: instance Network.OAuth2.Experiment.Types.HasOAuth2Key Network.OAuth2.Experiment.Grants.DeviceAuthorization.DeviceAuthorizationApplication
+ Network.OAuth2.Experiment.Grants.DeviceAuthorization: instance Network.OAuth2.Experiment.Types.ToQueryParam (Network.OAuth2.Experiment.Flows.TokenRequest.TokenRequest Network.OAuth2.Experiment.Grants.DeviceAuthorization.DeviceAuthorizationApplication)
+ Network.OAuth2.Experiment.Grants.DeviceAuthorization: pollDeviceTokenRequest :: MonadIO m => IdpApplication i DeviceAuthorizationApplication -> Manager -> DeviceAuthorizationResponse -> ExceptT TokenResponseError m OAuth2Token
+ Network.OAuth2.Experiment.Grants.JwtBearer: JwtBearerApplication :: Text -> ByteString -> JwtBearerApplication
+ Network.OAuth2.Experiment.Grants.JwtBearer: [jbJwtAssertion] :: JwtBearerApplication -> ByteString
+ Network.OAuth2.Experiment.Grants.JwtBearer: [jbName] :: JwtBearerApplication -> Text
+ Network.OAuth2.Experiment.Grants.JwtBearer: data JwtBearerApplication
+ Network.OAuth2.Experiment.Grants.JwtBearer: instance Network.OAuth2.Experiment.Flows.TokenRequest.HasTokenRequest Network.OAuth2.Experiment.Grants.JwtBearer.JwtBearerApplication
+ Network.OAuth2.Experiment.Grants.JwtBearer: instance Network.OAuth2.Experiment.Flows.TokenRequest.HasTokenRequestClientAuthenticationMethod Network.OAuth2.Experiment.Grants.JwtBearer.JwtBearerApplication
+ Network.OAuth2.Experiment.Grants.JwtBearer: instance Network.OAuth2.Experiment.Flows.UserInfoRequest.HasUserInfoRequest Network.OAuth2.Experiment.Grants.JwtBearer.JwtBearerApplication
+ Network.OAuth2.Experiment.Grants.JwtBearer: instance Network.OAuth2.Experiment.Types.HasOAuth2Key Network.OAuth2.Experiment.Grants.JwtBearer.JwtBearerApplication
+ Network.OAuth2.Experiment.Grants.JwtBearer: instance Network.OAuth2.Experiment.Types.ToQueryParam (Network.OAuth2.Experiment.Flows.TokenRequest.TokenRequest Network.OAuth2.Experiment.Grants.JwtBearer.JwtBearerApplication)
+ Network.OAuth2.Experiment.Grants.ResourceOwnerPassword: ResourceOwnerPasswordApplication :: ClientId -> ClientSecret -> Text -> Set Scope -> Username -> Password -> Map Text Text -> ResourceOwnerPasswordApplication
+ Network.OAuth2.Experiment.Grants.ResourceOwnerPassword: [ropClientId] :: ResourceOwnerPasswordApplication -> ClientId
+ Network.OAuth2.Experiment.Grants.ResourceOwnerPassword: [ropClientSecret] :: ResourceOwnerPasswordApplication -> ClientSecret
+ Network.OAuth2.Experiment.Grants.ResourceOwnerPassword: [ropName] :: ResourceOwnerPasswordApplication -> Text
+ Network.OAuth2.Experiment.Grants.ResourceOwnerPassword: [ropPassword] :: ResourceOwnerPasswordApplication -> Password
+ Network.OAuth2.Experiment.Grants.ResourceOwnerPassword: [ropScope] :: ResourceOwnerPasswordApplication -> Set Scope
+ Network.OAuth2.Experiment.Grants.ResourceOwnerPassword: [ropTokenRequestExtraParams] :: ResourceOwnerPasswordApplication -> Map Text Text
+ Network.OAuth2.Experiment.Grants.ResourceOwnerPassword: [ropUserName] :: ResourceOwnerPasswordApplication -> Username
+ Network.OAuth2.Experiment.Grants.ResourceOwnerPassword: data ResourceOwnerPasswordApplication
+ Network.OAuth2.Experiment.Grants.ResourceOwnerPassword: instance Network.OAuth2.Experiment.Flows.RefreshTokenRequest.HasRefreshTokenRequest Network.OAuth2.Experiment.Grants.ResourceOwnerPassword.ResourceOwnerPasswordApplication
+ Network.OAuth2.Experiment.Grants.ResourceOwnerPassword: instance Network.OAuth2.Experiment.Flows.TokenRequest.HasTokenRequest Network.OAuth2.Experiment.Grants.ResourceOwnerPassword.ResourceOwnerPasswordApplication
+ Network.OAuth2.Experiment.Grants.ResourceOwnerPassword: instance Network.OAuth2.Experiment.Flows.TokenRequest.HasTokenRequestClientAuthenticationMethod Network.OAuth2.Experiment.Grants.ResourceOwnerPassword.ResourceOwnerPasswordApplication
+ Network.OAuth2.Experiment.Grants.ResourceOwnerPassword: instance Network.OAuth2.Experiment.Flows.UserInfoRequest.HasUserInfoRequest Network.OAuth2.Experiment.Grants.ResourceOwnerPassword.ResourceOwnerPasswordApplication
+ Network.OAuth2.Experiment.Grants.ResourceOwnerPassword: instance Network.OAuth2.Experiment.Types.HasOAuth2Key Network.OAuth2.Experiment.Grants.ResourceOwnerPassword.ResourceOwnerPasswordApplication
+ Network.OAuth2.Experiment.Grants.ResourceOwnerPassword: instance Network.OAuth2.Experiment.Types.ToQueryParam (Network.OAuth2.Experiment.Flows.TokenRequest.TokenRequest Network.OAuth2.Experiment.Grants.ResourceOwnerPassword.ResourceOwnerPasswordApplication)
+ Network.OAuth2.Experiment.Pkce: CodeChallenge :: Text -> CodeChallenge
+ Network.OAuth2.Experiment.Pkce: CodeVerifier :: Text -> CodeVerifier
+ Network.OAuth2.Experiment.Pkce: PkceRequestParam :: CodeVerifier -> CodeChallenge -> CodeChallengeMethod -> PkceRequestParam
+ Network.OAuth2.Experiment.Pkce: S256 :: CodeChallengeMethod
+ Network.OAuth2.Experiment.Pkce: [codeChallengeMethod] :: PkceRequestParam -> CodeChallengeMethod
+ Network.OAuth2.Experiment.Pkce: [codeChallenge] :: PkceRequestParam -> CodeChallenge
+ Network.OAuth2.Experiment.Pkce: [codeVerifier] :: PkceRequestParam -> CodeVerifier
+ Network.OAuth2.Experiment.Pkce: [unCodeChallenge] :: CodeChallenge -> Text
+ Network.OAuth2.Experiment.Pkce: [unCodeVerifier] :: CodeVerifier -> Text
+ Network.OAuth2.Experiment.Pkce: data CodeChallengeMethod
+ Network.OAuth2.Experiment.Pkce: data PkceRequestParam
+ Network.OAuth2.Experiment.Pkce: instance GHC.Show.Show Network.OAuth2.Experiment.Pkce.CodeChallengeMethod
+ Network.OAuth2.Experiment.Pkce: mkPkceParam :: MonadIO m => m PkceRequestParam
+ Network.OAuth2.Experiment.Pkce: newtype CodeChallenge
+ Network.OAuth2.Experiment.Pkce: newtype CodeVerifier
+ Network.OAuth2.Experiment.Types: AuthorizeState :: Text -> AuthorizeState
+ Network.OAuth2.Experiment.Types: ClientId :: Text -> ClientId
+ Network.OAuth2.Experiment.Types: ClientSecret :: Text -> ClientSecret
+ Network.OAuth2.Experiment.Types: Code :: ResponseType
+ Network.OAuth2.Experiment.Types: GTAuthorizationCode :: GrantTypeValue
+ Network.OAuth2.Experiment.Types: GTClientCredentials :: GrantTypeValue
+ Network.OAuth2.Experiment.Types: GTDeviceCode :: GrantTypeValue
+ Network.OAuth2.Experiment.Types: GTJwtBearer :: GrantTypeValue
+ Network.OAuth2.Experiment.Types: GTPassword :: GrantTypeValue
+ Network.OAuth2.Experiment.Types: GTRefreshToken :: GrantTypeValue
+ Network.OAuth2.Experiment.Types: Idp :: URI -> URI -> URI -> Maybe URI -> Idp (i :: k)
+ Network.OAuth2.Experiment.Types: IdpApplication :: Idp i -> a -> IdpApplication (i :: k) a
+ Network.OAuth2.Experiment.Types: Password :: Text -> Password
+ Network.OAuth2.Experiment.Types: RedirectUri :: URI -> RedirectUri
+ Network.OAuth2.Experiment.Types: Scope :: Text -> Scope
+ Network.OAuth2.Experiment.Types: Username :: Text -> Username
+ Network.OAuth2.Experiment.Types: [application] :: IdpApplication (i :: k) a -> a
+ Network.OAuth2.Experiment.Types: [idpAuthorizeEndpoint] :: Idp (i :: k) -> URI
+ Network.OAuth2.Experiment.Types: [idpDeviceAuthorizationEndpoint] :: Idp (i :: k) -> Maybe URI
+ Network.OAuth2.Experiment.Types: [idpTokenEndpoint] :: Idp (i :: k) -> URI
+ Network.OAuth2.Experiment.Types: [idpUserInfoEndpoint] :: Idp (i :: k) -> URI
+ Network.OAuth2.Experiment.Types: [idp] :: IdpApplication (i :: k) a -> Idp i
+ Network.OAuth2.Experiment.Types: [unAuthorizeState] :: AuthorizeState -> Text
+ Network.OAuth2.Experiment.Types: [unClientId] :: ClientId -> Text
+ Network.OAuth2.Experiment.Types: [unClientSecret] :: ClientSecret -> Text
+ Network.OAuth2.Experiment.Types: [unPassword] :: Password -> Text
+ Network.OAuth2.Experiment.Types: [unRedirectUri] :: RedirectUri -> URI
+ Network.OAuth2.Experiment.Types: [unScope] :: Scope -> Text
+ Network.OAuth2.Experiment.Types: [unUsername] :: Username -> Text
+ Network.OAuth2.Experiment.Types: class HasOAuth2Key a
+ Network.OAuth2.Experiment.Types: class ToQueryParam a
+ Network.OAuth2.Experiment.Types: data GrantTypeValue
+ Network.OAuth2.Experiment.Types: data Idp (i :: k)
+ Network.OAuth2.Experiment.Types: data IdpApplication (i :: k) a
+ Network.OAuth2.Experiment.Types: data ResponseType
+ Network.OAuth2.Experiment.Types: instance Data.String.IsString Network.OAuth2.Experiment.Types.AuthorizeState
+ Network.OAuth2.Experiment.Types: instance Data.String.IsString Network.OAuth2.Experiment.Types.ClientId
+ Network.OAuth2.Experiment.Types: instance Data.String.IsString Network.OAuth2.Experiment.Types.ClientSecret
+ Network.OAuth2.Experiment.Types: instance Data.String.IsString Network.OAuth2.Experiment.Types.Password
+ Network.OAuth2.Experiment.Types: instance Data.String.IsString Network.OAuth2.Experiment.Types.Scope
+ Network.OAuth2.Experiment.Types: instance Data.String.IsString Network.OAuth2.Experiment.Types.Username
+ Network.OAuth2.Experiment.Types: instance GHC.Classes.Eq Network.OAuth2.Experiment.Types.AuthorizeState
+ Network.OAuth2.Experiment.Types: instance GHC.Classes.Eq Network.OAuth2.Experiment.Types.ClientId
+ Network.OAuth2.Experiment.Types: instance GHC.Classes.Eq Network.OAuth2.Experiment.Types.ClientSecret
+ Network.OAuth2.Experiment.Types: instance GHC.Classes.Eq Network.OAuth2.Experiment.Types.GrantTypeValue
+ Network.OAuth2.Experiment.Types: instance GHC.Classes.Eq Network.OAuth2.Experiment.Types.Password
+ Network.OAuth2.Experiment.Types: instance GHC.Classes.Eq Network.OAuth2.Experiment.Types.RedirectUri
+ Network.OAuth2.Experiment.Types: instance GHC.Classes.Eq Network.OAuth2.Experiment.Types.Scope
+ Network.OAuth2.Experiment.Types: instance GHC.Classes.Eq Network.OAuth2.Experiment.Types.Username
+ Network.OAuth2.Experiment.Types: instance GHC.Classes.Ord Network.OAuth2.Experiment.Types.Scope
+ Network.OAuth2.Experiment.Types: instance GHC.Show.Show Network.OAuth2.Experiment.Types.ClientId
+ Network.OAuth2.Experiment.Types: instance GHC.Show.Show Network.OAuth2.Experiment.Types.GrantTypeValue
+ Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.ToQueryParam (Data.Set.Internal.Set Network.OAuth2.Experiment.Types.Scope)
+ Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.ToQueryParam Network.OAuth.OAuth2.Internal.ExchangeToken
+ Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.ToQueryParam Network.OAuth.OAuth2.Internal.RefreshToken
+ Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.ToQueryParam Network.OAuth2.Experiment.Pkce.CodeChallenge
+ Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.ToQueryParam Network.OAuth2.Experiment.Pkce.CodeChallengeMethod
+ Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.ToQueryParam Network.OAuth2.Experiment.Pkce.CodeVerifier
+ Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.ToQueryParam Network.OAuth2.Experiment.Types.AuthorizeState
+ Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.ToQueryParam Network.OAuth2.Experiment.Types.ClientId
+ Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.ToQueryParam Network.OAuth2.Experiment.Types.ClientSecret
+ Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.ToQueryParam Network.OAuth2.Experiment.Types.GrantTypeValue
+ Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.ToQueryParam Network.OAuth2.Experiment.Types.Password
+ Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.ToQueryParam Network.OAuth2.Experiment.Types.RedirectUri
+ Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.ToQueryParam Network.OAuth2.Experiment.Types.ResponseType
+ Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.ToQueryParam Network.OAuth2.Experiment.Types.Username
+ Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.ToQueryParam a => Network.OAuth2.Experiment.Types.ToQueryParam (GHC.Maybe.Maybe a)
+ Network.OAuth2.Experiment.Types: mkOAuth2Key :: HasOAuth2Key a => a -> OAuth2
+ Network.OAuth2.Experiment.Types: newtype AuthorizeState
+ Network.OAuth2.Experiment.Types: newtype ClientId
+ Network.OAuth2.Experiment.Types: newtype ClientSecret
+ Network.OAuth2.Experiment.Types: newtype Password
+ Network.OAuth2.Experiment.Types: newtype RedirectUri
+ Network.OAuth2.Experiment.Types: newtype Scope
+ Network.OAuth2.Experiment.Types: newtype Username
+ Network.OAuth2.Experiment.Types: toOAuth2Key :: ClientId -> ClientSecret -> OAuth2
+ Network.OAuth2.Experiment.Types: toQueryParam :: ToQueryParam a => a -> Map Text Text
- Network.OAuth.OAuth2.AuthorizationRequest: AccessDenied :: Errors
+ Network.OAuth.OAuth2.AuthorizationRequest: AccessDenied :: AuthorizationResponseErrorCode
- Network.OAuth.OAuth2.AuthorizationRequest: InvalidRequest :: Errors
+ Network.OAuth.OAuth2.AuthorizationRequest: InvalidRequest :: AuthorizationResponseErrorCode
- Network.OAuth.OAuth2.AuthorizationRequest: InvalidScope :: Errors
+ Network.OAuth.OAuth2.AuthorizationRequest: InvalidScope :: AuthorizationResponseErrorCode
- Network.OAuth.OAuth2.AuthorizationRequest: ServerError :: Errors
+ Network.OAuth.OAuth2.AuthorizationRequest: ServerError :: AuthorizationResponseErrorCode
- Network.OAuth.OAuth2.AuthorizationRequest: TemporarilyUnavailable :: Errors
+ Network.OAuth.OAuth2.AuthorizationRequest: TemporarilyUnavailable :: AuthorizationResponseErrorCode
- Network.OAuth.OAuth2.AuthorizationRequest: UnauthorizedClient :: Errors
+ Network.OAuth.OAuth2.AuthorizationRequest: UnauthorizedClient :: AuthorizationResponseErrorCode
- Network.OAuth.OAuth2.AuthorizationRequest: UnsupportedResponseType :: Errors
+ Network.OAuth.OAuth2.AuthorizationRequest: UnsupportedResponseType :: AuthorizationResponseErrorCode
- Network.OAuth.OAuth2.HttpClient: authGetJSON :: (FromJSON a, MonadIO m) => Manager -> AccessToken -> URI -> ExceptT ByteString m a
+ Network.OAuth.OAuth2.HttpClient: authGetJSON :: (MonadIO m, FromJSON a) => Manager -> AccessToken -> URI -> ExceptT ByteString m a
- Network.OAuth.OAuth2.HttpClient: authGetJSONInternal :: (FromJSON a, MonadIO m) => APIAuthenticationMethod -> Manager -> AccessToken -> URI -> ExceptT ByteString m a
+ Network.OAuth.OAuth2.HttpClient: authGetJSONInternal :: (MonadIO m, FromJSON a) => APIAuthenticationMethod -> Manager -> AccessToken -> URI -> ExceptT ByteString m a
- Network.OAuth.OAuth2.HttpClient: authPostJSON :: (FromJSON a, MonadIO m) => Manager -> AccessToken -> URI -> PostBody -> ExceptT ByteString m a
+ Network.OAuth.OAuth2.HttpClient: authPostJSON :: (MonadIO m, FromJSON a) => Manager -> AccessToken -> URI -> PostBody -> ExceptT ByteString m a
- Network.OAuth.OAuth2.HttpClient: authPostJSONInternal :: (FromJSON a, MonadIO m) => APIAuthenticationMethod -> Manager -> AccessToken -> URI -> PostBody -> ExceptT ByteString m a
+ Network.OAuth.OAuth2.HttpClient: authPostJSONInternal :: (MonadIO m, FromJSON a) => APIAuthenticationMethod -> Manager -> AccessToken -> URI -> PostBody -> ExceptT ByteString m a
- Network.OAuth.OAuth2.HttpClient: authPostJSONWithAuthMethod :: (FromJSON a, MonadIO m) => APIAuthenticationMethod -> Manager -> AccessToken -> URI -> PostBody -> ExceptT ByteString m a
+ Network.OAuth.OAuth2.HttpClient: authPostJSONWithAuthMethod :: (MonadIO m, FromJSON a) => APIAuthenticationMethod -> Manager -> AccessToken -> URI -> PostBody -> ExceptT ByteString m a
- Network.OAuth.OAuth2.TokenRequest: InvalidClient :: TokenRequestErrorCode
+ Network.OAuth.OAuth2.TokenRequest: InvalidClient :: TokenResponseErrorCode
- Network.OAuth.OAuth2.TokenRequest: InvalidGrant :: TokenRequestErrorCode
+ Network.OAuth.OAuth2.TokenRequest: InvalidGrant :: TokenResponseErrorCode
- Network.OAuth.OAuth2.TokenRequest: InvalidRequest :: TokenRequestErrorCode
+ Network.OAuth.OAuth2.TokenRequest: InvalidRequest :: TokenResponseErrorCode
- Network.OAuth.OAuth2.TokenRequest: InvalidScope :: TokenRequestErrorCode
+ Network.OAuth.OAuth2.TokenRequest: InvalidScope :: TokenResponseErrorCode
- Network.OAuth.OAuth2.TokenRequest: UnauthorizedClient :: TokenRequestErrorCode
+ Network.OAuth.OAuth2.TokenRequest: UnauthorizedClient :: TokenResponseErrorCode
- Network.OAuth.OAuth2.TokenRequest: UnknownErrorCode :: Text -> TokenRequestErrorCode
+ Network.OAuth.OAuth2.TokenRequest: UnknownErrorCode :: Text -> TokenResponseErrorCode
- Network.OAuth.OAuth2.TokenRequest: UnsupportedGrantType :: TokenRequestErrorCode
+ Network.OAuth.OAuth2.TokenRequest: UnsupportedGrantType :: TokenResponseErrorCode
- Network.OAuth.OAuth2.TokenRequest: doJSONPostRequest :: (MonadIO m, FromJSON a) => Manager -> OAuth2 -> URI -> PostBody -> ExceptT TokenRequestError m a
+ Network.OAuth.OAuth2.TokenRequest: doJSONPostRequest :: (MonadIO m, FromJSON a) => Manager -> OAuth2 -> URI -> PostBody -> ExceptT TokenResponseError m a
- Network.OAuth.OAuth2.TokenRequest: doSimplePostRequest :: MonadIO m => Manager -> OAuth2 -> URI -> PostBody -> ExceptT TokenRequestError m ByteString
+ Network.OAuth.OAuth2.TokenRequest: doSimplePostRequest :: MonadIO m => Manager -> OAuth2 -> URI -> PostBody -> ExceptT TokenResponseError m ByteString
- Network.OAuth.OAuth2.TokenRequest: fetchAccessToken :: MonadIO m => Manager -> OAuth2 -> ExchangeToken -> ExceptT TokenRequestError m OAuth2Token
+ Network.OAuth.OAuth2.TokenRequest: fetchAccessToken :: MonadIO m => Manager -> OAuth2 -> ExchangeToken -> ExceptT TokenResponseError m OAuth2Token
- Network.OAuth.OAuth2.TokenRequest: fetchAccessToken2 :: MonadIO m => Manager -> OAuth2 -> ExchangeToken -> ExceptT TokenRequestError m OAuth2Token
+ Network.OAuth.OAuth2.TokenRequest: fetchAccessToken2 :: MonadIO m => Manager -> OAuth2 -> ExchangeToken -> ExceptT TokenResponseError m OAuth2Token
- Network.OAuth.OAuth2.TokenRequest: fetchAccessTokenInternal :: MonadIO m => ClientAuthenticationMethod -> Manager -> OAuth2 -> ExchangeToken -> ExceptT TokenRequestError m OAuth2Token
+ Network.OAuth.OAuth2.TokenRequest: fetchAccessTokenInternal :: MonadIO m => ClientAuthenticationMethod -> Manager -> OAuth2 -> ExchangeToken -> ExceptT TokenResponseError m OAuth2Token
- Network.OAuth.OAuth2.TokenRequest: fetchAccessTokenWithAuthMethod :: MonadIO m => ClientAuthenticationMethod -> Manager -> OAuth2 -> ExchangeToken -> ExceptT TokenRequestError m OAuth2Token
+ Network.OAuth.OAuth2.TokenRequest: fetchAccessTokenWithAuthMethod :: MonadIO m => ClientAuthenticationMethod -> Manager -> OAuth2 -> ExchangeToken -> ExceptT TokenResponseError m OAuth2Token
- Network.OAuth.OAuth2.TokenRequest: handleOAuth2TokenResponse :: Response ByteString -> Either TokenRequestError ByteString
+ Network.OAuth.OAuth2.TokenRequest: handleOAuth2TokenResponse :: Response ByteString -> Either TokenResponseError ByteString
- Network.OAuth.OAuth2.TokenRequest: parseResponseFlexible :: FromJSON a => ByteString -> Either TokenRequestError a
+ Network.OAuth.OAuth2.TokenRequest: parseResponseFlexible :: FromJSON a => ByteString -> Either TokenResponseError a
- Network.OAuth.OAuth2.TokenRequest: parseResponseString :: FromJSON a => ByteString -> Either TokenRequestError a
+ Network.OAuth.OAuth2.TokenRequest: parseResponseString :: FromJSON a => ByteString -> Either TokenResponseError a
- Network.OAuth.OAuth2.TokenRequest: refreshAccessToken :: MonadIO m => Manager -> OAuth2 -> RefreshToken -> ExceptT TokenRequestError m OAuth2Token
+ Network.OAuth.OAuth2.TokenRequest: refreshAccessToken :: MonadIO m => Manager -> OAuth2 -> RefreshToken -> ExceptT TokenResponseError m OAuth2Token
- Network.OAuth.OAuth2.TokenRequest: refreshAccessToken2 :: MonadIO m => Manager -> OAuth2 -> RefreshToken -> ExceptT TokenRequestError m OAuth2Token
+ Network.OAuth.OAuth2.TokenRequest: refreshAccessToken2 :: MonadIO m => Manager -> OAuth2 -> RefreshToken -> ExceptT TokenResponseError m OAuth2Token
- Network.OAuth.OAuth2.TokenRequest: refreshAccessTokenInternal :: MonadIO m => ClientAuthenticationMethod -> Manager -> OAuth2 -> RefreshToken -> ExceptT TokenRequestError m OAuth2Token
+ Network.OAuth.OAuth2.TokenRequest: refreshAccessTokenInternal :: MonadIO m => ClientAuthenticationMethod -> Manager -> OAuth2 -> RefreshToken -> ExceptT TokenResponseError m OAuth2Token
- Network.OAuth.OAuth2.TokenRequest: refreshAccessTokenWithAuthMethod :: MonadIO m => ClientAuthenticationMethod -> Manager -> OAuth2 -> RefreshToken -> ExceptT TokenRequestError m OAuth2Token
+ Network.OAuth.OAuth2.TokenRequest: refreshAccessTokenWithAuthMethod :: MonadIO m => ClientAuthenticationMethod -> Manager -> OAuth2 -> RefreshToken -> ExceptT TokenResponseError m OAuth2Token
- Network.OAuth2.Experiment: Idp :: URI -> URI -> URI -> (forall m. (FromJSON (IdpUserInfo a), MonadIO m) => Manager -> AccessToken -> URI -> ExceptT ByteString m (IdpUserInfo a)) -> Idp a
+ Network.OAuth2.Experiment: Idp :: URI -> URI -> URI -> Maybe URI -> Idp (i :: k)
- Network.OAuth2.Experiment: class HasAuthorizeRequest (a :: GrantTypeFlow) where {
+ Network.OAuth2.Experiment: class HasAuthorizeRequest a
- Network.OAuth2.Experiment: class HasRefreshTokenRequest (a :: GrantTypeFlow) where {
+ Network.OAuth2.Experiment: class (HasOAuth2Key a, HasTokenRequestClientAuthenticationMethod a) => HasRefreshTokenRequest a
- Network.OAuth2.Experiment: class HasTokenRequest (a :: GrantTypeFlow) where {
+ Network.OAuth2.Experiment: class (HasOAuth2Key a, HasTokenRequestClientAuthenticationMethod a) => HasTokenRequest a where {
- Network.OAuth2.Experiment: class HasUserInfoRequest (a :: GrantTypeFlow)
+ Network.OAuth2.Experiment: class HasUserInfoRequest a
- Network.OAuth2.Experiment: conduitPkceTokenRequest :: (HasPkceTokenRequest b, MonadIO m) => IdpApplication b i -> Manager -> (ExchangeToken, CodeVerifier) -> ExceptT TokenRequestError m OAuth2Token
+ Network.OAuth2.Experiment: conduitPkceTokenRequest :: (HasTokenRequest a, ToQueryParam (TokenRequest a), MonadIO m) => IdpApplication i a -> Manager -> (ExchangeTokenInfo a, CodeVerifier) -> ExceptT TokenResponseError m OAuth2Token
- Network.OAuth2.Experiment: conduitRefreshTokenRequest :: (HasRefreshTokenRequest a, MonadIO m) => IdpApplication a i -> Manager -> RefreshToken -> ExceptT TokenRequestError m OAuth2Token
+ Network.OAuth2.Experiment: conduitRefreshTokenRequest :: (MonadIO m, HasRefreshTokenRequest a) => IdpApplication i a -> Manager -> RefreshToken -> ExceptT TokenResponseError m OAuth2Token
- Network.OAuth2.Experiment: conduitTokenRequest :: (HasTokenRequest a, MonadIO m) => IdpApplication a i -> Manager -> WithExchangeToken a (ExceptT TokenRequestError m OAuth2Token)
+ Network.OAuth2.Experiment: conduitTokenRequest :: (HasTokenRequest a, ToQueryParam (TokenRequest a), MonadIO m) => IdpApplication i a -> Manager -> ExchangeTokenInfo a -> ExceptT TokenResponseError m OAuth2Token
- Network.OAuth2.Experiment: conduitUserInfoRequest :: (HasUserInfoRequest a, FromJSON (IdpUserInfo i)) => IdpApplication a i -> Manager -> AccessToken -> ExceptT ByteString IO (IdpUserInfo i)
+ Network.OAuth2.Experiment: conduitUserInfoRequest :: (MonadIO m, HasUserInfoRequest a, FromJSON b) => IdpApplication i a -> Manager -> AccessToken -> ExceptT ByteString m b
- Network.OAuth2.Experiment: data Idp a
+ Network.OAuth2.Experiment: data Idp (i :: k)
- Network.OAuth2.Experiment: mkPkceAuthorizeRequest :: (HasPkceAuthorizeRequest a, MonadIO m) => IdpApplication a i -> m (Text, CodeVerifier)
+ Network.OAuth2.Experiment: mkPkceAuthorizeRequest :: (MonadIO m, HasPkceAuthorizeRequest a) => IdpApplication i a -> m (URI, CodeVerifier)

Files

hoauth2.cabal view
@@ -2,9 +2,11 @@ name:               hoauth2  -- http://wiki.haskell.org/Package_versioning_policy-version:            2.8.1+version:            2.9.0 synopsis:           Haskell OAuth2 authentication client-description:        See readme for more details.+description:+  This is Haskell binding of OAuth2 Authorization framework and Bearer Token Usage framework.+ homepage:           https://github.com/freizl/hoauth2 license:            MIT license-file:       LICENSE@@ -26,9 +28,9 @@   default-language:   Haskell2010   autogen-modules:    Paths_hoauth2   other-modules:+    Network.HTTP.Client.Contrib     Network.OAuth.OAuth2.Internal-    Network.OAuth2.Experiment.Pkce-    Network.OAuth2.Experiment.Types+    Network.OAuth2.Experiment.Grants     Network.OAuth2.Experiment.Utils     Paths_hoauth2 @@ -38,15 +40,27 @@     Network.OAuth.OAuth2.HttpClient     Network.OAuth.OAuth2.TokenRequest     Network.OAuth2.Experiment+    Network.OAuth2.Experiment.Grants.AuthorizationCode+    Network.OAuth2.Experiment.Grants.ClientCredentials+    Network.OAuth2.Experiment.Grants.DeviceAuthorization+    Network.OAuth2.Experiment.Grants.JwtBearer+    Network.OAuth2.Experiment.Grants.ResourceOwnerPassword+    Network.OAuth2.Experiment.Flows.AuthorizationRequest+    Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest+    Network.OAuth2.Experiment.Flows.RefreshTokenRequest+    Network.OAuth2.Experiment.Flows.TokenRequest+    Network.OAuth2.Experiment.Flows.UserInfoRequest+    Network.OAuth2.Experiment.Types+    Network.OAuth2.Experiment.Pkce    default-extensions:-    DataKinds     DeriveGeneric     GeneralizedNewtypeDeriving     ImportQualifiedPost+    InstanceSigs     OverloadedStrings+    PolyKinds     RecordWildCards-    TypeApplications     TypeFamilies    build-depends:@@ -64,13 +78,13 @@     , memory                ^>=0.18     , microlens             ^>=0.4.0     , text                  ^>=2.0-    , transformers          >=0.6+    , transformers          >=0.4    && <0.7     , uri-bytestring        >=0.2.3  && <0.4     , uri-bytestring-aeson  ^>=0.1    ghc-options:     -Wall -Wtabs -Wno-unused-do-bind -Wunused-packages -Wpartial-fields-    -Wwarnings-deprecations+    -Wwarn -Wwarnings-deprecations  test-suite hoauth-tests   type:               exitcode-stdio-1.0@@ -78,10 +92,11 @@   hs-source-dirs:     test   ghc-options:        -Wall   build-depends:-    , aeson    >=2.0 && <2.2-    , base     >=4   && <5+    , aeson           >=2.0   && <2.2+    , base            >=4     && <5     , hoauth2-    , hspec    >=2   && <3+    , hspec           >=2     && <3+    , uri-bytestring  >=0.2.3 && <0.4    other-modules:      Network.OAuth.OAuth2.TokenRequestSpec   default-language:   Haskell2010@@ -90,3 +105,6 @@     OverloadedStrings    build-tool-depends: hspec-discover:hspec-discover >=2 && <3+  ghc-options:+    -Wall -Wtabs -Wno-unused-do-bind -Wunused-packages -Wpartial-fields+    -Wwarn -Wwarnings-deprecations
+ src/Network/HTTP/Client/Contrib.hs view
@@ -0,0 +1,25 @@+module Network.HTTP.Client.Contrib where++import Data.Aeson+import Data.Bifunctor+import Data.ByteString.Lazy.Char8 qualified as BSL+import Network.HTTP.Conduit+import Network.HTTP.Types qualified as HT++-- | Get response body out of a @Response@+handleResponse :: Response BSL.ByteString -> Either BSL.ByteString BSL.ByteString+handleResponse rsp+  | HT.statusIsSuccessful (responseStatus rsp) = Right (responseBody rsp)+  -- TODO: better to surface up entire resp so that client can decide what to do when error happens.+  -- e.g. when 404, the response body could be empty hence library user has no idea what's happening.+  -- Which will be breaking changes.+  -- The current work around is surface up entire response as string.+  | BSL.null (responseBody rsp) = Left (BSL.pack $ show rsp)+  | otherwise = Left (responseBody rsp)++handleResponseJSON ::+  FromJSON a =>+  Response BSL.ByteString ->+  Either BSL.ByteString a+handleResponseJSON =+  either Left (first BSL.pack . eitherDecode) . handleResponse
src/Network/OAuth/OAuth2.hs view
@@ -1,17 +1,26 @@ -- | A lightweight oauth2 Haskell binding. -- See Readme for more details module Network.OAuth.OAuth2 (-  module Network.OAuth.OAuth2.HttpClient,+  module Network.OAuth.OAuth2.Internal,++  -- * Authorization Requset   module Network.OAuth.OAuth2.AuthorizationRequest,++  -- * Token Request   module Network.OAuth.OAuth2.TokenRequest,-  module Network.OAuth.OAuth2.Internal,++  -- * OAuth'ed http client utilities+  module Network.OAuth.OAuth2.HttpClient, ) where  {-   Hiding Errors data type from default.   Shall qualified import given the naming collision. -}-import Network.OAuth.OAuth2.AuthorizationRequest hiding (Errors (..))+import Network.OAuth.OAuth2.AuthorizationRequest hiding (+  AuthorizationResponseError (..),+  AuthorizationResponseErrorCode (..),+ ) import Network.OAuth.OAuth2.HttpClient import Network.OAuth.OAuth2.Internal import Network.OAuth.OAuth2.TokenRequest
src/Network/OAuth/OAuth2/AuthorizationRequest.hs view
@@ -1,6 +1,3 @@-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE OverloadedStrings #-}- -- | Bindings Authorization part of The OAuth 2.0 Authorization Framework -- RFC6749 <https://www.rfc-editor.org/rfc/rfc6749> module Network.OAuth.OAuth2.AuthorizationRequest where@@ -8,28 +5,34 @@ import Data.Aeson import Data.Function (on) import Data.List qualified as List+import Data.Text (Text) import Data.Text.Encoding qualified as T-import GHC.Generics (Generic) import Lens.Micro (over) import Network.OAuth.OAuth2.Internal import URI.ByteString+import Prelude hiding (error)  -------------------------------------------------- --- * Errors+-- * Authorization Request Errors  -------------------------------------------------- -instance FromJSON Errors where-  parseJSON = genericParseJSON defaultOptions {constructorTagModifier = camelTo2 '_'}- -- | 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 Errors+data AuthorizationResponseError = AuthorizationResponseError+  { authorizationResponseError :: AuthorizationResponseErrorCode+  , authorizationResponseErrorDescription :: Maybe Text+  , authorizationResponseErrorUri :: Maybe (URIRef Absolute)+  }+  deriving (Show, Eq)++data AuthorizationResponseErrorCode   = InvalidRequest   | UnauthorizedClient   | AccessDenied@@ -37,7 +40,27 @@   | InvalidScope   | ServerError   | TemporarilyUnavailable-  deriving (Show, Eq, Generic)+  | UnknownErrorCode Text+  deriving (Show, Eq)++instance FromJSON AuthorizationResponseErrorCode where+  parseJSON = withText "parseJSON AuthorizationResponseErrorCode" $ \t ->+    pure $ case t of+      "invalid_request" -> InvalidRequest+      "unauthorized_client" -> UnauthorizedClient+      "access_denied" -> AccessDenied+      "unsupported_response_type" -> UnsupportedResponseType+      "invalid_scope" -> InvalidScope+      "server_error" -> ServerError+      "temporarily_unavailable" -> TemporarilyUnavailable+      _ -> UnknownErrorCode t++instance FromJSON AuthorizationResponseError where+  parseJSON = withObject "parseJSON AuthorizationResponseError" $ \t -> do+    authorizationResponseError <- t .: "error"+    authorizationResponseErrorDescription <- t .:? "error_description"+    authorizationResponseErrorUri <- t .:? "error_uri"+    pure AuthorizationResponseError {..}  -------------------------------------------------- 
src/Network/OAuth/OAuth2/HttpClient.hs view
@@ -1,7 +1,3 @@-{-# LANGUAGE ExplicitForAll #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedStrings #-}- -- | Bindings for The OAuth 2.0 Authorization Framework: Bearer Token Usage -- RFC6750 <https://www.rfc-editor.org/rfc/rfc6750> module Network.OAuth.OAuth2.HttpClient (@@ -34,6 +30,7 @@ import Data.Maybe (fromJust, isJust) import Data.Text.Encoding qualified as T import Lens.Micro (over)+import Network.HTTP.Client.Contrib (handleResponse) import Network.HTTP.Conduit import Network.HTTP.Types qualified as HT import Network.OAuth.OAuth2.Internal@@ -50,7 +47,7 @@ -- | Conduct an authorized GET request and return response as JSON. --   Inject Access Token to Authorization Header. authGetJSON ::-  (FromJSON a, MonadIO m) =>+  (MonadIO m, FromJSON a) =>   -- | HTTP connection manager.   Manager ->   AccessToken ->@@ -59,8 +56,9 @@   ExceptT BSL.ByteString m a authGetJSON = authGetJSONWithAuthMethod AuthInRequestHeader +-- | Deprecated. Use `authGetJSONWithAuthMethod` instead. authGetJSONInternal ::-  (FromJSON a, MonadIO m) =>+  (MonadIO m, FromJSON a) =>   APIAuthenticationMethod ->   -- | HTTP connection manager.   Manager ->@@ -91,7 +89,7 @@ -- | Conduct an authorized GET request. --   Inject Access Token to Authorization Header. authGetBS ::-  (MonadIO m) =>+  MonadIO m =>   -- | HTTP connection manager.   Manager ->   AccessToken ->@@ -102,7 +100,7 @@  -- | Same to 'authGetBS' but set access token to query parameter rather than header authGetBS2 ::-  (MonadIO m) =>+  MonadIO m =>   -- | HTTP connection manager.   Manager ->   AccessToken ->@@ -113,7 +111,7 @@ {-# DEPRECATED authGetBS2 "use authGetBSWithAuthMethod" #-}  authGetBSInternal ::-  (MonadIO m) =>+  MonadIO m =>   APIAuthenticationMethod ->   -- | HTTP connection manager.   Manager ->@@ -129,7 +127,7 @@ -- -- @since 2.6.0 authGetBSWithAuthMethod ::-  (MonadIO m) =>+  MonadIO m =>   -- | Specify the way that how to append the 'AccessToken' in the request   APIAuthenticationMethod ->   -- | HTTP connection manager.@@ -149,7 +147,7 @@ -- | Conduct POST request and return response as JSON. --   Inject Access Token to Authorization Header. authPostJSON ::-  (FromJSON a, MonadIO m) =>+  (MonadIO m, FromJSON a) =>   -- | HTTP connection manager.   Manager ->   AccessToken ->@@ -160,7 +158,7 @@ authPostJSON = authPostJSONWithAuthMethod AuthInRequestHeader  authPostJSONInternal ::-  (FromJSON a, MonadIO m) =>+  (MonadIO m, FromJSON a) =>   APIAuthenticationMethod ->   -- | HTTP connection manager.   Manager ->@@ -177,7 +175,7 @@ -- -- @since 2.6.0 authPostJSONWithAuthMethod ::-  (FromJSON a, MonadIO m) =>+  (MonadIO m, FromJSON a) =>   APIAuthenticationMethod ->   -- | HTTP connection manager.   Manager ->@@ -193,7 +191,7 @@ -- | Conduct POST request. --   Inject Access Token to http header (Authorization) authPostBS ::-  (MonadIO m) =>+  MonadIO m =>   -- | HTTP connection manager.   Manager ->   AccessToken ->@@ -205,7 +203,7 @@  -- | Conduct POST request with access token only in the request body but header. authPostBS2 ::-  (MonadIO m) =>+  MonadIO m =>   -- | HTTP connection manager.   Manager ->   AccessToken ->@@ -218,7 +216,7 @@  -- | Conduct POST request with access token only in the header and not in body authPostBS3 ::-  (MonadIO m) =>+  MonadIO m =>   -- | HTTP connection manager.   Manager ->   AccessToken ->@@ -230,7 +228,7 @@ {-# DEPRECATED authPostBS3 "use 'authPostBSWithAuthMethod'" #-}  authPostBSInternal ::-  (MonadIO m) =>+  MonadIO m =>   APIAuthenticationMethod ->   -- | HTTP connection manager.   Manager ->@@ -247,7 +245,7 @@ -- -- @since 2.6.0 authPostBSWithAuthMethod ::-  (MonadIO m) =>+  MonadIO m =>   APIAuthenticationMethod ->   -- | HTTP connection manager.   Manager ->@@ -293,7 +291,7 @@  -- | Send an HTTP request. authRequest ::-  (MonadIO m) =>+  MonadIO m =>   -- | Request to perform   Request ->   -- | Modify request before sending@@ -305,21 +303,10 @@   resp <- httpLbs (upReq req) manage   pure (handleResponse resp) --- | Get response body out of a @Response@-handleResponse :: Response BSL.ByteString -> Either BSL.ByteString BSL.ByteString-handleResponse rsp-  | HT.statusIsSuccessful (responseStatus rsp) = Right (responseBody rsp)-  -- FIXME: better to surface up entire resp so that client can decide what to do when error happens.-  -- e.g. when 404, the response body could be empty hence library user has no idea what's happening.-  -- Which will be breaking changes.-  -- The current work around is surface up entire response as string.-  | BSL.null (responseBody rsp) = Left (BSL.pack $ show rsp)-  | otherwise = Left (responseBody rsp)- -- | Set several header values:---   + userAgennt    : `hoauth2`---   + accept        : `application/json`---   + authorization : 'Bearer' `xxxxx` if 'AccessToken' provided.+--   + userAgennt    : "hoauth2"+--   + accept        : "application/json"+--   + authorization : "Bearer xxxxx" if 'Network.OAuth.OAuth2.AccessToken' provided. updateRequestHeaders :: Maybe AccessToken -> Request -> Request updateRequestHeaders t req =   let bearer = [(HT.hAuthorization, "Bearer " `BS.append` T.encodeUtf8 (atoken (fromJust t))) | isJust t]@@ -330,7 +317,7 @@ setMethod :: HT.StdMethod -> Request -> Request setMethod m req = req {method = HT.renderStdMethod m} --- | For `GET` method API.+-- | For @GET@ method API. appendAccessToken ::   -- | Base URI   URIRef a ->@@ -340,6 +327,6 @@   URIRef a appendAccessToken uri t = over (queryL . queryPairsL) (\query -> query ++ accessTokenToParam t) uri --- | Create 'QueryParams' with given access token value.+-- | Create `QueryParams` with given access token value. accessTokenToParam :: AccessToken -> [(BS.ByteString, BS.ByteString)] accessTokenToParam t = [("access_token", T.encodeUtf8 $ atoken t)]
src/Network/OAuth/OAuth2/Internal.hs view
@@ -1,8 +1,4 @@-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE RecordWildCards #-}  module Network.OAuth.OAuth2.Internal where @@ -28,11 +24,11 @@ import URI.ByteString.Aeson () import URI.ByteString.QQ ---------------------------------------------------+------------------------------------------------------------------------------- --- * Data Types+-- * OAuth2 Configuration ---------------------------------------------------+-------------------------------------------------------------------------------  -- | Query Parameter Representation data OAuth2 = OAuth2@@ -54,6 +50,12 @@       , oauth2RedirectUri = [uri|https://www.example.com/|]       } +-------------------------------------------------------------------------------++-- * Tokens++-------------------------------------------------------------------------------+ newtype AccessToken = AccessToken {atoken :: Text} deriving (Binary, Eq, Show, FromJSON, ToJSON)  newtype RefreshToken = RefreshToken {rtoken :: Text} deriving (Binary, Eq, Show, FromJSON, ToJSON)@@ -63,16 +65,18 @@ -- | Authorization Code newtype ExchangeToken = ExchangeToken {extoken :: Text} deriving (Show, FromJSON, ToJSON) +-- FIXME: rename to TokenResponse and move to that module+ -- | https://www.rfc-editor.org/rfc/rfc6749#section-4.1.4 data OAuth2Token = OAuth2Token   { accessToken :: AccessToken   , refreshToken :: Maybe RefreshToken-  -- ^ Exists when @offline_access@ scope is in the 'authorizeUrl' and the provider supports Refresh Access Token.+  -- ^ Exists when @offline_access@ scope is in the Authorization Request and the provider supports Refresh Access Token.   , expiresIn :: Maybe Int   , tokenType :: Maybe Text   -- ^ 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.   , idToken :: Maybe IdToken-  -- ^ Exists when @openid@ scope is in the 'authorizeUrl' and the provider supports OpenID.+  -- ^ Exists when @openid@ scope is in the Authorization Request and the provider supports OpenID protocol.   }   deriving (Eq, Show, Generic) @@ -82,15 +86,11 @@ instance FromJSON OAuth2Token where   parseJSON = withObject "OAuth2Token" $ \v ->     OAuth2Token-      <$> v-      .: "access_token"-      <*> v-      .:? "refresh_token"+      <$> v .: "access_token"+      <*> v .:? "refresh_token"       <*> explicitParseFieldMaybe parseIntFlexible v "expires_in"-      <*> v-      .:? "token_type"-      <*> v-      .:? "id_token"+      <*> v .:? "token_type"+      <*> v .:? "id_token"     where       parseIntFlexible :: Value -> Parser Int       parseIntFlexible (String s) = pure . read $ unpack s@@ -100,6 +100,12 @@   toJSON = genericToJSON defaultOptions {fieldLabelModifier = camelTo2 '_'}   toEncoding = genericToEncoding defaultOptions {fieldLabelModifier = camelTo2 '_'} +-------------------------------------------------------------------------------++-- * Client Authentication methods++-------------------------------------------------------------------------------+ -- | https://www.rfc-editor.org/rfc/rfc6749#section-2.3 -- According to spec: --@@ -107,33 +113,28 @@ -- -- Which means use Authorization header or Post body. ----- However, in reality, I always have to include authentication in the header.+-- However, I found I have to include authentication in the header all the time in real world. ----- In other words, 'ClientSecrectBasic' is always assured. 'ClientSecretPost' is optional.+-- 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   | ClientSecretPost   | ClientAssertionJwt-  deriving (Eq, Ord)+  deriving (Eq) ---------------------------------------------------+------------------------------------------------------------------------------- --- * Types Synonym+-- * Utilies for Request and URI ---------------------------------------------------+------------------------------------------------------------------------------- --- | type synonym of post body content+-- | Type synonym of post body content type PostBody = [(BS.ByteString, BS.ByteString)] +-- | Type sysnonym of request query params type QueryParams = [(BS.ByteString, BS.ByteString)]-------------------------------------------------------- * Utilies----------------------------------------------------  defaultRequestHeaders :: [(HT.HeaderName, BS.ByteString)] defaultRequestHeaders =
src/Network/OAuth/OAuth2/TokenRequest.hs view
@@ -1,5 +1,3 @@-{-# LANGUAGE OverloadedStrings #-}- -- | 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 where@@ -13,12 +11,12 @@ import Data.Text (Text) import Data.Text qualified as T import Data.Text.Encoding qualified as T-import GHC.Generics (Generic) import Network.HTTP.Conduit import Network.HTTP.Types qualified as HT import Network.HTTP.Types.URI (parseQuery) import Network.OAuth.OAuth2.Internal import URI.ByteString+import Prelude hiding (error)  -------------------------------------------------- @@ -26,15 +24,15 @@  -------------------------------------------------- -data TokenRequestError = TokenRequestError-  { error :: TokenRequestErrorCode-  , errorDescription :: Maybe Text-  , errorUri :: Maybe (URIRef Absolute)+data TokenResponseError = TokenResponseError+  { tokenResponseError :: TokenResponseErrorCode+  , tokenResponseErrorDescription :: Maybe Text+  , tokenResponseErrorUri :: Maybe (URIRef Absolute)   }-  deriving (Show, Eq, Generic)+  deriving (Show, Eq)  -- | Token Error Responses https://tools.ietf.org/html/rfc6749#section-5.2-data TokenRequestErrorCode+data TokenResponseErrorCode   = InvalidRequest   | InvalidClient   | InvalidGrant@@ -44,8 +42,8 @@   | UnknownErrorCode Text   deriving (Show, Eq) -instance FromJSON TokenRequestErrorCode where-  parseJSON = withText "parseJSON TokenRequestErrorCode" $ \t ->+instance FromJSON TokenResponseErrorCode where+  parseJSON = withText "parseJSON TokenResponseErrorCode" $ \t ->     pure $ case t of       "invalid_request" -> InvalidRequest       "invalid_client" -> InvalidClient@@ -55,18 +53,22 @@       "invalid_scope" -> InvalidScope       _ -> UnknownErrorCode t -instance FromJSON TokenRequestError where-  parseJSON = genericParseJSON defaultOptions {constructorTagModifier = camelTo2 '_'}+instance FromJSON TokenResponseError where+  parseJSON = withObject "parseJSON TokenResponseError" $ \t -> do+    tokenResponseError <- t .: "error"+    tokenResponseErrorDescription <- t .:? "error_description"+    tokenResponseErrorUri <- t .:? "error_uri"+    pure TokenResponseError {..} -parseTokeRequestError :: BSL.ByteString -> TokenRequestError-parseTokeRequestError string =+parseTokeResponseError :: BSL.ByteString -> TokenResponseError+parseTokeResponseError string =   either (mkDecodeOAuth2Error string) id (eitherDecode string)   where-    mkDecodeOAuth2Error :: BSL.ByteString -> String -> TokenRequestError+    mkDecodeOAuth2Error :: BSL.ByteString -> String -> TokenResponseError     mkDecodeOAuth2Error response err =-      TokenRequestError+      TokenResponseError         (UnknownErrorCode "")-        (Just $ T.pack $ "Decode TokenRequestError failed: " <> err <> "\n Original Response:\n" <> show (T.decodeUtf8 $ BSL.toStrict response))+        (Just $ T.pack $ "Decode TokenResponseError failed: " <> err <> "\n Original Response:\n" <> show (T.decodeUtf8 $ BSL.toStrict response))         Nothing  --------------------------------------------------@@ -114,7 +116,7 @@  -- | Exchange @code@ for an Access Token with authenticate in request header. fetchAccessToken ::-  (MonadIO m) =>+  MonadIO m =>   -- | HTTP connection manager   Manager ->   -- | OAuth Data@@ -122,11 +124,11 @@   -- | OAuth2 Code   ExchangeToken ->   -- | Access Token-  ExceptT TokenRequestError m OAuth2Token+  ExceptT TokenResponseError m OAuth2Token fetchAccessToken = fetchAccessTokenWithAuthMethod ClientSecretBasic  fetchAccessToken2 ::-  (MonadIO m) =>+  MonadIO m =>   -- | HTTP connection manager   Manager ->   -- | OAuth Data@@ -134,12 +136,12 @@   -- | Authorization Code   ExchangeToken ->   -- | Access Token-  ExceptT TokenRequestError m OAuth2Token+  ExceptT TokenResponseError m OAuth2Token fetchAccessToken2 = fetchAccessTokenWithAuthMethod ClientSecretPost {-# DEPRECATED fetchAccessToken2 "use 'fetchAccessTokenWithAuthMethod'" #-}  fetchAccessTokenInternal ::-  (MonadIO m) =>+  MonadIO m =>   ClientAuthenticationMethod ->   -- | HTTP connection manager   Manager ->@@ -148,24 +150,24 @@   -- | Authorization Code   ExchangeToken ->   -- | Access Token-  ExceptT TokenRequestError m OAuth2Token+  ExceptT TokenResponseError m OAuth2Token fetchAccessTokenInternal = fetchAccessTokenWithAuthMethod {-# DEPRECATED fetchAccessTokenInternal "use 'fetchAccessTokenWithAuthMethod'" #-}  -- | 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').+-- 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+-- If you`re uncertain, try `fetchAccessToken` which sends credential -- in authorization http header, which is common case. -- -- @since 2.6.0 fetchAccessTokenWithAuthMethod ::-  (MonadIO m) =>+  MonadIO m =>   ClientAuthenticationMethod ->   -- | HTTP connection manager   Manager ->@@ -174,7 +176,7 @@   -- | Authorization Code   ExchangeToken ->   -- | Access Token-  ExceptT TokenRequestError m OAuth2Token+  ExceptT TokenResponseError m OAuth2Token fetchAccessTokenWithAuthMethod authMethod manager oa code = do   let (uri, body) = accessTokenUrl oa code   let extraBody = if authMethod == ClientSecretPost then clientSecretPost oa else []@@ -182,30 +184,30 @@  -- | Fetch a new AccessToken using the Refresh Token with authentication in request header. refreshAccessToken ::-  (MonadIO m) =>+  MonadIO m =>   -- | HTTP connection manager.   Manager ->   -- | OAuth context   OAuth2 ->   -- | Refresh Token gained after authorization   RefreshToken ->-  ExceptT TokenRequestError m OAuth2Token+  ExceptT TokenResponseError m OAuth2Token refreshAccessToken = refreshAccessTokenWithAuthMethod ClientSecretBasic  refreshAccessToken2 ::-  (MonadIO m) =>+  MonadIO m =>   -- | HTTP connection manager.   Manager ->   -- | OAuth context   OAuth2 ->   -- | Refresh Token gained after authorization   RefreshToken ->-  ExceptT TokenRequestError m OAuth2Token+  ExceptT TokenResponseError m OAuth2Token refreshAccessToken2 = refreshAccessTokenWithAuthMethod ClientSecretPost {-# DEPRECATED refreshAccessToken2 "use 'refreshAccessTokenWithAuthMethod'" #-}  refreshAccessTokenInternal ::-  (MonadIO m) =>+  MonadIO m =>   ClientAuthenticationMethod ->   -- | HTTP connection manager.   Manager ->@@ -213,13 +215,13 @@   OAuth2 ->   -- | Refresh Token gained after authorization   RefreshToken ->-  ExceptT TokenRequestError m OAuth2Token+  ExceptT TokenResponseError m OAuth2Token refreshAccessTokenInternal = refreshAccessTokenWithAuthMethod {-# DEPRECATED refreshAccessTokenInternal "use 'refreshAccessTokenWithAuthMethod'" #-}  -- | Fetch a new AccessToken using the Refresh Token. ----- OAuth2 spec allows credential (`client_id`, `client_secret`) to be sent+-- 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'). --@@ -230,7 +232,7 @@ -- -- @since 2.6.0 refreshAccessTokenWithAuthMethod ::-  (MonadIO m) =>+  MonadIO m =>   ClientAuthenticationMethod ->   -- | HTTP connection manager.   Manager ->@@ -238,7 +240,7 @@   OAuth2 ->   -- | Refresh Token gained after authorization   RefreshToken ->-  ExceptT TokenRequestError m OAuth2Token+  ExceptT TokenResponseError m OAuth2Token refreshAccessTokenWithAuthMethod authMethod manager oa token = do   let (uri, body) = refreshAccessTokenUrl oa token   let extraBody = if authMethod == ClientSecretPost then clientSecretPost oa else []@@ -262,7 +264,7 @@   -- | request body   PostBody ->   -- | Response as JSON-  ExceptT TokenRequestError m a+  ExceptT TokenResponseError m a doJSONPostRequest manager oa uri body = do   resp <- doSimplePostRequest manager oa uri body   case parseResponseFlexible resp of@@ -271,7 +273,7 @@  -- | Conduct post request. doSimplePostRequest ::-  (MonadIO m) =>+  MonadIO m =>   -- | HTTP connection manager.   Manager ->   -- | OAuth options@@ -281,37 +283,36 @@   -- | Request body.   PostBody ->   -- | Response as ByteString-  ExceptT TokenRequestError m BSL.ByteString+  ExceptT TokenResponseError m BSL.ByteString doSimplePostRequest manager oa url body =   ExceptT . liftIO $ fmap handleOAuth2TokenResponse go   where-    addBasicAuth = applyBasicAuth (T.encodeUtf8 $ oauth2ClientId oa) (T.encodeUtf8 $ oauth2ClientSecret oa)     go = do       req <- uriToRequest url-      let req' = (addBasicAuth . addDefaultRequestHeaders) req+      let req' = (addBasicAuth oa . addDefaultRequestHeaders) req       httpLbs (urlEncodedBody body req') manager --- | Gets response body from a @Response@ if 200 otherwise assume 'OAuth2Error'-handleOAuth2TokenResponse :: Response BSL.ByteString -> Either TokenRequestError BSL.ByteString+-- | Gets response body from a @Response@ if 200 otherwise assume 'Network.OAuth.OAuth2.TokenRequest.TokenResponseError'+handleOAuth2TokenResponse :: Response BSL.ByteString -> Either TokenResponseError BSL.ByteString handleOAuth2TokenResponse rsp =   if HT.statusIsSuccessful (responseStatus rsp)     then Right $ responseBody rsp-    else Left $ parseTokeRequestError (responseBody rsp)+    else Left $ parseTokeResponseError (responseBody rsp)  -- | Try to parses response as JSON, if failed, try to parse as like query string. parseResponseFlexible ::-  (FromJSON a) =>+  FromJSON a =>   BSL.ByteString ->-  Either TokenRequestError a+  Either TokenResponseError a parseResponseFlexible r = case eitherDecode r of   Left _ -> parseResponseString r   Right x -> Right x  -- | Parses the response that contains not JSON but a Query String parseResponseString ::-  (FromJSON a) =>+  FromJSON a =>   BSL.ByteString ->-  Either TokenRequestError a+  Either TokenResponseError a parseResponseString b = case parseQuery $ BSL.toStrict b of   [] -> Left errorMessage   a -> case fromJSON $ queryToValue a of@@ -320,11 +321,18 @@   where     queryToValue = Object . KeyMap.fromList . map paramToPair     paramToPair (k, mv) = (Key.fromText $ T.decodeUtf8 k, maybe Null (String . T.decodeUtf8) mv)-    errorMessage = parseTokeRequestError b+    errorMessage = parseTokeResponseError b +-- | Add Basic Authentication header using client_id and client_secret.+addBasicAuth :: OAuth2 -> Request -> Request+addBasicAuth oa =+  applyBasicAuth+    (T.encodeUtf8 $ oauth2ClientId oa)+    (T.encodeUtf8 $ oauth2ClientSecret oa)+ -- | Set several header values:---   + userAgennt    : `hoauth2`---   + accept        : `application/json`+--   + userAgennt    : "hoauth2"+--   + accept        : "application/json" addDefaultRequestHeaders :: Request -> Request addDefaultRequestHeaders req =   let headers = defaultRequestHeaders ++ requestHeaders req
src/Network/OAuth2/Experiment.hs view
@@ -1,17 +1,9 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE DuplicateRecordFields #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE UndecidableInstances #-}- -- | This module contains a new way of doing OAuth2 authorization and authentication -- in order to obtain Access Token and maybe Refresh Token base on rfc6749. ----- This module will become default in future release. (TBD but likely 3.0).+-- This module will become default in future release. ----- The key concept/change is to introduce the 'GrantTypeFlow', which determines the entire work flow per spec.+-- The key concept/change is to introduce the Grant flow, which determines the entire work flow per spec. -- Each work flow will have slight different request parameters, which often time you'll see -- different configuration when creating OAuth2 application in the IdP developer application page. --@@ -30,48 +22,53 @@ -- 5. PKCE (rfc7636). This is enhancement on top of authorization code flow. -- -- Implicit flow is not supported because it is more for SPA (single page app)--- and more or less obsolete by Authorization Code flow with PKCE.+-- given it is deprecated by Authorization Code flow with PKCE. -- -- Here is quick sample for how to use vocabularies from this new module. -- -- Firstly, initialize your IdP (use google as example) and the application. -- -- @--- {\-# LANGUAGE DataKinds #-\} --+-- import Network.OAuth2.Experiment+-- import URI.ByteString.QQ+-- -- data Google = Google deriving (Eq, Show)+-- -- googleIdp :: Idp Google -- googleIdp = --   Idp---     { idpFetchUserInfo = authGetJSON @(IdpUserInfo Google),---       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|]+--     { 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 :: IdpApplication 'AuthorizationCode Google+-- fooApp :: AuthorizationCodeApplication -- fooApp =---   AuthorizationCodeIdpApplication---     { idpAppClientId = "xxxxx",---       idpAppClientSecret = "xxxxx",---       idpAppScope =+--   AuthorizationCodeApplication+--     { acClientId = "xxxxx",+--       acClientSecret = "xxxxx",+--       acScope = --         Set.fromList --           [ \"https:\/\/www.googleapis.com\/auth\/userinfo.email\", --             \"https:\/\/www.googleapis.com\/auth\/userinfo.profile\" --           ],---       idpAppAuthorizeState = \"CHANGE_ME\",---       idpAppAuthorizeExtraParams = Map.empty,---       idpAppRedirectUri = [uri|http:\/\/localhost\/oauth2\/callback|],---       idpAppName = "default-google-App",---       idpAppTokenRequestAuthenticationMethod = ClientSecretBasic,---       idp = googleIdp+--       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 = mkAuthorizeRequest fooApp+-- authorizeUrl = mkAuthorizationRequest fooIdpApplication -- @ -- -- Thirdly, after a successful redirect with authorize code,@@ -79,20 +76,86 @@ -- -- @ -- mgr <- liftIO $ newManager tlsManagerSettings--- tokenResp <- conduitTokenRequest fooApp mgr authorizeCode+-- tokenResp <- conduitTokenRequest fooIdpApplication mgr authorizeCode -- @ ----- Lastly, you probably like to fetch user info+-- If you'd like to fetch user info, uses this method -- -- @--- conduitUserInfoRequest fooApp mgr (accessToken tokenResp)+-- conduitUserInfoRequest fooIdpApplication mgr (accessToken tokenResp) -- @ ----- Also you could find example from @hoauth2-providers-tutorials@ module.+-- You could also find example from @hoauth2-providers-tutorials@ module. module Network.OAuth2.Experiment (+  -- * Application per Grant type+  module Network.OAuth2.Experiment.Grants,++  -- * Authorization Code+  module Network.OAuth2.Experiment.Flows.AuthorizationRequest,++  -- * Device Authorization+  module Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest,++  -- * Token Request+  module Network.OAuth2.Experiment.Flows.TokenRequest,++  -- * Refresh Token Request+  module Network.OAuth2.Experiment.Flows.RefreshTokenRequest,++  -- * UserInfo Request+  module Network.OAuth2.Experiment.Flows.UserInfoRequest,++  -- * Types   module Network.OAuth2.Experiment.Types,   module Network.OAuth2.Experiment.Pkce,+  module Network.OAuth.OAuth2,++  -- * Utils+  module Network.OAuth2.Experiment.Utils, ) where -import Network.OAuth2.Experiment.Pkce-import Network.OAuth2.Experiment.Types+import Network.OAuth.OAuth2 (ClientAuthenticationMethod (..))+import Network.OAuth2.Experiment.Flows.AuthorizationRequest (+  HasAuthorizeRequest,+  mkAuthorizationRequest,+  mkPkceAuthorizeRequest,+ )+import Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest (+  DeviceAuthorizationResponse (..),+  HasDeviceAuthorizationRequest,+  conduitDeviceAuthorizationRequest,+ )+import Network.OAuth2.Experiment.Flows.RefreshTokenRequest (+  HasRefreshTokenRequest,+  conduitRefreshTokenRequest,+ )+import Network.OAuth2.Experiment.Flows.TokenRequest (+  ExchangeTokenInfo,+  HasTokenRequest,+  NoNeedExchangeToken (..),+  TokenRequest,+  conduitPkceTokenRequest,+  conduitTokenRequest,+ )+import Network.OAuth2.Experiment.Flows.UserInfoRequest (+  HasUserInfoRequest,+  conduitUserInfoRequest,+  conduitUserInfoRequestWithCustomMethod,+ )+import Network.OAuth2.Experiment.Grants+import Network.OAuth2.Experiment.Pkce (+  CodeVerifier (..),+ )+import Network.OAuth2.Experiment.Types (+  AuthorizeState (..),+  ClientId (..),+  ClientSecret (..),+  HasOAuth2Key,+  Idp (..),+  IdpApplication (..),+  Password (..),+  RedirectUri (..),+  Scope (..),+  Username (..),+ )+import Network.OAuth2.Experiment.Utils (uriToText)
+ src/Network/OAuth2/Experiment/Flows/AuthorizationRequest.hs view
@@ -0,0 +1,78 @@+module Network.OAuth2.Experiment.Flows.AuthorizationRequest where++import Control.Monad.IO.Class (MonadIO (..))+import Data.Bifunctor+import Data.Map.Strict (Map)+import Data.Map.Strict qualified as Map+import Data.Set (Set)+import Data.Text.Lazy (Text)+import Network.OAuth.OAuth2 hiding (RefreshToken)+import Network.OAuth2.Experiment.Pkce+import Network.OAuth2.Experiment.Types+import Network.OAuth2.Experiment.Utils+import URI.ByteString hiding (UserInfo)++-------------------------------------------------------------------------------+--                           Authorization Request                           --+-------------------------------------------------------------------------------++data AuthorizationRequestParam = AuthorizationRequestParam+  { arScope :: Set Scope+  , arState :: AuthorizeState+  , arClientId :: ClientId+  , arRedirectUri :: Maybe RedirectUri+  , arResponseType :: ResponseType+  -- ^ It could be optional there is only one redirect_uri registered.+  -- See: https://www.rfc-editor.org/rfc/rfc6749#section-3.1.2.3+  , arExtraParams :: Map Text Text+  }++instance ToQueryParam AuthorizationRequestParam where+  toQueryParam AuthorizationRequestParam {..} =+    Map.unions+      [ toQueryParam arResponseType+      , toQueryParam arScope+      , toQueryParam arClientId+      , toQueryParam arState+      , toQueryParam arRedirectUri+      , arExtraParams+      ]++class HasAuthorizeRequest a where+  -- | Constructs Authorization Code request parameters+  -- | https://www.rfc-editor.org/rfc/rfc6749#section-4.1.1+  mkAuthorizationRequestParam :: a -> AuthorizationRequestParam++-- | Constructs Authorization Code request URI+-- https://www.rfc-editor.org/rfc/rfc6749#section-4.1.1+mkAuthorizationRequest :: HasAuthorizeRequest a => IdpApplication i a -> URI+mkAuthorizationRequest idpApp =+  let req = mkAuthorizationRequestParam (application idpApp)+      allParams =+        map (bimap tlToBS tlToBS) $+          Map.toList $+            toQueryParam req+   in appendQueryParams allParams $+        idpAuthorizeEndpoint (idp idpApp)++-------------------------------------------------------------------------------+--                                    PKCE                                   --+-------------------------------------------------------------------------------++-- | https://datatracker.ietf.org/doc/html/rfc7636+class HasAuthorizeRequest a => HasPkceAuthorizeRequest a where+  mkPkceAuthorizeRequestParam :: MonadIO m => a -> m (AuthorizationRequestParam, CodeVerifier)++-- | 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)+mkPkceAuthorizeRequest IdpApplication {..} = do+  (req, codeVerifier) <- mkPkceAuthorizeRequestParam application+  let allParams = map (bimap tlToBS tlToBS) $ Map.toList $ toQueryParam req+  let url =+        appendQueryParams allParams $+          idpAuthorizeEndpoint idp+  pure (url, codeVerifier)
+ src/Network/OAuth2/Experiment/Flows/DeviceAuthorizationRequest.hs view
@@ -0,0 +1,100 @@+{-# LANGUAGE DerivingStrategies #-}++module Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest where++import Control.Applicative+import Control.Monad.IO.Class (MonadIO (..))+import Control.Monad.Trans.Except (ExceptT (..), throwE)+import Data.Aeson.Types+import Data.Bifunctor+import Data.ByteString.Lazy.Char8 qualified as BSL+import Data.Map.Strict (Map)+import Data.Map.Strict qualified as Map+import Data.Set (Set)+import Data.Text.Lazy (Text)+import Network.HTTP.Client.Contrib+import Network.HTTP.Conduit+import Network.OAuth.OAuth2 hiding (RefreshToken)+import Network.OAuth2.Experiment.Types+import Network.OAuth2.Experiment.Utils+import URI.ByteString hiding (UserInfo)++-------------------------------------------------------------------------------+--                    Device Authorization Request                           --+-------------------------------------------------------------------------------+newtype DeviceCode = DeviceCode Text+  deriving newtype (FromJSON)++instance ToQueryParam DeviceCode where+  toQueryParam :: DeviceCode -> Map Text Text+  toQueryParam (DeviceCode dc) = Map.singleton "device_code" dc++-- | https://www.rfc-editor.org/rfc/rfc8628#section-3.2+data DeviceAuthorizationResponse = DeviceAuthorizationResponse+  { deviceCode :: DeviceCode+  , userCode :: Text+  , verificationUri :: URI+  , verificationUriComplete :: Maybe URI+  , expiresIn :: Integer+  , interval :: Maybe Int+  }++instance FromJSON DeviceAuthorizationResponse where+  parseJSON :: Value -> Parser DeviceAuthorizationResponse+  parseJSON = withObject "parse DeviceAuthorizationResponse" $ \t -> do+    deviceCode <- t .: "device_code"+    userCode <- t .: "user_code"+    -- https://stackoverflow.com/questions/76696956/shall-it-be-verification-uri-instead-of-verification-url-in-the-device-autho+    verificationUri <- t .: "verification_uri" <|> t .: "verification_url"+    verificationUriComplete <- t .:? "verification_uri_complete"+    expiresIn <- t .: "expires_in"+    interval <- t .:? "interval"+    pure DeviceAuthorizationResponse {..}++data DeviceAuthorizationRequestParam = DeviceAuthorizationRequestParam+  { arScope :: Set Scope+  , arClientId :: Maybe ClientId+  , arExtraParams :: Map Text Text+  }++instance ToQueryParam DeviceAuthorizationRequestParam where+  toQueryParam :: DeviceAuthorizationRequestParam -> Map Text Text+  toQueryParam DeviceAuthorizationRequestParam {..} =+    Map.unions+      [ toQueryParam arScope+      , toQueryParam arClientId+      , arExtraParams+      ]++class HasOAuth2Key a => HasDeviceAuthorizationRequest a where+  -- | Create Device Authorization Request parameters+  -- https://www.rfc-editor.org/rfc/rfc8628#section-3.1+  mkDeviceAuthorizationRequestParam :: a -> DeviceAuthorizationRequestParam++-- TODO: There is only (possibly always only) on instance of 'HasDeviceAuthorizationRequest'+-- Maybe consider to hard-code the data type instead of use type class.++-- | Makes Device Authorization Request+-- https://www.rfc-editor.org/rfc/rfc8628#section-3.1+conduitDeviceAuthorizationRequest ::+  (MonadIO m, HasDeviceAuthorizationRequest a) =>+  IdpApplication i a ->+  Manager ->+  ExceptT BSL.ByteString m DeviceAuthorizationResponse+conduitDeviceAuthorizationRequest IdpApplication {..} mgr = do+  case idpDeviceAuthorizationEndpoint idp of+    Nothing -> throwE "[conduiteDeviceAuthorizationRequest] Device Authorization Flow is not supported due to miss device_authorization_endpoint."+    Just deviceAuthEndpoint -> do+      let deviceAuthReq = mkDeviceAuthorizationRequestParam application+          oauth2Key = mkOAuth2Key application+          body = unionMapsToQueryParams [toQueryParam deviceAuthReq]+      ExceptT . liftIO $ do+        req <- addDefaultRequestHeaders <$> uriToRequest deviceAuthEndpoint+        -- Hacky:+        -- Missing clientId implies ClientSecretBasic authentication method.+        -- See Grant/DeviceAuthorization.hs+        let req' = case arClientId deviceAuthReq of+              Nothing -> addBasicAuth oauth2Key req+              Just _ -> req+        resp <- httpLbs (urlEncodedBody body req') mgr+        pure $ first ("[conduiteDeviceAuthorizationRequest] " <>) $ handleResponseJSON resp
+ src/Network/OAuth2/Experiment/Flows/RefreshTokenRequest.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE FlexibleContexts #-}++module Network.OAuth2.Experiment.Flows.RefreshTokenRequest where++import Control.Monad.IO.Class (MonadIO (..))+import Control.Monad.Trans.Except (ExceptT (..))+import Data.Map.Strict (Map)+import Data.Map.Strict qualified as Map+import Data.Set (Set)+import Data.Text.Lazy (Text)+import Network.HTTP.Conduit+import Network.OAuth.OAuth2 hiding (RefreshToken)+import Network.OAuth.OAuth2 qualified as OAuth2+import Network.OAuth2.Experiment.Flows.TokenRequest+import Network.OAuth2.Experiment.Types+import Network.OAuth2.Experiment.Utils++-------------------------------------------------------------------------------+--                            RefreshToken Requset                           --+-------------------------------------------------------------------------------++data RefreshTokenRequest = RefreshTokenRequest+  { rrRefreshToken :: OAuth2.RefreshToken+  , rrGrantType :: GrantTypeValue+  , rrScope :: Set Scope+  }++instance ToQueryParam RefreshTokenRequest where+  toQueryParam :: RefreshTokenRequest -> Map Text Text+  toQueryParam RefreshTokenRequest {..} =+    Map.unions+      [ toQueryParam rrGrantType+      , toQueryParam rrScope+      , toQueryParam rrRefreshToken+      ]++class (HasOAuth2Key a, HasTokenRequestClientAuthenticationMethod a) => HasRefreshTokenRequest a where+  -- | Make Refresh Token Request parameters+  -- | https://www.rfc-editor.org/rfc/rfc6749#section-6+  mkRefreshTokenRequestParam :: a -> OAuth2.RefreshToken -> RefreshTokenRequest++-- | Make Refresh Token Request+-- https://www.rfc-editor.org/rfc/rfc6749#section-6+conduitRefreshTokenRequest ::+  (MonadIO m, HasRefreshTokenRequest a) =>+  IdpApplication i a ->+  Manager ->+  OAuth2.RefreshToken ->+  ExceptT TokenResponseError m OAuth2Token+conduitRefreshTokenRequest IdpApplication {..} mgr rt =+  let tokenReq = mkRefreshTokenRequestParam application rt+      body = unionMapsToQueryParams [toQueryParam tokenReq]+   in doJSONPostRequest mgr (mkOAuth2Key application) (idpTokenEndpoint idp) body
+ src/Network/OAuth2/Experiment/Flows/TokenRequest.hs view
@@ -0,0 +1,83 @@+{-# LANGUAGE FlexibleContexts #-}++module Network.OAuth2.Experiment.Flows.TokenRequest where++import Control.Monad.IO.Class (MonadIO (..))+import Control.Monad.Trans.Except (ExceptT (..), throwE)+import Network.HTTP.Conduit+import Network.OAuth.OAuth2 hiding (RefreshToken)+import Network.OAuth2.Experiment.Pkce+import Network.OAuth2.Experiment.Types+import Network.OAuth2.Experiment.Utils++-------------------------------------------------------------------------------+--                               Token Request                               --+-------------------------------------------------------------------------------++class HasTokenRequestClientAuthenticationMethod a where+  getClientAuthenticationMethod :: a -> ClientAuthenticationMethod++-- | Only Authorization Code Grant involves a Exchange Token (Authorization Code).+-- ResourceOwnerPassword and Client Credentials make token request directly.+data NoNeedExchangeToken = NoNeedExchangeToken++class (HasOAuth2Key a, HasTokenRequestClientAuthenticationMethod a) => HasTokenRequest a where+  -- Each GrantTypeFlow has slightly different request parameter to /token endpoint.+  data TokenRequest a+  type ExchangeTokenInfo a++  -- | Only 'AuthorizationCode flow (but not resource owner password nor client credentials) will use 'ExchangeToken' in the token request+  -- create type family to be explicit on it.+  -- with 'type instance WithExchangeToken a b = b' implies no exchange token+  -- v.s. 'type instance WithExchangeToken a b = ExchangeToken -> b' implies needing an exchange token+  -- type WithExchangeToken a b+  mkTokenRequestParam :: a -> ExchangeTokenInfo a -> TokenRequest a++-- | 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+conduitTokenRequest IdpApplication {..} mgr exchangeToken = do+  let tokenReq = mkTokenRequestParam application exchangeToken+      body = unionMapsToQueryParams [toQueryParam tokenReq]+  if getClientAuthenticationMethod application == ClientAssertionJwt+    then do+      resp <- ExceptT . liftIO $ do+        req <- uriToRequest (idpTokenEndpoint idp)+        let req' = urlEncodedBody body (addDefaultRequestHeaders req)+        handleOAuth2TokenResponse <$> httpLbs req' mgr+      case parseResponseFlexible resp of+        Right obj -> return obj+        Left e -> throwE e+    else doJSONPostRequest mgr (mkOAuth2Key application) (idpTokenEndpoint idp) body++-------------------------------------------------------------------------------+--                                    PKCE                                   --+-------------------------------------------------------------------------------++-- | 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+conduitPkceTokenRequest IdpApplication {..} mgr (exchangeToken, codeVerifier) =+  let req = mkTokenRequestParam application exchangeToken+      key = mkOAuth2Key application+      clientSecretPostParam =+        if getClientAuthenticationMethod application == ClientSecretPost+          then clientSecretPost key+          else []+      body =+        unionMapsToQueryParams+          [ toQueryParam req+          , toQueryParam codeVerifier+          ]+          ++ clientSecretPostParam+   in doJSONPostRequest mgr key (idpTokenEndpoint idp) body
+ src/Network/OAuth2/Experiment/Flows/UserInfoRequest.hs view
@@ -0,0 +1,44 @@+{-# LANGUAGE FlexibleContexts #-}++module Network.OAuth2.Experiment.Flows.UserInfoRequest where++import Control.Monad.IO.Class (MonadIO (..))+import Control.Monad.Trans.Except (ExceptT (..))+import Data.Aeson (FromJSON)+import Data.ByteString.Lazy.Char8 qualified as BSL+import Network.HTTP.Conduit+import Network.OAuth.OAuth2+import Network.OAuth2.Experiment.Types+import URI.ByteString (URI)++-------------------------------------------------------------------------------+--                             User Info Request                             --+-------------------------------------------------------------------------------++class HasUserInfoRequest a++-- | Standard approach of fetching /userinfo+conduitUserInfoRequest ::+  (MonadIO m, HasUserInfoRequest a, FromJSON b) =>+  IdpApplication i a ->+  Manager ->+  AccessToken ->+  ExceptT BSL.ByteString m b+conduitUserInfoRequest = conduitUserInfoRequestWithCustomMethod authGetJSON++-- | 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 BSL.ByteString m b+  ) ->+  IdpApplication i a ->+  Manager ->+  AccessToken ->+  ExceptT BSL.ByteString m b+conduitUserInfoRequestWithCustomMethod fetchMethod IdpApplication {..} mgr at =+  fetchMethod mgr at (idpUserInfoEndpoint idp)
+ src/Network/OAuth2/Experiment/Grants.hs view
@@ -0,0 +1,16 @@+module Network.OAuth2.Experiment.Grants (+  module Network.OAuth2.Experiment.Grants.AuthorizationCode,+  module Network.OAuth2.Experiment.Grants.DeviceAuthorization,+  module Network.OAuth2.Experiment.Grants.ClientCredentials,+  module Network.OAuth2.Experiment.Grants.ResourceOwnerPassword,+  module Network.OAuth2.Experiment.Grants.JwtBearer,+) where++import Network.OAuth2.Experiment.Grants.AuthorizationCode (AuthorizationCodeApplication (..))+import Network.OAuth2.Experiment.Grants.ClientCredentials (ClientCredentialsApplication (..))+import Network.OAuth2.Experiment.Grants.DeviceAuthorization (+  DeviceAuthorizationApplication (..),+  pollDeviceTokenRequest,+ )+import Network.OAuth2.Experiment.Grants.JwtBearer (JwtBearerApplication (..))+import Network.OAuth2.Experiment.Grants.ResourceOwnerPassword (ResourceOwnerPasswordApplication (..))
+ src/Network/OAuth2/Experiment/Grants/AuthorizationCode.hs view
@@ -0,0 +1,102 @@+{-# LANGUAGE FlexibleInstances #-}++module Network.OAuth2.Experiment.Grants.AuthorizationCode where++import Control.Monad.IO.Class (MonadIO (..))+import Data.Map.Strict (Map)+import Data.Map.Strict qualified as Map+import Data.Set (Set)+import Data.Text.Lazy (Text)+import Network.OAuth.OAuth2 (ClientAuthenticationMethod (..), ExchangeToken (..), OAuth2)+import Network.OAuth.OAuth2 qualified as OAuth2+import Network.OAuth2.Experiment.Flows.AuthorizationRequest+import Network.OAuth2.Experiment.Flows.RefreshTokenRequest+import Network.OAuth2.Experiment.Flows.TokenRequest+import Network.OAuth2.Experiment.Flows.UserInfoRequest+import Network.OAuth2.Experiment.Pkce+import Network.OAuth2.Experiment.Types+import URI.ByteString hiding (UserInfo)++-- | An Application that supports "Authorization code" flow+--+-- https://www.rfc-editor.org/rfc/rfc6749#section-4.1+data AuthorizationCodeApplication = AuthorizationCodeApplication+  { acName :: Text+  , acClientId :: ClientId+  , acClientSecret :: ClientSecret+  , acScope :: Set Scope+  , acRedirectUri :: URI+  , acAuthorizeState :: AuthorizeState+  , acAuthorizeRequestExtraParams :: Map Text Text+  , acTokenRequestAuthenticationMethod :: ClientAuthenticationMethod+  }++instance HasOAuth2Key AuthorizationCodeApplication where+  mkOAuth2Key :: AuthorizationCodeApplication -> OAuth2+  mkOAuth2Key AuthorizationCodeApplication {..} = toOAuth2Key acClientId acClientSecret++instance HasTokenRequestClientAuthenticationMethod AuthorizationCodeApplication where+  getClientAuthenticationMethod :: AuthorizationCodeApplication -> ClientAuthenticationMethod+  getClientAuthenticationMethod AuthorizationCodeApplication {..} = acTokenRequestAuthenticationMethod++instance HasAuthorizeRequest AuthorizationCodeApplication where+  mkAuthorizationRequestParam :: AuthorizationCodeApplication -> AuthorizationRequestParam+  mkAuthorizationRequestParam AuthorizationCodeApplication {..} =+    AuthorizationRequestParam+      { arScope = acScope+      , arState = acAuthorizeState+      , arClientId = acClientId+      , arRedirectUri = Just (RedirectUri acRedirectUri)+      , arResponseType = Code+      , arExtraParams = acAuthorizeRequestExtraParams+      }++instance HasPkceAuthorizeRequest AuthorizationCodeApplication where+  mkPkceAuthorizeRequestParam :: MonadIO m => AuthorizationCodeApplication -> m (AuthorizationRequestParam, CodeVerifier)+  mkPkceAuthorizeRequestParam app = do+    PkceRequestParam {..} <- mkPkceParam+    let authReqParam = mkAuthorizationRequestParam app+        combinatedExtraParams =+          Map.unions+            [ arExtraParams authReqParam+            , toQueryParam codeChallenge+            , toQueryParam codeChallengeMethod+            ]+    pure (authReqParam {arExtraParams = combinatedExtraParams}, codeVerifier)++-- | https://www.rfc-editor.org/rfc/rfc6749#section-4.1.3+instance HasTokenRequest AuthorizationCodeApplication where+  type ExchangeTokenInfo AuthorizationCodeApplication = ExchangeToken+  data TokenRequest AuthorizationCodeApplication = AuthorizationCodeTokenRequest+    { trCode :: ExchangeToken+    , trGrantType :: GrantTypeValue+    , trRedirectUri :: RedirectUri+    }++  mkTokenRequestParam :: AuthorizationCodeApplication -> ExchangeToken -> TokenRequest AuthorizationCodeApplication+  mkTokenRequestParam AuthorizationCodeApplication {..} authCode =+    AuthorizationCodeTokenRequest+      { trCode = authCode+      , trGrantType = GTAuthorizationCode+      , trRedirectUri = RedirectUri acRedirectUri+      }++instance ToQueryParam (TokenRequest AuthorizationCodeApplication) where+  toQueryParam :: TokenRequest AuthorizationCodeApplication -> Map Text Text+  toQueryParam AuthorizationCodeTokenRequest {..} =+    Map.unions+      [ toQueryParam trCode+      , toQueryParam trGrantType+      , toQueryParam trRedirectUri+      ]++instance HasUserInfoRequest AuthorizationCodeApplication++instance HasRefreshTokenRequest AuthorizationCodeApplication where+  mkRefreshTokenRequestParam :: AuthorizationCodeApplication -> OAuth2.RefreshToken -> RefreshTokenRequest+  mkRefreshTokenRequestParam AuthorizationCodeApplication {..} rt =+    RefreshTokenRequest+      { rrScope = acScope+      , rrGrantType = GTRefreshToken+      , rrRefreshToken = rt+      }
+ src/Network/OAuth2/Experiment/Grants/ClientCredentials.hs view
@@ -0,0 +1,75 @@+{-# LANGUAGE FlexibleInstances #-}++module Network.OAuth2.Experiment.Grants.ClientCredentials where++import Data.Map.Strict (Map)+import Data.Map.Strict qualified as Map+import Data.Set (Set)+import Data.Text.Lazy (Text)+import Network.OAuth.OAuth2 (ClientAuthenticationMethod (..), OAuth2)+import Network.OAuth2.Experiment.Flows.TokenRequest+import Network.OAuth2.Experiment.Types+import Network.OAuth2.Experiment.Utils++-- | An Application that supports "Client Credentials" flow+--+-- https://www.rfc-editor.org/rfc/rfc6749#section-4.4+data ClientCredentialsApplication = ClientCredentialsApplication+  { ccClientId :: ClientId+  , ccClientSecret :: ClientSecret+  , ccName :: Text+  , ccScope :: Set Scope+  , ccTokenRequestExtraParams :: Map Text Text+  , ccTokenRequestAuthenticationMethod :: ClientAuthenticationMethod+  }++instance HasOAuth2Key ClientCredentialsApplication where+  mkOAuth2Key :: ClientCredentialsApplication -> OAuth2+  mkOAuth2Key ClientCredentialsApplication {..} = toOAuth2Key ccClientId ccClientSecret++instance HasTokenRequestClientAuthenticationMethod ClientCredentialsApplication where+  getClientAuthenticationMethod :: ClientCredentialsApplication -> ClientAuthenticationMethod+  getClientAuthenticationMethod ClientCredentialsApplication {..} = ccTokenRequestAuthenticationMethod++-- | https://www.rfc-editor.org/rfc/rfc6749#section-4.4.2+instance HasTokenRequest ClientCredentialsApplication where+  type ExchangeTokenInfo ClientCredentialsApplication = NoNeedExchangeToken+  data TokenRequest ClientCredentialsApplication = ClientCredentialsTokenRequest+    { trScope :: Set Scope+    , trGrantType :: GrantTypeValue+    , trClientSecret :: ClientSecret+    , trClientId :: ClientId+    , trExtraParams :: Map Text Text+    , trClientAuthenticationMethod :: ClientAuthenticationMethod+    }++  mkTokenRequestParam :: ClientCredentialsApplication -> NoNeedExchangeToken -> TokenRequest ClientCredentialsApplication+  mkTokenRequestParam ClientCredentialsApplication {..} _ =+    ClientCredentialsTokenRequest+      { trScope = ccScope+      , trGrantType = GTClientCredentials+      , trClientSecret = ccClientSecret+      , trClientAuthenticationMethod = ccTokenRequestAuthenticationMethod+      , trExtraParams = ccTokenRequestExtraParams+      , trClientId = ccClientId+      }++instance ToQueryParam (TokenRequest ClientCredentialsApplication) where+  toQueryParam :: TokenRequest ClientCredentialsApplication -> Map Text Text+  toQueryParam ClientCredentialsTokenRequest {..} =+    let jwtAssertionBody =+          if trClientAuthenticationMethod == ClientAssertionJwt+            then+              [ toQueryParam trClientId+              , Map.fromList+                  [ ("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer")+                  , ("client_assertion", bs8ToLazyText $ tlToBS $ unClientSecret trClientSecret)+                  ]+              ]+            else []+     in Map.unions $+          [ toQueryParam trGrantType+          , toQueryParam trScope+          , trExtraParams+          ]+            ++ jwtAssertionBody
+ src/Network/OAuth2/Experiment/Grants/DeviceAuthorization.hs view
@@ -0,0 +1,140 @@+{-# LANGUAGE FlexibleInstances #-}++module Network.OAuth2.Experiment.Grants.DeviceAuthorization (+  DeviceAuthorizationApplication (..),+  pollDeviceTokenRequest,+) where++import Control.Concurrent+import Control.Monad.IO.Class (MonadIO (..))+import Control.Monad.Trans.Except+import Data.Map.Strict (Map)+import Data.Map.Strict qualified as Map+import Data.Maybe+import Data.Set (Set)+import Data.Text.Lazy (Text)+import Network.HTTP.Conduit+import Network.OAuth.OAuth2+import Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest+import Network.OAuth2.Experiment.Flows.TokenRequest+import Network.OAuth2.Experiment.Flows.UserInfoRequest+import Network.OAuth2.Experiment.Types+import Prelude hiding (error)++-- | An Application that supports "Device Authorization Grant"+--+-- https://www.rfc-editor.org/rfc/rfc8628#section-3.1+data DeviceAuthorizationApplication = DeviceAuthorizationApplication+  { daName :: Text+  , daClientId :: ClientId+  , daClientSecret :: ClientSecret+  , daScope :: Set Scope+  , daAuthorizationRequestExtraParam :: Map Text Text+  -- ^ Additional parameters to the device authorization request.+  -- Most of identity providers follow the spec strictly but+  -- AzureAD requires "tenant" parameter.+  , daAuthorizationRequestAuthenticationMethod :: Maybe ClientAuthenticationMethod+  -- ^ The spec requires similar authentication method as /token request.+  -- Most of identity providers doesn't required it but some does like Okta.+  }++pollDeviceTokenRequest ::+  MonadIO m =>+  IdpApplication i DeviceAuthorizationApplication ->+  Manager ->+  DeviceAuthorizationResponse ->+  ExceptT TokenResponseError m OAuth2Token+pollDeviceTokenRequest idpApp mgr deviceAuthResp = do+  pollDeviceTokenRequestInternal+    idpApp+    mgr+    (deviceCode deviceAuthResp)+    (fromMaybe 5 $ interval deviceAuthResp)++pollDeviceTokenRequestInternal ::+  MonadIO m =>+  IdpApplication i DeviceAuthorizationApplication ->+  Manager ->+  DeviceCode ->+  Int ->+  -- | Polling Interval+  ExceptT TokenResponseError m OAuth2Token+pollDeviceTokenRequestInternal idpApp mgr deviceCode intervalSeconds = do+  resp <- runExceptT (conduitTokenRequest idpApp mgr deviceCode)+  case resp of+    Left trRespError -> do+      case tokenResponseError trRespError of+        -- TODO: Didn't have a good idea to expand the error code+        -- specifically for device token request flow+        -- Device Token Response additional error code: https://www.rfc-editor.org/rfc/rfc8628#section-3.5+        UnknownErrorCode "authorization_pending" -> do+          liftIO $ threadDelay $ intervalSeconds * 1000000+          pollDeviceTokenRequestInternal idpApp mgr deviceCode intervalSeconds+        UnknownErrorCode "slow_down" -> do+          let newIntervalSeconds = intervalSeconds + 5+          liftIO $ threadDelay $ newIntervalSeconds * 1000000+          pollDeviceTokenRequestInternal idpApp mgr deviceCode newIntervalSeconds+        _ -> throwE trRespError+    Right v -> pure v++instance HasOAuth2Key DeviceAuthorizationApplication where+  mkOAuth2Key :: DeviceAuthorizationApplication -> OAuth2+  mkOAuth2Key DeviceAuthorizationApplication {..} = toOAuth2Key daClientId daClientSecret++instance HasTokenRequestClientAuthenticationMethod DeviceAuthorizationApplication where+  getClientAuthenticationMethod :: DeviceAuthorizationApplication -> ClientAuthenticationMethod+  getClientAuthenticationMethod _ = ClientSecretBasic++instance HasDeviceAuthorizationRequest DeviceAuthorizationApplication where+  mkDeviceAuthorizationRequestParam :: DeviceAuthorizationApplication -> DeviceAuthorizationRequestParam+  mkDeviceAuthorizationRequestParam DeviceAuthorizationApplication {..} =+    DeviceAuthorizationRequestParam+      { arScope = daScope+      , arClientId =+          if daAuthorizationRequestAuthenticationMethod == Just ClientSecretBasic+            then Nothing+            else Just daClientId+      , arExtraParams = daAuthorizationRequestExtraParam+      }++-- | https://www.rfc-editor.org/rfc/rfc8628#section-3.4+instance HasTokenRequest DeviceAuthorizationApplication where+  type ExchangeTokenInfo DeviceAuthorizationApplication = DeviceCode+  data TokenRequest DeviceAuthorizationApplication = AuthorizationCodeTokenRequest+    { trCode :: DeviceCode+    , trGrantType :: GrantTypeValue+    , trClientId :: Maybe ClientId+    }++  mkTokenRequestParam ::+    DeviceAuthorizationApplication ->+    DeviceCode ->+    TokenRequest DeviceAuthorizationApplication+  mkTokenRequestParam DeviceAuthorizationApplication {..} deviceCode =+    --+    -- This is a bit hacky!+    -- The token request use `ClientSecretBasic` by default. (has to pick up one Client Authn Method)+    -- ClientId shall be also be in request body per spec.+    -- However, for some IdPs, e.g. Okta, when using `ClientSecretBasic` to authn Client,+    -- it doesn't allow @client_id@ in the request body+    -- 'daAuthorizationRequestAuthenticationMethod' set the tone for Authorization Request,+    -- hence just follow it in the token request+    AuthorizationCodeTokenRequest+      { trCode = deviceCode+      , trGrantType = GTDeviceCode+      , trClientId =+          if daAuthorizationRequestAuthenticationMethod == Just ClientSecretBasic+            then Nothing+            else Just daClientId+      }++instance ToQueryParam (TokenRequest DeviceAuthorizationApplication) where+  toQueryParam :: TokenRequest DeviceAuthorizationApplication -> Map Text Text+  toQueryParam AuthorizationCodeTokenRequest {..} =+    Map.unions+      [ toQueryParam trCode+      , toQueryParam trGrantType+      , toQueryParam trClientId+      ]++instance HasUserInfoRequest DeviceAuthorizationApplication
+ src/Network/OAuth2/Experiment/Grants/JwtBearer.hs view
@@ -0,0 +1,59 @@+{-# LANGUAGE FlexibleInstances #-}++module Network.OAuth2.Experiment.Grants.JwtBearer where++import Data.ByteString qualified as BS+import Data.Default (Default (def))+import Data.Map.Strict (Map)+import Data.Map.Strict qualified as Map+import Data.Text.Lazy (Text)+import Network.OAuth.OAuth2 (ClientAuthenticationMethod (..), OAuth2)+import Network.OAuth2.Experiment.Flows.TokenRequest+import Network.OAuth2.Experiment.Flows.UserInfoRequest+import Network.OAuth2.Experiment.Types+import Network.OAuth2.Experiment.Utils++-- | An Application that supports "JWT Bearer" flow+--+-- https://datatracker.ietf.org/doc/html/rfc7523+data JwtBearerApplication = JwtBearerApplication+  { jbName :: Text+  , jbJwtAssertion :: BS.ByteString+  }++-- JwtBearner doesn't use @client_id@ and @client_secret@ for authentication.+--+-- FIXME: The ideal solution shall be do not implement `HasOAuth2Key`+-- but it will stop to re-use the method 'conduitTokenRequest' for JwtBearer flow.+instance HasOAuth2Key JwtBearerApplication where+  mkOAuth2Key :: JwtBearerApplication -> OAuth2+  mkOAuth2Key _ = def++instance HasTokenRequestClientAuthenticationMethod JwtBearerApplication where+  getClientAuthenticationMethod :: JwtBearerApplication -> ClientAuthenticationMethod+  getClientAuthenticationMethod _ = ClientAssertionJwt++instance HasTokenRequest JwtBearerApplication where+  type ExchangeTokenInfo JwtBearerApplication = NoNeedExchangeToken++  data TokenRequest JwtBearerApplication = JwtBearerTokenRequest+    { trGrantType :: GrantTypeValue -- \| 'GTJwtBearer'+    , trAssertion :: BS.ByteString -- \| The the signed JWT token+    }++  mkTokenRequestParam :: JwtBearerApplication -> NoNeedExchangeToken -> TokenRequest JwtBearerApplication+  mkTokenRequestParam JwtBearerApplication {..} _ =+    JwtBearerTokenRequest+      { trGrantType = GTJwtBearer+      , trAssertion = jbJwtAssertion+      }++instance ToQueryParam (TokenRequest JwtBearerApplication) where+  toQueryParam :: TokenRequest JwtBearerApplication -> Map Text Text+  toQueryParam JwtBearerTokenRequest {..} =+    Map.unions+      [ toQueryParam trGrantType+      , Map.singleton "assertion" (bs8ToLazyText trAssertion)+      ]++instance HasUserInfoRequest JwtBearerApplication
+ src/Network/OAuth2/Experiment/Grants/ResourceOwnerPassword.hs view
@@ -0,0 +1,78 @@+{-# LANGUAGE FlexibleInstances #-}++module Network.OAuth2.Experiment.Grants.ResourceOwnerPassword where++import Data.Map.Strict (Map)+import Data.Map.Strict qualified as Map+import Data.Set (Set)+import Data.Text.Lazy (Text)+import Network.OAuth.OAuth2 (ClientAuthenticationMethod (..), OAuth2 (..))+import Network.OAuth.OAuth2 qualified as OAuth2+import Network.OAuth2.Experiment.Flows.RefreshTokenRequest+import Network.OAuth2.Experiment.Flows.TokenRequest+import Network.OAuth2.Experiment.Flows.UserInfoRequest+import Network.OAuth2.Experiment.Types++-- | An Application that supports "Resource Owner Password" flow+--+-- https://www.rfc-editor.org/rfc/rfc6749#section-4.3+data ResourceOwnerPasswordApplication = ResourceOwnerPasswordApplication+  { ropClientId :: ClientId+  , ropClientSecret :: ClientSecret+  , ropName :: Text+  , ropScope :: Set Scope+  , ropUserName :: Username+  , ropPassword :: Password+  , ropTokenRequestExtraParams :: Map Text Text+  }++instance HasOAuth2Key ResourceOwnerPasswordApplication where+  mkOAuth2Key :: ResourceOwnerPasswordApplication -> OAuth2+  mkOAuth2Key ResourceOwnerPasswordApplication {..} = toOAuth2Key ropClientId ropClientSecret++instance HasTokenRequestClientAuthenticationMethod ResourceOwnerPasswordApplication where+  getClientAuthenticationMethod :: ResourceOwnerPasswordApplication -> ClientAuthenticationMethod+  getClientAuthenticationMethod _ = ClientSecretBasic++-- | https://www.rfc-editor.org/rfc/rfc6749#section-4.3.2+instance HasTokenRequest ResourceOwnerPasswordApplication where+  type ExchangeTokenInfo ResourceOwnerPasswordApplication = NoNeedExchangeToken++  data TokenRequest ResourceOwnerPasswordApplication = PasswordTokenRequest+    { trScope :: Set Scope+    , trUsername :: Username+    , trPassword :: Password+    , trGrantType :: GrantTypeValue+    , trExtraParams :: Map Text Text+    }+  mkTokenRequestParam :: ResourceOwnerPasswordApplication -> NoNeedExchangeToken -> TokenRequest ResourceOwnerPasswordApplication+  mkTokenRequestParam ResourceOwnerPasswordApplication {..} _ =+    PasswordTokenRequest+      { trUsername = ropUserName+      , trPassword = ropPassword+      , trGrantType = GTPassword+      , trScope = ropScope+      , trExtraParams = ropTokenRequestExtraParams+      }++instance ToQueryParam (TokenRequest ResourceOwnerPasswordApplication) where+  toQueryParam :: TokenRequest ResourceOwnerPasswordApplication -> Map Text Text+  toQueryParam PasswordTokenRequest {..} =+    Map.unions+      [ toQueryParam trGrantType+      , toQueryParam trScope+      , toQueryParam trUsername+      , toQueryParam trPassword+      , trExtraParams+      ]++instance HasUserInfoRequest ResourceOwnerPasswordApplication++instance HasRefreshTokenRequest ResourceOwnerPasswordApplication where+  mkRefreshTokenRequestParam :: ResourceOwnerPasswordApplication -> OAuth2.RefreshToken -> RefreshTokenRequest+  mkRefreshTokenRequestParam ResourceOwnerPasswordApplication {..} rt =+    RefreshTokenRequest+      { rrScope = ropScope+      , rrGrantType = GTRefreshToken+      , rrRefreshToken = rt+      }
src/Network/OAuth2/Experiment/Pkce.hs view
@@ -18,7 +18,7 @@  newtype CodeChallenge = CodeChallenge {unCodeChallenge :: Text} -newtype CodeVerifier = CodeVerifier {unCodeVerifier :: Text} deriving (Show)+newtype CodeVerifier = CodeVerifier {unCodeVerifier :: Text}  data CodeChallengeMethod = S256   deriving (Show)@@ -27,7 +27,7 @@   { codeVerifier :: CodeVerifier   , codeChallenge :: CodeChallenge   , codeChallengeMethod :: CodeChallengeMethod-  -- ^ spec says optional but really it shall be s256 or can be omitted?+  -- ^ spec says optional but in practice it is S256   -- https://datatracker.ietf.org/doc/html/rfc7636#section-4.3   } 
src/Network/OAuth2/Experiment/Types.hs view
@@ -1,116 +1,107 @@-{-# LANGUAGE AllowAmbiguousTypes #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE ImportQualifiedPost #-}-{-# LANGUAGE InstanceSigs #-}-{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE UndecidableInstances #-}  module Network.OAuth2.Experiment.Types where -import Control.Monad.IO.Class (MonadIO (..))-import Control.Monad.Trans.Except (ExceptT (..), throwE)-import Data.Aeson (FromJSON)-import Data.Bifunctor-import Data.ByteString qualified as BS-import Data.ByteString.Lazy.Char8 qualified as BSL import Data.Default (Default (def))-import Data.Kind import Data.Map.Strict (Map) import Data.Map.Strict qualified as Map import Data.Set (Set) import Data.Set qualified as Set import Data.String-import Data.Text.Encoding qualified as T import Data.Text.Lazy (Text) import Data.Text.Lazy qualified as TL-import Network.HTTP.Conduit import Network.OAuth.OAuth2 hiding (RefreshToken) import Network.OAuth.OAuth2 qualified as OAuth2 import Network.OAuth2.Experiment.Pkce import Network.OAuth2.Experiment.Utils-import URI.ByteString hiding (UserInfo)--{- NOTE-  1. shall I lift the constrain of all 'a :: GrantTypeFlow' so that user has max customization/flexibility?--}+import URI.ByteString (URI, serializeURIRef')  ------------------------------------------------------------------------------- --- * Grant Type+-- * Idp App  ------------------------------------------------------------------------------- -data GrantTypeFlow-  = -- | https://www.rfc-editor.org/rfc/rfc6749#section-4.1-    AuthorizationCode-  | -- | https://www.rfc-editor.org/rfc/rfc6749#section-4.3-    ResourceOwnerPassword-  | -- | https://www.rfc-editor.org/rfc/rfc6749#section-4.4-    ClientCredentials-  | -- | https://www.rfc-editor.org/rfc/rfc7523.html#section-2.1-    JwtBearer+-- TODO: Distinct type per endpoint+-- Because I made mistake at passing to Authorize and Token Request --------------------------------------------------------------------------------+-- | @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+  { idpUserInfoEndpoint :: URI+  -- ^ Userinfo Endpoint+  , idpAuthorizeEndpoint :: URI+  -- ^ Authorization Endpoint+  , idpTokenEndpoint :: URI+  -- ^ Token Endpoint+  , idpDeviceAuthorizationEndpoint :: Maybe URI+  -- ^ Apparently not all IdP support device code flow+  } --- * Response Type value+-- | An OAuth2 Application "a" of IdP "i".+-- "a" can be one of following type:+--+-- * `Network.OAuth2.Experiment.AuthorizationCodeApplication`+-- * `Network.OAuth2.Experiment.DeviceAuthorizationApplication`+-- * `Network.OAuth2.Experiment.ClientCredentialsApplication`+-- * `Network.OAuth2.Experiment.ResourceOwnerPasswordApplication`+-- * `Network.OAuth2.Experiment.JwtBearerApplication`+data IdpApplication (i :: k) a = IdpApplication+  { idp :: Idp i+  , application :: a+  }  ------------------------------------------------------------------------------- -class ToResponseTypeValue (a :: GrantTypeFlow) where-  toResponseTypeValue :: IsString b => b+-- * Scope -instance ToResponseTypeValue 'AuthorizationCode where-  -- https://www.rfc-editor.org/rfc/rfc6749#section-3.1.1-  -- Only support "authorization code" flow-  toResponseTypeValue :: IsString b => b-  toResponseTypeValue = "code"+------------------------------------------------------------------------------- -toResponseTypeParam :: forall a b req. (ToResponseTypeValue a, IsString b) => req a -> Map b b-toResponseTypeParam _ = Map.singleton "response_type" (toResponseTypeValue @a)+-- TODO: What's best type for Scope?+-- Use 'Text' isn't super type safe. All cannot specify some standard scopes like openid, email etc.+-- But Following data type is not ideal as Idp would have lots of 'Custom Text'+--+-- @+-- data Scope = OPENID | PROFILE | EMAIL | OFFLINE_ACCESS | Custom Text+-- @+--+-- Would be nice to define Enum for standard Scope, plus allow user to define their own define (per Idp) and plugin somehow.+newtype Scope = Scope {unScope :: Text}+  deriving (Eq, Ord) +instance IsString Scope where+  fromString :: String -> Scope+  fromString = Scope . TL.pack+ -------------------------------------------------------------------------------  -- * Grant Type value  ------------------------------------------------------------------------------- --- | Grant type query parameter has association with 'GrantTypeFlow' but not completely strict.+-- | Grant type query parameter has association with different GrantType flows but not completely strict. ----- e.g. Both 'AuthorizationCode' and 'ResourceOwnerPassword' flow could support refresh token flow.+-- e.g. Both AuthorizationCode and ResourceOwnerPassword flow could support refresh token flow. data GrantTypeValue   = GTAuthorizationCode   | GTPassword   | GTClientCredentials   | GTRefreshToken   | GTJwtBearer+  | GTDeviceCode   deriving (Eq, Show)  ----------------------------------------------------------------------------------- * Scope-+--                               Response Type                               -- ----------------------------------------------------------------------------------- TODO: following data type is not ideal as Idp would have lots of 'Custom Text'------ @--- data Scope = OPENID | PROFILE | EMAIL | OFFLINE_ACCESS | Custom Text--- @------ Would be nice to define Enum for standard Scope, plus allow user to define their own define (per Idp) and plugin somehow.-newtype Scope = Scope {unScope :: Text}-  deriving (Show, Eq, Ord)--instance IsString Scope where-  fromString :: String -> Scope-  fromString = Scope . TL.pack+data ResponseType = Code  ------------------------------------------------------------------------------- @@ -180,6 +171,7 @@       val GTClientCredentials = "client_credentials"       val GTRefreshToken = "refresh_token"       val GTJwtBearer = "urn:ietf:params:oauth:grant-type:jwt-bearer"+      val GTDeviceCode = "urn:ietf:params:oauth:grant-type:device_code"  instance ToQueryParam ClientId where   toQueryParam :: ClientId -> Map Text Text@@ -208,7 +200,7 @@   toQueryParam :: Set Scope -> Map Text Text   toQueryParam = toScopeParam . Set.map unScope     where-      toScopeParam :: (IsString a) => Set Text -> Map a Text+      toScopeParam :: IsString a => Set Text -> Map a Text       toScopeParam scope = Map.singleton "scope" (TL.intercalate " " $ Set.toList scope)  instance ToQueryParam CodeVerifier where@@ -231,546 +223,16 @@   toQueryParam :: OAuth2.RefreshToken -> Map Text Text   toQueryParam (OAuth2.RefreshToken x) = Map.singleton "refresh_token" (TL.fromStrict x) ------------------------------------------------------------------------------------ * Authorization and Token Requests types-----------------------------------------------------------------------------------class HasAuthorizeRequest (a :: GrantTypeFlow) where-  data AuthorizationRequest a-  type MkAuthorizationRequestResponse a-  mkAuthorizeRequestParameter :: IdpApplication a i -> AuthorizationRequest a-  mkAuthorizeRequest :: IdpApplication a i -> MkAuthorizationRequestResponse a--class HasTokenRequest (a :: GrantTypeFlow) where-  -- | Each GrantTypeFlow has slightly different request parameter to /token endpoint.-  data TokenRequest a--  -- | Only 'AuthorizationCode flow (but not resource owner password nor client credentials) will use 'ExchangeToken' in the token request-  -- create type family to be explicit on it.-  -- with 'type instance WithExchangeToken a b = b' implies no exchange token-  -- v.s. 'type instance WithExchangeToken a b = ExchangeToken -> b' implies needing an exchange token-  type WithExchangeToken a b--  mkTokenRequest ::-    IdpApplication a i ->-    WithExchangeToken a (TokenRequest a)--  conduitTokenRequest ::-    (MonadIO m) =>-    IdpApplication a i ->-    Manager ->-    WithExchangeToken a (ExceptT TokenRequestError m OAuth2Token)--class HasPkceAuthorizeRequest (a :: GrantTypeFlow) where-  mkPkceAuthorizeRequest :: MonadIO m => IdpApplication a i -> m (TL.Text, CodeVerifier)--class HasPkceTokenRequest (b :: GrantTypeFlow) where-  conduitPkceTokenRequest ::-    (MonadIO m) =>-    IdpApplication b i ->-    Manager ->-    (ExchangeToken, CodeVerifier) ->-    ExceptT TokenRequestError m OAuth2Token--class HasRefreshTokenRequest (a :: GrantTypeFlow) where-  -- | https://www.rfc-editor.org/rfc/rfc6749#page-47-  data RefreshTokenRequest a--  mkRefreshTokenRequest :: IdpApplication a i -> OAuth2.RefreshToken -> RefreshTokenRequest a-  conduitRefreshTokenRequest ::-    (MonadIO m) =>-    IdpApplication a i ->-    Manager ->-    OAuth2.RefreshToken ->-    ExceptT TokenRequestError m OAuth2Token------------------------------------------------------------------------------------- * User Info types-----------------------------------------------------------------------------------type family IdpUserInfo a--class HasUserInfoRequest (a :: GrantTypeFlow) where-  conduitUserInfoRequest ::-    FromJSON (IdpUserInfo i) =>-    IdpApplication a i ->-    Manager ->-    AccessToken ->-    ExceptT BSL.ByteString IO (IdpUserInfo i)------------------------------------------------------------------------------------- * Idp App------------------------------------------------------------------------------------- | Shall IdpApplication has a field of 'Idp a'??-data Idp a = Idp-  { idpUserInfoEndpoint :: URI-  , -- NOTE: maybe worth data type to distinguish authorize and token endpoint-    -- as I made mistake at passing to Authorize and Token Request-    idpAuthorizeEndpoint :: URI-  , idpTokenEndpoint :: URI-  , idpFetchUserInfo ::-      forall m.-      (FromJSON (IdpUserInfo a), MonadIO m) =>-      Manager ->-      AccessToken ->-      URI ->-      ExceptT BSL.ByteString m (IdpUserInfo a)-  }------------------------------------------------------------------------------------- * Idp App Config-----------------------------------------------------------------------------------data family IdpApplication (a :: GrantTypeFlow) (i :: Type)------------------------------------------------------------------------------------- * Authorization Code flow------------------------------------------------------------------------------------- | An Application that supports "Authorization code" flow-data instance IdpApplication 'AuthorizationCode i = AuthorizationCodeIdpApplication-  { idpAppName :: Text-  , idpAppClientId :: ClientId-  , idpAppClientSecret :: ClientSecret-  , idpAppScope :: Set Scope-  , idpAppRedirectUri :: URI-  , idpAppAuthorizeState :: AuthorizeState-  , idpAppAuthorizeExtraParams :: Map Text Text-  -- ^ Though technically one key can have multiple value in query, but who actually does it?!-  , idpAppTokenRequestAuthenticationMethod :: ClientAuthenticationMethod-  , idp :: Idp i-  }---- NOTE: maybe add function for parase authorization response--- though seems overkill. https://github.com/freizl/hoauth2/issues/149--- parseAuthorizationResponse :: String -> AuthorizationResponse--- parseAuthorizationResponse :: ( String, String ) -> AuthorizationResponse--instance HasAuthorizeRequest 'AuthorizationCode where-  -- \| https://www.rfc-editor.org/rfc/rfc6749#section-4.1.1-  data AuthorizationRequest 'AuthorizationCode = AuthorizationCodeAuthorizationRequest-    { scope :: Set Scope-    , state :: AuthorizeState-    , clientId :: ClientId-    , redirectUri :: Maybe RedirectUri-    }-  type MkAuthorizationRequestResponse 'AuthorizationCode = Text--  mkAuthorizeRequestParameter :: IdpApplication 'AuthorizationCode i -> AuthorizationRequest 'AuthorizationCode-  mkAuthorizeRequestParameter AuthorizationCodeIdpApplication {..} =-    AuthorizationCodeAuthorizationRequest-      { scope = if null idpAppScope then Set.empty else idpAppScope-      , state = idpAppAuthorizeState-      , clientId = idpAppClientId-      , redirectUri = Just (RedirectUri idpAppRedirectUri)-      }--  mkAuthorizeRequest :: IdpApplication 'AuthorizationCode i -> Text-  mkAuthorizeRequest idpAppConfig@AuthorizationCodeIdpApplication {..} =-    let req = mkAuthorizeRequestParameter idpAppConfig-        allParams =-          map (bimap tlToBS tlToBS) $-            Map.toList $-              Map.unions [idpAppAuthorizeExtraParams, toQueryParam req]-     in TL.fromStrict $-          T.decodeUtf8 $-            serializeURIRef' $-              appendQueryParams allParams $-                idpAuthorizeEndpoint idp--instance HasTokenRequest 'AuthorizationCode where-  -- \| https://www.rfc-editor.org/rfc/rfc6749#section-4.1.3-  data TokenRequest 'AuthorizationCode = AuthorizationCodeTokenRequest-    { code :: ExchangeToken-    , clientId :: ClientId-    , grantType :: GrantTypeValue-    , redirectUri :: RedirectUri-    }-  type WithExchangeToken 'AuthorizationCode a = ExchangeToken -> a--  mkTokenRequest ::-    IdpApplication 'AuthorizationCode i ->-    ExchangeToken ->-    TokenRequest 'AuthorizationCode-  mkTokenRequest AuthorizationCodeIdpApplication {..} authCode =-    AuthorizationCodeTokenRequest-      { code = authCode-      , clientId = idpAppClientId-      , grantType = GTAuthorizationCode-      , redirectUri = RedirectUri idpAppRedirectUri-      }-  conduitTokenRequest ::-    forall m i.-    (MonadIO m) =>-    IdpApplication 'AuthorizationCode i ->-    Manager ->-    ExchangeToken ->-    ExceptT TokenRequestError m OAuth2Token-  conduitTokenRequest idpAppConfig@AuthorizationCodeIdpApplication {..} mgr exchangeToken =-    let req = mkTokenRequest idpAppConfig exchangeToken-        key = toOAuth2Key idpAppClientId idpAppClientSecret-        body =-          mapsToParams-            [ toQueryParam req-            , toQueryParam-                ( if idpAppTokenRequestAuthenticationMethod == ClientSecretPost-                    then Just idpAppClientSecret-                    else Nothing-                )-            ]-     in doJSONPostRequest mgr key (idpTokenEndpoint idp) body--instance HasPkceAuthorizeRequest 'AuthorizationCode where-  mkPkceAuthorizeRequest :: MonadIO m => IdpApplication 'AuthorizationCode i -> m (Text, CodeVerifier)-  mkPkceAuthorizeRequest idpAppConfig@AuthorizationCodeIdpApplication {..} = do-    PkceRequestParam {..} <- mkPkceParam-    let req = mkAuthorizeRequestParameter idpAppConfig-    let allParams =-          mapsToParams-            [ idpAppAuthorizeExtraParams-            , toQueryParam req-            , toQueryParam codeChallenge-            , toQueryParam codeChallengeMethod-            ]--    let url =-          TL.fromStrict $-            T.decodeUtf8 $-              serializeURIRef' $-                appendQueryParams allParams $-                  idpAuthorizeEndpoint idp-    pure (url, codeVerifier)--instance HasPkceTokenRequest 'AuthorizationCode where-  conduitPkceTokenRequest ::-    MonadIO m =>-    IdpApplication 'AuthorizationCode i ->-    Manager ->-    (ExchangeToken, CodeVerifier) ->-    ExceptT TokenRequestError m OAuth2Token-  conduitPkceTokenRequest idpAppConfig@AuthorizationCodeIdpApplication {..} mgr (exchangeToken, codeVerifier) =-    let req = mkTokenRequest idpAppConfig exchangeToken-        key = toOAuth2Key idpAppClientId idpAppClientSecret-        body =-          mapsToParams-            [ toQueryParam req-            , toQueryParam codeVerifier-            , toQueryParam (if idpAppTokenRequestAuthenticationMethod == ClientSecretPost then Just idpAppClientSecret else Nothing)-            ]-     in doJSONPostRequest mgr key (idpTokenEndpoint idp) body--instance HasRefreshTokenRequest 'AuthorizationCode where-  data RefreshTokenRequest 'AuthorizationCode = AuthorizationCodeTokenRefreshRequest-    { refreshToken :: OAuth2.RefreshToken-    , grantType :: GrantTypeValue-    , scope :: Set Scope-    }--  mkRefreshTokenRequest :: IdpApplication 'AuthorizationCode i -> OAuth2.RefreshToken -> RefreshTokenRequest 'AuthorizationCode-  mkRefreshTokenRequest AuthorizationCodeIdpApplication {..} rt =-    AuthorizationCodeTokenRefreshRequest-      { scope = idpAppScope-      , grantType = GTRefreshToken-      , refreshToken = rt-      }-  conduitRefreshTokenRequest ::-    (MonadIO m) =>-    IdpApplication 'AuthorizationCode i ->-    Manager ->-    OAuth2.RefreshToken ->-    ExceptT TokenRequestError m OAuth2Token-  conduitRefreshTokenRequest idpAppConfig@AuthorizationCodeIdpApplication {..} mgr rt =-    let req = mkRefreshTokenRequest idpAppConfig rt-        key = toOAuth2Key idpAppClientId idpAppClientSecret-        body =-          mapsToParams-            [ toQueryParam req-            , toQueryParam (if idpAppTokenRequestAuthenticationMethod == ClientSecretPost then Just idpAppClientSecret else Nothing)-            ]-     in doJSONPostRequest mgr key (idpTokenEndpoint idp) body--instance HasUserInfoRequest 'AuthorizationCode where-  conduitUserInfoRequest ::-    FromJSON (IdpUserInfo i) =>-    IdpApplication 'AuthorizationCode i ->-    Manager ->-    AccessToken ->-    ExceptT BSL.ByteString IO (IdpUserInfo i)-  conduitUserInfoRequest AuthorizationCodeIdpApplication {..} mgr at = do-    idpFetchUserInfo idp mgr at (idpUserInfoEndpoint idp)--instance ToQueryParam (AuthorizationRequest 'AuthorizationCode) where-  toQueryParam :: AuthorizationRequest 'AuthorizationCode -> Map Text Text-  toQueryParam req@AuthorizationCodeAuthorizationRequest {..} =-    Map.unions-      [ toResponseTypeParam req-      , toQueryParam scope-      , toQueryParam clientId-      , toQueryParam state-      , toQueryParam redirectUri-      ]--instance ToQueryParam (TokenRequest 'AuthorizationCode) where-  toQueryParam :: TokenRequest 'AuthorizationCode -> Map Text Text-  toQueryParam AuthorizationCodeTokenRequest {..} =-    Map.unions-      [ toQueryParam grantType-      , toQueryParam code-      , toQueryParam redirectUri-      ]--instance ToQueryParam (RefreshTokenRequest 'AuthorizationCode) where-  toQueryParam :: RefreshTokenRequest 'AuthorizationCode -> Map Text Text-  toQueryParam AuthorizationCodeTokenRefreshRequest {..} =-    Map.unions-      [ toQueryParam grantType-      , toQueryParam scope-      , toQueryParam refreshToken-      ]------------------------------------------------------------------------------------- * JWTBearer------------------------------------------------------------------------------------- | An Application that supports "Authorization code" flow-data instance IdpApplication 'JwtBearer i = JwtBearerIdpApplication-  { idpAppName :: Text-  , idpAppJwt :: BS.ByteString-  , idp :: Idp i-  }--instance HasTokenRequest 'JwtBearer where-  data TokenRequest 'JwtBearer = JwtBearerTokenRequest-    { grantType :: GrantTypeValue -- \| 'GTJwtBearer'-    , assertion :: BS.ByteString -- \| The the signed JWT token-    }-  type WithExchangeToken 'JwtBearer a = a--  mkTokenRequest ::-    IdpApplication 'JwtBearer i ->-    TokenRequest 'JwtBearer-  mkTokenRequest JwtBearerIdpApplication {..} =-    JwtBearerTokenRequest-      { grantType = GTJwtBearer-      , assertion = idpAppJwt-      }--  conduitTokenRequest ::-    forall m i.-    (MonadIO m) =>-    IdpApplication 'JwtBearer i ->-    Manager ->-    ExceptT TokenRequestError m OAuth2Token-  conduitTokenRequest idpAppConfig@JwtBearerIdpApplication {..} mgr = do-    resp <- ExceptT . liftIO $ do-      let tokenReq = mkTokenRequest idpAppConfig-      let body = mapsToParams [toQueryParam tokenReq]-      req <- uriToRequest (idpTokenEndpoint idp)-      handleOAuth2TokenResponse <$> httpLbs (urlEncodedBody body (addDefaultRequestHeaders req)) mgr-    case parseResponseFlexible resp of-      Right obj -> return obj-      Left e -> throwE e--instance ToQueryParam (TokenRequest 'JwtBearer) where-  toQueryParam :: TokenRequest 'JwtBearer -> Map Text Text-  toQueryParam JwtBearerTokenRequest {..} =-    Map.unions-      [ toQueryParam grantType-      , Map.fromList [("assertion", bs8ToLazyText assertion)]-      ]--instance HasUserInfoRequest 'JwtBearer where-  conduitUserInfoRequest JwtBearerIdpApplication {..} mgr at = do-    idpFetchUserInfo idp mgr at (idpUserInfoEndpoint idp)------------------------------------------------------------------------------------- * Password flow------------------------------------------------------------------------------------- https://www.rfc-editor.org/rfc/rfc6749#section-4.3.1--- 4.3.1.  Authorization Request and Response (Password grant type)--- The method through which the client obtains the resource owner--- credentials is beyond the scope of this specification.  The client--- MUST discard the credentials once an access token has been obtained.------ Hence no AuhorizationRequest instance--data instance IdpApplication 'ResourceOwnerPassword i = ResourceOwnerPasswordIDPApplication-  { idpAppClientId :: ClientId-  , idpAppClientSecret :: ClientSecret-  , idpAppName :: Text-  , idpAppScope :: Set Scope-  , idpAppUserName :: Username-  , idpAppPassword :: Password-  , idpAppTokenRequestExtraParams :: Map Text Text-  -- ^ Any parameter that required by your Idp and not mentioned in the OAuth2 spec-  , idp :: Idp i-  }--instance HasUserInfoRequest 'ResourceOwnerPassword where-  conduitUserInfoRequest ResourceOwnerPasswordIDPApplication {..} mgr at = do-    idpFetchUserInfo idp mgr at (idpUserInfoEndpoint idp)--instance HasTokenRequest 'ResourceOwnerPassword where-  -- \| https://www.rfc-editor.org/rfc/rfc6749#section-4.3.2-  data TokenRequest 'ResourceOwnerPassword = PasswordTokenRequest-    { scope :: Set Scope-    , username :: Username-    , password :: Password-    , grantType :: GrantTypeValue-    }-  type WithExchangeToken 'ResourceOwnerPassword a = a--  mkTokenRequest :: IdpApplication 'ResourceOwnerPassword i -> TokenRequest 'ResourceOwnerPassword-  mkTokenRequest ResourceOwnerPasswordIDPApplication {..} =-    PasswordTokenRequest-      { username = idpAppUserName-      , password = idpAppPassword-      , grantType = GTPassword-      , scope = idpAppScope-      }--  conduitTokenRequest ::-    (MonadIO m) =>-    IdpApplication 'ResourceOwnerPassword i ->-    Manager ->-    ExceptT TokenRequestError m OAuth2Token-  conduitTokenRequest idpAppConfig@ResourceOwnerPasswordIDPApplication {..} mgr =-    let req = mkTokenRequest idpAppConfig-        key = toOAuth2Key idpAppClientId idpAppClientSecret-        body = mapsToParams [idpAppTokenRequestExtraParams, toQueryParam req]-     in doJSONPostRequest mgr key (idpTokenEndpoint idp) body---- | TODO: TBD-instance HasRefreshTokenRequest 'ResourceOwnerPassword where-  data RefreshTokenRequest 'ResourceOwnerPassword = PasswordRefreshTokenRequest--  mkRefreshTokenRequest ::-    IdpApplication 'ResourceOwnerPassword i ->-    OAuth2.RefreshToken ->-    RefreshTokenRequest 'ResourceOwnerPassword-  mkRefreshTokenRequest = undefined--  conduitRefreshTokenRequest ::-    MonadIO m =>-    IdpApplication 'ResourceOwnerPassword i ->-    Manager ->-    OAuth2.RefreshToken ->-    ExceptT TokenRequestError m OAuth2Token-  conduitRefreshTokenRequest = undefined--instance ToQueryParam (TokenRequest 'ResourceOwnerPassword) where-  toQueryParam :: TokenRequest 'ResourceOwnerPassword -> Map Text Text-  toQueryParam PasswordTokenRequest {..} =-    Map.unions-      [ toQueryParam grantType-      , toQueryParam scope-      , toQueryParam username-      , toQueryParam password-      ]+instance ToQueryParam ResponseType where+  toQueryParam :: ResponseType -> Map Text Text+  toQueryParam Code = Map.singleton "response_type" "code"  ----------------------------------------------------------------------------------- * Client Credentials flow-+--                                HasOAuth2Key                               --+--                                                                           --+-- Find a way to reuse some methods from old implementation                  --+-- Probably will be removed when Experiment module becomes default           -- ------------------------------------------------------------------------------- --- https://www.rfc-editor.org/rfc/rfc6749#section-4.4.1--- 4.4.1.  Authorization Request and Response (Client Credentials grant type)--- Since the client authentication is used as the authorization grant,--- no additional authorization request is needed.------ Hence no AuhorizationRequest instance--data instance IdpApplication 'ClientCredentials i = ClientCredentialsIDPApplication-  { idpAppClientId :: ClientId-  , idpAppClientSecret :: ClientSecret-  , idpAppTokenRequestAuthenticationMethod :: ClientAuthenticationMethod-  -- ^ FIXME: rename to ClientCredential-  , idpAppName :: Text-  , idpAppScope :: Set Scope-  , idpAppTokenRequestExtraParams :: Map Text Text-  -- ^ Any parameter that required by your Idp and not mentioned in the OAuth2 spec-  , idp :: Idp i-  }--instance HasTokenRequest 'ClientCredentials where-  -- \| https://www.rfc-editor.org/rfc/rfc6749#section-4.4.2-  data TokenRequest 'ClientCredentials = ClientCredentialsTokenRequest-    { scope :: Set Scope-    , grantType :: GrantTypeValue-    , clientAssertionType :: Text-    , clientAssertion :: BS.ByteString-    , clientAuthenticationMethod :: ClientAuthenticationMethod-    }--  type WithExchangeToken 'ClientCredentials a = a--  mkTokenRequest :: IdpApplication 'ClientCredentials i -> TokenRequest 'ClientCredentials-  mkTokenRequest ClientCredentialsIDPApplication {..} =-    ClientCredentialsTokenRequest-      { scope = idpAppScope-      , grantType = GTClientCredentials-      , clientAssertionType = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"-      , clientAssertion = tlToBS $ unClientSecret idpAppClientSecret-      , clientAuthenticationMethod = idpAppTokenRequestAuthenticationMethod-      }--  conduitTokenRequest ::-    (MonadIO m) =>-    IdpApplication 'ClientCredentials i ->-    Manager ->-    ExceptT TokenRequestError m OAuth2Token-  conduitTokenRequest idpAppConfig@ClientCredentialsIDPApplication {..} mgr = do-    let tokenReq = mkTokenRequest idpAppConfig-        key =-          toOAuth2Key-            idpAppClientId-            idpAppClientSecret-        body =-          mapsToParams-            [ idpAppTokenRequestExtraParams-            , toQueryParam tokenReq-            ]-    if clientAuthenticationMethod tokenReq == ClientAssertionJwt-      then do-        resp <- ExceptT . liftIO $ do-          req <- uriToRequest (idpTokenEndpoint idp)-          let req' = urlEncodedBody body (addDefaultRequestHeaders req)-          handleOAuth2TokenResponse <$> httpLbs req' mgr-        case parseResponseFlexible resp of-          Right obj -> return obj-          Left e -> throwE e-      else doJSONPostRequest mgr key (idpTokenEndpoint idp) body--instance ToQueryParam (TokenRequest 'ClientCredentials) where-  toQueryParam :: TokenRequest 'ClientCredentials -> Map Text Text-  toQueryParam ClientCredentialsTokenRequest {..} =-    Map.unions $-      [ toQueryParam grantType-      , toQueryParam scope-      ]-        ++ [ Map.fromList-              ( if clientAuthenticationMethod == ClientAssertionJwt-                  then-                    [ ("client_assertion_type", clientAssertionType)-                    , ("client_assertion", bs8ToLazyText clientAssertion)-                    ]-                  else []-              )-           ]+class HasOAuth2Key a where+  mkOAuth2Key :: a -> OAuth2
src/Network/OAuth2/Experiment/Utils.hs view
@@ -5,8 +5,11 @@ import Data.ByteString.Char8 qualified as BS8 import Data.Map.Strict (Map) import Data.Map.Strict qualified as Map+import Data.Text qualified as T+import Data.Text.Encoding qualified as T import Data.Text.Encoding qualified as TE import Data.Text.Lazy qualified as TL+import URI.ByteString (URI, serializeURIRef')  tlToBS :: TL.Text -> ByteString tlToBS = TE.encodeUtf8 . TL.toStrict@@ -14,8 +17,11 @@ bs8ToLazyText :: BS8.ByteString -> TL.Text bs8ToLazyText = TL.pack . BS8.unpack -mapsToParams :: [Map TL.Text TL.Text] -> [(ByteString, ByteString)]-mapsToParams =+unionMapsToQueryParams :: [Map TL.Text TL.Text] -> [(ByteString, ByteString)]+unionMapsToQueryParams =   map (bimap tlToBS tlToBS)     . Map.toList     . Map.unions++uriToText :: URI -> T.Text+uriToText = T.decodeUtf8 . serializeURIRef'
test/Network/OAuth/OAuth2/TokenRequestSpec.hs view
@@ -1,12 +1,16 @@+{-# LANGUAGE QuasiQuotes #-}+ module Network.OAuth.OAuth2.TokenRequestSpec where  import Data.Aeson qualified as Aeson import Network.OAuth.OAuth2.TokenRequest import Test.Hspec+import URI.ByteString.QQ+import Prelude hiding (error)  spec :: Spec-spec =-  describe "parseJSON TokenRequestErrorCode" $ do+spec = do+  describe "parseJSON TokenResponseErrorCode" $ do     it "invalid_request" $ do       Aeson.eitherDecode "\"invalid_request\"" `shouldBe` Right InvalidRequest     it "invalid_client" $ do@@ -21,3 +25,32 @@       Aeson.eitherDecode "\"invalid_scope\"" `shouldBe` Right InvalidScope     it "foo_code" $ do       Aeson.eitherDecode "\"foo_code\"" `shouldBe` Right (UnknownErrorCode "foo_code")++  describe "parseJSON TokenResponseError" $ do+    it "parse error" $ do+      Aeson.eitherDecode "{\"error\": \"invalid_request\"}"+        `shouldBe` Right+          ( TokenResponseError+              { tokenResponseError = InvalidRequest+              , tokenResponseErrorDescription = Nothing+              , tokenResponseErrorUri = Nothing+              }+          )+    it "parse error_description" $ do+      Aeson.eitherDecode "{\"error\": \"invalid_request\", \"error_description\": \"token request error foo1\"}"+        `shouldBe` Right+          ( TokenResponseError+              { tokenResponseError = InvalidRequest+              , tokenResponseErrorDescription = Just "token request error foo1"+              , tokenResponseErrorUri = Nothing+              }+          )+    it "parse error_uri" $ do+      Aeson.eitherDecode "{\"error\": \"invalid_request\", \"error_uri\": \"https://example.com\"}"+        `shouldBe` Right+          ( TokenResponseError+              { tokenResponseError = InvalidRequest+              , tokenResponseErrorDescription = Nothing+              , tokenResponseErrorUri = Just [uri|https://example.com|]+              }+          )