diff --git a/ms-auth/ms-auth.cabal b/ms-auth/ms-auth.cabal index 11c7102..413ed74 100644 --- a/ms-auth/ms-auth.cabal +++ b/ms-auth/ms-auth.cabal @@ -1,5 +1,5 @@ name: ms-auth -version: 0.3.0.0 +version: 0.4.0.0 synopsis: Microsoft Authentication API description: Bindings to the Microsoft Identity API / Active Directory (AD) for building applications that use either Authorization Code (User-facing) or (App-only) authorization flows. Helper functions are provided for building OAuth2 authentication flows and keep tokens transactionally secure and up to date. homepage: https://github.com/unfoldml/ms-graph-api diff --git a/ms-auth/src/Network/OAuth2/Provider/AzureAD.hs b/ms-auth/src/Network/OAuth2/Provider/AzureAD.hs index b2e7f42..f1b5ae7 100644 --- a/ms-auth/src/Network/OAuth2/Provider/AzureAD.hs +++ b/ms-auth/src/Network/OAuth2/Provider/AzureAD.hs @@ -8,14 +8,18 @@ -- | Settings for using Azure Active Directory as OAuth identity provider -- -- Both @Auth Code Grant@ (i.e. with browser client interaction) and @App-only@ (i.e. Client Credentials) authentication flows are supported. The former is useful when a user needs to login and delegate some permissions to the application (i.e. accessing personal data), whereas the second is for server processes and automation accounts. +-- +-- Azure Bot Framework is supported since v 0. module Network.OAuth2.Provider.AzureAD ( AzureAD + , AzureBotFramework -- * Environment variables , envClientId , envClientSecret , envTenantId -- * App flow , azureADApp + , azureBotFrameworkOAuthADApp -- * Delegated permissions OAuth2 flow , OAuthCfg(..) , AzureADUser @@ -163,7 +167,7 @@ defaultAzureOAuthADApp = , idpAppAuthorizeState = "CHANGE_ME" -- https://stackoverflow.com/questions/26132066/what-is-the-purpose-of-the-state-parameter-in-oauth-authorization-request , idpAppAuthorizeExtraParams = Map.empty , idpAppRedirectUri = [uri|http://localhost|] -- - , idpAppName = "default-azure-app" -- + , idpAppName = "" -- , idpAppTokenRequestAuthenticationMethod = ClientSecretBasic , idp = defaultAzureADIdp } @@ -178,6 +182,31 @@ defaultAzureADIdp = , idpTokenEndpoint = [uri|https://login.microsoftonline.com/common/oauth2/v2.0/token|] } +-- | +azureBotFrameworkOAuthADApp :: ClientId + -> ClientSecret + -> TL.Text -- ^ app name + -> IdpApplication 'ClientCredentials AzureBotFramework +azureBotFrameworkOAuthADApp clid sec appname = ClientCredentialsIDPAppConfig { + idpAppClientId = clid, + idpAppClientSecret = sec, + idpAppName = appname, + idpAppScope = Set.fromList ["https://api.botframework.com/.default"], + idpAppTokenRequestExtraParams = mempty, + idp = defaultAzureBotFrameworkIdp + } + + +data AzureBotFramework = AzureBotFramework + +defaultAzureBotFrameworkIdp :: Idp AzureBotFramework +defaultAzureBotFrameworkIdp = Idp { + idpFetchUserInfo = authGetJSON @(IdpUserInfo AzureBotFramework) + , idpTokenEndpoint = [uri|https://login.microsoftonline.com/botframework.com/oauth2/v2.0/token|] + , idpUserInfoEndpoint = error $ unwords ["Azure Bot Framework Idp:", "OAuth user info endpoint is not defined"] + , idpAuthorizeEndpoint = error $ unwords ["Azure Bot Framework Idp:", "OAuth authorize endpoint is not defined"] + } + -- | https://learn.microsoft.com/en-us/azure/active-directory/develop/userinfo data AzureADUser = AzureADUser { sub :: T.Text diff --git a/ms-auth/src/Network/OAuth2/Session.hs b/ms-auth/src/Network/OAuth2/Session.hs index 0d6edb1..1e896b1 100644 --- a/ms-auth/src/Network/OAuth2/Session.hs +++ b/ms-auth/src/Network/OAuth2/Session.hs @@ -372,6 +372,27 @@ fetchUpdateTokenACG ts idpApp mgr etoken = ExceptT $ do Left es -> pure $ Left (OASEJWTException es) -- id token validation failed Left es -> pure $ Left (OASEOAuth2Errors es) + +-- -- -- for Bot Framework auth etc +-- fetchUpdateToken' :: MonadIO m => +-- Tokens UserSub OAuth2Token +-- -> IdpApplication 'ClientCredentials i +-- -> Manager +-- -> ExceptT OAuthSessionError m OAuth2Token +-- fetchUpdateToken' ts idpApp mgr = ExceptT $ do +-- tokenResp <- runExceptT $ conduitTokenRequest idpApp mgr -- OAuth2 token +-- case tokenResp of +-- Right oat -> case idToken oat of +-- Nothing -> pure $ Left OASENoOpenID +-- Just idt -> do +-- idtClaimsE <- decValidIdToken idt -- decode and validate ID token +-- case idtClaimsE of +-- Right uid -> do +-- _ <- refreshLoopACG ts idpApp mgr uid oat -- fork a thread and start refresh loop for this user +-- pure $ Right oat + + + -- | 2) fork a thread and start token refresh loop for user @uid@ refreshLoopACG :: (MonadIO m, Ord uid, HasRefreshTokenRequest a) => Tokens uid OAuth2Token