From 08d0f0eaa4f058c00a9fe9f75f2cb486314e281d Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Tue, 1 Aug 2023 07:59:37 -0400 Subject: [PATCH] Convert project to Fourmolu --- .restyled.yaml | 10 +-- .stylish-haskell.yaml | 21 ------ brittany.yaml | 44 ------------- example/Main.hs | 84 +++++++++++++----------- fourmolu.yaml | 15 +++++ src/Network/OAuth/OAuth2/Compat.hs | 66 +++++++++---------- src/URI/ByteString/Extension.hs | 12 ++-- src/UnliftIO/Except.hs | 3 +- src/Yesod/Auth/OAuth2.hs | 25 ++++--- src/Yesod/Auth/OAuth2/Auth0.hs | 43 ++++++------ src/Yesod/Auth/OAuth2/AzureAD.hs | 49 +++++++------- src/Yesod/Auth/OAuth2/AzureADv2.hs | 70 +++++++++++--------- src/Yesod/Auth/OAuth2/BattleNet.hs | 44 +++++++------ src/Yesod/Auth/OAuth2/Bitbucket.hs | 55 ++++++++-------- src/Yesod/Auth/OAuth2/ClassLink.hs | 41 ++++++------ src/Yesod/Auth/OAuth2/Dispatch.hs | 58 ++++++++-------- src/Yesod/Auth/OAuth2/DispatchError.hs | 35 +++++----- src/Yesod/Auth/OAuth2/ErrorResponse.hs | 29 ++++---- src/Yesod/Auth/OAuth2/EveOnline.hs | 72 ++++++++++---------- src/Yesod/Auth/OAuth2/Exception.hs | 19 +++--- src/Yesod/Auth/OAuth2/GitHub.hs | 43 ++++++------ src/Yesod/Auth/OAuth2/GitLab.hs | 30 +++++---- src/Yesod/Auth/OAuth2/Google.hs | 50 +++++++------- src/Yesod/Auth/OAuth2/Nylas.hs | 62 +++++++++-------- src/Yesod/Auth/OAuth2/Prelude.hs | 34 +++++----- src/Yesod/Auth/OAuth2/Random.hs | 4 +- src/Yesod/Auth/OAuth2/Salesforce.hs | 63 ++++++++++-------- src/Yesod/Auth/OAuth2/Slack.hs | 58 +++++++++------- src/Yesod/Auth/OAuth2/Spotify.hs | 43 ++++++------ src/Yesod/Auth/OAuth2/Twitch.hs | 51 +++++++------- src/Yesod/Auth/OAuth2/Upcase.hs | 39 ++++++----- src/Yesod/Auth/OAuth2/WordPressDotCom.hs | 50 +++++++------- test/URI/ByteString/ExtensionSpec.hs | 11 ++-- 33 files changed, 673 insertions(+), 660 deletions(-) delete mode 100644 .stylish-haskell.yaml delete mode 100644 brittany.yaml create mode 100644 fourmolu.yaml diff --git a/.restyled.yaml b/.restyled.yaml index fa61b26..be7a0c0 100644 --- a/.restyled.yaml +++ b/.restyled.yaml @@ -1,10 +1,4 @@ restylers: - - brittany: - include: - - "**/*.hs" - - "!src/Network/OAuth/OAuth2/Compat.hs" # CPP - - stylish-haskell: - include: - - "**/*.hs" - - "!src/Network/OAuth/OAuth2/Compat.hs" # CPP + - fourmolu + - "!stylish-haskell" - "*" diff --git a/.stylish-haskell.yaml b/.stylish-haskell.yaml deleted file mode 100644 index a25ae44..0000000 --- a/.stylish-haskell.yaml +++ /dev/null @@ -1,21 +0,0 @@ -steps: - - simple_align: - cases: false - top_level_patterns: false - records: false - - imports: - align: none - list_align: after_alias - pad_module_names: false - long_list_align: new_line_multiline - empty_list_align: right_after - list_padding: 4 - separate_lists: false - space_surround: false - - language_pragmas: - style: vertical - align: false - remove_redundant: true - - trailing_whitespace: {} -columns: 80 -newline: native diff --git a/brittany.yaml b/brittany.yaml deleted file mode 100644 index 15e0ab3..0000000 --- a/brittany.yaml +++ /dev/null @@ -1,44 +0,0 @@ ---- -conf_debug: - dconf_roundtrip_exactprint_only: false - dconf_dump_bridoc_simpl_par: false - dconf_dump_ast_unknown: false - dconf_dump_bridoc_simpl_floating: false - dconf_dump_config: false - dconf_dump_bridoc_raw: false - dconf_dump_bridoc_final: false - dconf_dump_bridoc_simpl_alt: false - dconf_dump_bridoc_simpl_indent: false - dconf_dump_annotations: false - dconf_dump_bridoc_simpl_columns: false - dconf_dump_ast_full: false -conf_errorHandling: - econf_ExactPrintFallback: ExactPrintFallbackModeInline - econf_Werror: false - econf_omit_output_valid_check: false - econf_produceOutputOnErrors: false -conf_preprocessor: - ppconf_CPPMode: CPPModeAbort - ppconf_hackAroundIncludes: false -conf_obfuscate: false -conf_roundtrip_exactprint_only: false -conf_version: 1 -conf_layout: - lconfig_reformatModulePreamble: true - lconfig_altChooser: - tag: AltChooserBoundedSearch - contents: 3 - lconfig_allowSingleLineExportList: false - lconfig_importColumn: 60 - lconfig_hangingTypeSignature: false - lconfig_importAsColumn: 50 - lconfig_alignmentLimit: 1 - lconfig_indentListSpecial: true - lconfig_indentAmount: 2 - lconfig_alignmentBreakOnMultiline: true - lconfig_cols: 80 - lconfig_indentPolicy: IndentPolicyLeft - lconfig_indentWhereSpecial: true - lconfig_columnAlignMode: - tag: ColumnAlignModeDisabled - contents: 0.7 diff --git a/example/Main.hs b/example/Main.hs index 715fa30..9c8d836 100644 --- a/example/Main.hs +++ b/example/Main.hs @@ -13,7 +13,7 @@ import Data.Aeson.Encode.Pretty import Data.ByteString.Lazy (fromStrict, toStrict) import qualified Data.Map as M import Data.Maybe (fromJust) -import Data.String (IsString(fromString)) +import Data.String (IsString (fromString)) import Data.Text (Text, pack) import qualified Data.Text as T import Data.Text.Encoding (decodeUtf8) @@ -46,13 +46,15 @@ data App = App , appAuthPlugins :: [AuthPlugin App] } -mkYesod "App" [parseRoutes| +mkYesod + "App" + [parseRoutes| / RootR GET /auth AuthR Auth getAuth |] instance Yesod App where - -- see https://github.com/thoughtbot/yesod-auth-oauth2/issues/87 + -- see https://github.com/thoughtbot/yesod-auth-oauth2/issues/87 approot = ApprootStatic "http://localhost:3000" instance YesodAuth App where @@ -65,9 +67,9 @@ instance YesodAuth App where -- Copy the Creds response into the session for viewing after authenticate c = do - mapM_ (uncurry setSession) - $ [("credsIdent", credsIdent c), ("credsPlugin", credsPlugin c)] - ++ credsExtra c + mapM_ (uncurry setSession) $ + [("credsIdent", credsIdent c), ("credsPlugin", credsPlugin c)] + ++ credsExtra c return $ Authenticated "1" @@ -80,23 +82,24 @@ instance RenderMessage App FormMessage where getRootR :: Handler Html getRootR = do - sess <- getSession + sess <- getSession - let - prettify - = decodeUtf8 - . toStrict - . encodePretty - . fromJust - . decode @Value - . fromStrict + let + prettify = + decodeUtf8 + . toStrict + . encodePretty + . fromJust + . decode @Value + . fromStrict - mCredsIdent = decodeUtf8 <$> M.lookup "credsIdent" sess - mCredsPlugin = decodeUtf8 <$> M.lookup "credsPlugin" sess - mAccessToken = decodeUtf8 <$> M.lookup "accessToken" sess - mUserResponse = prettify <$> M.lookup "userResponse" sess + mCredsIdent = decodeUtf8 <$> M.lookup "credsIdent" sess + mCredsPlugin = decodeUtf8 <$> M.lookup "credsPlugin" sess + mAccessToken = decodeUtf8 <$> M.lookup "accessToken" sess + mUserResponse = prettify <$> M.lookup "userResponse" sess - defaultLayout [whamlet| + defaultLayout + [whamlet|

Yesod Auth OAuth2 Example

