mirror of
https://github.com/freckle/yesod-auth-oauth2.git
synced 2026-04-21 18:27:43 +02:00
Reformat everything with Brittany
This commit is contained in:
parent
e3730ab99c
commit
d34efc18ca
@ -78,7 +78,7 @@ instance YesodAuth App where
|
|||||||
-- Copy the Creds response into the session for viewing after
|
-- Copy the Creds response into the session for viewing after
|
||||||
authenticate c = do
|
authenticate c = do
|
||||||
mapM_ (uncurry setSession)
|
mapM_ (uncurry setSession)
|
||||||
$ [("credsIdent", credsIdent c), ("credsPlugin", credsPlugin c)]
|
$ [("credsIdent", credsIdent c), ("credsPlugin", credsPlugin c)]
|
||||||
++ credsExtra c
|
++ credsExtra c
|
||||||
|
|
||||||
return $ Authenticated "1"
|
return $ Authenticated "1"
|
||||||
@ -131,7 +131,7 @@ mkFoundation :: IO App
|
|||||||
mkFoundation = do
|
mkFoundation = do
|
||||||
loadEnv
|
loadEnv
|
||||||
|
|
||||||
auth0Host <- getEnv "AUTH0_HOST"
|
auth0Host <- getEnv "AUTH0_HOST"
|
||||||
|
|
||||||
appHttpManager <- newManager tlsManagerSettings
|
appHttpManager <- newManager tlsManagerSettings
|
||||||
appAuthPlugins <- sequence
|
appAuthPlugins <- sequence
|
||||||
@ -140,28 +140,28 @@ mkFoundation = do
|
|||||||
--
|
--
|
||||||
-- FIXME: oauth2BattleNet is quite annoying!
|
-- FIXME: oauth2BattleNet is quite annoying!
|
||||||
--
|
--
|
||||||
[ loadPlugin oauth2AzureAD "AZURE_AD"
|
[ loadPlugin oauth2AzureAD "AZURE_AD"
|
||||||
, loadPlugin (oauth2Auth0Host $ fromString auth0Host) "AUTH0"
|
, loadPlugin (oauth2Auth0Host $ fromString auth0Host) "AUTH0"
|
||||||
, loadPlugin (oauth2BattleNet [whamlet|TODO|] "en") "BATTLE_NET"
|
, loadPlugin (oauth2BattleNet [whamlet|TODO|] "en") "BATTLE_NET"
|
||||||
, loadPlugin oauth2Bitbucket "BITBUCKET"
|
, loadPlugin oauth2Bitbucket "BITBUCKET"
|
||||||
, loadPlugin oauth2ClassLink "CLASSLINK"
|
, loadPlugin oauth2ClassLink "CLASSLINK"
|
||||||
, loadPlugin (oauth2Eve Plain) "EVE_ONLINE"
|
, loadPlugin (oauth2Eve Plain) "EVE_ONLINE"
|
||||||
, loadPlugin oauth2GitHub "GITHUB"
|
, loadPlugin oauth2GitHub "GITHUB"
|
||||||
, loadPlugin oauth2GitLab "GITLAB"
|
, loadPlugin oauth2GitLab "GITLAB"
|
||||||
, loadPlugin oauth2Google "GOOGLE"
|
, loadPlugin oauth2Google "GOOGLE"
|
||||||
, loadPlugin oauth2Nylas "NYLAS"
|
, loadPlugin oauth2Nylas "NYLAS"
|
||||||
, loadPlugin oauth2Salesforce "SALES_FORCE"
|
, loadPlugin oauth2Salesforce "SALES_FORCE"
|
||||||
, loadPlugin oauth2Slack "SLACK"
|
, loadPlugin oauth2Slack "SLACK"
|
||||||
, loadPlugin (oauth2Spotify []) "SPOTIFY"
|
, loadPlugin (oauth2Spotify []) "SPOTIFY"
|
||||||
, loadPlugin oauth2Twitch "TWITCH"
|
, loadPlugin oauth2Twitch "TWITCH"
|
||||||
, loadPlugin oauth2WordPressDotCom "WORDPRESS_DOT_COM"
|
, loadPlugin oauth2WordPressDotCom "WORDPRESS_DOT_COM"
|
||||||
, loadPlugin oauth2Upcase "UPCASE"
|
, loadPlugin oauth2Upcase "UPCASE"
|
||||||
]
|
]
|
||||||
|
|
||||||
return App { .. }
|
return App { .. }
|
||||||
where
|
where
|
||||||
loadPlugin f prefix = do
|
loadPlugin f prefix = do
|
||||||
clientId <- getEnv $ prefix <> "_CLIENT_ID"
|
clientId <- getEnv $ prefix <> "_CLIENT_ID"
|
||||||
clientSecret <- getEnv $ prefix <> "_CLIENT_SECRET"
|
clientSecret <- getEnv $ prefix <> "_CLIENT_SECRET"
|
||||||
pure $ f (T.pack clientId) (T.pack clientSecret)
|
pure $ f (T.pack clientId) (T.pack clientSecret)
|
||||||
|
|
||||||
|
|||||||
@ -13,30 +13,26 @@ import qualified Data.ByteString.Char8 as C8
|
|||||||
import URI.ByteString
|
import URI.ByteString
|
||||||
|
|
||||||
instance IsString Scheme where
|
instance IsString Scheme where
|
||||||
fromString = Scheme . fromString
|
fromString = Scheme . fromString
|
||||||
|
|
||||||
instance IsString Host where
|
instance IsString Host where
|
||||||
fromString = Host . fromString
|
fromString = Host . fromString
|
||||||
|
|
||||||
instance IsString (URIRef Absolute) where
|
instance IsString (URIRef Absolute) where
|
||||||
fromString = either (error . show) id
|
fromString =
|
||||||
. parseURI strictURIParserOptions
|
either (error . show) id . parseURI strictURIParserOptions . C8.pack
|
||||||
. C8.pack
|
|
||||||
|
|
||||||
instance IsString (URIRef Relative) where
|
instance IsString (URIRef Relative) where
|
||||||
fromString = either (error . show) id
|
fromString =
|
||||||
. parseRelativeRef strictURIParserOptions
|
either (error . show) id . parseRelativeRef strictURIParserOptions . C8.pack
|
||||||
. C8.pack
|
|
||||||
|
|
||||||
fromText :: Text -> Maybe URI
|
fromText :: Text -> Maybe URI
|
||||||
fromText = either (const Nothing) Just
|
fromText =
|
||||||
. parseURI strictURIParserOptions
|
either (const Nothing) Just . parseURI strictURIParserOptions . encodeUtf8
|
||||||
. encodeUtf8
|
|
||||||
|
|
||||||
unsafeFromText :: Text -> URI
|
unsafeFromText :: Text -> URI
|
||||||
unsafeFromText = either (error . show) id
|
unsafeFromText =
|
||||||
. parseURI strictURIParserOptions
|
either (error . show) id . parseURI strictURIParserOptions . encodeUtf8
|
||||||
. encodeUtf8
|
|
||||||
|
|
||||||
toText :: URI -> Text
|
toText :: URI -> Text
|
||||||
toText = decodeUtf8 . serializeURIRef'
|
toText = decodeUtf8 . serializeURIRef'
|
||||||
@ -46,8 +42,8 @@ fromRelative s h = flip withHost h . toAbsolute s
|
|||||||
|
|
||||||
withHost :: URIRef a -> Host -> URIRef a
|
withHost :: URIRef a -> Host -> URIRef a
|
||||||
withHost u h = u & authorityL %~ maybe
|
withHost u h = u & authorityL %~ maybe
|
||||||
(Just $ Authority Nothing h Nothing)
|
(Just $ Authority Nothing h Nothing)
|
||||||
(\a -> Just $ a & authorityHostL .~ h)
|
(\a -> Just $ a & authorityHostL .~ h)
|
||||||
|
|
||||||
withPath :: URIRef a -> ByteString -> URIRef a
|
withPath :: URIRef a -> ByteString -> URIRef a
|
||||||
withPath u p = u & pathL .~ p
|
withPath u p = u & pathL .~ p
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
{-# OPTIONS_GHC -Wno-orphans #-}
|
{-# OPTIONS_GHC -Wno-orphans #-}
|
||||||
|
|
||||||
module UnliftIO.Except
|
module UnliftIO.Except
|
||||||
() where
|
() where
|
||||||
|
|
||||||
import Control.Monad.Except
|
import Control.Monad.Except
|
||||||
import UnliftIO
|
import UnliftIO
|
||||||
|
|
||||||
instance (MonadUnliftIO m, Exception e) => MonadUnliftIO (ExceptT e m) where
|
instance (MonadUnliftIO m, Exception e) => MonadUnliftIO (ExceptT e m) where
|
||||||
withRunInIO exceptToIO = ExceptT $ try $ do
|
withRunInIO exceptToIO = ExceptT $ try $ do
|
||||||
withRunInIO $ \runInIO ->
|
withRunInIO $ \runInIO ->
|
||||||
exceptToIO (runInIO . (either throwIO pure <=< runExceptT))
|
exceptToIO (runInIO . (either throwIO pure <=< runExceptT))
|
||||||
|
|||||||
@ -36,21 +36,22 @@ oauth2Auth0HostScopes
|
|||||||
:: YesodAuth m => URI -> [Text] -> Text -> Text -> AuthPlugin m
|
:: YesodAuth m => URI -> [Text] -> Text -> Text -> AuthPlugin m
|
||||||
oauth2Auth0HostScopes host scopes clientId clientSecret =
|
oauth2Auth0HostScopes host scopes clientId clientSecret =
|
||||||
authOAuth2 pluginName oauth2 $ \manager token -> do
|
authOAuth2 pluginName oauth2 $ \manager token -> do
|
||||||
(User uid, userResponse) <- authGetProfile pluginName
|
(User uid, userResponse) <- authGetProfile
|
||||||
manager
|
pluginName
|
||||||
token
|
manager
|
||||||
(host `withPath` "/userinfo")
|
token
|
||||||
pure Creds { credsPlugin = pluginName
|
(host `withPath` "/userinfo")
|
||||||
, credsIdent = uid
|
pure Creds
|
||||||
, credsExtra = setExtra token userResponse
|
{ credsPlugin = pluginName
|
||||||
}
|
, credsIdent = uid
|
||||||
|
, credsExtra = setExtra token userResponse
|
||||||
|
}
|
||||||
where
|
where
|
||||||
oauth2 = OAuth2
|
oauth2 = OAuth2
|
||||||
{ oauth2ClientId = clientId
|
{ oauth2ClientId = clientId
|
||||||
, oauth2ClientSecret = Just clientSecret
|
, oauth2ClientSecret = Just clientSecret
|
||||||
, oauth2AuthorizeEndpoint = host
|
, oauth2AuthorizeEndpoint =
|
||||||
`withPath` "/authorize"
|
host `withPath` "/authorize" `withQuery` [scopeParam " " scopes]
|
||||||
`withQuery` [scopeParam " " scopes]
|
, oauth2TokenEndpoint = host `withPath` "/oauth/token"
|
||||||
, oauth2TokenEndpoint = host `withPath` "/oauth/token"
|
, oauth2RedirectUri = Nothing
|
||||||
, oauth2RedirectUri = Nothing
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,19 +37,20 @@ oauth2AzureADScoped scopes clientId clientSecret =
|
|||||||
token
|
token
|
||||||
"https://graph.microsoft.com/v1.0/me"
|
"https://graph.microsoft.com/v1.0/me"
|
||||||
|
|
||||||
pure Creds { credsPlugin = pluginName
|
pure Creds
|
||||||
, credsIdent = userId
|
{ credsPlugin = pluginName
|
||||||
, credsExtra = setExtra token userResponse
|
, credsIdent = userId
|
||||||
}
|
, credsExtra = setExtra token userResponse
|
||||||
|
}
|
||||||
where
|
where
|
||||||
oauth2 = OAuth2
|
oauth2 = OAuth2
|
||||||
{ oauth2ClientId = clientId
|
{ oauth2ClientId = clientId
|
||||||
, oauth2ClientSecret = Just clientSecret
|
, oauth2ClientSecret = Just clientSecret
|
||||||
, oauth2AuthorizeEndpoint =
|
, oauth2AuthorizeEndpoint =
|
||||||
"https://login.windows.net/common/oauth2/authorize"
|
"https://login.windows.net/common/oauth2/authorize"
|
||||||
`withQuery` [ scopeParam "," scopes
|
`withQuery` [ scopeParam "," scopes
|
||||||
, ("resource", "https://graph.microsoft.com")
|
, ("resource", "https://graph.microsoft.com")
|
||||||
]
|
]
|
||||||
, oauth2TokenEndpoint = "https://login.windows.net/common/oauth2/token"
|
, oauth2TokenEndpoint = "https://login.windows.net/common/oauth2/token"
|
||||||
, oauth2RedirectUri = Nothing
|
, oauth2RedirectUri = Nothing
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,27 +39,28 @@ oauth2BattleNet widget region clientId clientSecret =
|
|||||||
authGetProfile pluginName manager token
|
authGetProfile pluginName manager token
|
||||||
$ fromRelative "https" (apiHost $ T.toLower region) "/account/user"
|
$ fromRelative "https" (apiHost $ T.toLower region) "/account/user"
|
||||||
|
|
||||||
pure Creds { credsPlugin = pluginName
|
pure Creds
|
||||||
, credsIdent = T.pack $ show userId
|
{ credsPlugin = pluginName
|
||||||
, credsExtra = setExtra token userResponse
|
, credsIdent = T.pack $ show userId
|
||||||
}
|
, credsExtra = setExtra token userResponse
|
||||||
|
}
|
||||||
where
|
where
|
||||||
host = wwwHost $ T.toLower region
|
host = wwwHost $ T.toLower region
|
||||||
oauth2 = OAuth2
|
oauth2 = OAuth2
|
||||||
{ oauth2ClientId = clientId
|
{ oauth2ClientId = clientId
|
||||||
, oauth2ClientSecret = Just clientSecret
|
, oauth2ClientSecret = Just clientSecret
|
||||||
, oauth2AuthorizeEndpoint = fromRelative "https" host "/oauth/authorize"
|
, oauth2AuthorizeEndpoint = fromRelative "https" host "/oauth/authorize"
|
||||||
, oauth2TokenEndpoint = fromRelative "https" host "/oauth/token"
|
, oauth2TokenEndpoint = fromRelative "https" host "/oauth/token"
|
||||||
, oauth2RedirectUri = Nothing
|
, oauth2RedirectUri = Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
apiHost :: Text -> Host
|
apiHost :: Text -> Host
|
||||||
apiHost "cn" = "api.battlenet.com.cn"
|
apiHost "cn" = "api.battlenet.com.cn"
|
||||||
apiHost region = Host $ encodeUtf8 $ region <> ".api.battle.net"
|
apiHost region = Host $ encodeUtf8 $ region <> ".api.battle.net"
|
||||||
|
|
||||||
wwwHost :: Text -> Host
|
wwwHost :: Text -> Host
|
||||||
wwwHost "cn" = "www.battlenet.com.cn"
|
wwwHost "cn" = "www.battlenet.com.cn"
|
||||||
wwwHost region = Host $ encodeUtf8 $ region <> ".battle.net"
|
wwwHost region = Host $ encodeUtf8 $ region <> ".battle.net"
|
||||||
|
|
||||||
oAuth2BattleNet
|
oAuth2BattleNet
|
||||||
|
|||||||
@ -38,22 +38,24 @@ oauth2BitbucketScoped scopes clientId clientSecret =
|
|||||||
token
|
token
|
||||||
"https://api.bitbucket.com/2.0/user"
|
"https://api.bitbucket.com/2.0/user"
|
||||||
|
|
||||||
pure Creds { credsPlugin = pluginName
|
pure Creds
|
||||||
|
{ credsPlugin = pluginName
|
||||||
-- FIXME: Preserved bug. This should just be userId (it's already
|
-- FIXME: Preserved bug. This should just be userId (it's already
|
||||||
-- a Text), but because this code was shipped, folks likely have
|
-- a Text), but because this code was shipped, folks likely have
|
||||||
-- Idents in their database like @"\"...\""@, and if we fixed this
|
-- Idents in their database like @"\"...\""@, and if we fixed this
|
||||||
-- they would need migrating. We're keeping it for now as it's a
|
-- 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
|
-- minor wart. Breaking typed APIs is one thing, causing data to go
|
||||||
-- invalid is another.
|
-- invalid is another.
|
||||||
, credsIdent = T.pack $ show userId
|
, credsIdent = T.pack $ show userId
|
||||||
, credsExtra = setExtra token userResponse
|
, credsExtra = setExtra token userResponse
|
||||||
}
|
}
|
||||||
where
|
where
|
||||||
oauth2 = OAuth2
|
oauth2 = OAuth2
|
||||||
{ oauth2ClientId = clientId
|
{ oauth2ClientId = clientId
|
||||||
, oauth2ClientSecret = Just clientSecret
|
, oauth2ClientSecret = Just clientSecret
|
||||||
, oauth2AuthorizeEndpoint = "https://bitbucket.com/site/oauth2/authorize"
|
, oauth2AuthorizeEndpoint =
|
||||||
`withQuery` [scopeParam "," scopes]
|
"https://bitbucket.com/site/oauth2/authorize"
|
||||||
, oauth2TokenEndpoint = "https://bitbucket.com/site/oauth2/access_token"
|
`withQuery` [scopeParam "," scopes]
|
||||||
, oauth2RedirectUri = Nothing
|
, oauth2TokenEndpoint = "https://bitbucket.com/site/oauth2/access_token"
|
||||||
|
, oauth2RedirectUri = Nothing
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,16 +32,18 @@ oauth2ClassLinkScoped scopes clientId clientSecret =
|
|||||||
token
|
token
|
||||||
"https://nodeapi.classlink.com/v2/my/info"
|
"https://nodeapi.classlink.com/v2/my/info"
|
||||||
|
|
||||||
pure Creds { credsPlugin = pluginName
|
pure Creds
|
||||||
, credsIdent = T.pack $ show userId
|
{ credsPlugin = pluginName
|
||||||
, credsExtra = setExtra token userResponse
|
, credsIdent = T.pack $ show userId
|
||||||
}
|
, credsExtra = setExtra token userResponse
|
||||||
|
}
|
||||||
where
|
where
|
||||||
oauth2 = OAuth2
|
oauth2 = OAuth2
|
||||||
{ oauth2ClientId = clientId
|
{ oauth2ClientId = clientId
|
||||||
, oauth2ClientSecret = Just clientSecret
|
, oauth2ClientSecret = Just clientSecret
|
||||||
, oauth2AuthorizeEndpoint = "https://launchpad.classlink.com/oauth2/v2/auth"
|
, oauth2AuthorizeEndpoint =
|
||||||
`withQuery` [scopeParam "," scopes]
|
"https://launchpad.classlink.com/oauth2/v2/auth"
|
||||||
|
`withQuery` [scopeParam "," scopes]
|
||||||
, oauth2TokenEndpoint = "https://launchpad.classlink.com/oauth2/v2/token"
|
, oauth2TokenEndpoint = "https://launchpad.classlink.com/oauth2/v2/token"
|
||||||
, oauth2RedirectUri = Nothing
|
, oauth2RedirectUri = Nothing
|
||||||
}
|
}
|
||||||
|
|||||||
@ -62,7 +62,7 @@ dispatchForward
|
|||||||
-> OAuth2
|
-> OAuth2
|
||||||
-> m TypedContent
|
-> m TypedContent
|
||||||
dispatchForward name oauth2 = do
|
dispatchForward name oauth2 = do
|
||||||
csrf <- setSessionCSRF $ tokenSessionKey name
|
csrf <- setSessionCSRF $ tokenSessionKey name
|
||||||
oauth2' <- withCallbackAndState name oauth2 csrf
|
oauth2' <- withCallbackAndState name oauth2 csrf
|
||||||
redirect $ toText $ authorizationUrl oauth2'
|
redirect $ toText $ authorizationUrl oauth2'
|
||||||
|
|
||||||
@ -81,11 +81,11 @@ dispatchCallback
|
|||||||
-> m TypedContent
|
-> m TypedContent
|
||||||
dispatchCallback name oauth2 getToken getCreds = do
|
dispatchCallback name oauth2 getToken getCreds = do
|
||||||
onErrorResponse $ throwError . OAuth2HandshakeError
|
onErrorResponse $ throwError . OAuth2HandshakeError
|
||||||
csrf <- verifySessionCSRF $ tokenSessionKey name
|
csrf <- verifySessionCSRF $ tokenSessionKey name
|
||||||
code <- requireGetParam "code"
|
code <- requireGetParam "code"
|
||||||
manager <- authHttpManager
|
manager <- authHttpManager
|
||||||
oauth2' <- withCallbackAndState name oauth2 csrf
|
oauth2' <- withCallbackAndState name oauth2 csrf
|
||||||
token <- either (throwError . OAuth2ResultError) pure
|
token <- either (throwError . OAuth2ResultError) pure
|
||||||
=<< liftIO (getToken manager oauth2' $ ExchangeToken code)
|
=<< liftIO (getToken manager oauth2' $ ExchangeToken code)
|
||||||
creds <-
|
creds <-
|
||||||
liftIO (getCreds manager token)
|
liftIO (getCreds manager token)
|
||||||
@ -100,12 +100,12 @@ withCallbackAndState
|
|||||||
-> Text
|
-> Text
|
||||||
-> m OAuth2
|
-> m OAuth2
|
||||||
withCallbackAndState name oauth2 csrf = do
|
withCallbackAndState name oauth2 csrf = do
|
||||||
uri <- ($ PluginR name ["callback"]) <$> getParentUrlRender
|
uri <- ($ PluginR name ["callback"]) <$> getParentUrlRender
|
||||||
callback <- maybe (throwError $ InvalidCallbackUri uri) pure $ fromText uri
|
callback <- maybe (throwError $ InvalidCallbackUri uri) pure $ fromText uri
|
||||||
pure oauth2
|
pure oauth2
|
||||||
{ oauth2RedirectUri = Just callback
|
{ oauth2RedirectUri = Just callback
|
||||||
, oauth2AuthorizeEndpoint = oauth2AuthorizeEndpoint oauth2
|
, oauth2AuthorizeEndpoint =
|
||||||
`withQuery` [("state", encodeUtf8 csrf)]
|
oauth2AuthorizeEndpoint oauth2 `withQuery` [("state", encodeUtf8 csrf)]
|
||||||
}
|
}
|
||||||
|
|
||||||
getParentUrlRender :: MonadHandler m => m (Route (SubHandlerSite m) -> Text)
|
getParentUrlRender :: MonadHandler m => m (Route (SubHandlerSite m) -> Text)
|
||||||
@ -130,11 +130,12 @@ setSessionCSRF sessionKey = do
|
|||||||
verifySessionCSRF
|
verifySessionCSRF
|
||||||
:: (MonadError DispatchError m, MonadHandler m) => Text -> m Text
|
:: (MonadError DispatchError m, MonadHandler m) => Text -> m Text
|
||||||
verifySessionCSRF sessionKey = do
|
verifySessionCSRF sessionKey = do
|
||||||
token <- requireGetParam "state"
|
token <- requireGetParam "state"
|
||||||
sessionToken <- lookupSession sessionKey
|
sessionToken <- lookupSession sessionKey
|
||||||
deleteSession sessionKey
|
deleteSession sessionKey
|
||||||
token <$ unless (sessionToken == Just token)
|
token <$ unless
|
||||||
(throwError $ InvalidStateToken sessionToken token)
|
(sessionToken == Just token)
|
||||||
|
(throwError $ InvalidStateToken sessionToken token)
|
||||||
|
|
||||||
requireGetParam
|
requireGetParam
|
||||||
:: (MonadError DispatchError m, MonadHandler m) => Text -> m Text
|
:: (MonadError DispatchError m, MonadHandler m) => Text -> m Text
|
||||||
|
|||||||
@ -50,10 +50,10 @@ dispatchErrorMessage = \case
|
|||||||
InvalidCallbackUri{} ->
|
InvalidCallbackUri{} ->
|
||||||
"Callback URI was not valid, this server may be misconfigured (no approot)"
|
"Callback URI was not valid, this server may be misconfigured (no approot)"
|
||||||
OAuth2HandshakeError er -> "OAuth2 handshake failure: " <> erUserMessage er
|
OAuth2HandshakeError er -> "OAuth2 handshake failure: " <> erUserMessage er
|
||||||
OAuth2ResultError{} -> "Login failed, please try again"
|
OAuth2ResultError{} -> "Login failed, please try again"
|
||||||
FetchCredsIOException{} -> "Login failed, please try again"
|
FetchCredsIOException{} -> "Login failed, please try again"
|
||||||
FetchCredsYesodOAuth2Exception{} -> "Login failed, please try again"
|
FetchCredsYesodOAuth2Exception{} -> "Login failed, please try again"
|
||||||
OtherDispatchError{} -> "Login failed, please try again"
|
OtherDispatchError{} -> "Login failed, please try again"
|
||||||
|
|
||||||
handleDispatchError
|
handleDispatchError
|
||||||
:: MonadAuthHandler site m
|
:: MonadAuthHandler site m
|
||||||
@ -69,9 +69,10 @@ onDispatchError err = do
|
|||||||
let suffix = " [errorId=" <> errorId <> "]"
|
let suffix = " [errorId=" <> errorId <> "]"
|
||||||
$(logError) $ pack (displayException err) <> suffix
|
$(logError) $ pack (displayException err) <> suffix
|
||||||
|
|
||||||
let message = dispatchErrorMessage err <> suffix
|
let
|
||||||
messageValue =
|
message = dispatchErrorMessage err <> suffix
|
||||||
object ["error" .= object ["id" .= errorId, "message" .= message]]
|
messageValue =
|
||||||
|
object ["error" .= object ["id" .= errorId, "message" .= message]]
|
||||||
|
|
||||||
loginR <- ($ LoginR) <$> getRouteToParent
|
loginR <- ($ LoginR) <$> getRouteToParent
|
||||||
|
|
||||||
|
|||||||
@ -4,13 +4,12 @@
|
|||||||
-- <https://tools.ietf.org/html/rfc6749#section-4.1.2.1>
|
-- <https://tools.ietf.org/html/rfc6749#section-4.1.2.1>
|
||||||
--
|
--
|
||||||
module Yesod.Auth.OAuth2.ErrorResponse
|
module Yesod.Auth.OAuth2.ErrorResponse
|
||||||
( ErrorResponse(..)
|
( ErrorResponse(..)
|
||||||
, erUserMessage
|
, erUserMessage
|
||||||
, ErrorName(..)
|
, ErrorName(..)
|
||||||
, onErrorResponse
|
, onErrorResponse
|
||||||
, unknownError
|
, unknownError
|
||||||
)
|
) where
|
||||||
where
|
|
||||||
|
|
||||||
import Data.Foldable (traverse_)
|
import Data.Foldable (traverse_)
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
@ -29,30 +28,27 @@ data ErrorName
|
|||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
data ErrorResponse = ErrorResponse
|
data ErrorResponse = ErrorResponse
|
||||||
{ erName :: ErrorName
|
{ erName :: ErrorName
|
||||||
, erDescription :: Maybe Text
|
, erDescription :: Maybe Text
|
||||||
, erURI :: Maybe Text
|
, erURI :: Maybe Text
|
||||||
}
|
}
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
-- | Textual value suitable for display to a User
|
-- | Textual value suitable for display to a User
|
||||||
erUserMessage :: ErrorResponse -> Text
|
erUserMessage :: ErrorResponse -> Text
|
||||||
erUserMessage err = case erName err of
|
erUserMessage err = case erName err of
|
||||||
InvalidRequest -> "Invalid request"
|
InvalidRequest -> "Invalid request"
|
||||||
UnauthorizedClient -> "Unauthorized client"
|
UnauthorizedClient -> "Unauthorized client"
|
||||||
AccessDenied -> "Access denied"
|
AccessDenied -> "Access denied"
|
||||||
UnsupportedResponseType -> "Unsupported response type"
|
UnsupportedResponseType -> "Unsupported response type"
|
||||||
InvalidScope -> "Invalid scope"
|
InvalidScope -> "Invalid scope"
|
||||||
ServerError -> "Server error"
|
ServerError -> "Server error"
|
||||||
TemporarilyUnavailable -> "Temporarily unavailable"
|
TemporarilyUnavailable -> "Temporarily unavailable"
|
||||||
Unknown _ -> "Unknown error"
|
Unknown _ -> "Unknown error"
|
||||||
|
|
||||||
unknownError :: Text -> ErrorResponse
|
unknownError :: Text -> ErrorResponse
|
||||||
unknownError x = ErrorResponse
|
unknownError x =
|
||||||
{ erName = Unknown x
|
ErrorResponse { erName = Unknown x, erDescription = Nothing, erURI = Nothing }
|
||||||
, erDescription = Nothing
|
|
||||||
, erURI = Nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
-- | Check query parameters for an error, if found run the given action
|
-- | Check query parameters for an error, if found run the given action
|
||||||
--
|
--
|
||||||
@ -64,12 +60,12 @@ onErrorResponse f = traverse_ f =<< checkErrorResponse
|
|||||||
|
|
||||||
checkErrorResponse :: MonadHandler m => m (Maybe ErrorResponse)
|
checkErrorResponse :: MonadHandler m => m (Maybe ErrorResponse)
|
||||||
checkErrorResponse = do
|
checkErrorResponse = do
|
||||||
merror <- lookupGetParam "error"
|
merror <- lookupGetParam "error"
|
||||||
|
|
||||||
for merror $ \err ->
|
for merror $ \err ->
|
||||||
ErrorResponse (readErrorName err)
|
ErrorResponse (readErrorName err)
|
||||||
<$> lookupGetParam "error_description"
|
<$> lookupGetParam "error_description"
|
||||||
<*> lookupGetParam "error_uri"
|
<*> lookupGetParam "error_uri"
|
||||||
|
|
||||||
readErrorName :: Text -> ErrorName
|
readErrorName :: Text -> ErrorName
|
||||||
readErrorName "invalid_request" = InvalidRequest
|
readErrorName "invalid_request" = InvalidRequest
|
||||||
|
|||||||
@ -63,19 +63,19 @@ oauth2EveScoped scopes widgetType clientId clientSecret =
|
|||||||
token
|
token
|
||||||
"https://login.eveonline.com/oauth/verify"
|
"https://login.eveonline.com/oauth/verify"
|
||||||
|
|
||||||
pure Creds { credsPlugin = "eveonline"
|
pure Creds
|
||||||
|
{ credsPlugin = "eveonline"
|
||||||
-- FIXME: Preserved bug. See similar comment in Bitbucket provider.
|
-- FIXME: Preserved bug. See similar comment in Bitbucket provider.
|
||||||
, credsIdent = T.pack $ show userId
|
, credsIdent = T.pack $ show userId
|
||||||
, credsExtra = setExtra token userResponse
|
, credsExtra = setExtra token userResponse
|
||||||
}
|
}
|
||||||
where
|
where
|
||||||
oauth2 = OAuth2
|
oauth2 = OAuth2
|
||||||
{ oauth2ClientId = clientId
|
{ oauth2ClientId = clientId
|
||||||
, oauth2ClientSecret = Just clientSecret
|
, oauth2ClientSecret = Just clientSecret
|
||||||
, oauth2AuthorizeEndpoint = "https://login.eveonline.com/oauth/authorize"
|
, oauth2AuthorizeEndpoint =
|
||||||
`withQuery` [ ("response_type", "code")
|
"https://login.eveonline.com/oauth/authorize"
|
||||||
, scopeParam " " scopes
|
`withQuery` [("response_type", "code"), scopeParam " " scopes]
|
||||||
]
|
, oauth2TokenEndpoint = "https://login.eveonline.com/oauth/token"
|
||||||
, oauth2TokenEndpoint = "https://login.eveonline.com/oauth/token"
|
, oauth2RedirectUri = Nothing
|
||||||
, oauth2RedirectUri = Nothing
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
{-# LANGUAGE DeriveDataTypeable #-}
|
{-# LANGUAGE DeriveDataTypeable #-}
|
||||||
|
|
||||||
module Yesod.Auth.OAuth2.Exception
|
module Yesod.Auth.OAuth2.Exception
|
||||||
( YesodOAuth2Exception(..)
|
( YesodOAuth2Exception(..)
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Exception.Safe
|
import Control.Exception.Safe
|
||||||
import Data.ByteString.Lazy (ByteString)
|
import Data.ByteString.Lazy (ByteString)
|
||||||
|
|||||||
@ -38,16 +38,18 @@ oauth2GitHubScoped scopes clientId clientSecret =
|
|||||||
token
|
token
|
||||||
"https://api.github.com/user"
|
"https://api.github.com/user"
|
||||||
|
|
||||||
pure Creds { credsPlugin = pluginName
|
pure Creds
|
||||||
, credsIdent = T.pack $ show userId
|
{ credsPlugin = pluginName
|
||||||
, credsExtra = setExtra token userResponse
|
, credsIdent = T.pack $ show userId
|
||||||
}
|
, credsExtra = setExtra token userResponse
|
||||||
|
}
|
||||||
where
|
where
|
||||||
oauth2 = OAuth2
|
oauth2 = OAuth2
|
||||||
{ oauth2ClientId = clientId
|
{ oauth2ClientId = clientId
|
||||||
, oauth2ClientSecret = Just clientSecret
|
, oauth2ClientSecret = Just clientSecret
|
||||||
, oauth2AuthorizeEndpoint = "https://github.com/login/oauth/authorize"
|
, oauth2AuthorizeEndpoint =
|
||||||
`withQuery` [scopeParam "," scopes]
|
"https://github.com/login/oauth/authorize"
|
||||||
, oauth2TokenEndpoint = "https://github.com/login/oauth/access_token"
|
`withQuery` [scopeParam "," scopes]
|
||||||
, oauth2RedirectUri = Nothing
|
, oauth2TokenEndpoint = "https://github.com/login/oauth/access_token"
|
||||||
|
, oauth2RedirectUri = Nothing
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,17 +43,17 @@ oauth2GitLabHostScopes host scopes clientId clientSecret =
|
|||||||
(User userId, userResponse) <-
|
(User userId, userResponse) <-
|
||||||
authGetProfile pluginName manager token $ host `withPath` "/api/v4/user"
|
authGetProfile pluginName manager token $ host `withPath` "/api/v4/user"
|
||||||
|
|
||||||
pure Creds { credsPlugin = pluginName
|
pure Creds
|
||||||
, credsIdent = T.pack $ show userId
|
{ credsPlugin = pluginName
|
||||||
, credsExtra = setExtra token userResponse
|
, credsIdent = T.pack $ show userId
|
||||||
}
|
, credsExtra = setExtra token userResponse
|
||||||
|
}
|
||||||
where
|
where
|
||||||
oauth2 = OAuth2
|
oauth2 = OAuth2
|
||||||
{ oauth2ClientId = clientId
|
{ oauth2ClientId = clientId
|
||||||
, oauth2ClientSecret = Just clientSecret
|
, oauth2ClientSecret = Just clientSecret
|
||||||
, oauth2AuthorizeEndpoint = host
|
, oauth2AuthorizeEndpoint =
|
||||||
`withPath` "/oauth/authorize"
|
host `withPath` "/oauth/authorize" `withQuery` [scopeParam " " scopes]
|
||||||
`withQuery` [scopeParam " " scopes]
|
, oauth2TokenEndpoint = host `withPath` "/oauth/token"
|
||||||
, oauth2TokenEndpoint = host `withPath` "/oauth/token"
|
, oauth2RedirectUri = Nothing
|
||||||
, oauth2RedirectUri = Nothing
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -69,16 +69,18 @@ oauth2GoogleScopedWidget widget scopes clientId clientSecret =
|
|||||||
token
|
token
|
||||||
"https://www.googleapis.com/oauth2/v3/userinfo"
|
"https://www.googleapis.com/oauth2/v3/userinfo"
|
||||||
|
|
||||||
pure Creds { credsPlugin = pluginName
|
pure Creds
|
||||||
, credsIdent = userId
|
{ credsPlugin = pluginName
|
||||||
, credsExtra = setExtra token userResponse
|
, credsIdent = userId
|
||||||
}
|
, credsExtra = setExtra token userResponse
|
||||||
|
}
|
||||||
where
|
where
|
||||||
oauth2 = OAuth2
|
oauth2 = OAuth2
|
||||||
{ oauth2ClientId = clientId
|
{ oauth2ClientId = clientId
|
||||||
, oauth2ClientSecret = Just clientSecret
|
, oauth2ClientSecret = Just clientSecret
|
||||||
, oauth2AuthorizeEndpoint = "https://accounts.google.com/o/oauth2/auth"
|
, oauth2AuthorizeEndpoint =
|
||||||
`withQuery` [scopeParam " " scopes]
|
"https://accounts.google.com/o/oauth2/auth"
|
||||||
, oauth2TokenEndpoint = "https://www.googleapis.com/oauth2/v3/token"
|
`withQuery` [scopeParam " " scopes]
|
||||||
, oauth2RedirectUri = Nothing
|
, oauth2TokenEndpoint = "https://www.googleapis.com/oauth2/v3/token"
|
||||||
|
, oauth2RedirectUri = Nothing
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,33 +34,33 @@ oauth2Nylas clientId clientSecret =
|
|||||||
-- FIXME: was this working? I'm 95% sure that the client will throw its
|
-- FIXME: was this working? I'm 95% sure that the client will throw its
|
||||||
-- own exception on unsuccessful status codes.
|
-- own exception on unsuccessful status codes.
|
||||||
unless (HT.statusIsSuccessful $ responseStatus resp)
|
unless (HT.statusIsSuccessful $ responseStatus resp)
|
||||||
$ throwIO
|
$ throwIO
|
||||||
$ YesodOAuth2Exception.GenericError pluginName
|
$ YesodOAuth2Exception.GenericError pluginName
|
||||||
$ "Unsuccessful HTTP response: "
|
$ "Unsuccessful HTTP response: "
|
||||||
<> BL8.unpack userResponse
|
<> BL8.unpack userResponse
|
||||||
|
|
||||||
either
|
either
|
||||||
(throwIO . YesodOAuth2Exception.JSONDecodingError pluginName)
|
(throwIO . YesodOAuth2Exception.JSONDecodingError pluginName)
|
||||||
(\(User userId) -> pure Creds { credsPlugin = pluginName
|
(\(User userId) -> pure Creds
|
||||||
, credsIdent = userId
|
{ credsPlugin = pluginName
|
||||||
, credsExtra = setExtra token userResponse
|
, credsIdent = userId
|
||||||
}
|
, credsExtra = setExtra token userResponse
|
||||||
|
}
|
||||||
)
|
)
|
||||||
$ eitherDecode userResponse
|
$ eitherDecode userResponse
|
||||||
where
|
where
|
||||||
oauth = OAuth2
|
oauth = OAuth2
|
||||||
{ oauth2ClientId = clientId
|
{ oauth2ClientId = clientId
|
||||||
, oauth2ClientSecret = Just clientSecret
|
, oauth2ClientSecret = Just clientSecret
|
||||||
, oauth2AuthorizeEndpoint = "https://api.nylas.com/oauth/authorize"
|
, oauth2AuthorizeEndpoint =
|
||||||
`withQuery` [ ("response_type", "code")
|
"https://api.nylas.com/oauth/authorize"
|
||||||
, ( "client_id"
|
`withQuery` [ ("response_type", "code")
|
||||||
, encodeUtf8 clientId
|
, ("client_id", encodeUtf8 clientId)
|
||||||
)
|
|
||||||
-- N.B. The scopes delimeter is unknown/untested. Verify that before
|
-- N.B. The scopes delimeter is unknown/untested. Verify that before
|
||||||
-- extracting this to an argument and offering a Scoped function. In
|
-- extracting this to an argument and offering a Scoped function. In
|
||||||
-- its current state, it doesn't matter because it's only one scope.
|
-- its current state, it doesn't matter because it's only one scope.
|
||||||
, scopeParam "," defaultScopes
|
, scopeParam "," defaultScopes
|
||||||
]
|
]
|
||||||
, oauth2TokenEndpoint = "https://api.nylas.com/oauth/token"
|
, oauth2TokenEndpoint = "https://api.nylas.com/oauth/token"
|
||||||
, oauth2RedirectUri = Nothing
|
, oauth2RedirectUri = Nothing
|
||||||
}
|
}
|
||||||
|
|||||||
@ -85,7 +85,7 @@ authGetProfile
|
|||||||
-> URI
|
-> URI
|
||||||
-> IO (a, BL.ByteString)
|
-> IO (a, BL.ByteString)
|
||||||
authGetProfile name manager token url = do
|
authGetProfile name manager token url = do
|
||||||
resp <- fromAuthGet name =<< authGetBS manager (accessToken token) url
|
resp <- fromAuthGet name =<< authGetBS manager (accessToken token) url
|
||||||
decoded <- fromAuthJSON name resp
|
decoded <- fromAuthJSON name resp
|
||||||
pure (decoded, resp)
|
pure (decoded, resp)
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
{-# LANGUAGE TypeApplications #-}
|
{-# LANGUAGE TypeApplications #-}
|
||||||
|
|
||||||
module Yesod.Auth.OAuth2.Random
|
module Yesod.Auth.OAuth2.Random
|
||||||
( randomText
|
( randomText
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Crypto.Random (MonadRandom, getRandomBytes)
|
import Crypto.Random (MonadRandom, getRandomBytes)
|
||||||
import Data.ByteArray.Encoding (Base(Base64), convertToBase)
|
import Data.ByteArray.Encoding (Base(Base64), convertToBase)
|
||||||
@ -11,9 +11,9 @@ import Data.Text (Text)
|
|||||||
import Data.Text.Encoding (decodeUtf8)
|
import Data.Text.Encoding (decodeUtf8)
|
||||||
|
|
||||||
randomText
|
randomText
|
||||||
:: MonadRandom m
|
:: MonadRandom m
|
||||||
=> Int
|
=> Int
|
||||||
-- ^ Size in Bytes (note necessarily characters)
|
-- ^ Size in Bytes (note necessarily characters)
|
||||||
-> m Text
|
-> m Text
|
||||||
randomText size =
|
randomText size =
|
||||||
decodeUtf8 . convertToBase @ByteString Base64 <$> getRandomBytes size
|
decodeUtf8 . convertToBase @ByteString Base64 <$> getRandomBytes size
|
||||||
|
|||||||
@ -61,15 +61,16 @@ salesforceHelper name profileUri authorizeUri tokenUri scopes clientId clientSec
|
|||||||
= authOAuth2 name oauth2 $ \manager token -> do
|
= authOAuth2 name oauth2 $ \manager token -> do
|
||||||
(User userId, userResponse) <- authGetProfile name manager token profileUri
|
(User userId, userResponse) <- authGetProfile name manager token profileUri
|
||||||
|
|
||||||
pure Creds { credsPlugin = pluginName
|
pure Creds
|
||||||
, credsIdent = userId
|
{ credsPlugin = pluginName
|
||||||
, credsExtra = setExtra token userResponse
|
, credsIdent = userId
|
||||||
}
|
, credsExtra = setExtra token userResponse
|
||||||
|
}
|
||||||
where
|
where
|
||||||
oauth2 = OAuth2
|
oauth2 = OAuth2
|
||||||
{ oauth2ClientId = clientId
|
{ oauth2ClientId = clientId
|
||||||
, oauth2ClientSecret = Just clientSecret
|
, oauth2ClientSecret = Just clientSecret
|
||||||
, oauth2AuthorizeEndpoint = authorizeUri `withQuery` [scopeParam " " scopes]
|
, oauth2AuthorizeEndpoint = authorizeUri `withQuery` [scopeParam " " scopes]
|
||||||
, oauth2TokenEndpoint = tokenUri
|
, oauth2TokenEndpoint = tokenUri
|
||||||
, oauth2RedirectUri = Nothing
|
, oauth2RedirectUri = Nothing
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,7 +14,7 @@ module Yesod.Auth.OAuth2.Slack
|
|||||||
import Yesod.Auth.OAuth2.Prelude
|
import Yesod.Auth.OAuth2.Prelude
|
||||||
|
|
||||||
import Network.HTTP.Client
|
import Network.HTTP.Client
|
||||||
(httpLbs, parseUrlThrow, responseBody, setQueryString)
|
(httpLbs, parseUrlThrow, responseBody, setQueryString)
|
||||||
import Yesod.Auth.OAuth2.Exception as YesodOAuth2Exception
|
import Yesod.Auth.OAuth2.Exception as YesodOAuth2Exception
|
||||||
|
|
||||||
data SlackScope
|
data SlackScope
|
||||||
@ -24,9 +24,9 @@ data SlackScope
|
|||||||
| SlackAvatarScope
|
| SlackAvatarScope
|
||||||
|
|
||||||
scopeText :: SlackScope -> Text
|
scopeText :: SlackScope -> Text
|
||||||
scopeText SlackBasicScope = "identity.basic"
|
scopeText SlackBasicScope = "identity.basic"
|
||||||
scopeText SlackEmailScope = "identity.email"
|
scopeText SlackEmailScope = "identity.email"
|
||||||
scopeText SlackTeamScope = "identity.team"
|
scopeText SlackTeamScope = "identity.team"
|
||||||
scopeText SlackAvatarScope = "identity.avatar"
|
scopeText SlackAvatarScope = "identity.avatar"
|
||||||
|
|
||||||
newtype User = User Text
|
newtype User = User Text
|
||||||
@ -56,20 +56,20 @@ oauth2SlackScoped scopes clientId clientSecret =
|
|||||||
|
|
||||||
either
|
either
|
||||||
(throwIO . YesodOAuth2Exception.JSONDecodingError pluginName)
|
(throwIO . YesodOAuth2Exception.JSONDecodingError pluginName)
|
||||||
(\(User userId) -> pure Creds { credsPlugin = pluginName
|
(\(User userId) -> pure Creds
|
||||||
, credsIdent = userId
|
{ credsPlugin = pluginName
|
||||||
, credsExtra = setExtra token userResponse
|
, credsIdent = userId
|
||||||
}
|
, credsExtra = setExtra token userResponse
|
||||||
|
}
|
||||||
)
|
)
|
||||||
$ eitherDecode userResponse
|
$ eitherDecode userResponse
|
||||||
where
|
where
|
||||||
oauth2 = OAuth2
|
oauth2 = OAuth2
|
||||||
{ oauth2ClientId = clientId
|
{ oauth2ClientId = clientId
|
||||||
, oauth2ClientSecret = Just clientSecret
|
, oauth2ClientSecret = Just clientSecret
|
||||||
, oauth2AuthorizeEndpoint = "https://slack.com/oauth/authorize"
|
, oauth2AuthorizeEndpoint =
|
||||||
`withQuery` [ scopeParam ","
|
"https://slack.com/oauth/authorize"
|
||||||
$ map scopeText scopes
|
`withQuery` [scopeParam "," $ map scopeText scopes]
|
||||||
]
|
, oauth2TokenEndpoint = "https://slack.com/api/oauth.access"
|
||||||
, oauth2TokenEndpoint = "https://slack.com/api/oauth.access"
|
, oauth2RedirectUri = Nothing
|
||||||
, oauth2RedirectUri = Nothing
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,16 +26,18 @@ oauth2Spotify scopes clientId clientSecret =
|
|||||||
token
|
token
|
||||||
"https://api.spotify.com/v1/me"
|
"https://api.spotify.com/v1/me"
|
||||||
|
|
||||||
pure Creds { credsPlugin = pluginName
|
pure Creds
|
||||||
, credsIdent = userId
|
{ credsPlugin = pluginName
|
||||||
, credsExtra = setExtra token userResponse
|
, credsIdent = userId
|
||||||
}
|
, credsExtra = setExtra token userResponse
|
||||||
|
}
|
||||||
where
|
where
|
||||||
oauth2 = OAuth2
|
oauth2 = OAuth2
|
||||||
{ oauth2ClientId = clientId
|
{ oauth2ClientId = clientId
|
||||||
, oauth2ClientSecret = Just clientSecret
|
, oauth2ClientSecret = Just clientSecret
|
||||||
, oauth2AuthorizeEndpoint = "https://accounts.spotify.com/authorize"
|
, oauth2AuthorizeEndpoint =
|
||||||
`withQuery` [scopeParam " " scopes]
|
"https://accounts.spotify.com/authorize"
|
||||||
, oauth2TokenEndpoint = "https://accounts.spotify.com/api/token"
|
`withQuery` [scopeParam " " scopes]
|
||||||
, oauth2RedirectUri = Nothing
|
, oauth2TokenEndpoint = "https://accounts.spotify.com/api/token"
|
||||||
|
, oauth2RedirectUri = Nothing
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,19 +38,22 @@ oauth2TwitchScoped scopes clientId clientSecret =
|
|||||||
token
|
token
|
||||||
"https://id.twitch.tv/oauth2/validate"
|
"https://id.twitch.tv/oauth2/validate"
|
||||||
|
|
||||||
pure Creds { credsPlugin = pluginName
|
pure Creds
|
||||||
, credsIdent = userId
|
{ credsPlugin = pluginName
|
||||||
, credsExtra = setExtra token userResponse
|
, credsIdent = userId
|
||||||
}
|
, credsExtra = setExtra token userResponse
|
||||||
|
}
|
||||||
where
|
where
|
||||||
oauth2 = OAuth2
|
oauth2 = OAuth2
|
||||||
{ oauth2ClientId = clientId
|
{ oauth2ClientId = clientId
|
||||||
, oauth2ClientSecret = Just clientSecret
|
, oauth2ClientSecret = Just clientSecret
|
||||||
, oauth2AuthorizeEndpoint = "https://id.twitch.tv/oauth2/authorize"
|
, oauth2AuthorizeEndpoint =
|
||||||
`withQuery` [scopeParam " " scopes]
|
"https://id.twitch.tv/oauth2/authorize"
|
||||||
, oauth2TokenEndpoint = "https://id.twitch.tv/oauth2/token"
|
`withQuery` [scopeParam " " scopes]
|
||||||
`withQuery` [ ("client_id", T.encodeUtf8 clientId)
|
, oauth2TokenEndpoint =
|
||||||
, ("client_secret", T.encodeUtf8 clientSecret)
|
"https://id.twitch.tv/oauth2/token"
|
||||||
]
|
`withQuery` [ ("client_id", T.encodeUtf8 clientId)
|
||||||
, oauth2RedirectUri = Nothing
|
, ("client_secret", T.encodeUtf8 clientSecret)
|
||||||
|
]
|
||||||
|
, oauth2RedirectUri = Nothing
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,15 +33,16 @@ oauth2Upcase clientId clientSecret =
|
|||||||
token
|
token
|
||||||
"http://upcase.com/api/v1/me.json"
|
"http://upcase.com/api/v1/me.json"
|
||||||
|
|
||||||
pure Creds { credsPlugin = pluginName
|
pure Creds
|
||||||
, credsIdent = T.pack $ show userId
|
{ credsPlugin = pluginName
|
||||||
, credsExtra = setExtra token userResponse
|
, credsIdent = T.pack $ show userId
|
||||||
}
|
, credsExtra = setExtra token userResponse
|
||||||
|
}
|
||||||
where
|
where
|
||||||
oauth2 = OAuth2
|
oauth2 = OAuth2
|
||||||
{ oauth2ClientId = clientId
|
{ oauth2ClientId = clientId
|
||||||
, oauth2ClientSecret = Just clientSecret
|
, oauth2ClientSecret = Just clientSecret
|
||||||
, oauth2AuthorizeEndpoint = "http://upcase.com/oauth/authorize"
|
, oauth2AuthorizeEndpoint = "http://upcase.com/oauth/authorize"
|
||||||
, oauth2TokenEndpoint = "http://upcase.com/oauth/token"
|
, oauth2TokenEndpoint = "http://upcase.com/oauth/token"
|
||||||
, oauth2RedirectUri = Nothing
|
, oauth2RedirectUri = Nothing
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,18 +28,19 @@ oauth2WordPressDotCom clientId clientSecret =
|
|||||||
token
|
token
|
||||||
"https://public-api.wordpress.com/rest/v1/me/"
|
"https://public-api.wordpress.com/rest/v1/me/"
|
||||||
|
|
||||||
pure Creds { credsPlugin = pluginName
|
pure Creds
|
||||||
, credsIdent = T.pack $ show userId
|
{ credsPlugin = pluginName
|
||||||
, credsExtra = setExtra token userResponse
|
, credsIdent = T.pack $ show userId
|
||||||
}
|
, credsExtra = setExtra token userResponse
|
||||||
|
}
|
||||||
|
|
||||||
where
|
where
|
||||||
oauth2 = OAuth2
|
oauth2 = OAuth2
|
||||||
{ oauth2ClientId = clientId
|
{ oauth2ClientId = clientId
|
||||||
, oauth2ClientSecret = Just clientSecret
|
, oauth2ClientSecret = Just clientSecret
|
||||||
, oauth2AuthorizeEndpoint =
|
, oauth2AuthorizeEndpoint =
|
||||||
"https://public-api.wordpress.com/oauth2/authorize"
|
"https://public-api.wordpress.com/oauth2/authorize"
|
||||||
`withQuery` [scopeParam "," ["auth"]]
|
`withQuery` [scopeParam "," ["auth"]]
|
||||||
, oauth2TokenEndpoint = "https://public-api.wordpress.com/oauth2/token"
|
, oauth2TokenEndpoint = "https://public-api.wordpress.com/oauth2/token"
|
||||||
, oauth2RedirectUri = Nothing
|
, oauth2RedirectUri = Nothing
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
module URI.ByteString.ExtensionSpec
|
module URI.ByteString.ExtensionSpec
|
||||||
( spec
|
( spec
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
|
|
||||||
@ -14,65 +14,68 @@ import URI.ByteString.QQ
|
|||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = do
|
spec = do
|
||||||
describe "IsString Scheme" $ it "works" $ do
|
describe "IsString Scheme" $ it "works" $ do
|
||||||
"https" `shouldBe` Scheme "https"
|
"https" `shouldBe` Scheme "https"
|
||||||
|
|
||||||
describe "IsString Host" $ it "works" $ do
|
describe "IsString Host" $ it "works" $ do
|
||||||
"example.com" `shouldBe` Host "example.com"
|
"example.com" `shouldBe` Host "example.com"
|
||||||
|
|
||||||
describe "IsString URIRef Relative" $ it "works" $ do
|
describe "IsString URIRef Relative" $ it "works" $ do
|
||||||
"example.com/foo?bar=baz"
|
"example.com/foo?bar=baz" `shouldBe` [relativeRef|example.com/foo?bar=baz|]
|
||||||
`shouldBe` [relativeRef|example.com/foo?bar=baz|]
|
|
||||||
|
|
||||||
describe "IsString URIRef Absolute" $ it "works" $ do
|
describe "IsString URIRef Absolute" $ it "works" $ do
|
||||||
"https://example.com/foo?bar=baz"
|
"https://example.com/foo?bar=baz"
|
||||||
`shouldBe` [uri|https://example.com/foo?bar=baz|]
|
`shouldBe` [uri|https://example.com/foo?bar=baz|]
|
||||||
|
|
||||||
describe "fromText" $ do
|
describe "fromText" $ do
|
||||||
it "returns Just a URI for valid values, as the quasi-quoter would" $ do
|
it "returns Just a URI for valid values, as the quasi-quoter would" $ do
|
||||||
fromText "http://example.com/foo?bar=baz"
|
fromText "http://example.com/foo?bar=baz"
|
||||||
`shouldBe` Just [uri|http://example.com/foo?bar=baz|]
|
`shouldBe` Just [uri|http://example.com/foo?bar=baz|]
|
||||||
|
|
||||||
it "returns Nothing for invalid values" $ do
|
it "returns Nothing for invalid values" $ do
|
||||||
fromText "Oh my, what did I do?" `shouldBe` Nothing
|
fromText "Oh my, what did I do?" `shouldBe` Nothing
|
||||||
|
|
||||||
describe "unsafeFromText" $ do
|
describe "unsafeFromText" $ do
|
||||||
it "returns a URI for valid values, as the quasi-quoter would" $ do
|
it "returns a URI for valid values, as the quasi-quoter would" $ do
|
||||||
unsafeFromText "http://example.com/foo?bar=baz"
|
unsafeFromText "http://example.com/foo?bar=baz"
|
||||||
`shouldBe` [uri|http://example.com/foo?bar=baz|]
|
`shouldBe` [uri|http://example.com/foo?bar=baz|]
|
||||||
|
|
||||||
it "raises for invalid values" $ do
|
it "raises for invalid values" $ do
|
||||||
evaluate (unsafeFromText "Oh my, what did I do?")
|
evaluate (unsafeFromText "Oh my, what did I do?")
|
||||||
`shouldThrow` errorContaining "MissingColon"
|
`shouldThrow` errorContaining "MissingColon"
|
||||||
|
|
||||||
describe "toText" $ do
|
describe "toText" $ do
|
||||||
it "serializes the URI to text" $ do
|
it "serializes the URI to text" $ do
|
||||||
toText [uri|https://example.com/foo?bar=baz|]
|
toText [uri|https://example.com/foo?bar=baz|]
|
||||||
`shouldBe` "https://example.com/foo?bar=baz"
|
`shouldBe` "https://example.com/foo?bar=baz"
|
||||||
|
|
||||||
describe "fromRelative" $ do
|
describe "fromRelative" $ do
|
||||||
it "makes a URI absolute with a given host" $ do
|
it "makes a URI absolute with a given host" $ do
|
||||||
fromRelative "ftp" "foo.com" [relativeRef|/bar?baz=bat|]
|
fromRelative "ftp" "foo.com" [relativeRef|/bar?baz=bat|]
|
||||||
`shouldBe` [uri|ftp://foo.com/bar?baz=bat|]
|
`shouldBe` [uri|ftp://foo.com/bar?baz=bat|]
|
||||||
|
|
||||||
describe "withQuery" $ do
|
describe "withQuery" $ do
|
||||||
it "appends a query to a URI" $ do
|
it "appends a query to a URI" $ do
|
||||||
let uriWithQuery = [uri|http://example.com|] `withQuery` [("foo", "bar")]
|
let uriWithQuery = [uri|http://example.com|] `withQuery` [("foo", "bar")]
|
||||||
|
|
||||||
uriWithQuery `shouldBe` [uri|http://example.com?foo=bar|]
|
uriWithQuery `shouldBe` [uri|http://example.com?foo=bar|]
|
||||||
|
|
||||||
it "handles a URI with an existing query" $ do
|
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|]
|
uriWithQuery `shouldBe` [uri|http://example.com?foo=bar&baz=bat|]
|
||||||
|
|
||||||
-- This is arguably testing the internals of another package, but IMO
|
-- This is arguably testing the internals of another package, but IMO
|
||||||
-- it's worthwhile to show that you don't (and can't) pre-sanitize when
|
-- it's worthwhile to show that you don't (and can't) pre-sanitize when
|
||||||
-- using this function.
|
-- using this function.
|
||||||
it "handles santization of the query" $ do
|
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"
|
toText uriWithQuery `shouldBe` "http://example.com?foo=bar%20baz"
|
||||||
|
|
||||||
errorContaining :: String -> Selector ErrorCall
|
errorContaining :: String -> Selector ErrorCall
|
||||||
errorContaining msg = (msg `isInfixOf`) . show
|
errorContaining msg = (msg `isInfixOf`) . show
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user