packages feed

hoauth2-providers-tutorial 0.2 → 0.3.0

raw patch · 2 files changed

+95/−93 lines, 2 filesdep −aesondep −waidep −warpdep ~containersdep ~hoauth2dep ~hoauth2-providersPVP ok

version bump matches the API change (PVP)

Dependencies removed: aeson, wai, warp

Dependency ranges changed: containers, hoauth2, hoauth2-providers, text, transformers

API changes (from Hackage documentation)

- HOAuth2ProvidersTutorial: [$sel:email:DemoUser] :: DemoUser -> Maybe Text
- HOAuth2ProvidersTutorial: [$sel:name:DemoUser] :: DemoUser -> Text
- HOAuth2ProvidersTutorial: testAuth0App :: IdpApplication 'AuthorizationCode Auth0
- HOAuth2ProvidersTutorial: testAuth0Idp :: Idp Auth0
- HOAuth2ProvidersTutorial: testGoogleApp :: IdpApplication 'AuthorizationCode Google
- HOAuth2ProvidersTutorial: testGoogleIdp :: Idp Google
- HOAuth2ProvidersTutorial: uriToText :: URI -> Text
+ HOAuth2ProvidersTutorial: [email] :: DemoUser -> Maybe Text
+ HOAuth2ProvidersTutorial: [name] :: DemoUser -> Text
+ HOAuth2ProvidersTutorial: mkTestAuth0App :: ExceptT Text IO (IdpApplication Auth0 AuthorizationCodeApplication)
+ HOAuth2ProvidersTutorial: mkTestAuth0Idp :: ExceptT Text IO (Idp Auth0)
+ HOAuth2ProvidersTutorial: mkTestGoogleApp :: IdpApplication Google AuthorizationCodeApplication
- HOAuth2ProvidersTutorial: callbackH :: IORef (Maybe DemoUser) -> ActionM ()
+ HOAuth2ProvidersTutorial: callbackH :: IdpApplication Auth0 AuthorizationCodeApplication -> IdpApplication Google AuthorizationCodeApplication -> IORef (Maybe DemoUser) -> ActionM ()
- HOAuth2ProvidersTutorial: handleAuth0Callback :: ExchangeToken -> ExceptT Text IO DemoUser
+ HOAuth2ProvidersTutorial: handleAuth0Callback :: IdpApplication Auth0 AuthorizationCodeApplication -> ExchangeToken -> ExceptT Text IO DemoUser
- HOAuth2ProvidersTutorial: handleGoogleCallback :: ExchangeToken -> ExceptT Text IO DemoUser
+ HOAuth2ProvidersTutorial: handleGoogleCallback :: IdpApplication Google AuthorizationCodeApplication -> ExchangeToken -> ExceptT Text IO DemoUser
- HOAuth2ProvidersTutorial: loginAuth0H :: ActionM ()
+ HOAuth2ProvidersTutorial: loginAuth0H :: IdpApplication Auth0 AuthorizationCodeApplication -> ActionM ()
- HOAuth2ProvidersTutorial: loginGoogleH :: ActionM ()
+ HOAuth2ProvidersTutorial: loginGoogleH :: IdpApplication Google AuthorizationCodeApplication -> ActionM ()
- HOAuth2ProvidersTutorial: oauth2ErrorToText :: TokenRequestError -> Text
+ HOAuth2ProvidersTutorial: oauth2ErrorToText :: TokenResponseError -> Text

Files