Log in @@ -123,32 +126,33 @@ mkFoundation = do azureTenant <- getEnv "AZURE_ADV2_TENANT_ID" appHttpManager <- newManager tlsManagerSettings - appAuthPlugins <- sequence + appAuthPlugins <- + sequence -- When Providers are added, add them here and update .env.example. -- Nothing else should need changing. -- -- FIXME: oauth2BattleNet is quite annoying! -- - [ loadPlugin oauth2AzureAD "AZURE_AD" - , loadPlugin (oauth2AzureADv2 $ pack azureTenant) "AZURE_ADV2" - , loadPlugin (oauth2Auth0Host $ fromString auth0Host) "AUTH0" - , loadPlugin (oauth2BattleNet [whamlet|TODO|] "en") "BATTLE_NET" - , loadPlugin oauth2Bitbucket "BITBUCKET" - , loadPlugin oauth2ClassLink "CLASSLINK" - , loadPlugin (oauth2Eve Plain) "EVE_ONLINE" - , loadPlugin oauth2GitHub "GITHUB" - , loadPlugin oauth2GitLab "GITLAB" - , loadPlugin oauth2Google "GOOGLE" - , loadPlugin oauth2Nylas "NYLAS" - , loadPlugin oauth2Salesforce "SALES_FORCE" - , loadPlugin oauth2Slack "SLACK" - , loadPlugin (oauth2Spotify []) "SPOTIFY" - , loadPlugin oauth2Twitch "TWITCH" - , loadPlugin oauth2WordPressDotCom "WORDPRESS_DOT_COM" - , loadPlugin oauth2Upcase "UPCASE" - ] + [ loadPlugin oauth2AzureAD "AZURE_AD" + , loadPlugin (oauth2AzureADv2 $ pack azureTenant) "AZURE_ADV2" + , loadPlugin (oauth2Auth0Host $ fromString auth0Host) "AUTH0" + , loadPlugin (oauth2BattleNet [whamlet|TODO|] "en") "BATTLE_NET" + , loadPlugin oauth2Bitbucket "BITBUCKET" + , loadPlugin oauth2ClassLink "CLASSLINK" + , loadPlugin (oauth2Eve Plain) "EVE_ONLINE" + , loadPlugin oauth2GitHub "GITHUB" + , loadPlugin oauth2GitLab "GITLAB" + , loadPlugin oauth2Google "GOOGLE" + , loadPlugin oauth2Nylas "NYLAS" + , loadPlugin oauth2Salesforce "SALES_FORCE" + , loadPlugin oauth2Slack "SLACK" + , loadPlugin (oauth2Spotify []) "SPOTIFY" + , loadPlugin oauth2Twitch "TWITCH" + , loadPlugin oauth2WordPressDotCom "WORDPRESS_DOT_COM" + , loadPlugin oauth2Upcase "UPCASE" + ] - return App { .. } + return App {..} where loadPlugin f prefix = do clientId <- getEnv $ prefix <> "_CLIENT_ID" diff --git a/fourmolu.yaml b/fourmolu.yaml new file mode 100644 index 0000000..ef571e8 --- /dev/null +++ b/fourmolu.yaml @@ -0,0 +1,15 @@ +indentation: 2 +column-limit: 80 # ignored until v12 / ghc-9.6 +function-arrows: leading +comma-style: leading # default +import-export-style: leading +indent-wheres: false # default +record-brace-space: true +newlines-between-decls: 1 # default +haddock-style: single-line +let-style: mixed +in-style: left-align +single-constraint-parens: never # ignored until v12 / ghc-9.6 +unicode: never # default +respectful: true # default +fixities: [] # default diff --git a/src/Network/OAuth/OAuth2/Compat.hs b/src/Network/OAuth/OAuth2/Compat.hs index 18364d3..affb77b 100644 --- a/src/Network/OAuth/OAuth2/Compat.hs +++ b/src/Network/OAuth/OAuth2/Compat.hs @@ -1,17 +1,17 @@ {-# LANGUAGE CPP #-} module Network.OAuth.OAuth2.Compat - ( OAuth2(..) - , OAuth2Result - , Errors - , authorizationUrl - , fetchAccessToken - , fetchAccessToken2 - , authGetBS + ( OAuth2 (..) + , OAuth2Result + , Errors + , authorizationUrl + , fetchAccessToken + , fetchAccessToken2 + , authGetBS -- * Re-exports - , module Network.OAuth.OAuth2 - ) where + , module Network.OAuth.OAuth2 + ) where import Data.ByteString.Lazy (ByteString) import Data.Text (Text) @@ -39,12 +39,12 @@ import Data.Maybe (fromMaybe) #endif data OAuth2 = OAuth2 - { oauth2ClientId :: Text - , oauth2ClientSecret :: Maybe Text - , oauth2AuthorizeEndpoint :: URIRef Absolute - , oauth2TokenEndpoint :: URIRef Absolute - , oauth2RedirectUri :: Maybe (URIRef Absolute) - } + { oauth2ClientId :: Text + , oauth2ClientSecret :: Maybe Text + , oauth2AuthorizeEndpoint :: URIRef Absolute + , oauth2TokenEndpoint :: URIRef Absolute + , oauth2RedirectUri :: Maybe (URIRef Absolute) + } #if MIN_VERSION_hoauth2(2,7,0) type Errors = TokenRequestError @@ -58,17 +58,17 @@ authorizationUrl :: OAuth2 -> URI authorizationUrl = OAuth2.authorizationUrl . getOAuth2 fetchAccessToken - :: Manager - -> OAuth2 - -> ExchangeToken - -> IO (OAuth2Result Errors OAuth2Token) + :: Manager + -> OAuth2 + -> ExchangeToken + -> IO (OAuth2Result Errors OAuth2Token) fetchAccessToken = fetchAccessTokenBasic fetchAccessToken2 - :: Manager - -> OAuth2 - -> ExchangeToken - -> IO (OAuth2Result Errors OAuth2Token) + :: Manager + -> OAuth2 + -> ExchangeToken + -> IO (OAuth2Result Errors OAuth2Token) fetchAccessToken2 = fetchAccessTokenPost authGetBS :: Manager -> AccessToken -> URI -> IO (Either ByteString ByteString) @@ -141,12 +141,12 @@ runOAuth2 = id -- directly. fetchAccessTokenBasic - :: Manager - -> OAuth2 - -> ExchangeToken - -> IO (OAuth2Result Errors OAuth2Token) + :: Manager + -> OAuth2 + -> ExchangeToken + -> IO (OAuth2Result Errors OAuth2Token) fetchAccessTokenBasic m o e = runOAuth2 $ f m (getOAuth2 o) e - where + where #if MIN_VERSION_hoauth2(2,6,0) f = OAuth2.fetchAccessTokenWithAuthMethod OAuth2.ClientSecretBasic #elif MIN_VERSION_hoauth2(2,3,0) @@ -156,12 +156,12 @@ fetchAccessTokenBasic m o e = runOAuth2 $ f m (getOAuth2 o) e #endif fetchAccessTokenPost - :: Manager - -> OAuth2 - -> ExchangeToken - -> IO (OAuth2Result Errors OAuth2Token) + :: Manager + -> OAuth2 + -> ExchangeToken + -> IO (OAuth2Result Errors OAuth2Token) fetchAccessTokenPost m o e = runOAuth2 $ f m (getOAuth2 o) e - where + where #if MIN_VERSION_hoauth2(2, 6, 0) f = OAuth2.fetchAccessTokenWithAuthMethod OAuth2.ClientSecretPost #elif MIN_VERSION_hoauth2(2,3,0) diff --git a/src/URI/ByteString/Extension.hs b/src/URI/ByteString/Extension.hs index ac04ec2..bb3b1dd 100644 --- a/src/URI/ByteString/Extension.hs +++ b/src/URI/ByteString/Extension.hs @@ -1,9 +1,10 @@ {-# LANGUAGE FlexibleInstances #-} {-# OPTIONS_GHC -fno-warn-orphans #-} + module URI.ByteString.Extension where import Data.ByteString (ByteString) -import Data.String (IsString(..)) +import Data.String (IsString (..)) import Data.Text (Text) import Data.Text.Encoding (decodeUtf8, encodeUtf8) import Lens.Micro @@ -41,9 +42,12 @@ fromRelative :: Scheme -> Host -> RelativeRef -> URI fromRelative s h = flip withHost h . toAbsolute s withHost :: URIRef a -> Host -> URIRef a -withHost u h = u & authorityL %~ maybe - (Just $ Authority Nothing h Nothing) - (\a -> Just $ a & authorityHostL .~ h) +withHost u h = + u + & authorityL + %~ maybe + (Just $ Authority Nothing h Nothing) + (\a -> Just $ a & authorityHostL .~ h) withPath :: URIRef a -> ByteString -> URIRef a withPath u p = u & pathL .~ p diff --git a/src/UnliftIO/Except.hs b/src/UnliftIO/Except.hs index 865deb2..3dce8db 100644 --- a/src/UnliftIO/Except.hs +++ b/src/UnliftIO/Except.hs @@ -1,7 +1,6 @@ {-# OPTIONS_GHC -Wno-orphans #-} -module UnliftIO.Except - () where +module UnliftIO.Except () where import Control.Monad.Except import UnliftIO diff --git a/src/Yesod/Auth/OAuth2.hs b/src/Yesod/Auth/OAuth2.hs index 52a9064..99b8c8e 100644 --- a/src/Yesod/Auth/OAuth2.hs +++ b/src/Yesod/Auth/OAuth2.hs @@ -1,18 +1,18 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} + -- | -- -- Generic OAuth2 plugin for Yesod -- -- See @"Yesod.Auth.OAuth2.GitHub"@ for example usage. --- module Yesod.Auth.OAuth2 - ( OAuth2(..) + ( OAuth2 (..) , FetchCreds , Manager - , OAuth2Token(..) - , Creds(..) + , OAuth2Token (..) + , Creds (..) , oauth2Url , authOAuth2 , authOAuth2Widget @@ -46,14 +46,12 @@ oauth2Url name = PluginR name ["forward"] -- | Create an @'AuthPlugin'@ for the given OAuth2 provider -- -- Presents a generic @"Login via #{name}"@ link --- authOAuth2 :: YesodAuth m => Text -> OAuth2 -> FetchCreds m -> AuthPlugin m authOAuth2 name = authOAuth2Widget [whamlet|Login via #{name}|] name -- | A version of 'authOAuth2' that uses 'fetchAccessToken2' -- -- See --- authOAuth2' :: YesodAuth m => Text -> OAuth2 -> FetchCreds m -> AuthPlugin m authOAuth2' name = authOAuth2Widget' [whamlet|Login via #{name}|] name @@ -61,7 +59,6 @@ authOAuth2' name = authOAuth2Widget' [whamlet|Login via #{name}|] name -- -- Allows passing a custom widget for the login link. See @'oauth2Eve'@ for an -- example. --- authOAuth2Widget :: YesodAuth m => WidgetFor m () @@ -74,7 +71,6 @@ authOAuth2Widget = buildPlugin fetchAccessToken -- | A version of 'authOAuth2Widget' that uses 'fetchAccessToken2' -- -- See --- authOAuth2Widget' :: YesodAuth m => WidgetFor m () @@ -92,11 +88,13 @@ buildPlugin -> OAuth2 -> FetchCreds m -> AuthPlugin m -buildPlugin getToken widget name oauth getCreds = AuthPlugin - name - (dispatchAuthRequest name oauth getToken getCreds) - login - where login tm = [whamlet|^{widget}|] +buildPlugin getToken widget name oauth getCreds = + AuthPlugin + name + (dispatchAuthRequest name oauth getToken getCreds) + login + where + login tm = [whamlet|^{widget}|] -- | Read the @'AccessToken'@ from the values set via @'setExtra'@ getAccessToken :: Creds m -> Maybe AccessToken @@ -105,7 +103,6 @@ getAccessToken = (AccessToken <$>) . lookup "accessToken" . credsExtra -- | Read the @'RefreshToken'@ from the values set via @'setExtra'@ -- -- N.B. not all providers supply this value. --- getRefreshToken :: Creds m -> Maybe RefreshToken getRefreshToken = (RefreshToken <$>) . lookup "refreshToken" . credsExtra diff --git a/src/Yesod/Auth/OAuth2/Auth0.hs b/src/Yesod/Auth/OAuth2/Auth0.hs index b1a1cb8..999d2f9 100644 --- a/src/Yesod/Auth/OAuth2/Auth0.hs +++ b/src/Yesod/Auth/OAuth2/Auth0.hs @@ -1,10 +1,10 @@ {-# LANGUAGE OverloadedStrings #-} + -- | -- OAuth2 plugin for -- -- * Authenticates against specific auth0 tenant -- * Uses Auth0 user id (a.k.a [sub](https://auth0.com/docs/api/authentication#get-user-info)) as credentials identifier --- module Yesod.Auth.OAuth2.Auth0 ( oauth2Auth0HostScopes , oauth2Auth0Host @@ -13,8 +13,8 @@ module Yesod.Auth.OAuth2.Auth0 import Data.Aeson as Aeson import qualified Data.Text as T -import Prelude import Yesod.Auth.OAuth2.Prelude +import Prelude -- | https://auth0.com/docs/api/authentication#get-user-info newtype User = User T.Text @@ -36,22 +36,25 @@ oauth2Auth0HostScopes :: YesodAuth m => URI -> [Text] -> Text -> Text -> AuthPlugin m oauth2Auth0HostScopes host scopes clientId clientSecret = authOAuth2 pluginName oauth2 $ \manager token -> do - (User uid, userResponse) <- authGetProfile - pluginName - manager - token - (host `withPath` "/userinfo") - pure Creds - { credsPlugin = pluginName - , credsIdent = uid - , credsExtra = setExtra token userResponse - } + (User uid, userResponse) <- + authGetProfile + pluginName + manager + token + (host `withPath` "/userinfo") + pure + Creds + { credsPlugin = pluginName + , credsIdent = uid + , credsExtra = setExtra token userResponse + } where - oauth2 = OAuth2 - { oauth2ClientId = clientId - , oauth2ClientSecret = Just clientSecret - , oauth2AuthorizeEndpoint = - host `withPath` "/authorize" `withQuery` [scopeParam " " scopes] - , oauth2TokenEndpoint = host `withPath` "/oauth/token" - , oauth2RedirectUri = Nothing - } + oauth2 = + OAuth2 + { oauth2ClientId = clientId + , oauth2ClientSecret = Just clientSecret + , oauth2AuthorizeEndpoint = + host `withPath` "/authorize" `withQuery` [scopeParam " " scopes] + , oauth2TokenEndpoint = host `withPath` "/oauth/token" + , oauth2RedirectUri = Nothing + } diff --git a/src/Yesod/Auth/OAuth2/AzureAD.hs b/src/Yesod/Auth/OAuth2/AzureAD.hs index 6538646..f8ca8f5 100644 --- a/src/Yesod/Auth/OAuth2/AzureAD.hs +++ b/src/Yesod/Auth/OAuth2/AzureAD.hs @@ -1,18 +1,18 @@ {-# LANGUAGE OverloadedStrings #-} + -- | -- -- OAuth2 plugin for Azure AD. -- -- * Authenticates against Azure AD -- * Uses email as credentials identifier --- module Yesod.Auth.OAuth2.AzureAD ( oauth2AzureAD , oauth2AzureADScoped ) where -import Prelude import Yesod.Auth.OAuth2.Prelude +import Prelude newtype User = User Text @@ -31,26 +31,29 @@ oauth2AzureAD = oauth2AzureADScoped defaultScopes oauth2AzureADScoped :: YesodAuth m => [Text] -> Text -> Text -> AuthPlugin m oauth2AzureADScoped scopes clientId clientSecret = authOAuth2 pluginName oauth2 $ \manager token -> do - (User userId, userResponse) <- authGetProfile - pluginName - manager - token - "https://graph.microsoft.com/v1.0/me" + (User userId, userResponse) <- + authGetProfile + pluginName + manager + token + "https://graph.microsoft.com/v1.0/me" - pure Creds - { credsPlugin = pluginName - , credsIdent = userId - , credsExtra = setExtra token userResponse - } + pure + Creds + { credsPlugin = pluginName + , credsIdent = userId + , credsExtra = setExtra token userResponse + } where - oauth2 = OAuth2 - { oauth2ClientId = clientId - , oauth2ClientSecret = Just clientSecret - , oauth2AuthorizeEndpoint = - "https://login.windows.net/common/oauth2/authorize" - `withQuery` [ scopeParam "," scopes - , ("resource", "https://graph.microsoft.com") - ] - , oauth2TokenEndpoint = "https://login.windows.net/common/oauth2/token" - , oauth2RedirectUri = Nothing - } + oauth2 = + OAuth2 + { oauth2ClientId = clientId + , oauth2ClientSecret = Just clientSecret + , oauth2AuthorizeEndpoint = + "https://login.windows.net/common/oauth2/authorize" + `withQuery` [ scopeParam "," scopes + , ("resource", "https://graph.microsoft.com") + ] + , oauth2TokenEndpoint = "https://login.windows.net/common/oauth2/token" + , oauth2RedirectUri = Nothing + } diff --git a/src/Yesod/Auth/OAuth2/AzureADv2.hs b/src/Yesod/Auth/OAuth2/AzureADv2.hs index 96d9cde..bb0d556 100644 --- a/src/Yesod/Auth/OAuth2/AzureADv2.hs +++ b/src/Yesod/Auth/OAuth2/AzureADv2.hs @@ -1,18 +1,18 @@ {-# LANGUAGE OverloadedStrings #-} + -- | -- -- OAuth2 plugin for Azure AD using the new v2 endpoints. -- -- * Authenticates against Azure AD -- * Uses email as credentials identifier --- module Yesod.Auth.OAuth2.AzureADv2 ( oauth2AzureADv2 , oauth2AzureADv2Scoped ) where -import Prelude import Yesod.Auth.OAuth2.Prelude +import Prelude import Data.String import Data.Text (unpack) @@ -34,49 +34,55 @@ oauth2AzureADv2 -- ^ Tenant Id -- -- If using a multi-tenant App, @common@ can be given here. - -- - -> Text -- ^ Client Id - -> Text -- ^ Client secret + -> Text + -- ^ Client Id + -> Text + -- ^ Client secret -> AuthPlugin m oauth2AzureADv2 = oauth2AzureADv2Scoped defaultScopes oauth2AzureADv2Scoped :: YesodAuth m - => [Text] -- ^ Scopes + => [Text] + -- ^ Scopes -> Text -- ^ Tenant Id -- -- If using a multi-tenant App, @common@ can be given here. - -- - -> Text -- ^ Client Id - -> Text -- ^ Client Secret + -> Text + -- ^ Client Id + -> Text + -- ^ Client Secret -> AuthPlugin m oauth2AzureADv2Scoped scopes tenantId clientId clientSecret = authOAuth2 pluginName oauth2 $ \manager token -> do - (User userId, userResponse) <- authGetProfile - pluginName - manager - token - "https://graph.microsoft.com/v1.0/me" + (User userId, userResponse) <- + authGetProfile + pluginName + manager + token + "https://graph.microsoft.com/v1.0/me" - pure Creds - { credsPlugin = pluginName - , credsIdent = userId - , credsExtra = setExtra token userResponse - } + pure + Creds + { credsPlugin = pluginName + , credsIdent = userId + , credsExtra = setExtra token userResponse + } where - oauth2 = OAuth2 - { oauth2ClientId = clientId - , oauth2ClientSecret = Just clientSecret - , oauth2AuthorizeEndpoint = - tenantUrl "/authorize" `withQuery` [scopeParam " " scopes] - , oauth2TokenEndpoint = tenantUrl "/token" - , oauth2RedirectUri = Nothing - } + oauth2 = + OAuth2 + { oauth2ClientId = clientId + , oauth2ClientSecret = Just clientSecret + , oauth2AuthorizeEndpoint = + tenantUrl "/authorize" `withQuery` [scopeParam " " scopes] + , oauth2TokenEndpoint = tenantUrl "/token" + , oauth2RedirectUri = Nothing + } tenantUrl path = - fromString - $ "https://login.microsoftonline.com/" - <> unpack tenantId - <> "/oauth2/v2.0" - <> path + fromString $ + "https://login.microsoftonline.com/" + <> unpack tenantId + <> "/oauth2/v2.0" + <> path diff --git a/src/Yesod/Auth/OAuth2/BattleNet.hs b/src/Yesod/Auth/OAuth2/BattleNet.hs index f84ba2f..4529299 100644 --- a/src/Yesod/Auth/OAuth2/BattleNet.hs +++ b/src/Yesod/Auth/OAuth2/BattleNet.hs @@ -7,7 +7,6 @@ -- * Authenticates against battle.net. -- * Uses user's id as credentials identifier. -- * Returns user's battletag in extras. --- module Yesod.Auth.OAuth2.BattleNet ( oauth2BattleNet , oAuth2BattleNet @@ -28,32 +27,37 @@ pluginName = "battle.net" oauth2BattleNet :: YesodAuth m - => WidgetFor m () -- ^ Login widget - -> Text -- ^ User region (e.g. "eu", "cn", "us") - -> Text -- ^ Client ID - -> Text -- ^ Client Secret + => WidgetFor m () + -- ^ Login widget + -> Text + -- ^ User region (e.g. "eu", "cn", "us") + -> Text + -- ^ Client ID + -> Text + -- ^ Client Secret -> AuthPlugin m oauth2BattleNet widget region clientId clientSecret = authOAuth2Widget widget pluginName oauth2 $ \manager token -> do (User userId, userResponse) <- - authGetProfile pluginName manager token - $ fromRelative "https" (apiHost $ T.toLower region) "/account/user" + authGetProfile pluginName manager token $ + fromRelative "https" (apiHost $ T.toLower region) "/account/user" - pure Creds - { credsPlugin = pluginName - , credsIdent = T.pack $ show userId - , credsExtra = setExtra token userResponse - } + pure + Creds + { credsPlugin = pluginName + , credsIdent = T.pack $ show userId + , credsExtra = setExtra token userResponse + } where host = wwwHost $ T.toLower region - oauth2 = OAuth2 - { oauth2ClientId = clientId - , oauth2ClientSecret = Just clientSecret - , oauth2AuthorizeEndpoint = fromRelative "https" host "/oauth/authorize" - , oauth2TokenEndpoint = fromRelative "https" host "/oauth/token" - , oauth2RedirectUri = Nothing - } - + oauth2 = + OAuth2 + { oauth2ClientId = clientId + , oauth2ClientSecret = Just clientSecret + , oauth2AuthorizeEndpoint = fromRelative "https" host "/oauth/authorize" + , oauth2TokenEndpoint = fromRelative "https" host "/oauth/token" + , oauth2RedirectUri = Nothing + } apiHost :: Text -> Host apiHost "cn" = "api.battlenet.com.cn" diff --git a/src/Yesod/Auth/OAuth2/Bitbucket.hs b/src/Yesod/Auth/OAuth2/Bitbucket.hs index 0c53819..1ce5f55 100644 --- a/src/Yesod/Auth/OAuth2/Bitbucket.hs +++ b/src/Yesod/Auth/OAuth2/Bitbucket.hs @@ -1,11 +1,11 @@ {-# LANGUAGE OverloadedStrings #-} + -- | -- -- OAuth2 plugin for http://bitbucket.com -- -- * Authenticates against bitbucket -- * Uses bitbucket uuid as credentials identifier --- module Yesod.Auth.OAuth2.Bitbucket ( oauth2Bitbucket , oauth2BitbucketScoped @@ -32,30 +32,33 @@ oauth2Bitbucket = oauth2BitbucketScoped defaultScopes oauth2BitbucketScoped :: YesodAuth m => [Text] -> Text -> Text -> AuthPlugin m oauth2BitbucketScoped scopes clientId clientSecret = authOAuth2 pluginName oauth2 $ \manager token -> do - (User userId, userResponse) <- authGetProfile - pluginName - manager - token - "https://api.bitbucket.com/2.0/user" + (User userId, userResponse) <- + authGetProfile + pluginName + manager + token + "https://api.bitbucket.com/2.0/user" - pure Creds - { credsPlugin = pluginName - -- FIXME: Preserved bug. This should just be userId (it's already - -- a Text), but because this code was shipped, folks likely have - -- Idents in their database like @"\"...\""@, and if we fixed this - -- they would need migrating. We're keeping it for now as it's a - -- minor wart. Breaking typed APIs is one thing, causing data to go - -- invalid is another. - , credsIdent = T.pack $ show userId - , credsExtra = setExtra token userResponse - } + pure + Creds + { credsPlugin = pluginName + , -- FIXME: Preserved bug. This should just be userId (it's already + -- a Text), but because this code was shipped, folks likely have + -- Idents in their database like @"\"...\""@, and if we fixed this + -- they would need migrating. We're keeping it for now as it's a + -- minor wart. Breaking typed APIs is one thing, causing data to go + -- invalid is another. + credsIdent = T.pack $ show userId + , credsExtra = setExtra token userResponse + } where - oauth2 = OAuth2 - { oauth2ClientId = clientId - , oauth2ClientSecret = Just clientSecret - , oauth2AuthorizeEndpoint = - "https://bitbucket.com/site/oauth2/authorize" - `withQuery` [scopeParam "," scopes] - , oauth2TokenEndpoint = "https://bitbucket.com/site/oauth2/access_token" - , oauth2RedirectUri = Nothing - } + oauth2 = + OAuth2 + { oauth2ClientId = clientId + , oauth2ClientSecret = Just clientSecret + , oauth2AuthorizeEndpoint = + "https://bitbucket.com/site/oauth2/authorize" + `withQuery` [scopeParam "," scopes] + , oauth2TokenEndpoint = "https://bitbucket.com/site/oauth2/access_token" + , oauth2RedirectUri = Nothing + } diff --git a/src/Yesod/Auth/OAuth2/ClassLink.hs b/src/Yesod/Auth/OAuth2/ClassLink.hs index 0cc6146..62f6294 100644 --- a/src/Yesod/Auth/OAuth2/ClassLink.hs +++ b/src/Yesod/Auth/OAuth2/ClassLink.hs @@ -26,24 +26,27 @@ oauth2ClassLink = oauth2ClassLinkScoped defaultScopes oauth2ClassLinkScoped :: YesodAuth m => [Text] -> Text -> Text -> AuthPlugin m oauth2ClassLinkScoped scopes clientId clientSecret = authOAuth2 pluginName oauth2 $ \manager token -> do - (User userId, userResponse) <- authGetProfile - pluginName - manager - token - "https://nodeapi.classlink.com/v2/my/info" + (User userId, userResponse) <- + authGetProfile + pluginName + manager + token + "https://nodeapi.classlink.com/v2/my/info" - pure Creds - { credsPlugin = pluginName - , credsIdent = T.pack $ show userId - , credsExtra = setExtra token userResponse - } + pure + Creds + { credsPlugin = pluginName + , credsIdent = T.pack $ show userId + , credsExtra = setExtra token userResponse + } where - oauth2 = OAuth2 - { oauth2ClientId = clientId - , oauth2ClientSecret = Just clientSecret - , oauth2AuthorizeEndpoint = - "https://launchpad.classlink.com/oauth2/v2/auth" - `withQuery` [scopeParam "," scopes] - , oauth2TokenEndpoint = "https://launchpad.classlink.com/oauth2/v2/token" - , oauth2RedirectUri = Nothing - } + oauth2 = + OAuth2 + { oauth2ClientId = clientId + , oauth2ClientSecret = Just clientSecret + , oauth2AuthorizeEndpoint = + "https://launchpad.classlink.com/oauth2/v2/auth" + `withQuery` [scopeParam "," scopes] + , oauth2TokenEndpoint = "https://launchpad.classlink.com/oauth2/v2/token" + , oauth2RedirectUri = Nothing + } diff --git a/src/Yesod/Auth/OAuth2/Dispatch.hs b/src/Yesod/Auth/OAuth2/Dispatch.hs index 244e535..e03865f 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 @@ -29,21 +29,26 @@ import Yesod.Core hiding (ErrorResponse) -- | How to fetch an @'OAuth2Token'@ -- -- This will be 'fetchAccessToken' or 'fetchAccessToken2' --- -type FetchToken - = Manager -> OAuth2 -> ExchangeToken -> IO (OAuth2Result Errors OAuth2Token) +type FetchToken = + Manager -> OAuth2 -> ExchangeToken -> IO (OAuth2Result Errors OAuth2Token) -- | How to take an @'OAuth2Token'@ and retrieve user credentials type FetchCreds m = Manager -> OAuth2Token -> IO (Creds m) -- | Dispatch the various OAuth2 handshake routes dispatchAuthRequest - :: Text -- ^ Name - -> OAuth2 -- ^ Service details - -> FetchToken -- ^ How to get a token - -> FetchCreds m -- ^ How to get credentials - -> Text -- ^ Method - -> [Text] -- ^ Path pieces + :: Text + -- ^ Name + -> OAuth2 + -- ^ Service details + -> FetchToken + -- ^ How to get a token + -> FetchCreds m + -- ^ How to get credentials + -> Text + -- ^ Method + -> [Text] + -- ^ Path pieces -> AuthHandler m TypedContent dispatchAuthRequest name oauth2 _ _ "GET" ["forward"] = handleDispatchError $ dispatchForward name oauth2 @@ -55,7 +60,6 @@ dispatchAuthRequest _ _ _ _ _ _ = notFound -- -- 1. Set a random CSRF token in our session -- 2. Redirect to the Provider's authorization URL --- dispatchForward :: (MonadError DispatchError m, MonadAuthHandler site m) => Text @@ -71,7 +75,6 @@ dispatchForward name oauth2 = do -- 1. Verify the URL's CSRF token matches our session -- 2. Use the code parameter to fetch an AccessToken for the Provider -- 3. Use the AccessToken to construct a @'Creds'@ value for the Provider --- dispatchCallback :: (MonadError DispatchError m, MonadAuthHandler site m) => Text @@ -85,12 +88,13 @@ dispatchCallback name oauth2 getToken getCreds = do code <- requireGetParam "code" manager <- authHttpManager oauth2' <- withCallbackAndState name oauth2 csrf - token <- either (throwError . OAuth2ResultError) pure - =<< liftIO (getToken manager oauth2' $ ExchangeToken code) + token <- + either (throwError . OAuth2ResultError) pure + =<< liftIO (getToken manager oauth2' $ ExchangeToken code) creds <- liftIO (getCreds manager token) - `catch` (throwError . FetchCredsIOException) - `catch` (throwError . FetchCredsYesodOAuth2Exception) + `catch` (throwError . FetchCredsIOException) + `catch` (throwError . FetchCredsYesodOAuth2Exception) setCredsRedirect creds withCallbackAndState @@ -102,11 +106,12 @@ withCallbackAndState withCallbackAndState name oauth2 csrf = do uri <- ($ PluginR name ["callback"]) <$> getParentUrlRender callback <- maybe (throwError $ InvalidCallbackUri uri) pure $ fromText uri - pure oauth2 - { oauth2RedirectUri = Just callback - , oauth2AuthorizeEndpoint = - oauth2AuthorizeEndpoint oauth2 `withQuery` [("state", encodeUtf8 csrf)] - } + pure + oauth2 + { oauth2RedirectUri = Just callback + , oauth2AuthorizeEndpoint = + oauth2AuthorizeEndpoint oauth2 `withQuery` [("state", encodeUtf8 csrf)] + } getParentUrlRender :: MonadHandler m => m (Route (SubHandlerSite m) -> Text) getParentUrlRender = (.) <$> getUrlRender <*> getRouteToParent @@ -119,12 +124,12 @@ getParentUrlRender = (.) <$> getUrlRender <*> getRouteToParent -- -- Therefore, we just exclude @+@ in our tokens, which means this function may -- return slightly less than 30 characters. --- setSessionCSRF :: MonadHandler m => Text -> m Text setSessionCSRF sessionKey = do csrfToken <- liftIO randomToken csrfToken <$ setSession sessionKey csrfToken - where randomToken = T.filter (/= '+') <$> randomText 64 + where + randomToken = T.filter (/= '+') <$> randomText 64 -- | Verify the callback provided the same CSRF token as in our session verifySessionCSRF @@ -133,9 +138,10 @@ verifySessionCSRF sessionKey = do token <- requireGetParam "state" sessionToken <- lookupSession sessionKey deleteSession sessionKey - token <$ unless - (sessionToken == Just token) - (throwError $ InvalidStateToken sessionToken token) + token + <$ unless + (sessionToken == Just token) + (throwError $ InvalidStateToken sessionToken token) requireGetParam :: (MonadError DispatchError m, MonadHandler m) => Text -> m Text diff --git a/src/Yesod/Auth/OAuth2/DispatchError.hs b/src/Yesod/Auth/OAuth2/DispatchError.hs index 1dcf920..4f8f732 100644 --- a/src/Yesod/Auth/OAuth2/DispatchError.hs +++ b/src/Yesod/Auth/OAuth2/DispatchError.hs @@ -9,7 +9,7 @@ {-# LANGUAGE TypeFamilies #-} module Yesod.Auth.OAuth2.DispatchError - ( DispatchError(..) + ( DispatchError (..) , handleDispatchError , onDispatchError ) where @@ -26,34 +26,33 @@ import Yesod.Auth.OAuth2.Random import Yesod.Core hiding (ErrorResponse) data DispatchError - = MissingParameter Text - | InvalidStateToken (Maybe Text) Text - | InvalidCallbackUri Text - | OAuth2HandshakeError ErrorResponse - | OAuth2ResultError Errors - | FetchCredsIOException IOException - | FetchCredsYesodOAuth2Exception YesodOAuth2Exception - | OtherDispatchError Text - deriving stock Show - deriving anyclass Exception + = MissingParameter Text + | InvalidStateToken (Maybe Text) Text + | InvalidCallbackUri Text + | OAuth2HandshakeError ErrorResponse + | OAuth2ResultError Errors + | FetchCredsIOException IOException + | FetchCredsYesodOAuth2Exception YesodOAuth2Exception + | OtherDispatchError Text + deriving stock (Show) + deriving anyclass (Exception) -- | User-friendly message for any given 'DispatchError' -- -- Most of these are opaque to the user. The exception details are present for -- the server logs. --- dispatchErrorMessage :: DispatchError -> Text dispatchErrorMessage = \case MissingParameter name -> "Parameter '" <> name <> "' is required, but not present in the URL" - InvalidStateToken{} -> "State token is invalid, please try again" - InvalidCallbackUri{} -> + InvalidStateToken {} -> "State token is invalid, please try again" + InvalidCallbackUri {} -> "Callback URI was not valid, this server may be misconfigured (no approot)" OAuth2HandshakeError er -> "OAuth2 handshake failure: " <> erUserMessage er - OAuth2ResultError{} -> "Login failed, please try again" - FetchCredsIOException{} -> "Login failed, please try again" - FetchCredsYesodOAuth2Exception{} -> "Login failed, please try again" - OtherDispatchError{} -> "Login failed, please try again" + OAuth2ResultError {} -> "Login failed, please try again" + FetchCredsIOException {} -> "Login failed, please try again" + FetchCredsYesodOAuth2Exception {} -> "Login failed, please try again" + OtherDispatchError {} -> "Login failed, please try again" handleDispatchError :: MonadAuthHandler site m diff --git a/src/Yesod/Auth/OAuth2/ErrorResponse.hs b/src/Yesod/Auth/OAuth2/ErrorResponse.hs index 3692d42..962fd0f 100644 --- a/src/Yesod/Auth/OAuth2/ErrorResponse.hs +++ b/src/Yesod/Auth/OAuth2/ErrorResponse.hs @@ -1,12 +1,12 @@ {-# LANGUAGE OverloadedStrings #-} + -- | OAuth callback error response -- -- --- module Yesod.Auth.OAuth2.ErrorResponse - ( ErrorResponse(..) + ( ErrorResponse (..) , erUserMessage - , ErrorName(..) + , ErrorName (..) , onErrorResponse , unknownError ) where @@ -17,22 +17,22 @@ import Data.Traversable (for) import Yesod.Core (MonadHandler, lookupGetParam) data ErrorName - = InvalidRequest - | UnauthorizedClient - | AccessDenied - | UnsupportedResponseType - | InvalidScope - | ServerError - | TemporarilyUnavailable - | Unknown Text - deriving Show + = InvalidRequest + | UnauthorizedClient + | AccessDenied + | UnsupportedResponseType + | InvalidScope + | ServerError + | TemporarilyUnavailable + | Unknown Text + deriving (Show) data ErrorResponse = ErrorResponse { erName :: ErrorName , erDescription :: Maybe Text , erURI :: Maybe Text } - deriving Show + deriving (Show) -- | Textual value suitable for display to a User erUserMessage :: ErrorResponse -> Text @@ -48,13 +48,12 @@ erUserMessage err = case erName err of unknownError :: Text -> ErrorResponse unknownError x = - ErrorResponse { erName = Unknown x, erDescription = Nothing, erURI = Nothing } + ErrorResponse {erName = Unknown x, erDescription = Nothing, erURI = Nothing} -- | Check query parameters for an error, if found run the given action -- -- The action is expected to use a short-circuit response function like -- @'permissionDenied'@, hence this returning @()@. --- onErrorResponse :: MonadHandler m => (ErrorResponse -> m a) -> m () onErrorResponse f = traverse_ f =<< checkErrorResponse diff --git a/src/Yesod/Auth/OAuth2/EveOnline.hs b/src/Yesod/Auth/OAuth2/EveOnline.hs index b31f948..fa330f9 100644 --- a/src/Yesod/Auth/OAuth2/EveOnline.hs +++ b/src/Yesod/Auth/OAuth2/EveOnline.hs @@ -1,16 +1,16 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} + -- | -- -- OAuth2 plugin for http://eveonline.com -- -- * Authenticates against eveonline -- * Uses EVEs unique account-user-char-hash as credentials identifier --- module Yesod.Auth.OAuth2.EveOnline ( oauth2Eve , oauth2EveScoped - , WidgetType(..) + , WidgetType (..) ) where import Yesod.Auth.OAuth2.Prelude @@ -24,23 +24,24 @@ instance FromJSON User where parseJSON = withObject "User" $ \o -> User <$> o .: "CharacterOwnerHash" data WidgetType m - = Plain -- ^ Simple "Login via eveonline" text - | BigWhite - | SmallWhite - | BigBlack - | SmallBlack - | Custom (WidgetFor m ()) + = -- | Simple "Login via eveonline" text + Plain + | BigWhite + | SmallWhite + | BigBlack + | SmallBlack + | Custom (WidgetFor m ()) asWidget :: YesodAuth m => WidgetType m -> WidgetFor m () asWidget Plain = [whamlet|Login via eveonline|] asWidget BigWhite = [whamlet||] -asWidget BigBlack - = [whamlet||] -asWidget SmallWhite - = [whamlet||] -asWidget SmallBlack - = [whamlet||] +asWidget BigBlack = + [whamlet||] +asWidget SmallWhite = + [whamlet||] +asWidget SmallBlack = + [whamlet||] asWidget (Custom a) = a pluginName :: Text @@ -57,25 +58,28 @@ oauth2EveScoped oauth2EveScoped scopes widgetType clientId clientSecret = authOAuth2Widget (asWidget widgetType) pluginName oauth2 $ \manager token -> do - (User userId, userResponse) <- authGetProfile - pluginName - manager - token - "https://login.eveonline.com/oauth/verify" + (User userId, userResponse) <- + authGetProfile + pluginName + manager + token + "https://login.eveonline.com/oauth/verify" - pure Creds - { credsPlugin = "eveonline" - -- FIXME: Preserved bug. See similar comment in Bitbucket provider. - , credsIdent = T.pack $ show userId - , credsExtra = setExtra token userResponse - } + pure + Creds + { credsPlugin = "eveonline" + , -- FIXME: Preserved bug. See similar comment in Bitbucket provider. + credsIdent = T.pack $ show userId + , credsExtra = setExtra token userResponse + } where - oauth2 = OAuth2 - { oauth2ClientId = clientId - , oauth2ClientSecret = Just clientSecret - , oauth2AuthorizeEndpoint = - "https://login.eveonline.com/oauth/authorize" - `withQuery` [("response_type", "code"), scopeParam " " scopes] - , oauth2TokenEndpoint = "https://login.eveonline.com/oauth/token" - , oauth2RedirectUri = Nothing - } + oauth2 = + OAuth2 + { oauth2ClientId = clientId + , oauth2ClientSecret = Just clientSecret + , oauth2AuthorizeEndpoint = + "https://login.eveonline.com/oauth/authorize" + `withQuery` [("response_type", "code"), scopeParam " " scopes] + , oauth2TokenEndpoint = "https://login.eveonline.com/oauth/token" + , oauth2RedirectUri = Nothing + } diff --git a/src/Yesod/Auth/OAuth2/Exception.hs b/src/Yesod/Auth/OAuth2/Exception.hs index 22b169a..5b05512 100644 --- a/src/Yesod/Auth/OAuth2/Exception.hs +++ b/src/Yesod/Auth/OAuth2/Exception.hs @@ -1,7 +1,7 @@ {-# LANGUAGE DeriveDataTypeable #-} module Yesod.Auth.OAuth2.Exception - ( YesodOAuth2Exception(..) + ( YesodOAuth2Exception (..) ) where import Control.Exception.Safe @@ -9,21 +9,18 @@ import Data.ByteString.Lazy (ByteString) import Data.Text (Text) data YesodOAuth2Exception - = OAuth2Error Text ByteString - -- ^ HTTP error during OAuth2 handshake + = -- | HTTP error during OAuth2 handshake -- -- Plugin name and JSON-encoded @OAuth2Error@ from @hoauth2@. - -- - | JSONDecodingError Text String - -- ^ User profile was not as expected + OAuth2Error Text ByteString + | -- | User profile was not as expected -- -- Plugin name and Aeson parse error message. - -- - | GenericError Text String - -- ^ Other error conditions + JSONDecodingError Text String + | -- | Other error conditions -- -- Plugin name and error message. - -- - deriving (Show, Typeable) + GenericError Text String + deriving (Show, Typeable) instance Exception YesodOAuth2Exception diff --git a/src/Yesod/Auth/OAuth2/GitHub.hs b/src/Yesod/Auth/OAuth2/GitHub.hs index 753ba82..9725703 100644 --- a/src/Yesod/Auth/OAuth2/GitHub.hs +++ b/src/Yesod/Auth/OAuth2/GitHub.hs @@ -1,11 +1,11 @@ {-# LANGUAGE OverloadedStrings #-} + -- | -- -- OAuth2 plugin for http://github.com -- -- * Authenticates against github -- * Uses github user id as credentials identifier --- module Yesod.Auth.OAuth2.GitHub ( oauth2GitHub , oauth2GitHubScoped @@ -32,24 +32,27 @@ oauth2GitHub = oauth2GitHubScoped defaultScopes oauth2GitHubScoped :: YesodAuth m => [Text] -> Text -> Text -> AuthPlugin m oauth2GitHubScoped scopes clientId clientSecret = authOAuth2 pluginName oauth2 $ \manager token -> do - (User userId, userResponse) <- authGetProfile - pluginName - manager - token - "https://api.github.com/user" + (User userId, userResponse) <- + authGetProfile + pluginName + manager + token + "https://api.github.com/user" - pure Creds - { credsPlugin = pluginName - , credsIdent = T.pack $ show userId - , credsExtra = setExtra token userResponse - } + pure + Creds + { credsPlugin = pluginName + , credsIdent = T.pack $ show userId + , credsExtra = setExtra token userResponse + } where - oauth2 = OAuth2 - { oauth2ClientId = clientId - , oauth2ClientSecret = Just clientSecret - , oauth2AuthorizeEndpoint = - "https://github.com/login/oauth/authorize" - `withQuery` [scopeParam "," scopes] - , oauth2TokenEndpoint = "https://github.com/login/oauth/access_token" - , oauth2RedirectUri = Nothing - } + oauth2 = + OAuth2 + { oauth2ClientId = clientId + , oauth2ClientSecret = Just clientSecret + , oauth2AuthorizeEndpoint = + "https://github.com/login/oauth/authorize" + `withQuery` [scopeParam "," scopes] + , oauth2TokenEndpoint = "https://github.com/login/oauth/access_token" + , oauth2RedirectUri = Nothing + } diff --git a/src/Yesod/Auth/OAuth2/GitLab.hs b/src/Yesod/Auth/OAuth2/GitLab.hs index 72d0bbd..e5a05fc 100644 --- a/src/Yesod/Auth/OAuth2/GitLab.hs +++ b/src/Yesod/Auth/OAuth2/GitLab.hs @@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-} + module Yesod.Auth.OAuth2.GitLab ( oauth2GitLab , oauth2GitLabHostScopes @@ -32,7 +33,6 @@ defaultScopes = ["read_user"] -- -- > oauth2GitLabHostScopes defaultHost ["api", "read_user"] -- > oauth2GitLabHostScopes "https://gitlab.example.com" defaultScopes --- oauth2GitLab :: YesodAuth m => Text -> Text -> AuthPlugin m oauth2GitLab = oauth2GitLabHostScopes defaultHost defaultScopes @@ -43,17 +43,19 @@ oauth2GitLabHostScopes host scopes clientId clientSecret = (User userId, userResponse) <- authGetProfile pluginName manager token $ host `withPath` "/api/v4/user" - pure Creds - { credsPlugin = pluginName - , credsIdent = T.pack $ show userId - , credsExtra = setExtra token userResponse - } + pure + Creds + { credsPlugin = pluginName + , credsIdent = T.pack $ show userId + , credsExtra = setExtra token userResponse + } where - oauth2 = OAuth2 - { oauth2ClientId = clientId - , oauth2ClientSecret = Just clientSecret - , oauth2AuthorizeEndpoint = - host `withPath` "/oauth/authorize" `withQuery` [scopeParam " " scopes] - , oauth2TokenEndpoint = host `withPath` "/oauth/token" - , oauth2RedirectUri = Nothing - } + oauth2 = + OAuth2 + { oauth2ClientId = clientId + , oauth2ClientSecret = Just clientSecret + , oauth2AuthorizeEndpoint = + host `withPath` "/oauth/authorize" `withQuery` [scopeParam " " scopes] + , oauth2TokenEndpoint = host `withPath` "/oauth/token" + , oauth2RedirectUri = Nothing + } diff --git a/src/Yesod/Auth/OAuth2/Google.hs b/src/Yesod/Auth/OAuth2/Google.hs index 2e293ae..77e7726 100644 --- a/src/Yesod/Auth/OAuth2/Google.hs +++ b/src/Yesod/Auth/OAuth2/Google.hs @@ -1,5 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} + -- | -- -- OAuth2 plugin for http://www.google.com @@ -23,7 +24,6 @@ -- > updatedCreds = creds { credsIdent = email } -- > -- > -- continue normally with updatedCreds --- module Yesod.Auth.OAuth2.Google ( oauth2Google , oauth2GoogleWidget @@ -38,9 +38,10 @@ newtype User = User Text instance FromJSON User where parseJSON = - withObject "User" $ \o -> User - -- Required for data backwards-compatibility - <$> (("google-uid:" <>) <$> o .: "sub") + withObject "User" $ \o -> + User + -- Required for data backwards-compatibility + <$> (("google-uid:" <>) <$> o .: "sub") pluginName :: Text pluginName = "google" @@ -63,24 +64,27 @@ oauth2GoogleScopedWidget :: YesodAuth m => WidgetFor m () -> [Text] -> Text -> Text -> AuthPlugin m oauth2GoogleScopedWidget widget scopes clientId clientSecret = authOAuth2Widget widget pluginName oauth2 $ \manager token -> do - (User userId, userResponse) <- authGetProfile - pluginName - manager - token - "https://www.googleapis.com/oauth2/v3/userinfo" + (User userId, userResponse) <- + authGetProfile + pluginName + manager + token + "https://www.googleapis.com/oauth2/v3/userinfo" - pure Creds - { credsPlugin = pluginName - , credsIdent = userId - , credsExtra = setExtra token userResponse - } + pure + Creds + { credsPlugin = pluginName + , credsIdent = userId + , credsExtra = setExtra token userResponse + } where - oauth2 = OAuth2 - { oauth2ClientId = clientId - , oauth2ClientSecret = Just clientSecret - , oauth2AuthorizeEndpoint = - "https://accounts.google.com/o/oauth2/auth" - `withQuery` [scopeParam " " scopes] - , oauth2TokenEndpoint = "https://www.googleapis.com/oauth2/v3/token" - , oauth2RedirectUri = Nothing - } + oauth2 = + OAuth2 + { oauth2ClientId = clientId + , oauth2ClientSecret = Just clientSecret + , oauth2AuthorizeEndpoint = + "https://accounts.google.com/o/oauth2/auth" + `withQuery` [scopeParam " " scopes] + , oauth2TokenEndpoint = "https://www.googleapis.com/oauth2/v3/token" + , oauth2RedirectUri = Nothing + } diff --git a/src/Yesod/Auth/OAuth2/Nylas.hs b/src/Yesod/Auth/OAuth2/Nylas.hs index 99b5dce..fa043d5 100644 --- a/src/Yesod/Auth/OAuth2/Nylas.hs +++ b/src/Yesod/Auth/OAuth2/Nylas.hs @@ -26,41 +26,45 @@ defaultScopes = ["email"] oauth2Nylas :: YesodAuth m => Text -> Text -> AuthPlugin m oauth2Nylas clientId clientSecret = authOAuth2 pluginName oauth $ \manager token -> do - req <- applyBasicAuth (encodeUtf8 $ atoken $ accessToken token) "" - <$> parseRequest "https://api.nylas.com/account" + req <- + applyBasicAuth (encodeUtf8 $ atoken $ accessToken token) "" + <$> parseRequest "https://api.nylas.com/account" resp <- httpLbs req manager let userResponse = responseBody resp -- FIXME: was this working? I'm 95% sure that the client will throw its -- own exception on unsuccessful status codes. - unless (HT.statusIsSuccessful $ responseStatus resp) - $ throwIO - $ YesodOAuth2Exception.GenericError pluginName - $ "Unsuccessful HTTP response: " - <> BL8.unpack userResponse + unless (HT.statusIsSuccessful $ responseStatus resp) $ + throwIO $ + YesodOAuth2Exception.GenericError pluginName $ + "Unsuccessful HTTP response: " + <> BL8.unpack userResponse either - (throwIO . YesodOAuth2Exception.JSONDecodingError pluginName) - (\(User userId) -> pure Creds - { credsPlugin = pluginName - , credsIdent = userId - , credsExtra = setExtra token userResponse - } - ) + (throwIO . YesodOAuth2Exception.JSONDecodingError pluginName) + ( \(User userId) -> + pure + Creds + { credsPlugin = pluginName + , credsIdent = userId + , credsExtra = setExtra token userResponse + } + ) $ eitherDecode userResponse where - oauth = OAuth2 - { oauth2ClientId = clientId - , oauth2ClientSecret = Just clientSecret - , oauth2AuthorizeEndpoint = - "https://api.nylas.com/oauth/authorize" - `withQuery` [ ("response_type", "code") - , ("client_id", encodeUtf8 clientId) - -- N.B. The scopes delimeter is unknown/untested. Verify that before - -- extracting this to an argument and offering a Scoped function. In - -- its current state, it doesn't matter because it's only one scope. - , scopeParam "," defaultScopes - ] - , oauth2TokenEndpoint = "https://api.nylas.com/oauth/token" - , oauth2RedirectUri = Nothing - } + oauth = + OAuth2 + { oauth2ClientId = clientId + , oauth2ClientSecret = Just clientSecret + , oauth2AuthorizeEndpoint = + "https://api.nylas.com/oauth/authorize" + `withQuery` [ ("response_type", "code") + , ("client_id", encodeUtf8 clientId) + , -- N.B. The scopes delimeter is unknown/untested. Verify that before + -- extracting this to an argument and offering a Scoped function. In + -- its current state, it doesn't matter because it's only one scope. + scopeParam "," defaultScopes + ] + , oauth2TokenEndpoint = "https://api.nylas.com/oauth/token" + , oauth2RedirectUri = Nothing + } diff --git a/src/Yesod/Auth/OAuth2/Prelude.hs b/src/Yesod/Auth/OAuth2/Prelude.hs index 8eba4a0..cc4d8c4 100644 --- a/src/Yesod/Auth/OAuth2/Prelude.hs +++ b/src/Yesod/Auth/OAuth2/Prelude.hs @@ -1,10 +1,10 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TupleSections #-} + -- | -- -- Modules and support functions required by most or all provider -- implementations. May also be useful for writing local providers. --- module Yesod.Auth.OAuth2.Prelude ( authGetProfile , scopeParam @@ -20,8 +20,8 @@ module Yesod.Auth.OAuth2.Prelude , (.:?) , (.=) , (<>) - , FromJSON(..) - , ToJSON(..) + , FromJSON (..) + , ToJSON (..) , eitherDecode , withObject @@ -29,22 +29,22 @@ module Yesod.Auth.OAuth2.Prelude , throwIO -- * OAuth2 - , OAuth2(..) - , OAuth2Token(..) - , AccessToken(..) - , RefreshToken(..) + , OAuth2 (..) + , OAuth2Token (..) + , AccessToken (..) + , RefreshToken (..) -- * HTTP , Manager -- * Yesod - , YesodAuth(..) - , AuthPlugin(..) - , Creds(..) + , YesodAuth (..) + , AuthPlugin (..) + , Creds (..) -- * Bytestring URI types , URI - , Host(..) + , Host (..) -- * Bytestring URI extensions , module URI.ByteString.Extension @@ -74,7 +74,6 @@ import qualified Yesod.Auth.OAuth2.Exception as YesodOAuth2Exception -- The response should be parsed only far enough to read the required -- @'credsIdent'@. Additional information should either be re-parsed by or -- fetched via additional requests by consumers. --- authGetProfile :: FromJSON a => Text @@ -101,7 +100,7 @@ fromAuthJSON name = -- | A tuple of @\"scope\"@ and the given scopes separated by a delimiter scopeParam :: Text -> [Text] -> (ByteString, ByteString) -scopeParam d = ("scope", ) . encodeUtf8 . T.intercalate d +scopeParam d = ("scope",) . encodeUtf8 . T.intercalate d -- brittany-disable-next-binding @@ -115,10 +114,9 @@ scopeParam d = ("scope", ) . encodeUtf8 . T.intercalate d -- May set the following keys: -- -- - @refreshToken@: if the provider supports refreshing the @accessToken@ --- setExtra :: OAuth2Token -> BL.ByteString -> [(Text, Text)] setExtra token userResponse = - [ ("accessToken", atoken $ accessToken token) - , ("userResponse", decodeUtf8 $ BL.toStrict userResponse) - ] - <> maybe [] (pure . ("refreshToken", ) . rtoken) (refreshToken token) + [ ("accessToken", atoken $ accessToken token) + , ("userResponse", decodeUtf8 $ BL.toStrict userResponse) + ] + <> maybe [] (pure . ("refreshToken",) . rtoken) (refreshToken token) diff --git a/src/Yesod/Auth/OAuth2/Random.hs b/src/Yesod/Auth/OAuth2/Random.hs index b69835b..8c63925 100644 --- a/src/Yesod/Auth/OAuth2/Random.hs +++ b/src/Yesod/Auth/OAuth2/Random.hs @@ -5,7 +5,7 @@ module Yesod.Auth.OAuth2.Random ) where import Crypto.Random (MonadRandom, getRandomBytes) -import Data.ByteArray.Encoding (Base(Base64), convertToBase) +import Data.ByteArray.Encoding (Base (Base64), convertToBase) import Data.ByteString (ByteString) import Data.Text (Text) import Data.Text.Encoding (decodeUtf8) @@ -13,7 +13,7 @@ import Data.Text.Encoding (decodeUtf8) randomText :: MonadRandom m => Int - -- ^ Size in Bytes (note necessarily characters) + -- ^ Size in Bytes (note necessarily characters) -> m Text randomText size = decodeUtf8 . convertToBase @ByteString Base64 <$> getRandomBytes size diff --git a/src/Yesod/Auth/OAuth2/Salesforce.hs b/src/Yesod/Auth/OAuth2/Salesforce.hs index 0fbff78..c874d84 100644 --- a/src/Yesod/Auth/OAuth2/Salesforce.hs +++ b/src/Yesod/Auth/OAuth2/Salesforce.hs @@ -1,11 +1,11 @@ {-# LANGUAGE OverloadedStrings #-} + -- | -- -- OAuth2 plugin for http://login.salesforce.com -- -- * Authenticates against Salesforce (or sandbox) -- * Uses Salesforce user id as credentials identifier --- module Yesod.Auth.OAuth2.Salesforce ( oauth2Salesforce , oauth2SalesforceScoped @@ -30,47 +30,54 @@ oauth2Salesforce :: YesodAuth m => Text -> Text -> AuthPlugin m oauth2Salesforce = oauth2SalesforceScoped defaultScopes oauth2SalesforceScoped :: YesodAuth m => [Text] -> Text -> Text -> AuthPlugin m -oauth2SalesforceScoped = salesforceHelper - pluginName - "https://login.salesforce.com/services/oauth2/userinfo" - "https://login.salesforce.com/services/oauth2/authorize" - "https://login.salesforce.com/services/oauth2/token" +oauth2SalesforceScoped = + salesforceHelper + pluginName + "https://login.salesforce.com/services/oauth2/userinfo" + "https://login.salesforce.com/services/oauth2/authorize" + "https://login.salesforce.com/services/oauth2/token" oauth2SalesforceSandbox :: YesodAuth m => Text -> Text -> AuthPlugin m oauth2SalesforceSandbox = oauth2SalesforceSandboxScoped defaultScopes oauth2SalesforceSandboxScoped :: YesodAuth m => [Text] -> Text -> Text -> AuthPlugin m -oauth2SalesforceSandboxScoped = salesforceHelper - (pluginName <> "-sandbox") - "https://test.salesforce.com/services/oauth2/userinfo" - "https://test.salesforce.com/services/oauth2/authorize" - "https://test.salesforce.com/services/oauth2/token" +oauth2SalesforceSandboxScoped = + salesforceHelper + (pluginName <> "-sandbox") + "https://test.salesforce.com/services/oauth2/userinfo" + "https://test.salesforce.com/services/oauth2/authorize" + "https://test.salesforce.com/services/oauth2/token" salesforceHelper :: YesodAuth m => Text - -> URI -- ^ User profile - -> URI -- ^ Authorize - -> URI -- ^ Token + -> URI + -- ^ User profile + -> URI + -- ^ Authorize + -> URI + -- ^ Token -> [Text] -> Text -> Text -> AuthPlugin m -salesforceHelper name profileUri authorizeUri tokenUri scopes clientId clientSecret - = authOAuth2 name oauth2 $ \manager token -> do +salesforceHelper name profileUri authorizeUri tokenUri scopes clientId clientSecret = + authOAuth2 name oauth2 $ \manager token -> do (User userId, userResponse) <- authGetProfile name manager token profileUri - pure Creds - { credsPlugin = pluginName - , credsIdent = userId - , credsExtra = setExtra token userResponse - } + pure + Creds + { credsPlugin = pluginName + , credsIdent = userId + , credsExtra = setExtra token userResponse + } where - oauth2 = OAuth2 - { oauth2ClientId = clientId - , oauth2ClientSecret = Just clientSecret - , oauth2AuthorizeEndpoint = authorizeUri `withQuery` [scopeParam " " scopes] - , oauth2TokenEndpoint = tokenUri - , oauth2RedirectUri = Nothing - } + oauth2 = + OAuth2 + { oauth2ClientId = clientId + , oauth2ClientSecret = Just clientSecret + , oauth2AuthorizeEndpoint = authorizeUri `withQuery` [scopeParam " " scopes] + , oauth2TokenEndpoint = tokenUri + , oauth2RedirectUri = Nothing + } diff --git a/src/Yesod/Auth/OAuth2/Slack.hs b/src/Yesod/Auth/OAuth2/Slack.hs index b909b5b..d416444 100644 --- a/src/Yesod/Auth/OAuth2/Slack.hs +++ b/src/Yesod/Auth/OAuth2/Slack.hs @@ -1,12 +1,12 @@ {-# LANGUAGE OverloadedStrings #-} + -- | -- OAuth2 plugin for https://slack.com/ -- -- * Authenticates against slack -- * Uses slack user id as credentials identifier --- module Yesod.Auth.OAuth2.Slack - ( SlackScope(..) + ( SlackScope (..) , oauth2Slack , oauth2SlackScoped ) where @@ -14,14 +14,18 @@ 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 - = SlackBasicScope - | SlackEmailScope - | SlackTeamScope - | SlackAvatarScope + = SlackBasicScope + | SlackEmailScope + | SlackTeamScope + | SlackAvatarScope scopeText :: SlackScope -> Text scopeText SlackBasicScope = "identity.basic" @@ -50,26 +54,30 @@ oauth2SlackScoped oauth2SlackScoped scopes clientId clientSecret = authOAuth2 pluginName oauth2 $ \manager token -> do let param = encodeUtf8 $ atoken $ accessToken token - req <- setQueryString [("token", Just param)] - <$> parseUrlThrow "https://slack.com/api/users.identity" + req <- + setQueryString [("token", Just param)] + <$> parseUrlThrow "https://slack.com/api/users.identity" userResponse <- responseBody <$> httpLbs req manager either - (throwIO . YesodOAuth2Exception.JSONDecodingError pluginName) - (\(User userId) -> pure Creds - { credsPlugin = pluginName - , credsIdent = userId - , credsExtra = setExtra token userResponse - } - ) + (throwIO . YesodOAuth2Exception.JSONDecodingError pluginName) + ( \(User userId) -> + pure + Creds + { credsPlugin = pluginName + , credsIdent = userId + , credsExtra = setExtra token userResponse + } + ) $ eitherDecode userResponse where - oauth2 = OAuth2 - { oauth2ClientId = clientId - , oauth2ClientSecret = Just clientSecret - , oauth2AuthorizeEndpoint = - "https://slack.com/oauth/authorize" - `withQuery` [scopeParam "," $ map scopeText scopes] - , oauth2TokenEndpoint = "https://slack.com/api/oauth.access" - , oauth2RedirectUri = Nothing - } + oauth2 = + OAuth2 + { oauth2ClientId = clientId + , oauth2ClientSecret = Just clientSecret + , oauth2AuthorizeEndpoint = + "https://slack.com/oauth/authorize" + `withQuery` [scopeParam "," $ map scopeText scopes] + , oauth2TokenEndpoint = "https://slack.com/api/oauth.access" + , oauth2RedirectUri = Nothing + } diff --git a/src/Yesod/Auth/OAuth2/Spotify.hs b/src/Yesod/Auth/OAuth2/Spotify.hs index 93bcc48..47c3e39 100644 --- a/src/Yesod/Auth/OAuth2/Spotify.hs +++ b/src/Yesod/Auth/OAuth2/Spotify.hs @@ -1,8 +1,8 @@ {-# LANGUAGE OverloadedStrings #-} + -- | -- -- OAuth2 plugin for http://spotify.com --- module Yesod.Auth.OAuth2.Spotify ( oauth2Spotify ) where @@ -20,24 +20,27 @@ pluginName = "spotify" oauth2Spotify :: YesodAuth m => [Text] -> Text -> Text -> AuthPlugin m oauth2Spotify scopes clientId clientSecret = authOAuth2 pluginName oauth2 $ \manager token -> do - (User userId, userResponse) <- authGetProfile - pluginName - manager - token - "https://api.spotify.com/v1/me" + (User userId, userResponse) <- + authGetProfile + pluginName + manager + token + "https://api.spotify.com/v1/me" - pure Creds - { credsPlugin = pluginName - , credsIdent = userId - , credsExtra = setExtra token userResponse - } + pure + Creds + { credsPlugin = pluginName + , credsIdent = userId + , credsExtra = setExtra token userResponse + } where - oauth2 = OAuth2 - { oauth2ClientId = clientId - , oauth2ClientSecret = Just clientSecret - , oauth2AuthorizeEndpoint = - "https://accounts.spotify.com/authorize" - `withQuery` [scopeParam " " scopes] - , oauth2TokenEndpoint = "https://accounts.spotify.com/api/token" - , oauth2RedirectUri = Nothing - } + oauth2 = + OAuth2 + { oauth2ClientId = clientId + , oauth2ClientSecret = Just clientSecret + , oauth2AuthorizeEndpoint = + "https://accounts.spotify.com/authorize" + `withQuery` [scopeParam " " scopes] + , oauth2TokenEndpoint = "https://accounts.spotify.com/api/token" + , oauth2RedirectUri = Nothing + } diff --git a/src/Yesod/Auth/OAuth2/Twitch.hs b/src/Yesod/Auth/OAuth2/Twitch.hs index cfa066f..8b6e46e 100644 --- a/src/Yesod/Auth/OAuth2/Twitch.hs +++ b/src/Yesod/Auth/OAuth2/Twitch.hs @@ -1,11 +1,11 @@ {-# LANGUAGE OverloadedStrings #-} + -- | -- -- OAuth2 plugin for http://twitch.tv -- -- * Authenticates against twitch -- * Uses twitch user id as credentials identifier --- module Yesod.Auth.OAuth2.Twitch ( oauth2Twitch , oauth2TwitchScoped @@ -32,28 +32,31 @@ oauth2Twitch = oauth2TwitchScoped defaultScopes oauth2TwitchScoped :: YesodAuth m => [Text] -> Text -> Text -> AuthPlugin m oauth2TwitchScoped scopes clientId clientSecret = authOAuth2 pluginName oauth2 $ \manager token -> do - (User userId, userResponse) <- authGetProfile - pluginName - manager - token - "https://id.twitch.tv/oauth2/validate" + (User userId, userResponse) <- + authGetProfile + pluginName + manager + token + "https://id.twitch.tv/oauth2/validate" - pure Creds - { credsPlugin = pluginName - , credsIdent = userId - , credsExtra = setExtra token userResponse - } + pure + Creds + { credsPlugin = pluginName + , credsIdent = userId + , credsExtra = setExtra token userResponse + } where - oauth2 = OAuth2 - { oauth2ClientId = clientId - , oauth2ClientSecret = Just clientSecret - , oauth2AuthorizeEndpoint = - "https://id.twitch.tv/oauth2/authorize" - `withQuery` [scopeParam " " scopes] - , oauth2TokenEndpoint = - "https://id.twitch.tv/oauth2/token" - `withQuery` [ ("client_id", T.encodeUtf8 clientId) - , ("client_secret", T.encodeUtf8 clientSecret) - ] - , oauth2RedirectUri = Nothing - } + oauth2 = + OAuth2 + { oauth2ClientId = clientId + , oauth2ClientSecret = Just clientSecret + , oauth2AuthorizeEndpoint = + "https://id.twitch.tv/oauth2/authorize" + `withQuery` [scopeParam " " scopes] + , oauth2TokenEndpoint = + "https://id.twitch.tv/oauth2/token" + `withQuery` [ ("client_id", T.encodeUtf8 clientId) + , ("client_secret", T.encodeUtf8 clientSecret) + ] + , oauth2RedirectUri = Nothing + } diff --git a/src/Yesod/Auth/OAuth2/Upcase.hs b/src/Yesod/Auth/OAuth2/Upcase.hs index 3d69474..ee903ed 100644 --- a/src/Yesod/Auth/OAuth2/Upcase.hs +++ b/src/Yesod/Auth/OAuth2/Upcase.hs @@ -1,11 +1,11 @@ {-# LANGUAGE OverloadedStrings #-} + -- | -- -- OAuth2 plugin for http://upcase.com -- -- * Authenticates against upcase -- * Uses upcase user id as credentials identifier --- module Yesod.Auth.OAuth2.Upcase ( oauth2Upcase ) where @@ -27,22 +27,25 @@ pluginName = "upcase" oauth2Upcase :: YesodAuth m => Text -> Text -> AuthPlugin m oauth2Upcase clientId clientSecret = authOAuth2 pluginName oauth2 $ \manager token -> do - (User userId, userResponse) <- authGetProfile - pluginName - manager - token - "http://upcase.com/api/v1/me.json" + (User userId, userResponse) <- + authGetProfile + pluginName + manager + token + "http://upcase.com/api/v1/me.json" - pure Creds - { credsPlugin = pluginName - , credsIdent = T.pack $ show userId - , credsExtra = setExtra token userResponse - } + pure + Creds + { credsPlugin = pluginName + , credsIdent = T.pack $ show userId + , credsExtra = setExtra token userResponse + } where - oauth2 = OAuth2 - { oauth2ClientId = clientId - , oauth2ClientSecret = Just clientSecret - , oauth2AuthorizeEndpoint = "http://upcase.com/oauth/authorize" - , oauth2TokenEndpoint = "http://upcase.com/oauth/token" - , oauth2RedirectUri = Nothing - } + oauth2 = + OAuth2 + { oauth2ClientId = clientId + , oauth2ClientSecret = Just clientSecret + , oauth2AuthorizeEndpoint = "http://upcase.com/oauth/authorize" + , oauth2TokenEndpoint = "http://upcase.com/oauth/token" + , oauth2RedirectUri = Nothing + } diff --git a/src/Yesod/Auth/OAuth2/WordPressDotCom.hs b/src/Yesod/Auth/OAuth2/WordPressDotCom.hs index 4c6b36d..5059413 100644 --- a/src/Yesod/Auth/OAuth2/WordPressDotCom.hs +++ b/src/Yesod/Auth/OAuth2/WordPressDotCom.hs @@ -16,31 +16,35 @@ instance FromJSON WpUser where parseJSON = withObject "WpUser" $ \o -> WpUser <$> o .: "ID" oauth2WordPressDotCom - :: (YesodAuth m) - => Text -- ^ Client Id - -> Text -- ^ Client Secret + :: YesodAuth m + => Text + -- ^ Client Id + -> Text + -- ^ Client Secret -> AuthPlugin m oauth2WordPressDotCom clientId clientSecret = authOAuth2 pluginName oauth2 $ \manager token -> do - (WpUser userId, userResponse) <- authGetProfile - pluginName - manager - token - "https://public-api.wordpress.com/rest/v1/me/" - - pure Creds - { credsPlugin = pluginName - , credsIdent = T.pack $ show userId - , credsExtra = setExtra token userResponse - } + (WpUser userId, userResponse) <- + authGetProfile + pluginName + manager + token + "https://public-api.wordpress.com/rest/v1/me/" + pure + Creds + { credsPlugin = pluginName + , credsIdent = T.pack $ show userId + , credsExtra = setExtra token userResponse + } where - oauth2 = OAuth2 - { oauth2ClientId = clientId - , oauth2ClientSecret = Just clientSecret - , oauth2AuthorizeEndpoint = - "https://public-api.wordpress.com/oauth2/authorize" - `withQuery` [scopeParam "," ["auth"]] - , oauth2TokenEndpoint = "https://public-api.wordpress.com/oauth2/token" - , oauth2RedirectUri = Nothing - } + oauth2 = + OAuth2 + { oauth2ClientId = clientId + , oauth2ClientSecret = Just clientSecret + , oauth2AuthorizeEndpoint = + "https://public-api.wordpress.com/oauth2/authorize" + `withQuery` [scopeParam "," ["auth"]] + , oauth2TokenEndpoint = "https://public-api.wordpress.com/oauth2/token" + , oauth2RedirectUri = Nothing + } diff --git a/test/URI/ByteString/ExtensionSpec.hs b/test/URI/ByteString/ExtensionSpec.hs index a5ea600..3533a68 100644 --- a/test/URI/ByteString/ExtensionSpec.hs +++ b/test/URI/ByteString/ExtensionSpec.hs @@ -1,5 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} + module URI.ByteString.ExtensionSpec ( spec ) where @@ -61,9 +62,8 @@ spec = do uriWithQuery `shouldBe` [uri|http://example.com?foo=bar|] it "handles a URI with an existing query" $ do - let - uriWithQuery = - [uri|http://example.com?foo=bar|] `withQuery` [("baz", "bat")] + let uriWithQuery = + [uri|http://example.com?foo=bar|] `withQuery` [("baz", "bat")] uriWithQuery `shouldBe` [uri|http://example.com?foo=bar&baz=bat|] @@ -71,9 +71,8 @@ spec = do -- it's worthwhile to show that you don't (and can't) pre-sanitize when -- using this function. it "handles santization of the query" $ do - let - uriWithQuery = - [uri|http://example.com|] `withQuery` [("foo", "bar baz")] + let uriWithQuery = + [uri|http://example.com|] `withQuery` [("foo", "bar baz")] toText uriWithQuery `shouldBe` "http://example.com?foo=bar%20baz"