yesod-auth-oauth2 0.1.10 → 0.2.0
raw patch · 2 files changed
+129/−1 lines, 2 filesnew-uploader
Files
- Yesod/Auth/OAuth2/Slack.hs +127/−0
- yesod-auth-oauth2.cabal +2/−1
+ Yesod/Auth/OAuth2/Slack.hs view
@@ -0,0 +1,127 @@+{-# LANGUAGE OverloadedStrings #-}+--+-- OAuth2 plugin for https://slack.com/+--+-- * Authenticates against slack+-- * Uses slack user id as credentials identifier+-- * Returns name, access_token, email, avatar, team_id, and team_name as extras+--+module Yesod.Auth.OAuth2.Slack+ ( SlackScope(..)+ , oauth2Slack+ , oauth2SlackScoped+ ) where++import Data.Aeson+import Yesod.Auth+import Yesod.Auth.OAuth2++import Control.Exception.Lifted (throwIO)+import Data.Maybe (catMaybes)+import Data.Monoid ((<>))+import Data.Text (Text)+import Data.Text.Encoding (decodeUtf8, encodeUtf8)+import Network.HTTP.Conduit (Manager)++import qualified Data.Text as Text+import qualified Network.HTTP.Conduit as HTTP++data SlackScope+ = SlackEmailScope+ | SlackTeamScope+ | SlackAvatarScope++data SlackUser = SlackUser+ { slackUserId :: Text+ , slackUserName :: Text+ , slackUserEmail :: Maybe Text+ , slackUserAvatarUrl :: Maybe Text+ , slackUserTeam :: Maybe SlackTeam+ }++data SlackTeam = SlackTeam+ { slackTeamId :: Text+ , slackTeamName :: Text+ }++instance FromJSON SlackUser where+ parseJSON = withObject "root" $ \root -> do+ user <- root .: "user"++ SlackUser+ <$> user .: "id"+ <*> user .: "name"+ <*> user .:? "email"+ <*> user .:? "image_512"+ <*> root .:? "team"++instance FromJSON SlackTeam where+ parseJSON = withObject "team" $ \team ->+ SlackTeam+ <$> team .: "id"+ <*> team .: "name"++-- | Auth with Slack+--+-- Requests @identity.basic@ scopes and uses the user's Slack ID as the @'Creds'@+-- identifier.+--+oauth2Slack :: YesodAuth m+ => Text -- ^ Client ID+ -> Text -- ^ Client Secret+ -> AuthPlugin m+oauth2Slack clientId clientSecret = oauth2SlackScoped clientId clientSecret []++-- | Auth with Slack+--+-- Requests custom scopes and uses the user's Slack ID as the @'Creds'@+-- identifier.+--+oauth2SlackScoped :: YesodAuth m+ => Text -- ^ Client ID+ -> Text -- ^ Client Secret+ -> [SlackScope]+ -> AuthPlugin m+oauth2SlackScoped clientId clientSecret scopes =+ authOAuth2 "slack" oauth fetchSlackProfile+ where+ oauth = OAuth2+ { oauthClientId = encodeUtf8 clientId+ , oauthClientSecret = encodeUtf8 clientSecret+ , oauthOAuthorizeEndpoint =+ encodeUtf8+ $ "https://slack.com/oauth/authorize?scope="+ <> Text.intercalate "," scopeTexts+ , oauthAccessTokenEndpoint = "https://slack.com/api/oauth.access"+ , oauthCallback = Nothing+ }+ scopeTexts = "identity.basic":map scopeText scopes++scopeText :: SlackScope -> Text+scopeText SlackEmailScope = "identity.email"+scopeText SlackTeamScope = "identity.team"+scopeText SlackAvatarScope = "identity.avatar"++fetchSlackProfile :: Manager -> AccessToken -> IO (Creds m)+fetchSlackProfile manager token = do+ request+ <- HTTP.setQueryString [("token", Just $ accessToken token)]+ <$> HTTP.parseUrl "https://slack.com/api/users.identity"+ body <- HTTP.responseBody <$> HTTP.httpLbs request manager+ case eitherDecode body of+ Left _ -> throwIO $ InvalidProfileResponse "slack" body+ Right u -> return $ toCreds u token++toCreds :: SlackUser -> AccessToken -> Creds m+toCreds user token = Creds+ { credsPlugin = "slack"+ , credsIdent = slackUserId user+ , credsExtra = catMaybes+ [ Just ("name", slackUserName user)+ , Just ("access_token", decodeUtf8 $ accessToken token)+ , (,) <$> pure "email" <*> slackUserEmail user+ , (,) <$> pure "avatar" <*> slackUserAvatarUrl user+ , (,) <$> pure "team_name" <*> (slackTeamName <$> slackUserTeam user)+ , (,) <$> pure "team_id" <*> (slackTeamId <$> slackUserTeam user)+ ]+ }
yesod-auth-oauth2.cabal view
@@ -1,5 +1,5 @@ name: yesod-auth-oauth2-version: 0.1.10+version: 0.2.0 license: BSD3 license-file: LICENSE author: Tom Streller@@ -50,6 +50,7 @@ Yesod.Auth.OAuth2.Upcase Yesod.Auth.OAuth2.EveOnline Yesod.Auth.OAuth2.Nylas+ Yesod.Auth.OAuth2.Slack ghc-options: -Wall