diff --git a/example/Main.hs b/example/Main.hs index cdab1dd..54105f7 100644 --- a/example/Main.hs +++ b/example/Main.hs @@ -34,13 +34,13 @@ import Yesod.Auth.OAuth2.GitHub import Yesod.Auth.OAuth2.GitLab import Yesod.Auth.OAuth2.Google import Yesod.Auth.OAuth2.Nylas +import Yesod.Auth.OAuth2.Okta import Yesod.Auth.OAuth2.Salesforce import Yesod.Auth.OAuth2.Slack import Yesod.Auth.OAuth2.Spotify import Yesod.Auth.OAuth2.Twitch import Yesod.Auth.OAuth2.Upcase import Yesod.Auth.OAuth2.WordPressDotCom -import Yesod.Auth.OAuth2.Okta data App = App { appHttpManager :: Manager @@ -148,7 +148,9 @@ mkFoundation = do , loadPlugin oauth2Twitch "TWITCH" , loadPlugin oauth2WordPressDotCom "WORDPRESS_DOT_COM" , loadPlugin oauth2Upcase "UPCASE" - , loadPlugin (oauth2Okta False (fromString oktaHost) "default" Nothing) "OKTA" + , loadPlugin + (oauth2Okta False (fromString oktaHost) "default" Nothing) + "OKTA" ] return App { .. } diff --git a/src/Yesod/Auth/OAuth2/Dispatch.hs b/src/Yesod/Auth/OAuth2/Dispatch.hs index 534e908..41b9c75 100644 --- a/src/Yesod/Auth/OAuth2/Dispatch.hs +++ b/src/Yesod/Auth/OAuth2/Dispatch.hs @@ -18,8 +18,8 @@ import qualified Data.Text as T import Data.Text.Encoding (encodeUtf8) import Network.HTTP.Conduit (Manager) import Network.OAuth.OAuth2.Compat -import UnliftIO.Exception import URI.ByteString.Extension +import UnliftIO.Exception import Yesod.Auth hiding (ServerError) import Yesod.Auth.OAuth2.DispatchError import Yesod.Auth.OAuth2.ErrorResponse @@ -101,10 +101,10 @@ withCallbackAndState -> m OAuth2 withCallbackAndState name oauth2 csrf = do pluginURI <- ($ PluginR name ["callback"]) <$> getParentUrlRender - let uri = - case oauth2AppRoot oauth2 of - Just root -> root <> pluginURI - Nothing -> pluginURI + let + uri = case oauth2AppRoot oauth2 of + Just root -> root <> pluginURI + Nothing -> pluginURI callback <- maybe (throwError $ InvalidCallbackUri uri) pure $ fromText uri pure oauth2 { oauth2RedirectUri = Just callback diff --git a/src/Yesod/Auth/OAuth2/Okta.hs b/src/Yesod/Auth/OAuth2/Okta.hs index 26e30c3..03a301b 100644 --- a/src/Yesod/Auth/OAuth2/Okta.hs +++ b/src/Yesod/Auth/OAuth2/Okta.hs @@ -8,18 +8,17 @@ -- -- * Authenticates against a specific Okta application -- -- * Uses Okta sub as user id module Yesod.Auth.OAuth2.Okta - ( oauth2Okta, - oauth2OktaWithScopes, - defaultOktaScopes, - pluginName, - User (..), - ) -where + ( oauth2Okta + , oauth2OktaWithScopes + , defaultOktaScopes + , pluginName + , User(..) + ) where import Data.Aeson as Aeson import Data.ByteString (ByteString) -import Yesod.Auth.OAuth2.Prelude import Prelude +import Yesod.Auth.OAuth2.Prelude -- | Okta User's info: https://developer.okta.com/docs/reference/api/oidc/#userinfo newtype User = User Text @@ -36,80 +35,92 @@ pluginName :: Text pluginName = "okta" -- | Creates an Okta 'AuthPlugin' for application using the default scopes. -oauth2Okta :: - YesodAuth m => +oauth2Okta + :: YesodAuth m + => -- | Prompt login on authorize redirect - Bool -> + Bool + -> -- | The host address of the Okta application (absolute) - URI -> + URI + -> -- | The authorization server - ByteString -> + ByteString + -> -- | Application Root for redirect links - Maybe Text -> + Maybe Text + -> -- | Client ID of the Okta application - Text -> + Text + -> -- | Client Secret of the Okta application - Text -> - AuthPlugin m + Text + -> AuthPlugin m oauth2Okta = oauth2OktaWithScopes defaultOktaScopes -- | Creates an Okta 'AuthPlugin' for application with access to the provided scopes. -oauth2OktaWithScopes :: - YesodAuth m => +oauth2OktaWithScopes + :: YesodAuth m + => -- | The scopes accessible to the 'AuthPlugin' - [Text] -> + [Text] + -> -- | Prompt login on authorize redirect - Bool -> + Bool + -> -- | The host address of the Okta application (absolute) - URI -> + URI + -> -- | The authorization server - ByteString -> + ByteString + -> -- | Application Root for building callbacks - Maybe Text -> + Maybe Text + -> -- | Client ID of the Okta application - Text -> + Text + -> -- | Client Secret of the Okta application - Text -> - AuthPlugin m -oauth2OktaWithScopes scopes shouldPrompt host authorizationServer appRoot clientId clientSecret = - authOAuth2 pluginName oauth2 $ \manager token -> do - (User uid, userResponse) <- - authGetProfile - pluginName - manager - token - (host `withPath` (mkEndpointSegment authorizationServer "userinfo")) - pure - Creds - { credsPlugin = pluginName, - credsIdent = uid, - credsExtra = setExtra token userResponse - } - where - queryParams = - if shouldPrompt - then [scopeParam " " scopes, ("prompt", "login")] - else [scopeParam " " scopes] - oauth2 = - OAuth2 - { oauth2ClientId = clientId, - oauth2ClientSecret = Just clientSecret, - oauth2AuthorizeEndpoint = - host - `withPath` (mkEndpointSegment authorizationServer "authorize") - `withQuery` queryParams, - oauth2TokenEndpoint = host `withPath` (mkEndpointSegment authorizationServer "token"), - oauth2RedirectUri = Nothing, - oauth2AppRoot = appRoot - } + Text + -> AuthPlugin m +oauth2OktaWithScopes scopes shouldPrompt host authorizationServer appRoot clientId clientSecret + = authOAuth2 pluginName oauth2 $ \manager token -> do + (User uid, userResponse) <- authGetProfile + pluginName + manager + token + (host `withPath` (mkEndpointSegment authorizationServer "userinfo")) + pure Creds + { credsPlugin = pluginName + , credsIdent = uid + , credsExtra = setExtra token userResponse + } + where + queryParams = if shouldPrompt + then [scopeParam " " scopes, ("prompt", "login")] + else [scopeParam " " scopes] + oauth2 = OAuth2 + { oauth2ClientId = clientId + , oauth2ClientSecret = Just clientSecret + , oauth2AuthorizeEndpoint = + host + `withPath` (mkEndpointSegment authorizationServer "authorize") + `withQuery` queryParams + , oauth2TokenEndpoint = + host `withPath` (mkEndpointSegment authorizationServer "token") + , oauth2RedirectUri = Nothing + , oauth2AppRoot = appRoot + } -- | Helper function for creating an endpoint path segment for the given authorization server -- and endpoint. -mkEndpointSegment :: +mkEndpointSegment + :: -- | Authorization server ID - ByteString -> + ByteString + -> -- | Endpoint - ByteString -> - ByteString + ByteString + -> ByteString mkEndpointSegment authorizationServer endpoint = "/oauth2/" <> authorizationServer <> "/v1/" <> endpoint diff --git a/src/Yesod/Auth/OAuth2/Slack.hs b/src/Yesod/Auth/OAuth2/Slack.hs index 293b118..9aaddd1 100644 --- a/src/Yesod/Auth/OAuth2/Slack.hs +++ b/src/Yesod/Auth/OAuth2/Slack.hs @@ -14,7 +14,7 @@ module Yesod.Auth.OAuth2.Slack import Yesod.Auth.OAuth2.Prelude import Network.HTTP.Client - (httpLbs, parseUrlThrow, responseBody, setQueryString) + (httpLbs, parseUrlThrow, responseBody, setQueryString) import Yesod.Auth.OAuth2.Exception as YesodOAuth2Exception data SlackScope