ms-graph-api 0.4.0.0 → 0.5.0.0
raw patch · 5 files changed
+42/−9 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ MSGraphAPI.Internal.Common: getE :: FromJSON a => [Text] -> Option 'Https -> AccessToken -> Req (Either HttpException a)
+ MSGraphAPI.Internal.Common: postE :: (ToJSON a, FromJSON b) => [Text] -> Option 'Https -> a -> AccessToken -> Req (Either HttpException b)
+ MSGraphAPI.Internal.Common: tryReq :: Req a -> Req (Either HttpException a)
- MSGraphAPI.Internal.Common: post :: (FromJSON b, ToJSON a) => [Text] -> Option 'Https -> a -> AccessToken -> Req b
+ MSGraphAPI.Internal.Common: post :: (ToJSON a, FromJSON b) => [Text] -> Option 'Https -> a -> AccessToken -> Req b
Files
- CHANGELOG.md +4/−0
- ms-graph-api.cabal +1/−1
- src/MSGraphAPI/Internal/Common.hs +29/−2
- src/Network/OAuth2/Provider/AzureAD.hs +2/−1
- src/Network/OAuth2/Session.hs +6/−5
CHANGELOG.md view
@@ -8,6 +8,10 @@ ## Unreleased +## 0.5.0.0++Add 'getE', 'postE', 'tryReq' to pattern match against HTTP exceptions+ ## 0.4.0.0 Add Session.tokensToList and Session.newTokens
ms-graph-api.cabal view
@@ -1,5 +1,5 @@ name: ms-graph-api-version: 0.4.0.0+version: 0.5.0.0 synopsis: Microsoft Graph API description: Bindings to the Microsoft Graph API homepage: https://github.com/unfoldml/ms-graph-api
src/MSGraphAPI/Internal/Common.hs view
@@ -7,10 +7,15 @@ -- * GET get , getLbs+ -- ** catch HTTP exceptions+ , getE -- * POST , post+ -- ** catch HTTP exceptions+ , postE -- * running requests , runReq+ , tryReq -- * JSON : aeson helpers , Collection(..) , aesonOptions@@ -38,17 +43,27 @@ -- modern-uri import Text.URI (URI, mkURI) -- req-import Network.HTTP.Req (Req, runReq, defaultHttpConfig, req, Option, (=:), GET(..), POST(..), Url, Scheme(..), useHttpsURI, https, (/:), ReqBodyJson(..), NoReqBody(..), oAuth2Bearer, HttpResponse(..), jsonResponse, JsonResponse, lbsResponse, LbsResponse, bsResponse, BsResponse, responseBody)+import Network.HTTP.Req (Req, runReq, HttpException(..), defaultHttpConfig, req, Option, (=:), GET(..), POST(..), Url, Scheme(..), useHttpsURI, https, (/:), ReqBodyJson(..), NoReqBody(..), oAuth2Bearer, HttpResponse(..), jsonResponse, JsonResponse, lbsResponse, LbsResponse, bsResponse, BsResponse, responseBody) -- text import Data.Text (Text, pack, unpack)+-- unliftio+import UnliftIO (MonadUnliftIO(..))+import UnliftIO.Exception (try) import Network.OAuth2.Session (Tokens) +-- | Specialized version of 'try' to 'HttpException's+--+-- This can be used to catch exceptions of composite 'Req' statements, e.g. around a @do@ block+tryReq :: Req a -> Req (Either HttpException a)+tryReq = try++ -- -- GET, POST -- | @POST https:\/\/graph.microsoft.com\/v1.0\/...@-post :: (A.FromJSON b, A.ToJSON a) =>+post :: (A.ToJSON a, A.FromJSON b) => [Text] -- ^ URI path segments -> Option 'Https -> a -- ^ request body@@ -59,6 +74,12 @@ opts = auth <> params (url, auth) = msGraphReqConfig tok paths +-- | Like 'post' but catches 'HttpException's to allow pattern matching+postE :: (A.ToJSON a, A.FromJSON b) =>+ [Text] -- ^ URI path segments+ -> Option 'Https -> a -> AccessToken -> Req (Either HttpException b)+postE paths params bdy tok = tryReq (post paths params bdy tok)+ -- | @GET https:\/\/graph.microsoft.com\/v1.0\/...@ get :: A.FromJSON a => [Text] -- ^ URI path segments@@ -69,6 +90,12 @@ where opts = auth <> params (url, auth) = msGraphReqConfig tok paths++-- | Like 'get' but catches 'HttpException's to allow pattern matching+getE :: (A.FromJSON a) =>+ [Text] -- ^ URI path segments+ -> Option 'Https -> AccessToken -> Req (Either HttpException a)+getE paths params tok = tryReq (get paths params tok) -- | @GET https:\/\/graph.microsoft.com\/v1.0\/...@ --
src/Network/OAuth2/Provider/AzureAD.hs view
@@ -34,6 +34,7 @@ type instance IdpUserInfo AzureAD = AzureADUser +-- | Configuration object of the OAuth2 application data OAuthCfg = OAuthCfg { oacAppName :: TL.Text -- ^ application name , oacClientId :: ClientId -- ^ app client ID : see https://stackoverflow.com/a/70670961@@ -43,7 +44,7 @@ , oacRedirectURI :: URI -- ^ OAuth2 redirect URI } --- | NB : OIDC scopes @openid@ and @offline_access@ are ALWAYS requested since the library assumes we have access to refresh tokens and ID tokens+-- | NB : scopes @openid@ and @offline_access@ are ALWAYS requested since the library assumes we have access to refresh tokens and ID tokens azureADApp :: OAuthCfg -- ^ OAuth configuration -> IdpApplication 'AuthorizationCode AzureAD azureADApp (OAuthCfg appname clid sec scopes authstate reduri) = defaultAzureADApp{
src/Network/OAuth2/Session.hs view
@@ -136,7 +136,7 @@ IdpApplication 'AuthorizationCode AzureAD -> Action m () loginH idpApp = do- setHeader "Location" (mkAuthorizeRequest idpApp) -- $ azureADApp oacfg)+ setHeader "Location" (mkAuthorizeRequest idpApp) -- redirect to OAuth consent screen status status302 -- | The identity provider redirects the client to the 'reply' endpoint as part of the OAuth flow : https://learn.microsoft.com/en-us/graph/auth-v2-user?view=graph-rest-1.0&tabs=http#authorization-response@@ -165,7 +165,7 @@ etoken = ExchangeToken $ TL.toStrict codeP _ <- fetchUpdateToken ts idpApp mgr etoken pure ()- Nothing -> throwE OASEExchangeTokenNotFound -- $ T.pack $ unwords ["cannot decode token"]+ Nothing -> throwE OASEExchangeTokenNotFound -- @@ -183,7 +183,7 @@ -> IdpApplication 'AuthorizationCode AzureAD -> Manager -> ExchangeToken -- ^ also called 'code'. Expires in 10 minutes- -> ExceptT OAuthSessionError m OAuth2Token -- IO (Either T.Text OAuth2Token)+ -> ExceptT OAuthSessionError m OAuth2Token fetchUpdateToken ts idpApp mgr etoken = ExceptT $ do tokenResp <- runExceptT $ conduitTokenRequest idpApp mgr etoken -- OAuth2 token case tokenResp of@@ -195,14 +195,15 @@ Right uid -> do _ <- refreshLoop ts idpApp mgr uid oat -- fork a thread and start refresh loop for this user pure $ Right oat- Left es -> pure $ Left (OASEJWTException es) -- $ T.pack (show e) -- ^ id token validation failed+ Left es -> pure $ Left (OASEJWTException es) -- id token validation failed Left es -> pure $ Left (OASEOAuth2Errors es) +-- | 2) fork a thread and start token refresh loop for user @uid@ refreshLoop :: (MonadUnliftIO m, Ord uid, HasRefreshTokenRequest a) => Tokens uid OAuth2Token -> IdpApplication a i -> Manager- -> uid+ -> uid -- ^ user ID -> OAuth2Token -> m ThreadId refreshLoop ts idpApp mgr uid oaToken = forkFinally (act oaToken) cleanup