hoauth2 2.6.0 → 2.7.0
raw patch · 11 files changed
+450/−323 lines, 11 filesdep +hoauth2dep +hspecPVP ok
version bump matches the API change (PVP)
Dependencies added: hoauth2, hspec
API changes (from Hackage documentation)
- Network.OAuth.OAuth2: accessTokenUrl :: OAuth2 -> ExchangeToken -> (URI, PostBody)
- Network.OAuth.OAuth2: addDefaultRequestHeaders :: Request -> Request
- Network.OAuth.OAuth2: clientSecretPost :: OAuth2 -> PostBody
- Network.OAuth.OAuth2: doJSONPostRequest :: (MonadIO m, FromJSON err, FromJSON a) => Manager -> OAuth2 -> URI -> PostBody -> ExceptT (OAuth2Error err) m a
- Network.OAuth.OAuth2: doSimplePostRequest :: (MonadIO m, FromJSON err) => Manager -> OAuth2 -> URI -> PostBody -> ExceptT (OAuth2Error err) m ByteString
- Network.OAuth.OAuth2: fetchAccessToken :: MonadIO m => Manager -> OAuth2 -> ExchangeToken -> ExceptT (OAuth2Error Errors) m OAuth2Token
- Network.OAuth.OAuth2: fetchAccessToken2 :: MonadIO m => Manager -> OAuth2 -> ExchangeToken -> ExceptT (OAuth2Error Errors) m OAuth2Token
- Network.OAuth.OAuth2: fetchAccessTokenInternal :: MonadIO m => ClientAuthenticationMethod -> Manager -> OAuth2 -> ExchangeToken -> ExceptT (OAuth2Error Errors) m OAuth2Token
- Network.OAuth.OAuth2: fetchAccessTokenWithAuthMethod :: MonadIO m => ClientAuthenticationMethod -> Manager -> OAuth2 -> ExchangeToken -> ExceptT (OAuth2Error Errors) m OAuth2Token
- Network.OAuth.OAuth2: handleOAuth2TokenResponse :: FromJSON err => Response ByteString -> Either (OAuth2Error err) ByteString
- Network.OAuth.OAuth2: parseResponseFlexible :: (FromJSON err, FromJSON a) => ByteString -> Either (OAuth2Error err) a
- Network.OAuth.OAuth2: parseResponseString :: (FromJSON err, FromJSON a) => ByteString -> Either (OAuth2Error err) a
- Network.OAuth.OAuth2: refreshAccessToken :: MonadIO m => Manager -> OAuth2 -> RefreshToken -> ExceptT (OAuth2Error Errors) m OAuth2Token
- Network.OAuth.OAuth2: refreshAccessToken2 :: MonadIO m => Manager -> OAuth2 -> RefreshToken -> ExceptT (OAuth2Error Errors) m OAuth2Token
- Network.OAuth.OAuth2: refreshAccessTokenInternal :: MonadIO m => ClientAuthenticationMethod -> Manager -> OAuth2 -> RefreshToken -> ExceptT (OAuth2Error Errors) m OAuth2Token
- Network.OAuth.OAuth2: refreshAccessTokenUrl :: OAuth2 -> RefreshToken -> (URI, PostBody)
- Network.OAuth.OAuth2: refreshAccessTokenWithAuthMethod :: MonadIO m => ClientAuthenticationMethod -> Manager -> OAuth2 -> RefreshToken -> ExceptT (OAuth2Error Errors) m OAuth2Token
- Network.OAuth.OAuth2.AuthorizationRequest: instance Data.Aeson.Types.ToJSON.ToJSON Network.OAuth.OAuth2.AuthorizationRequest.Errors
- Network.OAuth.OAuth2.Internal: AccessToken :: Text -> AccessToken
- Network.OAuth.OAuth2.Internal: ClientSecretBasic :: ClientAuthenticationMethod
- Network.OAuth.OAuth2.Internal: ClientSecretPost :: ClientAuthenticationMethod
- Network.OAuth.OAuth2.Internal: ExchangeToken :: Text -> ExchangeToken
- Network.OAuth.OAuth2.Internal: IdToken :: Text -> IdToken
- Network.OAuth.OAuth2.Internal: OAuth2 :: Text -> Text -> URIRef Absolute -> URIRef Absolute -> URIRef Absolute -> OAuth2
- Network.OAuth.OAuth2.Internal: OAuth2Error :: Either Text a -> Maybe Text -> Maybe (URIRef Absolute) -> OAuth2Error a
- Network.OAuth.OAuth2.Internal: OAuth2Token :: AccessToken -> Maybe RefreshToken -> Maybe Int -> Maybe Text -> Maybe IdToken -> OAuth2Token
- Network.OAuth.OAuth2.Internal: RefreshToken :: Text -> RefreshToken
- Network.OAuth.OAuth2.Internal: [accessToken] :: OAuth2Token -> AccessToken
- Network.OAuth.OAuth2.Internal: [atoken] :: AccessToken -> Text
- Network.OAuth.OAuth2.Internal: [errorDescription] :: OAuth2Error a -> Maybe Text
- Network.OAuth.OAuth2.Internal: [errorUri] :: OAuth2Error a -> Maybe (URIRef Absolute)
- Network.OAuth.OAuth2.Internal: [error] :: OAuth2Error a -> Either Text a
- Network.OAuth.OAuth2.Internal: [expiresIn] :: OAuth2Token -> Maybe Int
- Network.OAuth.OAuth2.Internal: [extoken] :: ExchangeToken -> Text
- Network.OAuth.OAuth2.Internal: [idToken] :: OAuth2Token -> Maybe IdToken
- Network.OAuth.OAuth2.Internal: [idtoken] :: IdToken -> Text
- Network.OAuth.OAuth2.Internal: [oauth2AuthorizeEndpoint] :: OAuth2 -> URIRef Absolute
- Network.OAuth.OAuth2.Internal: [oauth2ClientId] :: OAuth2 -> Text
- Network.OAuth.OAuth2.Internal: [oauth2ClientSecret] :: OAuth2 -> Text
- Network.OAuth.OAuth2.Internal: [oauth2RedirectUri] :: OAuth2 -> URIRef Absolute
- Network.OAuth.OAuth2.Internal: [oauth2TokenEndpoint] :: OAuth2 -> URIRef Absolute
- Network.OAuth.OAuth2.Internal: [refreshToken] :: OAuth2Token -> Maybe RefreshToken
- Network.OAuth.OAuth2.Internal: [rtoken] :: RefreshToken -> Text
- Network.OAuth.OAuth2.Internal: [tokenType] :: OAuth2Token -> Maybe Text
- Network.OAuth.OAuth2.Internal: appendQueryParams :: [(ByteString, ByteString)] -> URIRef a -> URIRef a
- Network.OAuth.OAuth2.Internal: data ClientAuthenticationMethod
- Network.OAuth.OAuth2.Internal: data OAuth2
- Network.OAuth.OAuth2.Internal: data OAuth2Error a
- Network.OAuth.OAuth2.Internal: data OAuth2Token
- Network.OAuth.OAuth2.Internal: defaultRequestHeaders :: [(HeaderName, ByteString)]
- Network.OAuth.OAuth2.Internal: hostLens :: Lens' Request ByteString
- Network.OAuth.OAuth2.Internal: instance Data.Aeson.Types.FromJSON.FromJSON Network.OAuth.OAuth2.Internal.AccessToken
- Network.OAuth.OAuth2.Internal: instance Data.Aeson.Types.FromJSON.FromJSON Network.OAuth.OAuth2.Internal.ExchangeToken
- Network.OAuth.OAuth2.Internal: instance Data.Aeson.Types.FromJSON.FromJSON Network.OAuth.OAuth2.Internal.IdToken
- Network.OAuth.OAuth2.Internal: instance Data.Aeson.Types.FromJSON.FromJSON Network.OAuth.OAuth2.Internal.OAuth2Token
- Network.OAuth.OAuth2.Internal: instance Data.Aeson.Types.FromJSON.FromJSON Network.OAuth.OAuth2.Internal.RefreshToken
- Network.OAuth.OAuth2.Internal: instance Data.Aeson.Types.FromJSON.FromJSON err => Data.Aeson.Types.FromJSON.FromJSON (Network.OAuth.OAuth2.Internal.OAuth2Error err)
- Network.OAuth.OAuth2.Internal: instance Data.Aeson.Types.ToJSON.ToJSON Network.OAuth.OAuth2.Internal.AccessToken
- Network.OAuth.OAuth2.Internal: instance Data.Aeson.Types.ToJSON.ToJSON Network.OAuth.OAuth2.Internal.ExchangeToken
- Network.OAuth.OAuth2.Internal: instance Data.Aeson.Types.ToJSON.ToJSON Network.OAuth.OAuth2.Internal.IdToken
- Network.OAuth.OAuth2.Internal: instance Data.Aeson.Types.ToJSON.ToJSON Network.OAuth.OAuth2.Internal.OAuth2Token
- Network.OAuth.OAuth2.Internal: instance Data.Aeson.Types.ToJSON.ToJSON Network.OAuth.OAuth2.Internal.RefreshToken
- Network.OAuth.OAuth2.Internal: instance Data.Aeson.Types.ToJSON.ToJSON err => Data.Aeson.Types.ToJSON.ToJSON (Network.OAuth.OAuth2.Internal.OAuth2Error err)
- Network.OAuth.OAuth2.Internal: instance Data.Binary.Class.Binary Network.OAuth.OAuth2.Internal.AccessToken
- Network.OAuth.OAuth2.Internal: instance Data.Binary.Class.Binary Network.OAuth.OAuth2.Internal.IdToken
- Network.OAuth.OAuth2.Internal: instance Data.Binary.Class.Binary Network.OAuth.OAuth2.Internal.OAuth2Token
- Network.OAuth.OAuth2.Internal: instance Data.Binary.Class.Binary Network.OAuth.OAuth2.Internal.RefreshToken
- Network.OAuth.OAuth2.Internal: instance Data.Default.Class.Default Network.OAuth.OAuth2.Internal.OAuth2
- Network.OAuth.OAuth2.Internal: instance GHC.Classes.Eq Network.OAuth.OAuth2.Internal.AccessToken
- Network.OAuth.OAuth2.Internal: instance GHC.Classes.Eq Network.OAuth.OAuth2.Internal.ClientAuthenticationMethod
- Network.OAuth.OAuth2.Internal: instance GHC.Classes.Eq Network.OAuth.OAuth2.Internal.IdToken
- Network.OAuth.OAuth2.Internal: instance GHC.Classes.Eq Network.OAuth.OAuth2.Internal.OAuth2
- Network.OAuth.OAuth2.Internal: instance GHC.Classes.Eq Network.OAuth.OAuth2.Internal.OAuth2Token
- Network.OAuth.OAuth2.Internal: instance GHC.Classes.Eq Network.OAuth.OAuth2.Internal.RefreshToken
- Network.OAuth.OAuth2.Internal: instance GHC.Classes.Eq a => GHC.Classes.Eq (Network.OAuth.OAuth2.Internal.OAuth2Error a)
- Network.OAuth.OAuth2.Internal: instance GHC.Classes.Ord Network.OAuth.OAuth2.Internal.ClientAuthenticationMethod
- Network.OAuth.OAuth2.Internal: instance GHC.Generics.Generic (Network.OAuth.OAuth2.Internal.OAuth2Error a)
- Network.OAuth.OAuth2.Internal: instance GHC.Generics.Generic Network.OAuth.OAuth2.Internal.OAuth2Token
- Network.OAuth.OAuth2.Internal: instance GHC.Show.Show Network.OAuth.OAuth2.Internal.AccessToken
- Network.OAuth.OAuth2.Internal: instance GHC.Show.Show Network.OAuth.OAuth2.Internal.ExchangeToken
- Network.OAuth.OAuth2.Internal: instance GHC.Show.Show Network.OAuth.OAuth2.Internal.IdToken
- Network.OAuth.OAuth2.Internal: instance GHC.Show.Show Network.OAuth.OAuth2.Internal.OAuth2
- Network.OAuth.OAuth2.Internal: instance GHC.Show.Show Network.OAuth.OAuth2.Internal.OAuth2Token
- Network.OAuth.OAuth2.Internal: instance GHC.Show.Show Network.OAuth.OAuth2.Internal.RefreshToken
- Network.OAuth.OAuth2.Internal: instance GHC.Show.Show a => GHC.Show.Show (Network.OAuth.OAuth2.Internal.OAuth2Error a)
- Network.OAuth.OAuth2.Internal: mkDecodeOAuth2Error :: ByteString -> String -> OAuth2Error err
- Network.OAuth.OAuth2.Internal: newtype AccessToken
- Network.OAuth.OAuth2.Internal: newtype ExchangeToken
- Network.OAuth.OAuth2.Internal: newtype IdToken
- Network.OAuth.OAuth2.Internal: newtype RefreshToken
- Network.OAuth.OAuth2.Internal: parseOAuth2Error :: FromJSON err => ByteString -> OAuth2Error err
- Network.OAuth.OAuth2.Internal: portLens :: Lens' Request Int
- Network.OAuth.OAuth2.Internal: requestToUri :: Request -> URI
- Network.OAuth.OAuth2.Internal: type PostBody = [(ByteString, ByteString)]
- Network.OAuth.OAuth2.Internal: type QueryParams = [(ByteString, ByteString)]
- Network.OAuth.OAuth2.Internal: uriToRequest :: MonadThrow m => URI -> m Request
- Network.OAuth.OAuth2.TokenRequest: data Errors
- Network.OAuth.OAuth2.TokenRequest: instance Data.Aeson.Types.FromJSON.FromJSON Network.OAuth.OAuth2.TokenRequest.Errors
- Network.OAuth.OAuth2.TokenRequest: instance Data.Aeson.Types.ToJSON.ToJSON Network.OAuth.OAuth2.TokenRequest.Errors
- Network.OAuth.OAuth2.TokenRequest: instance GHC.Classes.Eq Network.OAuth.OAuth2.TokenRequest.Errors
- Network.OAuth.OAuth2.TokenRequest: instance GHC.Generics.Generic Network.OAuth.OAuth2.TokenRequest.Errors
- Network.OAuth.OAuth2.TokenRequest: instance GHC.Show.Show Network.OAuth.OAuth2.TokenRequest.Errors
- 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: instance GHC.Show.Show Network.OAuth2.Experiment.Pkce.CodeVerifier
- 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: -- WithExchangeToken a b = b' implies no exchange token v.s. 'type
- Network.OAuth2.Experiment.Types: -- client credentials) will use <a>ExchangeToken</a> in the token request
- Network.OAuth2.Experiment.Types: -- create type family to be explicit on it. with 'type instance
- Network.OAuth2.Experiment.Types: -- endpoint.
- Network.OAuth2.Experiment.Types: -- instance WithExchangeToken a b = ExchangeToken -> b' implies
- Network.OAuth2.Experiment.Types: -- needing an exchange token
- Network.OAuth2.Experiment.Types: -- | <a>https://www.rfc-editor.org/rfc/rfc6749#page-47</a>
- Network.OAuth2.Experiment.Types: AuthorizationCode :: GrantTypeFlow
- Network.OAuth2.Experiment.Types: AuthorizeState :: Text -> AuthorizeState
- Network.OAuth2.Experiment.Types: ClientCredentials :: GrantTypeFlow
- Network.OAuth2.Experiment.Types: ClientId :: Text -> ClientId
- Network.OAuth2.Experiment.Types: ClientSecret :: Text -> ClientSecret
- Network.OAuth2.Experiment.Types: GTAuthorizationCode :: GrantTypeValue
- Network.OAuth2.Experiment.Types: GTClientCredentials :: GrantTypeValue
- Network.OAuth2.Experiment.Types: GTPassword :: GrantTypeValue
- Network.OAuth2.Experiment.Types: GTRefreshToken :: GrantTypeValue
- Network.OAuth2.Experiment.Types: Idp :: URI -> URI -> URI -> (forall m. (FromJSON (IdpUserInfo a), MonadIO m) => Manager -> AccessToken -> URI -> ExceptT ByteString m (IdpUserInfo a)) -> Idp a
- Network.OAuth2.Experiment.Types: Password :: Text -> Password
- Network.OAuth2.Experiment.Types: RedirectUri :: URI -> RedirectUri
- Network.OAuth2.Experiment.Types: ResourceOwnerPassword :: GrantTypeFlow
- Network.OAuth2.Experiment.Types: Scope :: Text -> Scope
- Network.OAuth2.Experiment.Types: Username :: Text -> Username
- Network.OAuth2.Experiment.Types: [$sel:idpAuthorizeEndpoint:Idp] :: Idp a -> URI
- Network.OAuth2.Experiment.Types: [$sel:idpFetchUserInfo:Idp] :: Idp a -> forall m. (FromJSON (IdpUserInfo a), MonadIO m) => Manager -> AccessToken -> URI -> ExceptT ByteString m (IdpUserInfo a)
- Network.OAuth2.Experiment.Types: [$sel:idpTokenEndpoint:Idp] :: Idp a -> URI
- Network.OAuth2.Experiment.Types: [$sel:idpUserInfoEndpoint:Idp] :: Idp a -> URI
- Network.OAuth2.Experiment.Types: [$sel:unAuthorizeState:AuthorizeState] :: AuthorizeState -> Text
- Network.OAuth2.Experiment.Types: [$sel:unClientId:ClientId] :: ClientId -> Text
- Network.OAuth2.Experiment.Types: [$sel:unClientSecret:ClientSecret] :: ClientSecret -> Text
- Network.OAuth2.Experiment.Types: [$sel:unPassword:Password] :: Password -> Text
- Network.OAuth2.Experiment.Types: [$sel:unRedirectUri:RedirectUri] :: RedirectUri -> URI
- Network.OAuth2.Experiment.Types: [$sel:unScope:Scope] :: Scope -> Text
- Network.OAuth2.Experiment.Types: [$sel:unUsername:Username] :: Username -> Text
- Network.OAuth2.Experiment.Types: class HasAuthorizeRequest (a :: GrantTypeFlow) where {
- Network.OAuth2.Experiment.Types: class HasIdpAppName (a :: GrantTypeFlow)
- Network.OAuth2.Experiment.Types: class HasPkceAuthorizeRequest (a :: GrantTypeFlow)
- Network.OAuth2.Experiment.Types: class HasPkceTokenRequest (b :: GrantTypeFlow)
- Network.OAuth2.Experiment.Types: class HasRefreshTokenRequest (a :: GrantTypeFlow) where {
- Network.OAuth2.Experiment.Types: class HasTokenRequest (a :: GrantTypeFlow) where {
- Network.OAuth2.Experiment.Types: class HasUserInfoRequest (a :: GrantTypeFlow)
- Network.OAuth2.Experiment.Types: class ToQueryParam a
- Network.OAuth2.Experiment.Types: class ToResponseTypeValue (a :: GrantTypeFlow)
- Network.OAuth2.Experiment.Types: conduitPkceTokenRequest :: (HasPkceTokenRequest b, MonadIO m) => IdpApplication b i -> Manager -> (ExchangeToken, CodeVerifier) -> ExceptT (OAuth2Error Errors) m OAuth2Token
- Network.OAuth2.Experiment.Types: conduitRefreshTokenRequest :: (HasRefreshTokenRequest a, MonadIO m) => IdpApplication a i -> Manager -> RefreshToken -> ExceptT (OAuth2Error Errors) m OAuth2Token
- Network.OAuth2.Experiment.Types: conduitTokenRequest :: (HasTokenRequest a, MonadIO m) => IdpApplication a i -> Manager -> WithExchangeToken a (ExceptT (OAuth2Error Errors) m OAuth2Token)
- Network.OAuth2.Experiment.Types: conduitUserInfoRequest :: (HasUserInfoRequest a, FromJSON (IdpUserInfo i)) => IdpApplication a i -> Manager -> AccessToken -> ExceptT ByteString IO (IdpUserInfo i)
- Network.OAuth2.Experiment.Types: data AuthorizationRequest a;
- Network.OAuth2.Experiment.Types: data GrantTypeFlow
- Network.OAuth2.Experiment.Types: data GrantTypeValue
- Network.OAuth2.Experiment.Types: data Idp a
- Network.OAuth2.Experiment.Types: data RefreshTokenRequest a;
- Network.OAuth2.Experiment.Types: data TokenRequest a;
- Network.OAuth2.Experiment.Types: data family IdpApplication (a :: GrantTypeFlow) (i :: Type)
- Network.OAuth2.Experiment.Types: getIdpAppName :: HasIdpAppName a => IdpApplication a i -> Text
- 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 GHC.Show.Show Network.OAuth2.Experiment.Types.Scope
- Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.HasAuthorizeRequest 'Network.OAuth2.Experiment.Types.AuthorizationCode
- Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.HasIdpAppName 'Network.OAuth2.Experiment.Types.AuthorizationCode
- Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.HasIdpAppName 'Network.OAuth2.Experiment.Types.ClientCredentials
- Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.HasIdpAppName 'Network.OAuth2.Experiment.Types.ResourceOwnerPassword
- Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.HasPkceAuthorizeRequest 'Network.OAuth2.Experiment.Types.AuthorizationCode
- Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.HasPkceTokenRequest 'Network.OAuth2.Experiment.Types.AuthorizationCode
- Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.HasRefreshTokenRequest 'Network.OAuth2.Experiment.Types.AuthorizationCode
- Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.HasRefreshTokenRequest 'Network.OAuth2.Experiment.Types.ResourceOwnerPassword
- Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.HasTokenRequest 'Network.OAuth2.Experiment.Types.AuthorizationCode
- Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.HasTokenRequest 'Network.OAuth2.Experiment.Types.ClientCredentials
- Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.HasTokenRequest 'Network.OAuth2.Experiment.Types.ResourceOwnerPassword
- Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.HasUserInfoRequest 'Network.OAuth2.Experiment.Types.AuthorizationCode
- Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.HasUserInfoRequest 'Network.OAuth2.Experiment.Types.ResourceOwnerPassword
- 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.OAuth2.Experiment.Types.AuthorizationRequest 'Network.OAuth2.Experiment.Types.AuthorizationCode)
- Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.ToQueryParam (Network.OAuth2.Experiment.Types.RefreshTokenRequest 'Network.OAuth2.Experiment.Types.AuthorizationCode)
- Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.ToQueryParam (Network.OAuth2.Experiment.Types.TokenRequest 'Network.OAuth2.Experiment.Types.AuthorizationCode)
- Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.ToQueryParam (Network.OAuth2.Experiment.Types.TokenRequest 'Network.OAuth2.Experiment.Types.ClientCredentials)
- Network.OAuth2.Experiment.Types: instance Network.OAuth2.Experiment.Types.ToQueryParam (Network.OAuth2.Experiment.Types.TokenRequest 'Network.OAuth2.Experiment.Types.ResourceOwnerPassword)
- 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.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: instance Network.OAuth2.Experiment.Types.ToResponseTypeValue 'Network.OAuth2.Experiment.Types.AuthorizationCode
- Network.OAuth2.Experiment.Types: mkAuthorizeRequest :: HasAuthorizeRequest a => IdpApplication a i -> MkAuthorizationRequestResponse a
- Network.OAuth2.Experiment.Types: mkAuthorizeRequestParameter :: HasAuthorizeRequest a => IdpApplication a i -> AuthorizationRequest a
- Network.OAuth2.Experiment.Types: mkPkceAuthorizeRequest :: (HasPkceAuthorizeRequest a, MonadIO m) => IdpApplication a i -> m (Text, CodeVerifier)
- Network.OAuth2.Experiment.Types: mkRefreshTokenRequest :: HasRefreshTokenRequest a => IdpApplication a i -> RefreshToken -> RefreshTokenRequest a
- Network.OAuth2.Experiment.Types: mkTokenRequest :: HasTokenRequest a => IdpApplication a i -> WithExchangeToken a (TokenRequest a)
- 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.OAuth2.Experiment.Types: toResponseTypeParam :: forall a b req. (ToResponseTypeValue a, IsString b) => req a -> Map b b
- Network.OAuth2.Experiment.Types: toResponseTypeValue :: (ToResponseTypeValue a, IsString b) => b
- Network.OAuth2.Experiment.Types: type MkAuthorizationRequestResponse a;
- Network.OAuth2.Experiment.Types: type WithExchangeToken a b;
- Network.OAuth2.Experiment.Types: type family IdpUserInfo a
- Network.OAuth2.Experiment.Types: }
- Network.OAuth2.Experiment.Utils: bs8ToLazyText :: ByteString -> Text
- Network.OAuth2.Experiment.Utils: mapsToParams :: [Map Text Text] -> [(ByteString, ByteString)]
- Network.OAuth2.Experiment.Utils: tlToBS :: Text -> ByteString
+ Network.OAuth.OAuth2: AccessToken :: Text -> AccessToken
+ Network.OAuth.OAuth2: ClientAssertionJwt :: ClientAuthenticationMethod
+ Network.OAuth.OAuth2: ClientSecretBasic :: ClientAuthenticationMethod
+ Network.OAuth.OAuth2: ClientSecretPost :: ClientAuthenticationMethod
+ Network.OAuth.OAuth2: ExchangeToken :: Text -> ExchangeToken
+ Network.OAuth.OAuth2: IdToken :: Text -> IdToken
+ Network.OAuth.OAuth2: OAuth2 :: Text -> Text -> URIRef Absolute -> URIRef Absolute -> URIRef Absolute -> OAuth2
+ Network.OAuth.OAuth2: OAuth2Token :: AccessToken -> Maybe RefreshToken -> Maybe Int -> Maybe Text -> Maybe IdToken -> OAuth2Token
+ Network.OAuth.OAuth2: RefreshToken :: Text -> RefreshToken
+ Network.OAuth.OAuth2: [accessToken] :: OAuth2Token -> AccessToken
+ Network.OAuth.OAuth2: [atoken] :: AccessToken -> Text
+ Network.OAuth.OAuth2: [expiresIn] :: OAuth2Token -> Maybe Int
+ Network.OAuth.OAuth2: [extoken] :: ExchangeToken -> Text
+ Network.OAuth.OAuth2: [idToken] :: OAuth2Token -> Maybe IdToken
+ Network.OAuth.OAuth2: [idtoken] :: IdToken -> Text
+ Network.OAuth.OAuth2: [oauth2AuthorizeEndpoint] :: OAuth2 -> URIRef Absolute
+ Network.OAuth.OAuth2: [oauth2ClientId] :: OAuth2 -> Text
+ Network.OAuth.OAuth2: [oauth2ClientSecret] :: OAuth2 -> Text
+ Network.OAuth.OAuth2: [oauth2RedirectUri] :: OAuth2 -> URIRef Absolute
+ Network.OAuth.OAuth2: [oauth2TokenEndpoint] :: OAuth2 -> URIRef Absolute
+ Network.OAuth.OAuth2: [refreshToken] :: OAuth2Token -> Maybe RefreshToken
+ Network.OAuth.OAuth2: [rtoken] :: RefreshToken -> Text
+ Network.OAuth.OAuth2: [tokenType] :: OAuth2Token -> Maybe Text
+ Network.OAuth.OAuth2: appendQueryParams :: [(ByteString, ByteString)] -> URIRef a -> URIRef a
+ Network.OAuth.OAuth2: data ClientAuthenticationMethod
+ Network.OAuth.OAuth2: data OAuth2
+ Network.OAuth.OAuth2: data OAuth2Token
+ Network.OAuth.OAuth2: defaultRequestHeaders :: [(HeaderName, ByteString)]
+ Network.OAuth.OAuth2: hostLens :: Lens' Request ByteString
+ Network.OAuth.OAuth2: newtype AccessToken
+ Network.OAuth.OAuth2: newtype ExchangeToken
+ Network.OAuth.OAuth2: newtype IdToken
+ Network.OAuth.OAuth2: newtype RefreshToken
+ Network.OAuth.OAuth2: portLens :: Lens' Request Int
+ Network.OAuth.OAuth2: requestToUri :: Request -> URI
+ Network.OAuth.OAuth2: type PostBody = [(ByteString, ByteString)]
+ Network.OAuth.OAuth2: type QueryParams = [(ByteString, ByteString)]
+ Network.OAuth.OAuth2: uriToRequest :: MonadThrow m => URI -> m Request
+ Network.OAuth.OAuth2.TokenRequest: TokenRequestError :: TokenRequestErrorCode -> Maybe Text -> Maybe (URIRef Absolute) -> TokenRequestError
+ Network.OAuth.OAuth2.TokenRequest: UnknownErrorCode :: Text -> TokenRequestErrorCode
+ 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 -> 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: AuthorizeState :: Text -> AuthorizeState
+ Network.OAuth2.Experiment: ClientCredentials :: GrantTypeFlow
+ Network.OAuth2.Experiment: ClientId :: Text -> ClientId
+ Network.OAuth2.Experiment: ClientSecret :: Text -> ClientSecret
+ Network.OAuth2.Experiment: CodeChallenge :: Text -> CodeChallenge
+ Network.OAuth2.Experiment: CodeVerifier :: Text -> CodeVerifier
+ 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: Idp :: URI -> URI -> URI -> (forall m. (FromJSON (IdpUserInfo a), MonadIO m) => Manager -> AccessToken -> URI -> ExceptT ByteString m (IdpUserInfo a)) -> Idp a
+ Network.OAuth2.Experiment: JwtBearer :: GrantTypeFlow
+ Network.OAuth2.Experiment: Password :: Text -> Password
+ Network.OAuth2.Experiment: PkceRequestParam :: CodeVerifier -> CodeChallenge -> CodeChallengeMethod -> PkceRequestParam
+ Network.OAuth2.Experiment: RedirectUri :: URI -> RedirectUri
+ Network.OAuth2.Experiment: ResourceOwnerPassword :: GrantTypeFlow
+ Network.OAuth2.Experiment: S256 :: CodeChallengeMethod
+ Network.OAuth2.Experiment: Scope :: Text -> Scope
+ Network.OAuth2.Experiment: UrnOAuthParam :: a -> UrnOAuthParam a
+ Network.OAuth2.Experiment: Username :: Text -> Username
+ 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: [unCodeVerifier] :: CodeVerifier -> Text
+ Network.OAuth2.Experiment: class HasAuthorizeRequest (a :: GrantTypeFlow) where {
+ Network.OAuth2.Experiment: class HasPkceAuthorizeRequest (a :: GrantTypeFlow)
+ Network.OAuth2.Experiment: class HasPkceTokenRequest (b :: GrantTypeFlow)
+ Network.OAuth2.Experiment: class HasRefreshTokenRequest (a :: GrantTypeFlow) where {
+ Network.OAuth2.Experiment: class HasTokenRequest (a :: GrantTypeFlow) where {
+ Network.OAuth2.Experiment: class HasUserInfoRequest (a :: GrantTypeFlow)
+ Network.OAuth2.Experiment: class ToQueryParam a
+ Network.OAuth2.Experiment: class ToResponseTypeValue (a :: GrantTypeFlow)
+ Network.OAuth2.Experiment: conduitPkceTokenRequest :: (HasPkceTokenRequest b, MonadIO m) => IdpApplication b i -> Manager -> (ExchangeToken, CodeVerifier) -> ExceptT TokenRequestError m OAuth2Token
+ Network.OAuth2.Experiment: conduitRefreshTokenRequest :: (HasRefreshTokenRequest a, MonadIO m) => IdpApplication a i -> Manager -> RefreshToken -> ExceptT TokenRequestError m OAuth2Token
+ Network.OAuth2.Experiment: conduitTokenRequest :: (HasTokenRequest a, MonadIO m) => IdpApplication a i -> Manager -> WithExchangeToken a (ExceptT TokenRequestError m OAuth2Token)
+ Network.OAuth2.Experiment: conduitUserInfoRequest :: (HasUserInfoRequest a, FromJSON (IdpUserInfo i)) => IdpApplication a i -> Manager -> AccessToken -> ExceptT ByteString IO (IdpUserInfo i)
+ 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 Idp a
+ Network.OAuth2.Experiment: data PkceRequestParam
+ Network.OAuth2.Experiment: data RefreshTokenRequest a;
+ Network.OAuth2.Experiment: data TokenRequest 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: mkPkceAuthorizeRequest :: (HasPkceAuthorizeRequest a, MonadIO m) => IdpApplication a i -> m (Text, CodeVerifier)
+ 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 AuthorizeState
+ Network.OAuth2.Experiment: newtype ClientId
+ Network.OAuth2.Experiment: newtype ClientSecret
+ Network.OAuth2.Experiment: newtype CodeChallenge
+ Network.OAuth2.Experiment: newtype CodeVerifier
+ Network.OAuth2.Experiment: newtype Password
+ Network.OAuth2.Experiment: newtype RedirectUri
+ Network.OAuth2.Experiment: newtype Scope
+ Network.OAuth2.Experiment: newtype UrnOAuthParam a
+ Network.OAuth2.Experiment: newtype Username
+ 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.OAuth2.Experiment: }
- Network.OAuth.OAuth2.TokenRequest: InvalidClient :: Errors
+ Network.OAuth.OAuth2.TokenRequest: InvalidClient :: TokenRequestErrorCode
- Network.OAuth.OAuth2.TokenRequest: InvalidGrant :: Errors
+ Network.OAuth.OAuth2.TokenRequest: InvalidGrant :: TokenRequestErrorCode
- Network.OAuth.OAuth2.TokenRequest: InvalidRequest :: Errors
+ Network.OAuth.OAuth2.TokenRequest: InvalidRequest :: TokenRequestErrorCode
- Network.OAuth.OAuth2.TokenRequest: InvalidScope :: Errors
+ Network.OAuth.OAuth2.TokenRequest: InvalidScope :: TokenRequestErrorCode
- Network.OAuth.OAuth2.TokenRequest: UnauthorizedClient :: Errors
+ Network.OAuth.OAuth2.TokenRequest: UnauthorizedClient :: TokenRequestErrorCode
- Network.OAuth.OAuth2.TokenRequest: UnsupportedGrantType :: Errors
+ Network.OAuth.OAuth2.TokenRequest: UnsupportedGrantType :: TokenRequestErrorCode
- Network.OAuth.OAuth2.TokenRequest: doJSONPostRequest :: (MonadIO m, FromJSON err, FromJSON a) => Manager -> OAuth2 -> URI -> PostBody -> ExceptT (OAuth2Error err) m a
+ Network.OAuth.OAuth2.TokenRequest: doJSONPostRequest :: (MonadIO m, FromJSON a) => Manager -> OAuth2 -> URI -> PostBody -> ExceptT TokenRequestError m a
- Network.OAuth.OAuth2.TokenRequest: doSimplePostRequest :: (MonadIO m, FromJSON err) => Manager -> OAuth2 -> URI -> PostBody -> ExceptT (OAuth2Error err) m ByteString
+ Network.OAuth.OAuth2.TokenRequest: doSimplePostRequest :: MonadIO m => Manager -> OAuth2 -> URI -> PostBody -> ExceptT TokenRequestError m ByteString
- Network.OAuth.OAuth2.TokenRequest: fetchAccessToken :: MonadIO m => Manager -> OAuth2 -> ExchangeToken -> ExceptT (OAuth2Error Errors) m OAuth2Token
+ Network.OAuth.OAuth2.TokenRequest: fetchAccessToken :: MonadIO m => Manager -> OAuth2 -> ExchangeToken -> ExceptT TokenRequestError m OAuth2Token
- Network.OAuth.OAuth2.TokenRequest: fetchAccessToken2 :: MonadIO m => Manager -> OAuth2 -> ExchangeToken -> ExceptT (OAuth2Error Errors) m OAuth2Token
+ Network.OAuth.OAuth2.TokenRequest: fetchAccessToken2 :: MonadIO m => Manager -> OAuth2 -> ExchangeToken -> ExceptT TokenRequestError m OAuth2Token
- Network.OAuth.OAuth2.TokenRequest: fetchAccessTokenInternal :: MonadIO m => ClientAuthenticationMethod -> Manager -> OAuth2 -> ExchangeToken -> ExceptT (OAuth2Error Errors) m OAuth2Token
+ Network.OAuth.OAuth2.TokenRequest: fetchAccessTokenInternal :: MonadIO m => ClientAuthenticationMethod -> Manager -> OAuth2 -> ExchangeToken -> ExceptT TokenRequestError m OAuth2Token
- Network.OAuth.OAuth2.TokenRequest: fetchAccessTokenWithAuthMethod :: MonadIO m => ClientAuthenticationMethod -> Manager -> OAuth2 -> ExchangeToken -> ExceptT (OAuth2Error Errors) m OAuth2Token
+ Network.OAuth.OAuth2.TokenRequest: fetchAccessTokenWithAuthMethod :: MonadIO m => ClientAuthenticationMethod -> Manager -> OAuth2 -> ExchangeToken -> ExceptT TokenRequestError m OAuth2Token
- Network.OAuth.OAuth2.TokenRequest: handleOAuth2TokenResponse :: FromJSON err => Response ByteString -> Either (OAuth2Error err) ByteString
+ Network.OAuth.OAuth2.TokenRequest: handleOAuth2TokenResponse :: Response ByteString -> Either TokenRequestError ByteString
- Network.OAuth.OAuth2.TokenRequest: parseResponseFlexible :: (FromJSON err, FromJSON a) => ByteString -> Either (OAuth2Error err) a
+ Network.OAuth.OAuth2.TokenRequest: parseResponseFlexible :: FromJSON a => ByteString -> Either TokenRequestError a
- Network.OAuth.OAuth2.TokenRequest: parseResponseString :: (FromJSON err, FromJSON a) => ByteString -> Either (OAuth2Error err) a
+ Network.OAuth.OAuth2.TokenRequest: parseResponseString :: FromJSON a => ByteString -> Either TokenRequestError a
- Network.OAuth.OAuth2.TokenRequest: refreshAccessToken :: MonadIO m => Manager -> OAuth2 -> RefreshToken -> ExceptT (OAuth2Error Errors) m OAuth2Token
+ Network.OAuth.OAuth2.TokenRequest: refreshAccessToken :: MonadIO m => Manager -> OAuth2 -> RefreshToken -> ExceptT TokenRequestError m OAuth2Token
- Network.OAuth.OAuth2.TokenRequest: refreshAccessToken2 :: MonadIO m => Manager -> OAuth2 -> RefreshToken -> ExceptT (OAuth2Error Errors) m OAuth2Token
+ Network.OAuth.OAuth2.TokenRequest: refreshAccessToken2 :: MonadIO m => Manager -> OAuth2 -> RefreshToken -> ExceptT TokenRequestError m OAuth2Token
- Network.OAuth.OAuth2.TokenRequest: refreshAccessTokenInternal :: MonadIO m => ClientAuthenticationMethod -> Manager -> OAuth2 -> RefreshToken -> ExceptT (OAuth2Error Errors) m OAuth2Token
+ Network.OAuth.OAuth2.TokenRequest: refreshAccessTokenInternal :: MonadIO m => ClientAuthenticationMethod -> Manager -> OAuth2 -> RefreshToken -> ExceptT TokenRequestError m OAuth2Token
- Network.OAuth.OAuth2.TokenRequest: refreshAccessTokenWithAuthMethod :: MonadIO m => ClientAuthenticationMethod -> Manager -> OAuth2 -> RefreshToken -> ExceptT (OAuth2Error Errors) m OAuth2Token
+ Network.OAuth.OAuth2.TokenRequest: refreshAccessTokenWithAuthMethod :: MonadIO m => ClientAuthenticationMethod -> Manager -> OAuth2 -> RefreshToken -> ExceptT TokenRequestError m OAuth2Token
Files
- hoauth2.cabal +36/−13
- src/Network/OAuth/OAuth2.hs +8/−10
- src/Network/OAuth/OAuth2/AuthorizationRequest.hs +8/−11
- src/Network/OAuth/OAuth2/HttpClient.hs +35/−31
- src/Network/OAuth/OAuth2/Internal.hs +30/−60
- src/Network/OAuth/OAuth2/TokenRequest.hs +68/−40
- src/Network/OAuth2/Experiment.hs +4/−6
- src/Network/OAuth2/Experiment/Pkce.hs +15/−16
- src/Network/OAuth2/Experiment/Types.hs +221/−136
- test/Network/OAuth/OAuth2/TokenRequestSpec.hs +23/−0
- test/Spec.hs +2/−0
hoauth2.cabal view
@@ -2,7 +2,7 @@ name: hoauth2 -- http://wiki.haskell.org/Package_versioning_policy-version: 2.6.0+version: 2.7.0 synopsis: Haskell OAuth2 authentication client description: Haskell OAuth2 authentication client.@@ -30,16 +30,20 @@ library hs-source-dirs: src default-language: Haskell2010+ autogen-modules: Paths_hoauth2+ other-modules:+ Network.OAuth.OAuth2.Internal+ Network.OAuth2.Experiment.Pkce+ Network.OAuth2.Experiment.Types+ Network.OAuth2.Experiment.Utils+ Paths_hoauth2+ exposed-modules: Network.OAuth.OAuth2 Network.OAuth.OAuth2.AuthorizationRequest Network.OAuth.OAuth2.HttpClient- Network.OAuth.OAuth2.Internal Network.OAuth.OAuth2.TokenRequest Network.OAuth2.Experiment- Network.OAuth2.Experiment.Pkce- Network.OAuth2.Experiment.Types- Network.OAuth2.Experiment.Utils default-extensions: DataKinds@@ -52,24 +56,43 @@ TypeFamilies build-depends:- , aeson >=2.0 && <2.2- , base >=4 && <5+ , aeson >=2.0 && <2.2+ , base >=4 && <5 , base64 ^>=0.4 , binary ^>=0.8- , bytestring >=0.9 && <0.12+ , bytestring >=0.9 && <0.12 , containers ^>=0.6 , cryptonite ^>=0.30 , data-default ^>=0.7- , exceptions >=0.8.3 && <0.11- , http-conduit >=2.1 && <2.4- , http-types >=0.11 && <0.13+ , exceptions >=0.8.3 && <0.11+ , http-conduit >=2.1 && <2.4+ , http-types >=0.11 && <0.13 , memory ^>=0.17 , microlens ^>=0.4.0- , text >=0.11 && <1.3+ , text >=0.11 && <1.3 , transformers ^>=0.5- , uri-bytestring >=0.2.3 && <0.4+ , 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++test-suite hoauth-tests+ type: exitcode-stdio-1.0+ main-is: Spec.hs+ hs-source-dirs: test+ ghc-options: -Wall+ build-depends:+ , aeson >=2.0 && <2.2+ , base >=4 && <5+ , hoauth2+ , hspec >=2 && <3++ other-modules: Network.OAuth.OAuth2.TokenRequestSpec+ default-language: Haskell2010+ default-extensions:+ ImportQualifiedPost+ OverloadedStrings++ build-tool-depends: hspec-discover:hspec-discover >=2 && <3
src/Network/OAuth/OAuth2.hs view
@@ -1,19 +1,17 @@ -- | A lightweight oauth2 Haskell binding. -- See Readme for more details----module Network.OAuth.OAuth2- ( module Network.OAuth.OAuth2.HttpClient,- module Network.OAuth.OAuth2.AuthorizationRequest,- module Network.OAuth.OAuth2.TokenRequest,- module Network.OAuth.OAuth2.Internal,- )-where+module Network.OAuth.OAuth2 (+ module Network.OAuth.OAuth2.HttpClient,+ module Network.OAuth.OAuth2.AuthorizationRequest,+ module Network.OAuth.OAuth2.TokenRequest,+ module Network.OAuth.OAuth2.Internal,+) 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 (Errors (..)) import Network.OAuth.OAuth2.HttpClient import Network.OAuth.OAuth2.Internal-import Network.OAuth.OAuth2.TokenRequest hiding (Errors(..))+import Network.OAuth.OAuth2.TokenRequest
src/Network/OAuth/OAuth2/AuthorizationRequest.hs view
@@ -7,8 +7,8 @@ import Data.Aeson import Data.Function (on)-import qualified Data.List as List-import qualified Data.Text.Encoding as T+import Data.List qualified as List+import Data.Text.Encoding qualified as T import GHC.Generics (Generic) import Lens.Micro (over) import Network.OAuth.OAuth2.Internal@@ -21,15 +21,12 @@ -------------------------------------------------- instance FromJSON Errors where- parseJSON = genericParseJSON defaultOptions {constructorTagModifier = camelTo2 '_', allNullaryToStringTag = True}--instance ToJSON Errors where- toEncoding = genericToEncoding defaultOptions {constructorTagModifier = camelTo2 '_', allNullaryToStringTag = True}+ 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 (redirect to OAuth2 provider),--- it will end-up at the Provider page hence no way for this library to parse error response.+-- 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@@ -62,7 +59,7 @@ queryParts = List.nubBy ((==) `on` fst) $ qs- ++ [ ("client_id", T.encodeUtf8 $ oauth2ClientId oa),- ("response_type", "code"),- ("redirect_uri", serializeURIRef' $ oauth2RedirectUri oa)+ ++ [ ("client_id", T.encodeUtf8 $ oauth2ClientId oa)+ , ("response_type", "code")+ , ("redirect_uri", serializeURIRef' $ oauth2RedirectUri oa) ]
src/Network/OAuth/OAuth2/HttpClient.hs view
@@ -4,39 +4,38 @@ -- | Bindings for The OAuth 2.0 Authorization Framework: Bearer Token Usage -- RFC6750 <https://www.rfc-editor.org/rfc/rfc6750>-module Network.OAuth.OAuth2.HttpClient- ( -- * AUTH requests- authGetJSON,- authGetBS,- authGetBS2,- authGetJSONWithAuthMethod,- authGetJSONInternal,- authGetBSWithAuthMethod,- authGetBSInternal,- authPostJSON,- authPostBS,- authPostBS2,- authPostBS3,- authPostJSONWithAuthMethod,- authPostJSONInternal,- authPostBSWithAuthMethod,- authPostBSInternal,+module Network.OAuth.OAuth2.HttpClient (+ -- * AUTH requests+ authGetJSON,+ authGetBS,+ authGetBS2,+ authGetJSONWithAuthMethod,+ authGetJSONInternal,+ authGetBSWithAuthMethod,+ authGetBSInternal,+ authPostJSON,+ authPostBS,+ authPostBS2,+ authPostBS3,+ authPostJSONWithAuthMethod,+ authPostJSONInternal,+ authPostBSWithAuthMethod,+ authPostBSInternal, - -- * Types- APIAuthenticationMethod (..),- )-where+ -- * Types+ APIAuthenticationMethod (..),+) where import Control.Monad.IO.Class (MonadIO (..)) import Control.Monad.Trans.Except (ExceptT (..), throwE) import Data.Aeson (FromJSON, eitherDecode)-import qualified Data.ByteString.Char8 as BS-import qualified Data.ByteString.Lazy.Char8 as BSL+import Data.ByteString.Char8 qualified as BS+import Data.ByteString.Lazy.Char8 qualified as BSL import Data.Maybe (fromJust, isJust)-import qualified Data.Text.Encoding as T+import Data.Text.Encoding qualified as T import Lens.Micro (over) import Network.HTTP.Conduit-import qualified Network.HTTP.Types as HT+import Network.HTTP.Types qualified as HT import Network.OAuth.OAuth2.Internal import URI.ByteString (URI, URIRef, queryL, queryPairsL) @@ -302,15 +301,20 @@ -- | HTTP connection manager. Manager -> ExceptT BSL.ByteString m BSL.ByteString-authRequest req upReq manage = ExceptT $ handleResponse <$> httpLbs (upReq req) manage+authRequest req upReq manage = ExceptT $ do+ 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 =- if HT.statusIsSuccessful (responseStatus rsp)- then Right $ responseBody rsp- else -- TODO: better to surface up entire resp so that client can decide what to do when error happens.- Left $ responseBody rsp+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`
src/Network/OAuth/OAuth2/Internal.hs view
@@ -6,24 +6,24 @@ module Network.OAuth.OAuth2.Internal where -import Control.Applicative import Control.Arrow (second) import Control.Monad.Catch import Data.Aeson import Data.Aeson.Types (Parser, explicitParseFieldMaybe) import Data.Binary (Binary)-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as BSL+import Data.ByteString qualified as BS+import Data.ByteString.Char8 qualified as BS8 import Data.Default import Data.Maybe-import Data.Text (Text, pack, unpack)-import Data.Text.Encoding+import Data.Text (Text, unpack)+import Data.Version (showVersion) import GHC.Generics import Lens.Micro import Lens.Micro.Extras import Network.HTTP.Conduit as C-import qualified Network.HTTP.Types as H-import qualified Network.HTTP.Types as HT+import Network.HTTP.Types qualified as H+import Network.HTTP.Types qualified as HT+import Paths_hoauth2 (version) import URI.ByteString import URI.ByteString.Aeson () import URI.ByteString.QQ@@ -36,22 +36,22 @@ -- | Query Parameter Representation data OAuth2 = OAuth2- { oauth2ClientId :: Text,- oauth2ClientSecret :: Text,- oauth2AuthorizeEndpoint :: URIRef Absolute,- oauth2TokenEndpoint :: URIRef Absolute,- oauth2RedirectUri :: URIRef Absolute+ { oauth2ClientId :: Text+ , oauth2ClientSecret :: Text+ , oauth2AuthorizeEndpoint :: URIRef Absolute+ , oauth2TokenEndpoint :: URIRef Absolute+ , oauth2RedirectUri :: URIRef Absolute } deriving (Show, Eq) instance Default OAuth2 where def = OAuth2- { oauth2ClientId = "",- oauth2ClientSecret = "",- oauth2AuthorizeEndpoint = [uri|https://www.example.com/|],- oauth2TokenEndpoint = [uri|https://www.example.com/|],- oauth2RedirectUri = [uri|https://www.example.com/|]+ { oauth2ClientId = ""+ , oauth2ClientSecret = ""+ , oauth2AuthorizeEndpoint = [uri|https://www.example.com/|]+ , oauth2TokenEndpoint = [uri|https://www.example.com/|]+ , oauth2RedirectUri = [uri|https://www.example.com/|] } newtype AccessToken = AccessToken {atoken :: Text} deriving (Binary, Eq, Show, FromJSON, ToJSON)@@ -65,14 +65,14 @@ -- | https://www.rfc-editor.org/rfc/rfc6749#section-4.1.4 data OAuth2Token = OAuth2Token- { accessToken :: AccessToken,- -- | Exists when @offline_access@ scope is in the 'authorizeUrl' and the provider supports Refresh Access Token.- refreshToken :: Maybe RefreshToken,- expiresIn :: Maybe Int,- -- | See https://www.rfc-editor.org/rfc/rfc6749#section-5.1. It's required per spec. But OAuth2 provider implementation are vary. Maybe will remove 'Maybe' in future release.- tokenType :: Maybe Text,- -- | Exists when @openid@ scope is in the 'authorizeUrl' and the provider supports OpenID.- idToken :: Maybe IdToken+ { accessToken :: AccessToken+ , refreshToken :: Maybe RefreshToken+ -- ^ Exists when @offline_access@ scope is in the 'authorizeUrl' 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. } deriving (Eq, Show, Generic) @@ -100,37 +100,6 @@ toJSON = genericToJSON defaultOptions {fieldLabelModifier = camelTo2 '_'} toEncoding = genericToEncoding defaultOptions {fieldLabelModifier = camelTo2 '_'} -data OAuth2Error a = OAuth2Error- { error :: Either Text a,- errorDescription :: Maybe Text,- errorUri :: Maybe (URIRef Absolute)- }- deriving (Show, Eq, Generic)--instance FromJSON err => FromJSON (OAuth2Error err) where- parseJSON (Object a) =- do- err <- (a .: "error") >>= (\str -> Right <$> parseJSON str <|> Left <$> parseJSON str)- desc <- a .:? "error_description"- errorUri <- a .:? "error_uri"- return $ OAuth2Error err desc errorUri- parseJSON _ = fail "Expected an object"--instance ToJSON err => ToJSON (OAuth2Error err) where- toJSON = genericToJSON defaultOptions {constructorTagModifier = camelTo2 '_', allNullaryToStringTag = True}- toEncoding = genericToEncoding defaultOptions {constructorTagModifier = camelTo2 '_', allNullaryToStringTag = True}--parseOAuth2Error :: FromJSON err => BSL.ByteString -> OAuth2Error err-parseOAuth2Error string =- either (mkDecodeOAuth2Error string) id (eitherDecode string)--mkDecodeOAuth2Error :: BSL.ByteString -> String -> OAuth2Error err-mkDecodeOAuth2Error response err =- OAuth2Error- (Left "Decode error")- (Just $ pack $ "Error: " <> err <> "\n Original Response:\n" <> show (decodeUtf8 $ BSL.toStrict response))- Nothing- -- | https://www.rfc-editor.org/rfc/rfc6749#section-2.3 -- According to spec: --@@ -146,6 +115,7 @@ data ClientAuthenticationMethod = ClientSecretBasic | ClientSecretPost+ | ClientAssertionJwt deriving (Eq, Ord) --------------------------------------------------@@ -167,8 +137,8 @@ defaultRequestHeaders :: [(HT.HeaderName, BS.ByteString)] defaultRequestHeaders =- [ (HT.hUserAgent, "hoauth2"),- (HT.hAccept, "application/json")+ [ (HT.hUserAgent, "hoauth2-" <> BS8.pack (showVersion version))+ , (HT.hAccept, "application/json") ] appendQueryParams :: [(BS.ByteString, BS.ByteString)] -> URIRef a -> URIRef a@@ -189,8 +159,8 @@ req = setQueryString query $ defaultRequest- { secure = ssl,- path = view pathL auri+ { secure = ssl+ , path = view pathL auri } req2 = (over hostLens . maybe id const . preview hostL) auri req req3 = (over portLens . (const . fromMaybe defaultPort) . preview portL) auri req2
src/Network/OAuth/OAuth2/TokenRequest.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE OverloadedStrings #-} -- | Bindings Access Token and Refresh Token part of The OAuth 2.0 Authorization Framework@@ -8,16 +7,18 @@ import Control.Monad.IO.Class (MonadIO (..)) import Control.Monad.Trans.Except (ExceptT (..), throwE) import Data.Aeson-import qualified Data.Aeson.Key as Key-import qualified Data.Aeson.KeyMap as KeyMap-import qualified Data.ByteString.Lazy.Char8 as BSL-import qualified Data.Text.Encoding as T+import Data.Aeson.Key qualified as Key+import Data.Aeson.KeyMap qualified as KeyMap+import Data.ByteString.Lazy.Char8 qualified as BSL+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 qualified Network.HTTP.Types as HT+import Network.HTTP.Types qualified as HT import Network.HTTP.Types.URI (parseQuery) import Network.OAuth.OAuth2.Internal-import URI.ByteString (URI, serializeURIRef')+import URI.ByteString -------------------------------------------------- @@ -25,22 +26,49 @@ -------------------------------------------------- -instance FromJSON Errors where- parseJSON = genericParseJSON defaultOptions {constructorTagModifier = camelTo2 '_', allNullaryToStringTag = True}--instance ToJSON Errors where- toEncoding = genericToEncoding defaultOptions {constructorTagModifier = camelTo2 '_', allNullaryToStringTag = True}+data TokenRequestError = TokenRequestError+ { error :: TokenRequestErrorCode+ , errorDescription :: Maybe Text+ , errorUri :: Maybe (URIRef Absolute)+ }+ deriving (Show, Eq, Generic) -- | Token Error Responses https://tools.ietf.org/html/rfc6749#section-5.2-data Errors+data TokenRequestErrorCode = InvalidRequest | InvalidClient | InvalidGrant | UnauthorizedClient | UnsupportedGrantType | InvalidScope- deriving (Show, Eq, Generic)+ | UnknownErrorCode Text+ deriving (Show, Eq) +instance FromJSON TokenRequestErrorCode where+ parseJSON = withText "parseJSON TokenRequestErrorCode" $ \t ->+ pure $ case t of+ "invalid_request" -> InvalidRequest+ "invalid_client" -> InvalidClient+ "invalid_grant" -> InvalidGrant+ "unauthorized_client" -> UnauthorizedClient+ "unsupported_grant_type" -> UnsupportedGrantType+ "invalid_scope" -> InvalidScope+ _ -> UnknownErrorCode t++instance FromJSON TokenRequestError where+ parseJSON = genericParseJSON defaultOptions {constructorTagModifier = camelTo2 '_'}++parseTokeRequestError :: BSL.ByteString -> TokenRequestError+parseTokeRequestError string =+ either (mkDecodeOAuth2Error string) id (eitherDecode string)+ where+ mkDecodeOAuth2Error :: BSL.ByteString -> String -> TokenRequestError+ mkDecodeOAuth2Error response err =+ TokenRequestError+ (UnknownErrorCode "")+ (Just $ T.pack $ "Decode TokenRequestError failed: " <> err <> "\n Original Response:\n" <> show (T.decodeUtf8 $ BSL.toStrict response))+ Nothing+ -------------------------------------------------- -- * URL@@ -57,9 +85,9 @@ accessTokenUrl oa code = let uri = oauth2TokenEndpoint oa body =- [ ("code", T.encodeUtf8 $ extoken code),- ("redirect_uri", serializeURIRef' $ oauth2RedirectUri oa),- ("grant_type", "authorization_code")+ [ ("code", T.encodeUtf8 $ extoken code)+ , ("redirect_uri", serializeURIRef' $ oauth2RedirectUri oa)+ , ("grant_type", "authorization_code") ] in (uri, body) @@ -74,8 +102,8 @@ where uri = oauth2TokenEndpoint oa body =- [ ("grant_type", "refresh_token"),- ("refresh_token", T.encodeUtf8 $ rtoken token)+ [ ("grant_type", "refresh_token")+ , ("refresh_token", T.encodeUtf8 $ rtoken token) ] --------------------------------------------------@@ -94,7 +122,7 @@ -- | OAuth2 Code ExchangeToken -> -- | Access Token- ExceptT (OAuth2Error Errors) m OAuth2Token+ ExceptT TokenRequestError m OAuth2Token fetchAccessToken = fetchAccessTokenWithAuthMethod ClientSecretBasic fetchAccessToken2 ::@@ -106,7 +134,7 @@ -- | Authorization Code ExchangeToken -> -- | Access Token- ExceptT (OAuth2Error Errors) m OAuth2Token+ ExceptT TokenRequestError m OAuth2Token fetchAccessToken2 = fetchAccessTokenWithAuthMethod ClientSecretPost {-# DEPRECATED fetchAccessToken2 "use 'fetchAccessTokenWithAuthMethod'" #-} @@ -120,7 +148,7 @@ -- | Authorization Code ExchangeToken -> -- | Access Token- ExceptT (OAuth2Error Errors) m OAuth2Token+ ExceptT TokenRequestError m OAuth2Token fetchAccessTokenInternal = fetchAccessTokenWithAuthMethod {-# DEPRECATED fetchAccessTokenInternal "use 'fetchAccessTokenWithAuthMethod'" #-} @@ -146,7 +174,7 @@ -- | Authorization Code ExchangeToken -> -- | Access Token- ExceptT (OAuth2Error Errors) m OAuth2Token+ ExceptT TokenRequestError m OAuth2Token fetchAccessTokenWithAuthMethod authMethod manager oa code = do let (uri, body) = accessTokenUrl oa code let extraBody = if authMethod == ClientSecretPost then clientSecretPost oa else []@@ -161,7 +189,7 @@ OAuth2 -> -- | Refresh Token gained after authorization RefreshToken ->- ExceptT (OAuth2Error Errors) m OAuth2Token+ ExceptT TokenRequestError m OAuth2Token refreshAccessToken = refreshAccessTokenWithAuthMethod ClientSecretBasic refreshAccessToken2 ::@@ -172,7 +200,7 @@ OAuth2 -> -- | Refresh Token gained after authorization RefreshToken ->- ExceptT (OAuth2Error Errors) m OAuth2Token+ ExceptT TokenRequestError m OAuth2Token refreshAccessToken2 = refreshAccessTokenWithAuthMethod ClientSecretPost {-# DEPRECATED refreshAccessToken2 "use 'refreshAccessTokenWithAuthMethod'" #-} @@ -185,7 +213,7 @@ OAuth2 -> -- | Refresh Token gained after authorization RefreshToken ->- ExceptT (OAuth2Error Errors) m OAuth2Token+ ExceptT TokenRequestError m OAuth2Token refreshAccessTokenInternal = refreshAccessTokenWithAuthMethod {-# DEPRECATED refreshAccessTokenInternal "use 'refreshAccessTokenWithAuthMethod'" #-} @@ -210,7 +238,7 @@ OAuth2 -> -- | Refresh Token gained after authorization RefreshToken ->- ExceptT (OAuth2Error Errors) m OAuth2Token+ ExceptT TokenRequestError m OAuth2Token refreshAccessTokenWithAuthMethod authMethod manager oa token = do let (uri, body) = refreshAccessTokenUrl oa token let extraBody = if authMethod == ClientSecretPost then clientSecretPost oa else []@@ -224,7 +252,7 @@ -- | Conduct post request and return response as JSON. doJSONPostRequest ::- (MonadIO m, FromJSON err, FromJSON a) =>+ (MonadIO m, FromJSON a) => -- | HTTP connection manager. Manager -> -- | OAuth options@@ -234,7 +262,7 @@ -- | request body PostBody -> -- | Response as JSON- ExceptT (OAuth2Error err) m a+ ExceptT TokenRequestError m a doJSONPostRequest manager oa uri body = do resp <- doSimplePostRequest manager oa uri body case parseResponseFlexible resp of@@ -243,7 +271,7 @@ -- | Conduct post request. doSimplePostRequest ::- (MonadIO m, FromJSON err) =>+ (MonadIO m) => -- | HTTP connection manager. Manager -> -- | OAuth options@@ -253,7 +281,7 @@ -- | Request body. PostBody -> -- | Response as ByteString- ExceptT (OAuth2Error err) m BSL.ByteString+ ExceptT TokenRequestError m BSL.ByteString doSimplePostRequest manager oa url body = ExceptT . liftIO $ fmap handleOAuth2TokenResponse go where@@ -264,26 +292,26 @@ httpLbs (urlEncodedBody body req') manager -- | Gets response body from a @Response@ if 200 otherwise assume 'OAuth2Error'-handleOAuth2TokenResponse :: FromJSON err => Response BSL.ByteString -> Either (OAuth2Error err) BSL.ByteString+handleOAuth2TokenResponse :: Response BSL.ByteString -> Either TokenRequestError BSL.ByteString handleOAuth2TokenResponse rsp = if HT.statusIsSuccessful (responseStatus rsp) then Right $ responseBody rsp- else Left $ parseOAuth2Error (responseBody rsp)+ else Left $ parseTokeRequestError (responseBody rsp) -- | Try to parses response as JSON, if failed, try to parse as like query string. parseResponseFlexible ::- (FromJSON err, FromJSON a) =>+ (FromJSON a) => BSL.ByteString ->- Either (OAuth2Error err) a+ Either TokenRequestError 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 err, FromJSON a) =>+ (FromJSON a) => BSL.ByteString ->- Either (OAuth2Error err) a+ Either TokenRequestError a parseResponseString b = case parseQuery $ BSL.toStrict b of [] -> Left errorMessage a -> case fromJSON $ queryToValue a of@@ -292,7 +320,7 @@ where queryToValue = Object . KeyMap.fromList . map paramToPair paramToPair (k, mv) = (Key.fromText $ T.decodeUtf8 k, maybe Null (String . T.decodeUtf8) mv)- errorMessage = parseOAuth2Error b+ errorMessage = parseTokeRequestError b -- | Set several header values: -- + userAgennt : `hoauth2`@@ -305,6 +333,6 @@ -- | Add Credential (client_id, client_secret) to the request post body. clientSecretPost :: OAuth2 -> PostBody clientSecretPost oa =- [ ("client_id", T.encodeUtf8 $ oauth2ClientId oa),- ("client_secret", T.encodeUtf8 $ oauth2ClientSecret oa)+ [ ("client_id", T.encodeUtf8 $ oauth2ClientId oa)+ , ("client_secret", T.encodeUtf8 $ oauth2ClientSecret oa) ]
src/Network/OAuth2/Experiment.hs view
@@ -86,12 +86,10 @@ -- @ -- -- Also you could find example from @hoauth2-providers-tutorials@ module.----module Network.OAuth2.Experiment- ( module Network.OAuth2.Experiment.Types,- module Network.OAuth2.Experiment.Pkce- )-where+module Network.OAuth2.Experiment (+ module Network.OAuth2.Experiment.Types,+ module Network.OAuth2.Experiment.Pkce,+) where import Network.OAuth2.Experiment.Pkce import Network.OAuth2.Experiment.Types
src/Network/OAuth2/Experiment/Pkce.hs view
@@ -1,11 +1,10 @@-module Network.OAuth2.Experiment.Pkce- ( mkPkceParam,- CodeChallenge (..),- CodeVerifier (..),- CodeChallengeMethod (..),- PkceRequestParam (..),- )-where+module Network.OAuth2.Experiment.Pkce (+ mkPkceParam,+ CodeChallenge (..),+ CodeVerifier (..),+ CodeChallengeMethod (..),+ PkceRequestParam (..),+) where import Control.Monad.IO.Class import Crypto.Hash qualified as H@@ -25,11 +24,11 @@ deriving (Show) data PkceRequestParam = PkceRequestParam- { codeVerifier :: CodeVerifier,- codeChallenge :: CodeChallenge,- -- | spec says optional but really it shall be s256 or can be omitted?- -- https://datatracker.ietf.org/doc/html/rfc7636#section-4.3- codeChallengeMethod :: CodeChallengeMethod+ { codeVerifier :: CodeVerifier+ , codeChallenge :: CodeChallenge+ , codeChallengeMethod :: CodeChallengeMethod+ -- ^ spec says optional but really it shall be s256 or can be omitted?+ -- https://datatracker.ietf.org/doc/html/rfc7636#section-4.3 } mkPkceParam :: MonadIO m => m PkceRequestParam@@ -37,9 +36,9 @@ codeV <- genCodeVerifier pure PkceRequestParam- { codeVerifier = CodeVerifier (T.decodeUtf8 codeV),- codeChallenge = CodeChallenge (encodeCodeVerifier codeV),- codeChallengeMethod = S256+ { codeVerifier = CodeVerifier (T.decodeUtf8 codeV)+ , codeChallenge = CodeChallenge (encodeCodeVerifier codeV)+ , codeChallengeMethod = S256 } encodeCodeVerifier :: BS.ByteString -> Text
src/Network/OAuth2/Experiment/Types.hs view
@@ -15,9 +15,10 @@ module Network.OAuth2.Experiment.Types where import Control.Monad.IO.Class (MonadIO (..))-import Control.Monad.Trans.Except (ExceptT)+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@@ -29,10 +30,9 @@ import Data.Text.Encoding qualified as T import Data.Text.Lazy (Text) import Data.Text.Lazy qualified as TL-import Network.HTTP.Conduit (Manager)+import Network.HTTP.Conduit import Network.OAuth.OAuth2 hiding (RefreshToken) import Network.OAuth.OAuth2 qualified as OAuth2-import Network.OAuth.OAuth2.TokenRequest qualified as TR import Network.OAuth2.Experiment.Pkce import Network.OAuth2.Experiment.Utils import URI.ByteString hiding (UserInfo)@@ -47,7 +47,15 @@ ------------------------------------------------------------------------------- -data GrantTypeFlow = AuthorizationCode | ResourceOwnerPassword | ClientCredentials+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 ------------------------------------------------------------------------------- @@ -73,10 +81,17 @@ ------------------------------------------------------------------------------- +newtype UrnOAuthParam a = UrnOAuthParam a+ -- | Grant type query parameter has association with 'GrantTypeFlow' but not completely strict. -- -- e.g. Both 'AuthorizationCode' and 'ResourceOwnerPassword' flow could support refresh token flow.-data GrantTypeValue = GTAuthorizationCode | GTPassword | GTClientCredentials | GTRefreshToken+data GrantTypeValue+ = GTAuthorizationCode+ | GTPassword+ | GTClientCredentials+ | GTRefreshToken+ | GTJwtBearer deriving (Eq, Show) -------------------------------------------------------------------------------@@ -107,6 +122,7 @@ newtype ClientId = ClientId {unClientId :: Text} deriving (Show, Eq, IsString) +-- | Can be either "Client Secret" or JWT base on client authentication method newtype ClientSecret = ClientSecret {unClientSecret :: Text} deriving (Eq, IsString) @@ -115,8 +131,8 @@ toOAuth2Key :: ClientId -> ClientSecret -> OAuth2 toOAuth2Key cid csecret = def- { oauth2ClientId = TL.toStrict $ unClientId cid,- oauth2ClientSecret = TL.toStrict $ unClientSecret csecret+ { oauth2ClientId = TL.toStrict $ unClientId cid+ , oauth2ClientSecret = TL.toStrict $ unClientSecret csecret } newtype RedirectUri = RedirectUri {unRedirectUri :: URI}@@ -165,6 +181,7 @@ val GTPassword = "password" val GTClientCredentials = "client_credentials" val GTRefreshToken = "refresh_token"+ val GTJwtBearer = "urn:ietf:params:oauth:grant-type:jwt-bearer" instance ToQueryParam ClientId where toQueryParam :: ClientId -> Map Text Text@@ -222,9 +239,6 @@ ------------------------------------------------------------------------------- -class HasIdpAppName (a :: GrantTypeFlow) where- getIdpAppName :: IdpApplication a i -> Text- class HasAuthorizeRequest (a :: GrantTypeFlow) where data AuthorizationRequest a type MkAuthorizationRequestResponse a@@ -249,7 +263,7 @@ (MonadIO m) => IdpApplication a i -> Manager ->- WithExchangeToken a (ExceptT (OAuth2Error TR.Errors) m OAuth2Token)+ WithExchangeToken a (ExceptT TokenRequestError m OAuth2Token) class HasPkceAuthorizeRequest (a :: GrantTypeFlow) where mkPkceAuthorizeRequest :: MonadIO m => IdpApplication a i -> m (TL.Text, CodeVerifier)@@ -260,7 +274,7 @@ IdpApplication b i -> Manager -> (ExchangeToken, CodeVerifier) ->- ExceptT (OAuth2Error TR.Errors) m OAuth2Token+ ExceptT TokenRequestError m OAuth2Token class HasRefreshTokenRequest (a :: GrantTypeFlow) where -- | https://www.rfc-editor.org/rfc/rfc6749#page-47@@ -272,7 +286,7 @@ IdpApplication a i -> Manager -> OAuth2.RefreshToken ->- ExceptT (OAuth2Error TR.Errors) m OAuth2Token+ ExceptT TokenRequestError m OAuth2Token ------------------------------------------------------------------------------- @@ -298,12 +312,12 @@ -- | 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+ { 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 ::+ idpAuthorizeEndpoint :: URI+ , idpTokenEndpoint :: URI+ , idpFetchUserInfo :: forall m. (FromJSON (IdpUserInfo a), MonadIO m) => Manager ->@@ -328,16 +342,16 @@ -- | 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,- -- | Though technically one key can have multiple value in query, but who actually does it?!- idpAppAuthorizeExtraParams :: Map Text Text,- idpAppTokenRequestAuthenticationMethod :: ClientAuthenticationMethod,- idp :: Idp i+ { 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@@ -345,27 +359,23 @@ -- parseAuthorizationResponse :: String -> AuthorizationResponse -- parseAuthorizationResponse :: ( String, String ) -> AuthorizationResponse -instance HasIdpAppName 'AuthorizationCode where- getIdpAppName :: IdpApplication 'AuthorizationCode i -> Text- getIdpAppName AuthorizationCodeIdpApplication {..} = idpAppName- 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+ { 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)+ { scope = if null idpAppScope then Set.empty else idpAppScope+ , state = idpAppAuthorizeState+ , clientId = idpAppClientId+ , redirectUri = Just (RedirectUri idpAppRedirectUri) } mkAuthorizeRequest :: IdpApplication 'AuthorizationCode i -> Text@@ -384,10 +394,10 @@ 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+ { code :: ExchangeToken+ , clientId :: ClientId+ , grantType :: GrantTypeValue+ , redirectUri :: RedirectUri } type WithExchangeToken 'AuthorizationCode a = ExchangeToken -> a @@ -397,10 +407,10 @@ TokenRequest 'AuthorizationCode mkTokenRequest AuthorizationCodeIdpApplication {..} authCode = AuthorizationCodeTokenRequest- { code = authCode,- clientId = idpAppClientId,- grantType = GTAuthorizationCode,- redirectUri = RedirectUri idpAppRedirectUri+ { code = authCode+ , clientId = idpAppClientId+ , grantType = GTAuthorizationCode+ , redirectUri = RedirectUri idpAppRedirectUri } conduitTokenRequest :: forall m i.@@ -408,14 +418,14 @@ IdpApplication 'AuthorizationCode i -> Manager -> ExchangeToken ->- ExceptT (OAuth2Error TR.Errors) m OAuth2Token+ ExceptT TokenRequestError m OAuth2Token conduitTokenRequest idpAppConfig@AuthorizationCodeIdpApplication {..} mgr exchangeToken = let req = mkTokenRequest idpAppConfig exchangeToken key = toOAuth2Key idpAppClientId idpAppClientSecret body = mapsToParams- [ toQueryParam req,- toQueryParam+ [ toQueryParam req+ , toQueryParam ( if idpAppTokenRequestAuthenticationMethod == ClientSecretPost then Just idpAppClientSecret else Nothing@@ -430,10 +440,10 @@ let req = mkAuthorizeRequestParameter idpAppConfig let allParams = mapsToParams- [ idpAppAuthorizeExtraParams,- toQueryParam req,- toQueryParam codeChallenge,- toQueryParam codeChallengeMethod+ [ idpAppAuthorizeExtraParams+ , toQueryParam req+ , toQueryParam codeChallenge+ , toQueryParam codeChallengeMethod ] let url =@@ -450,45 +460,45 @@ IdpApplication 'AuthorizationCode i -> Manager -> (ExchangeToken, CodeVerifier) ->- ExceptT (OAuth2Error TR.Errors) m OAuth2Token+ 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)+ [ 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+ { 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+ { scope = idpAppScope+ , grantType = GTRefreshToken+ , refreshToken = rt } conduitRefreshTokenRequest :: (MonadIO m) => IdpApplication 'AuthorizationCode i -> Manager -> OAuth2.RefreshToken ->- ExceptT (OAuth2Error TR.Errors) m OAuth2Token+ 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)+ [ toQueryParam req+ , toQueryParam (if idpAppTokenRequestAuthenticationMethod == ClientSecretPost then Just idpAppClientSecret else Nothing) ] in doJSONPostRequest mgr key (idpTokenEndpoint idp) body @@ -506,33 +516,90 @@ toQueryParam :: AuthorizationRequest 'AuthorizationCode -> Map Text Text toQueryParam req@AuthorizationCodeAuthorizationRequest {..} = Map.unions- [ toResponseTypeParam req,- toQueryParam scope,- toQueryParam clientId,- toQueryParam state,- toQueryParam redirectUri+ [ 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+ [ 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+ [ 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 -------------------------------------------------------------------------------@@ -545,51 +612,47 @@ -- -- Hence no AuhorizationRequest instance -data instance IdpApplication 'ResourceOwnerPassword i = ResourceOwnerPasswordIDPAppConfig- { idpAppClientId :: ClientId,- idpAppClientSecret :: ClientSecret,- idpAppName :: Text,- idpAppScope :: Set Scope,- idpAppUserName :: Username,- idpAppPassword :: Password,- -- | Any parameter that required by your Idp and not mentioned in the OAuth2 spec- idpAppTokenRequestExtraParams :: Map Text Text,- idp :: Idp i+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 HasIdpAppName 'ResourceOwnerPassword where- getIdpAppName :: IdpApplication 'ResourceOwnerPassword i -> Text- getIdpAppName ResourceOwnerPasswordIDPAppConfig {..} = idpAppName- instance HasUserInfoRequest 'ResourceOwnerPassword where- conduitUserInfoRequest ResourceOwnerPasswordIDPAppConfig {..} mgr at = do+ 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+ { scope :: Set Scope+ , username :: Username+ , password :: Password+ , grantType :: GrantTypeValue } type WithExchangeToken 'ResourceOwnerPassword a = a mkTokenRequest :: IdpApplication 'ResourceOwnerPassword i -> TokenRequest 'ResourceOwnerPassword- mkTokenRequest ResourceOwnerPasswordIDPAppConfig {..} =+ mkTokenRequest ResourceOwnerPasswordIDPApplication {..} = PasswordTokenRequest- { username = idpAppUserName,- password = idpAppPassword,- grantType = GTPassword,- scope = idpAppScope+ { username = idpAppUserName+ , password = idpAppPassword+ , grantType = GTPassword+ , scope = idpAppScope } conduitTokenRequest :: (MonadIO m) => IdpApplication 'ResourceOwnerPassword i -> Manager ->- ExceptT (OAuth2Error TR.Errors) m OAuth2Token- conduitTokenRequest idpAppConfig@ResourceOwnerPasswordIDPAppConfig {..} mgr =+ ExceptT TokenRequestError m OAuth2Token+ conduitTokenRequest idpAppConfig@ResourceOwnerPasswordIDPApplication {..} mgr = let req = mkTokenRequest idpAppConfig key = toOAuth2Key idpAppClientId idpAppClientSecret body = mapsToParams [idpAppTokenRequestExtraParams, toQueryParam req]@@ -610,17 +673,17 @@ IdpApplication 'ResourceOwnerPassword i -> Manager -> OAuth2.RefreshToken ->- ExceptT (OAuth2Error TR.Errors) m OAuth2Token+ 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+ [ toQueryParam grantType+ , toQueryParam scope+ , toQueryParam username+ , toQueryParam password ] -------------------------------------------------------------------------------@@ -636,58 +699,80 @@ -- -- Hence no AuhorizationRequest instance -data instance IdpApplication 'ClientCredentials i = ClientCredentialsIDPAppConfig- { idpAppClientId :: ClientId,- idpAppClientSecret :: ClientSecret,- idpAppName :: Text,- idpAppScope :: Set Scope,- -- | Any parameter that required by your Idp and not mentioned in the OAuth2 spec- idpAppTokenRequestExtraParams :: Map Text Text,- idp :: Idp i+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 HasIdpAppName 'ClientCredentials where- getIdpAppName :: IdpApplication 'ClientCredentials i -> Text- getIdpAppName ClientCredentialsIDPAppConfig {..} = idpAppName- instance HasTokenRequest 'ClientCredentials where -- \| https://www.rfc-editor.org/rfc/rfc6749#section-4.4.2 data TokenRequest 'ClientCredentials = ClientCredentialsTokenRequest- { scope :: Set Scope,- grantType :: GrantTypeValue+ { scope :: Set Scope+ , grantType :: GrantTypeValue+ , clientAssertionType :: Text+ , clientAssertion :: BS.ByteString+ , clientAuthenticationMethod :: ClientAuthenticationMethod } type WithExchangeToken 'ClientCredentials a = a mkTokenRequest :: IdpApplication 'ClientCredentials i -> TokenRequest 'ClientCredentials- mkTokenRequest ClientCredentialsIDPAppConfig {..} =+ mkTokenRequest ClientCredentialsIDPApplication {..} = ClientCredentialsTokenRequest- { scope = idpAppScope,- grantType = GTClientCredentials+ { 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 (OAuth2Error TR.Errors) m OAuth2Token- conduitTokenRequest idpAppConfig@ClientCredentialsIDPAppConfig {..} mgr =- let req = mkTokenRequest idpAppConfig+ ExceptT TokenRequestError m OAuth2Token+ conduitTokenRequest idpAppConfig@ClientCredentialsIDPApplication {..} mgr = do+ let tokenReq = mkTokenRequest idpAppConfig key = toOAuth2Key idpAppClientId idpAppClientSecret body = mapsToParams- [ idpAppTokenRequestExtraParams,- toQueryParam req+ [ idpAppTokenRequestExtraParams+ , toQueryParam tokenReq ]- in doJSONPostRequest mgr key (idpTokenEndpoint idp) body+ 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.unions $+ [ toQueryParam grantType+ , toQueryParam scope ]+ ++ [ Map.fromList+ ( if clientAuthenticationMethod == ClientAssertionJwt+ then+ [ ("client_assertion_type", clientAssertionType)+ , ("client_assertion", bs8ToLazyText clientAssertion)+ ]+ else []+ )+ ]
+ test/Network/OAuth/OAuth2/TokenRequestSpec.hs view
@@ -0,0 +1,23 @@+module Network.OAuth.OAuth2.TokenRequestSpec where++import Data.Aeson qualified as Aeson+import Network.OAuth.OAuth2.TokenRequest+import Test.Hspec++spec :: Spec+spec =+ describe "parseJSON TokenRequestErrorCode" $ do+ it "invalid_request" $ do+ Aeson.eitherDecode "\"invalid_request\"" `shouldBe` Right InvalidRequest+ it "invalid_client" $ do+ Aeson.eitherDecode "\"invalid_client\"" `shouldBe` Right InvalidClient+ it "invalid_grant" $ do+ Aeson.eitherDecode "\"invalid_grant\"" `shouldBe` Right InvalidGrant+ it "unauthorized_client" $ do+ Aeson.eitherDecode "\"unauthorized_client\"" `shouldBe` Right UnauthorizedClient+ it "unsupported_grant_type" $ do+ Aeson.eitherDecode "\"unsupported_grant_type\"" `shouldBe` Right UnsupportedGrantType+ it "invalid_scope" $ do+ Aeson.eitherDecode "\"invalid_scope\"" `shouldBe` Right InvalidScope+ it "foo_code" $ do+ Aeson.eitherDecode "\"foo_code\"" `shouldBe` Right (UnknownErrorCode "foo_code")
+ test/Spec.hs view
@@ -0,0 +1,2 @@+-- file test/Spec.hs+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}