packages feed

yesod-auth-oauth2 0.1.2 → 0.1.3

raw patch · 4 files changed

+246/−8 lines, 4 filesdep +http-clientdep +vectordep ~aeson

Dependencies added: http-client, vector

Dependency ranges changed: aeson

Files

Yesod/Auth/OAuth2.hs view
@@ -11,6 +11,7 @@ -- module Yesod.Auth.OAuth2     ( authOAuth2+    , authOAuth2Widget     , oauth2Url     , fromProfileURL     , YesodOAuth2Exception(..)@@ -47,6 +48,10 @@ oauth2Url :: Text -> AuthRoute oauth2Url name = PluginR name ["forward"] +-- | Create an @'AuthPlugin'@ for the given OAuth2 provider+--+-- Presents a generic @"Login via name"@ link+-- authOAuth2 :: YesodAuth m            => Text   -- ^ Service name            -> OAuth2 -- ^ Service details@@ -57,10 +62,22 @@            --   second authorized request to @api/me.json@.            --            --   See @'fromProfileURL'@ for an example.-           --            -> AuthPlugin m-authOAuth2 name oauth getCreds = AuthPlugin name dispatch login+authOAuth2 name = authOAuth2Widget [whamlet|Login via #{name}|] name +-- | Create an @'AuthPlugin'@ for the given OAuth2 provider+--+-- Allows passing a custom widget for the login link. See @'oauth2Eve'@ for an+-- example.+--+authOAuth2Widget :: YesodAuth m+                 => WidgetT m IO ()+                 -> Text+                 -> OAuth2+                 -> (Manager -> AccessToken -> IO (Creds m))+                 -> AuthPlugin m+authOAuth2Widget widget name oauth getCreds = AuthPlugin name dispatch login+   where     url = PluginR name ["callback"] @@ -104,11 +121,9 @@     tokenSessionKey :: Text     tokenSessionKey = "_yesod_oauth2_" <> name -    login tm = [whamlet|-        <a href=@{tm $ oauth2Url name}>Login via #{name}-        |]+    login tm = [whamlet|<a href=@{tm $ oauth2Url name}>^{widget}|] --- | Handle the common case of fetching Profile information a JSON endpoint+-- | Handle the common case of fetching Profile information from a JSON endpoint -- -- Throws @'InvalidProfileResponse'@ if JSON parsing fails --
+ Yesod/Auth/OAuth2/EveOnline.hs view
@@ -0,0 +1,115 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+-- |+--+-- OAuth2 plugin for http://eveonline.com+--+-- * Authenticates against eveonline+-- * Uses EVEs unique account-user-char-hash as credentials identifier+-- * Returns charName, charId, tokenType, accessToken and expires as extras+--+module Yesod.Auth.OAuth2.EveOnline+    ( oauth2Eve+    , oauth2EveScoped+    , WidgetType(..)+    , module Yesod.Auth.OAuth2+    ) where++#if __GLASGOW_HASKELL__ < 710+import Control.Applicative ((<$>), (<*>))+#endif++import Control.Exception.Lifted+import Control.Monad (mzero)+import Data.Aeson+import Data.Monoid ((<>))+import Data.Text (Text)+import Data.Text.Encoding (encodeUtf8, decodeUtf8)+import Network.HTTP.Conduit (Manager)+import Yesod.Auth+import Yesod.Auth.OAuth2+import Yesod.Core.Widget++import qualified Data.Text as T++data WidgetType m+    = Plain -- ^ Simple "Login via eveonline" text+    | BigWhite+    | SmallWhite+    | BigBlack+    | SmallBlack+    | Custom (WidgetT m IO ())++data EveUser = EveUser+    { eveUserName :: Text+    , eveUserExpire :: Text+    , eveTokenType :: Text+    , eveCharOwnerHash :: Text+    , eveCharId :: Integer+    }++instance FromJSON EveUser where+    parseJSON (Object o) = EveUser+        <$> o .: "CharacterName"+        <*> o .: "ExpiresOn"+        <*> o .: "TokenType"+        <*> o .: "CharacterOwnerHash"+        <*> o .: "CharacterID"++    parseJSON _ = mzero++oauth2Eve :: YesodAuth m+          => Text -- ^ Client ID+          -> Text -- ^ Client Secret+          -> WidgetType m+          -> AuthPlugin m+oauth2Eve clientId clientSecret = oauth2EveScoped clientId clientSecret ["publicData"] . asWidget++  where+    asWidget :: YesodAuth m => WidgetType m -> WidgetT m IO ()+    asWidget Plain = [whamlet|Login via eveonline|]+    asWidget BigWhite = [whamlet|<img src="https://images.contentful.com/idjq7aai9ylm/4PTzeiAshqiM8osU2giO0Y/5cc4cb60bac52422da2e45db87b6819c/EVE_SSO_Login_Buttons_Large_White.png?w=270&h=45">|]+    asWidget BigBlack = [whamlet|<img src="https://images.contentful.com/idjq7aai9ylm/4fSjj56uD6CYwYyus4KmES/4f6385c91e6de56274d99496e6adebab/EVE_SSO_Login_Buttons_Large_Black.png?w=270&h=45">|]+    asWidget SmallWhite = [whamlet|<img src="https://images.contentful.com/idjq7aai9ylm/18BxKSXCymyqY4QKo8KwKe/c2bdded6118472dd587c8107f24104d7/EVE_SSO_Login_Buttons_Small_White.png?w=195&h=30">|]+    asWidget SmallBlack = [whamlet|<img src="https://images.contentful.com/idjq7aai9ylm/12vrPsIMBQi28QwCGOAqGk/33234da7672c6b0cdca394fc8e0b1c2b/EVE_SSO_Login_Buttons_Small_Black.png?w=195&h=30">|]+    asWidget (Custom a) = a++oauth2EveScoped :: YesodAuth m+                => Text -- ^ Client ID+                -> Text -- ^ Client Secret+                -> [Text] -- ^ List of scopes to request+                -> WidgetT m IO () -- ^ Login widget+                -> AuthPlugin m+oauth2EveScoped clientId clientSecret scopes widget =+    authOAuth2Widget widget "eveonline" oauth fetchEveProfile++  where+    oauth = OAuth2+        { oauthClientId = encodeUtf8 clientId+        , oauthClientSecret = encodeUtf8 clientSecret+        , oauthOAuthorizeEndpoint = encodeUtf8 $ "https://login.eveonline.com/oauth/authorize?response_type=code&scope=" <> T.intercalate " " scopes+        , oauthAccessTokenEndpoint = "https://login.eveonline.com/oauth/token"+        , oauthCallback = Nothing+        }++fetchEveProfile :: Manager -> AccessToken -> IO (Creds m)+fetchEveProfile manager token = do+    userResult <- authGetJSON manager token "https://login.eveonline.com/oauth/verify"++    case userResult of+        Right user -> return $ toCreds user token+        Left err-> throwIO $ InvalidProfileResponse "eveonline" err++toCreds :: EveUser -> AccessToken -> Creds m+toCreds user token = Creds+    { credsPlugin = "eveonline"+    , credsIdent = T.pack $ show $ eveCharOwnerHash user+    , credsExtra =+        [ ("charName", eveUserName user)+        , ("charId", T.pack . show . eveCharId $ user)+        , ("tokenType", eveTokenType user)+        , ("expires", eveUserExpire user)+        , ("accessToken", decodeUtf8 . accessToken $ token)+        ]+    }
+ Yesod/Auth/OAuth2/Nylas.hs view
@@ -0,0 +1,104 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}++module Yesod.Auth.OAuth2.Nylas+    ( oauth2Nylas+    , module Yesod.Auth.OAuth2+    ) where++#if __GLASGOW_HASKELL__ < 710+import Control.Applicative ((<$>), (<*>))+#endif++import Control.Monad (mzero)+import Control.Exception.Lifted (throwIO)+import Data.Aeson (FromJSON, Value(..), parseJSON, decode, (.:))+import Data.Monoid ((<>))+import Data.Text (Text)+import Data.Text.Encoding (encodeUtf8, decodeUtf8)+import Data.Vector ((!?))+import Network.HTTP.Client (applyBasicAuth, parseUrl, httpLbs, responseStatus+                           , responseBody)+import Network.HTTP.Conduit (Manager)+import Yesod.Auth (Creds(..), YesodAuth, AuthPlugin)+import Yesod.Auth.OAuth2 (OAuth2(..), AccessToken(..)+                         , YesodOAuth2Exception(InvalidProfileResponse)+                         , authOAuth2)++import qualified Data.Text as T+import qualified Network.HTTP.Types as HT++data NylasNamespace = NylasNamespace+    { nylasNamespaceId :: Text+    , nylasNamespaceAccountId :: Text+    , nylasNamespaceEmailAddress :: Text+    , nylasNamespaceName :: Text+    , nylasNamespaceProvider :: Text+    , nylasNamespaceOrganizationUnit :: Text+    }++instance FromJSON NylasNamespace where+    parseJSON (Array singleton) = case singleton !? 0 of+        Just (Object o) -> NylasNamespace+            <$> o .: "id"+            <*> o .: "account_id"+            <*> o .: "email_address"+            <*> o .: "name"+            <*> o .: "provider"+            <*> o .: "organization_unit"+        _ -> mzero+    parseJSON _ = mzero++oauth2Nylas :: YesodAuth m+            => Text -- ^ Client ID+            -> Text -- ^ Client Secret+            -> AuthPlugin m+oauth2Nylas = oauth2NylasScoped ["email"]++oauth2NylasScoped :: YesodAuth m+                  => [Text] -- ^ Scopes+                  -> Text   -- ^ Client ID+                  -> Text   -- ^ Client Secret+                  -> AuthPlugin m+oauth2NylasScoped scopes clientId clientSecret =+    authOAuth2 "nylas" oauth fetchCreds+  where+    authorizeUrl = encodeUtf8+                 $ "https://api.nylas.com/oauth/authorize?scope="+                 <> T.intercalate "," scopes+    tokenUrl = "https://api.nylas.com/oauth/token"+    oauth = OAuth2+        { oauthClientId = encodeUtf8 clientId+        , oauthClientSecret = encodeUtf8 clientSecret+        , oauthOAuthorizeEndpoint = authorizeUrl+        , oauthAccessTokenEndpoint = tokenUrl+        , oauthCallback = Nothing+        }++fetchCreds :: Manager -> AccessToken -> IO (Creds a)+fetchCreds manager token = do+    req <- authorize <$> parseUrl "https://api.nylas.com/n"+    resp <- httpLbs req manager+    if HT.statusIsSuccessful (responseStatus resp)+        then case decode (responseBody resp) of+            Just ns -> return $ toCreds ns token+            Nothing -> throwIO parseFailure+        else throwIO requestFailure+  where+    authorize = applyBasicAuth (accessToken token) ""+    parseFailure = InvalidProfileResponse "nylas" "failed to parse namespace"+    requestFailure = InvalidProfileResponse "nylas" "failed to get namespace"++toCreds :: NylasNamespace -> AccessToken -> Creds a+toCreds ns token = Creds+    { credsPlugin = "nylas"+    , credsIdent = nylasNamespaceId ns+    , credsExtra =+        [ ("account_id", nylasNamespaceAccountId ns)+        , ("email_address", nylasNamespaceEmailAddress ns)+        , ("name", nylasNamespaceName ns)+        , ("provider", nylasNamespaceProvider ns)+        , ("organization_unit", nylasNamespaceOrganizationUnit ns)+        , ("access_token", decodeUtf8 $ accessToken token)+        ]+    }
yesod-auth-oauth2.cabal view
@@ -1,5 +1,5 @@ name:            yesod-auth-oauth2-version:         0.1.2+version:         0.1.3 license:         BSD3 license-file:    LICENSE author:          Tom Streller@@ -24,9 +24,10 @@      build-depends:   base                    >= 4.5       && < 5                    , bytestring              >= 0.9.1.4+                   , http-client             >= 0.4.0     && < 0.5                    , http-conduit            >= 2.0       && < 3.0                    , http-types              >= 0.8       && < 0.9-                   , aeson                   >= 0.6       && < 0.9+                   , aeson                   >= 0.6       && < 0.10                    , yesod-core              >= 1.2       && < 1.5                    , authenticate            >= 1.3.2.7   && < 1.4                    , random@@ -36,6 +37,7 @@                    , transformers            >= 0.2.2     && < 0.5                    , hoauth2                 >= 0.4.7     && < 0.5                    , lifted-base             >= 0.2       && < 0.4+                   , vector                  >= 0.10      && < 0.11      exposed-modules: Yesod.Auth.OAuth2                      Yesod.Auth.OAuth2.Github@@ -43,6 +45,8 @@                      Yesod.Auth.OAuth2.Spotify                      Yesod.Auth.OAuth2.Twitter                      Yesod.Auth.OAuth2.Upcase+                     Yesod.Auth.OAuth2.EveOnline+                     Yesod.Auth.OAuth2.Nylas      ghc-options:     -Wall