hoauth2-providers-tutorial.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name:          hoauth2-providers-tutorial-version:       0.2+version:       0.3.0 synopsis:      tutorial for hoauth2-providers module description:   A tutorial that demostrates how to use hoauth2-providers package@@ -11,24 +11,22 @@ author:        Haisheng Wu (M) maintainer:    freizl@gmail.com category:      Network+tested-with:   GHC <=9.6.1  library   exposed-modules:  HOAuth2ProvidersTutorial   build-depends:-    , aeson              >=2.0    && <2.2     , base               >=4.14   && <5     , bytestring         >=0.9    && <0.12-    , containers-    , hoauth2            >=2.7-    , hoauth2-providers  >=0.2+    , containers         ^>=0.6+    , hoauth2            ^>=2.9+    , hoauth2-providers  ^>=0.3     , http-conduit       >=2.1    && <2.4     , http-types         >=0.11   && <0.13     , scotty             >=0.10.0 && <0.13-    , text               >=0.11   && <1.3-    , transformers       ^>=0.5+    , text               >=0.11   && <2.1+    , transformers       >=0.4    && <0.7     , uri-bytestring     >=0.2.3  && <0.4-    , wai                ^>=3.2-    , warp               >=3.2    && <3.4    hs-source-dirs:   src   default-language: Haskell2010@@ -36,14 +34,11 @@     -Wall -Wtabs -Wno-unused-do-bind -Wunused-packages -Wpartial-fields     -Wwarnings-deprecations +  if impl(ghc <9.4)+    ghc-options: -Wno-unticked-promoted-constructors+ executable hoauth2-providers-tutorial   main-is:          Main.hs--  -- Modules included in this executable, other than Main.-  -- other-modules:--  -- LANGUAGE extensions used by modules in this package.-  -- other-extensions:   build-depends:     , base                        >=4.14 && <5     , hoauth2-providers-tutorial
src/HOAuth2ProvidersTutorial.hs view
@@ -1,10 +1,7 @@ {-# LANGUAGE DataKinds #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE RankNTypes #-} {-# LANGUAGE RecordWildCards #-}  module HOAuth2ProvidersTutorial where@@ -13,30 +10,23 @@ import Control.Monad.Trans.Except import Data.ByteString.Lazy.Char8 qualified as BSL import Data.IORef (IORef, newIORef, readIORef, writeIORef)+import Data.Map.Strict qualified as Map import Data.Set qualified as Set-import Data.Text.Encoding qualified as T+import Data.Text.Lazy (Text) import Data.Text.Lazy qualified as TL import Network.HTTP.Conduit (newManager, tlsManagerSettings) import Network.HTTP.Types (status302) import Network.OAuth.OAuth2 (   ExchangeToken (ExchangeToken),   OAuth2Token (accessToken),-  TokenRequestError,+  TokenResponseError,  ) import Network.OAuth2.Experiment-import Network.OAuth2.Provider.Auth0 (-  Auth0,-  Auth0User (Auth0User, email, name, sub),-  defaultAuth0App,-  defaultAuth0Idp,- )-import Network.OAuth2.Provider.Google (-  Google,-  GoogleUser (GoogleUser, email, id, name),-  defaultGoogleApp,-  defaultGoogleIdp,- )-import URI.ByteString (URI, serializeURIRef')+import Network.OAuth2.Provider+import Network.OAuth2.Provider.Auth0 (Auth0User (..), mkAuth0Idp)+import Network.OAuth2.Provider.Auth0 qualified as Auth0+import Network.OAuth2.Provider.Google (GoogleUser (..))+import Network.OAuth2.Provider.Google qualified as Google import URI.ByteString.QQ (uri) import Web.Scotty (ActionM, scotty) import Web.Scotty qualified as Scotty@@ -48,38 +38,44 @@  ------------------------------ -testAuth0App :: IdpApplication 'AuthorizationCode Auth0-testAuth0App =-  (defaultAuth0App testAuth0Idp)-    { idpAppClientId = ""-    , idpAppClientSecret = ""-    , idpAppAuthorizeState = AuthorizeState ("auth0." <> randomStateValue)-    , idpAppScope = Set.fromList ["openid", "email", "profile"]-    , idpAppRedirectUri = [uri|http://localhost:9988/oauth2/callback|]-    , idpAppName = "foo-auth0-app"-    }--testAuth0Idp :: Idp Auth0-testAuth0Idp =-  defaultAuth0Idp-    { idpUserInfoEndpoint = [uri|https://freizl.auth0.com/userinfo|]-    , idpAuthorizeEndpoint = [uri|https://freizl.auth0.com/authorize|]-    , idpTokenEndpoint = [uri|https://freizl.auth0.com/oauth/token|]-    }+mkTestAuth0App :: ExceptT Text IO (IdpApplication Auth0 AuthorizationCodeApplication)+mkTestAuth0App = do+  idp <- mkTestAuth0Idp+  let application =+        AuthorizationCodeApplication+          { acClientId = ""+          , acClientSecret = ""+          , acAuthorizeState = AuthorizeState ("auth0." <> randomStateValue)+          , acScope = Set.fromList ["openid", "email", "profile"]+          , acRedirectUri = [uri|http://localhost:9988/oauth2/callback|]+          , acName = "foo-auth0-app"+          , acAuthorizeRequestExtraParams = Map.empty+          , acTokenRequestAuthenticationMethod = ClientSecretBasic+          }+  pure IdpApplication {..} -testGoogleIdp :: Idp Google-testGoogleIdp = defaultGoogleIdp+mkTestAuth0Idp :: ExceptT Text IO (Idp Auth0)+mkTestAuth0Idp = mkAuth0Idp "freizl.auth0.com" -testGoogleApp :: IdpApplication 'AuthorizationCode Google-testGoogleApp =-  defaultGoogleApp-    { idpAppClientId = ""-    , idpAppClientSecret = ""-    , idpAppAuthorizeState = AuthorizeState ("google." <> randomStateValue)-    , idpAppRedirectUri = [uri|http://localhost:9988/oauth2/callback|]-    , idpAppName = "foo-google-app"-    , idp = testGoogleIdp-    }+mkTestGoogleApp :: IdpApplication Google AuthorizationCodeApplication+mkTestGoogleApp =+  let application =+        AuthorizationCodeApplication+          { acClientId = ""+          , acClientSecret = ""+          , acAuthorizeState = AuthorizeState ("google." <> randomStateValue)+          , acRedirectUri = [uri|http://localhost:9988/oauth2/callback|]+          , acScope =+              Set.fromList+                [ "https://www.googleapis.com/auth/userinfo.email"+                , "https://www.googleapis.com/auth/userinfo.profile"+                ]+          , acName = "foo-google-app"+          , acAuthorizeRequestExtraParams = Map.empty+          , acTokenRequestAuthenticationMethod = ClientSecretBasic+          }+      idp = Google.defaultGoogleIdp+   in IdpApplication {..}  -- | You'll need to find out an better way to create @state@ -- which is recommended in <https://www.rfc-editor.org/rfc/rfc6749#section-10.12>@@ -100,14 +96,20 @@ -- | The 'scotty' application app :: IO () app = do-  -- Poor man's solution for creating user session.-  refUser <- newIORef Nothing-  scotty 9988 $ do-    Scotty.get "/" $ indexH refUser-    Scotty.get "/login/auth0" loginAuth0H-    Scotty.get "/login/google" loginGoogleH-    Scotty.get "/logout" (logoutH refUser)-    Scotty.get "/oauth2/callback" $ callbackH refUser+  eAuth0App <- runExceptT mkTestAuth0App+  either (error . TL.unpack) runApp eAuth0App+  where+    runApp :: IdpApplication Auth0 AuthorizationCodeApplication -> IO ()+    runApp auth0App = do+      -- Poor man's solution for creating user session.+      refUser <- newIORef Nothing+      let googleApp = mkTestGoogleApp+      scotty 9988 $ do+        Scotty.get "/" $ indexH refUser+        Scotty.get "/login/auth0" (loginAuth0H auth0App)+        Scotty.get "/login/google" (loginGoogleH googleApp)+        Scotty.get "/logout" (logoutH refUser)+        Scotty.get "/oauth2/callback" $ callbackH auth0App googleApp refUser  -- | @/@ endpoint handler indexH :: IORef (Maybe DemoUser) -> ActionM ()@@ -138,15 +140,15 @@   Scotty.html . mconcat $ "<h1>hoauth2 providers Tutorial</h1>" : info  -- | @/login/auth0@ endpoint handler-loginAuth0H :: ActionM ()-loginAuth0H = do-  Scotty.setHeader "Location" (mkAuthorizeRequest testAuth0App)+loginAuth0H :: IdpApplication Auth0 AuthorizationCodeApplication -> ActionM ()+loginAuth0H auth0App = do+  Scotty.setHeader "Location" (TL.fromStrict $ uriToText $ mkAuthorizationRequest auth0App)   Scotty.status status302  -- | @/login/google@ endpoint handler-loginGoogleH :: ActionM ()-loginGoogleH = do-  Scotty.setHeader "Location" (mkAuthorizeRequest testGoogleApp)+loginGoogleH :: IdpApplication Google AuthorizationCodeApplication -> ActionM ()+loginGoogleH googleApp = do+  Scotty.setHeader "Location" (TL.fromStrict $ uriToText $ mkAuthorizationRequest googleApp)   Scotty.status status302  -- | @/logout@ endpoint handler@@ -156,8 +158,12 @@   Scotty.redirect "/"  -- | @/oauth2/callback@ endpoint handler-callbackH :: IORef (Maybe DemoUser) -> ActionM ()-callbackH refUser = do+callbackH ::+  IdpApplication Auth0 AuthorizationCodeApplication ->+  IdpApplication Google AuthorizationCodeApplication ->+  IORef (Maybe DemoUser) ->+  ActionM ()+callbackH auth0App googleApp refUser = do   pas <- Scotty.params    excepttToActionM $ do@@ -168,28 +174,32 @@     let idpName = TL.takeWhile ('.' /=) state      user <- case idpName of-      "google" -> handleGoogleCallback code-      "auth0" -> handleAuth0Callback code+      "google" -> handleGoogleCallback googleApp code+      "auth0" -> handleAuth0Callback auth0App code       _ -> throwE $ "unable to find idp app of: " <> idpName      liftIO $ writeIORef refUser (Just user)    Scotty.redirect "/" -handleAuth0Callback :: ExchangeToken -> ExceptT TL.Text IO DemoUser-handleAuth0Callback code = do-  let idpApp = testAuth0App+handleAuth0Callback ::+  IdpApplication Auth0 AuthorizationCodeApplication ->+  ExchangeToken ->+  ExceptT TL.Text IO DemoUser+handleAuth0Callback idpApp code = do   mgr <- liftIO $ newManager tlsManagerSettings   tokenResp <- withExceptT oauth2ErrorToText (conduitTokenRequest idpApp mgr code)-  Auth0User {..} <- withExceptT bslToText $ conduitUserInfoRequest idpApp mgr (accessToken tokenResp)+  Auth0User {..} <- withExceptT bslToText $ Auth0.fetchUserInfo idpApp mgr (accessToken tokenResp)   pure (DemoUser name (Just email)) -handleGoogleCallback :: ExchangeToken -> ExceptT TL.Text IO DemoUser-handleGoogleCallback code = do-  let idpApp = testGoogleApp+handleGoogleCallback ::+  IdpApplication Google AuthorizationCodeApplication ->+  ExchangeToken ->+  ExceptT TL.Text IO DemoUser+handleGoogleCallback idpApp code = do   mgr <- liftIO $ newManager tlsManagerSettings   tokenResp <- withExceptT oauth2ErrorToText (conduitTokenRequest idpApp mgr code)-  GoogleUser {..} <- withExceptT bslToText $ conduitUserInfoRequest idpApp mgr (accessToken tokenResp)+  GoogleUser {..} <- withExceptT bslToText $ Google.fetchUserInfo idpApp mgr (accessToken tokenResp)   pure (DemoUser name (Just email))  ------------------------------@@ -198,9 +208,6 @@  ------------------------------ -uriToText :: URI -> TL.Text-uriToText = TL.fromStrict . T.decodeUtf8 . serializeURIRef'- bslToText :: BSL.ByteString -> TL.Text bslToText = TL.pack . BSL.unpack @@ -225,5 +232,5 @@   result <- liftIO $ runExceptT e   either Scotty.raise pure result -oauth2ErrorToText :: TokenRequestError -> TL.Text+oauth2ErrorToText :: TokenResponseError -> TL.Text oauth2ErrorToText e = TL.pack $ "Unable fetch access token. error detail: " ++ show e