/// <summary> /// Get account info for the user /// </summary> /// <param name="application"></param> /// <param name="user"></param> /// <returns></returns> public static async Task <IAccount> GetUserAccountAsync(IClientApplicationBase application, ClaimsPrincipal user) { var accountId = user.GetMsalAccountId(); IAccount account = null; if (accountId != null) { account = await application.GetAccountAsync(accountId); // Special case for guest users as the Guest oid / tenant id are not surfaced. if (account == null) { var loginHint = user.GetLoginHint(); if (loginHint == null) { throw new ArgumentNullException(nameof(loginHint)); } var accounts = await application.GetAccountsAsync(); account = accounts.FirstOrDefault(a => a.Username == loginHint); } } return(account); }
private ClientContext BuildClientContext(IClientApplicationBase application, string siteUrl, string[] scopes, ClientContextType contextType) { var clientContext = new ClientContext(siteUrl) { DisableReturnValueCache = true }; clientContext.ExecutingWebRequest += (sender, args) => { AuthenticationResult ar = null; var accounts = application.GetAccountsAsync().GetAwaiter().GetResult(); if (accounts.Count() > 0) { ar = application.AcquireTokenSilent(scopes, accounts.First()).ExecuteAsync().GetAwaiter().GetResult(); } else { switch (contextType) { case ClientContextType.AzureADCertificate: { ar = ((IConfidentialClientApplication)application).AcquireTokenForClient(scopes).ExecuteAsync().GetAwaiter().GetResult(); break; } case ClientContextType.AzureADCredentials: { ar = ((IPublicClientApplication)application).AcquireTokenByUsernamePassword(scopes, username, password).ExecuteAsync().GetAwaiter().GetResult(); break; } case ClientContextType.AzureADInteractive: { ar = ((IPublicClientApplication)application).AcquireTokenInteractive(scopes).ExecuteAsync().GetAwaiter().GetResult(); break; } } } if (ar != null && ar.AccessToken != null) { args.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer " + ar.AccessToken; } }; ClientContextSettings clientContextSettings = new ClientContextSettings() { Type = contextType, SiteUrl = siteUrl, AuthenticationManager = this, }; clientContext.AddContextSettings(clientContextSettings); return(clientContext); }
/// <summary> /// Attempts to acquire access token silently from the token cache. /// </summary> /// <exception cref="AuthenticationException">An exception occured when attempting to get access token silently.</exception> internal static async Task <AuthenticationResult> GetAccessTokenSilentAsync(this IClientApplicationBase clientApplication, AuthenticationProviderOption msalAuthProviderOption) { IAccount account; if (msalAuthProviderOption.UserAccount?.ObjectId != null) { // Parse GraphUserAccount to IAccount instance account = new GraphAccount(msalAuthProviderOption.UserAccount); } else { // If no graph user account is passed, try get the one in cache. IEnumerable <IAccount> accounts = await clientApplication.GetAccountsAsync(); account = accounts.FirstOrDefault(); } if (account == null) { return(null); } try { AcquireTokenSilentParameterBuilder tokenSilentBuilder = clientApplication.AcquireTokenSilent(msalAuthProviderOption.Scopes, account) .WithForceRefresh(msalAuthProviderOption.ForceRefresh); if (!ContainsWellKnownTenantName(clientApplication.Authority)) { tokenSilentBuilder.WithAuthority(clientApplication.Authority); } if (!string.IsNullOrEmpty(msalAuthProviderOption.Claims)) { tokenSilentBuilder.WithClaims(msalAuthProviderOption.Claims); } return(await tokenSilentBuilder.ExecuteAsync()); } catch (MsalException) { return(null); } catch (Exception exception) { throw new AuthenticationException( new Error { Code = ErrorConstants.Codes.GeneralException, Message = ErrorConstants.Message.UnexpectedException }, exception); } }
/// <summary> /// Apply this authenticator to the given authentication parameters. /// </summary> /// <param name="parameters">The complex object containing authentication specific information.</param> /// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param> /// <returns> /// An instance of <see cref="AuthenticationResult" /> that represents the access token generated as result of a successful authenication. /// </returns> public override async Task <AuthenticationResult> AuthenticateAsync(AuthenticationParameters parameters, CancellationToken cancellationToken = default) { IClientApplicationBase app = GetClient(parameters.Account, parameters.Environment); IEnumerable <IAccount> data = await app.GetAccountsAsync().ConfigureAwait(false); ServiceClientTracing.Information($"[SilentAuthenticator] data count is {data.Count()}"); ServiceClientTracing.Information("[SilentAuthenticator] Calling GetAccountsAsync"); IEnumerable <IAccount> accounts = await app.AsPublicClient().GetAccountsAsync().ConfigureAwait(false); ServiceClientTracing.Information($"[SilentAuthenticator] Calling AcquireTokenSilent - Scopes: '{string.Join(",", parameters.Scopes)}', UserId: '{((SilentParameters)parameters).UserId}', Number of accounts: '{accounts.Count()}'"); AuthenticationResult authResult = await app.AsPublicClient().AcquireTokenSilent( parameters.Scopes, accounts.FirstOrDefault(a => a.HomeAccountId.ObjectId.Equals(((SilentParameters)parameters).UserId, StringComparison.InvariantCultureIgnoreCase))) .ExecuteAsync(cancellationToken).ConfigureAwait(false); return(authResult); }
public Task <IEnumerable <IAccount> > GetAccountsAsync() => _Identity .GetAccountsAsync();
protected override void ProcessRecord() { base.ProcessRecord(); IAuthContext authContext = new AuthContext { TenantId = TenantId }; cancellationTokenSource = new CancellationTokenSource(); // Set selected environment to the session object. GraphSession.Instance.Environment = environment; switch (ParameterSetName) { case Constants.UserParameterSet: { // 2 mins timeout. 1 min < HTTP timeout. TimeSpan authTimeout = new TimeSpan(0, 0, Constants.MaxDeviceCodeTimeOut); cancellationTokenSource = new CancellationTokenSource(authTimeout); authContext.AuthType = AuthenticationType.Delegated; string[] processedScopes = ProcessScopes(Scopes); authContext.Scopes = processedScopes.Length == 0 ? new string[] { "User.Read" } : processedScopes; // Default to CurrentUser but allow the customer to change this via `ContextScope` param. authContext.ContextScope = this.IsParameterBound(nameof(ContextScope)) ? ContextScope : ContextScope.CurrentUser; } break; case Constants.AppParameterSet: { authContext.AuthType = AuthenticationType.AppOnly; authContext.ClientId = ClientId; authContext.CertificateThumbprint = CertificateThumbprint; authContext.CertificateName = CertificateName; // Default to Process but allow the customer to change this via `ContextScope` param. authContext.ContextScope = this.IsParameterBound(nameof(ContextScope)) ? ContextScope : ContextScope.Process; } break; case Constants.AccessTokenParameterSet: { authContext.AuthType = AuthenticationType.UserProvidedAccessToken; authContext.ContextScope = ContextScope.Process; // Store user provided access token to a session object. GraphSession.Instance.UserProvidedToken = new NetworkCredential(string.Empty, AccessToken).SecurePassword; } break; } CancellationToken cancellationToken = cancellationTokenSource.Token; try { // Gets a static instance of IAuthenticationProvider when the client app hasn't changed. IAuthenticationProvider authProvider = AuthenticationHelpers.GetAuthProvider(authContext); IClientApplicationBase clientApplication = null; if (ParameterSetName == Constants.UserParameterSet) { clientApplication = (authProvider as DeviceCodeProvider).ClientApplication; } else if (ParameterSetName == Constants.AppParameterSet) { clientApplication = (authProvider as ClientCredentialProvider).ClientApplication; } // Incremental scope consent without re-instantiating the auth provider. We will use a static instance. GraphRequestContext graphRequestContext = new GraphRequestContext(); graphRequestContext.CancellationToken = cancellationToken; graphRequestContext.MiddlewareOptions = new Dictionary <string, IMiddlewareOption> { { typeof(AuthenticationHandlerOption).ToString(), new AuthenticationHandlerOption { AuthenticationProviderOption = new AuthenticationProviderOption { Scopes = authContext.Scopes, ForceRefresh = ForceRefresh } } } }; // Trigger consent. HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/me"); httpRequestMessage.Properties.Add(typeof(GraphRequestContext).ToString(), graphRequestContext); authProvider.AuthenticateRequestAsync(httpRequestMessage).GetAwaiter().GetResult(); IAccount account = null; if (clientApplication != null) { // Only get accounts when we are using MSAL to get an access token. IEnumerable <IAccount> accounts = clientApplication.GetAccountsAsync().GetAwaiter().GetResult(); account = accounts.FirstOrDefault(); } DecodeJWT(httpRequestMessage.Headers.Authorization?.Parameter, account, ref authContext); // Save auth context to session state. GraphSession.Instance.AuthContext = authContext; } catch (AuthenticationException authEx) { if ((authEx.InnerException is TaskCanceledException) && cancellationToken.IsCancellationRequested) { // DeviceCodeTimeout throw new Exception(string.Format( CultureInfo.CurrentCulture, ErrorConstants.Message.DeviceCodeTimeout, Constants.MaxDeviceCodeTimeOut)); } else { throw authEx.InnerException ?? authEx; } } catch (Exception ex) { throw ex.InnerException ?? ex; } WriteObject("Welcome To Microsoft Graph!"); }
protected override void ProcessRecord() { base.ProcessRecord(); AuthConfig authConfig = new AuthConfig { TenantId = TenantId }; CancellationToken cancellationToken = CancellationToken.None; if (ParameterSetName == Constants.UserParameterSet) { // 2 mins timeout. 1 min < HTTP timeout. TimeSpan authTimeout = new TimeSpan(0, 0, Constants.MaxDeviceCodeTimeOut); CancellationTokenSource cts = new CancellationTokenSource(authTimeout); cancellationToken = cts.Token; authConfig.AuthType = AuthenticationType.Delegated; authConfig.Scopes = Scopes ?? new string[] { "User.Read" }; } else { authConfig.AuthType = AuthenticationType.AppOnly; authConfig.ClientId = ClientId; authConfig.CertificateThumbprint = CertificateThumbprint; authConfig.CertificateName = CertificateName; } try { // Gets a static instance of IAuthenticationProvider when the client app hasn't changed. IAuthenticationProvider authProvider = AuthenticationHelpers.GetAuthProvider(authConfig); IClientApplicationBase clientApplication = null; if (ParameterSetName == Constants.UserParameterSet) { clientApplication = (authProvider as DeviceCodeProvider).ClientApplication; } else { clientApplication = (authProvider as ClientCredentialProvider).ClientApplication; } // Incremental scope consent without re-instanciating the auth provider. We will use a static instance. GraphRequestContext graphRequestContext = new GraphRequestContext(); graphRequestContext.CancellationToken = cancellationToken; graphRequestContext.MiddlewareOptions = new Dictionary <string, IMiddlewareOption> { { typeof(AuthenticationHandlerOption).ToString(), new AuthenticationHandlerOption { AuthenticationProviderOption = new AuthenticationProviderOption { Scopes = authConfig.Scopes, ForceRefresh = ForceRefresh } } } }; // Trigger consent. HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/me"); httpRequestMessage.Properties.Add(typeof(GraphRequestContext).ToString(), graphRequestContext); authProvider.AuthenticateRequestAsync(httpRequestMessage).GetAwaiter().GetResult(); var accounts = clientApplication.GetAccountsAsync().GetAwaiter().GetResult(); var account = accounts.FirstOrDefault(); JwtPayload jwtPayload = JwtHelpers.DecodeToObject <JwtPayload>(httpRequestMessage.Headers.Authorization?.Parameter); authConfig.Scopes = jwtPayload?.Scp?.Split(' ') ?? jwtPayload?.Roles; authConfig.TenantId = jwtPayload?.Tid ?? account?.HomeAccountId?.TenantId; authConfig.AppName = jwtPayload?.AppDisplayname; authConfig.Account = jwtPayload?.Upn ?? account?.Username; // Save auth config to session state. SessionState.PSVariable.Set(Constants.GraphAuthConfigId, authConfig); } catch (AuthenticationException authEx) { if ((authEx.InnerException is TaskCanceledException) && cancellationToken.IsCancellationRequested) { throw new Exception($"Device code terminal timed-out after {Constants.MaxDeviceCodeTimeOut} seconds. Please try again."); } else { throw authEx.InnerException ?? authEx; } } catch (Exception ex) { throw ex.InnerException ?? ex; } WriteObject("Welcome To Microsoft Graph!"); }
/// <summary> /// Authenticates the client using the provided <see cref="IAuthContext"/>. /// </summary> /// <param name="authContext">The <see cref="IAuthContext"/> to authenticate.</param> /// <param name="forceRefresh">Whether or not to force refresh a token if one exists.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="fallBackWarning">Callback to report FallBack to DeviceCode Authentication</param> /// <returns></returns> public static async Task <IAuthContext> AuthenticateAsync(IAuthContext authContext, bool forceRefresh, CancellationToken cancellationToken, Action fallBackWarning = null) { // Gets a static instance of IAuthenticationProvider when the client app hasn't changed. var authProvider = AuthenticationHelpers.GetAuthProvider(authContext); IClientApplicationBase clientApplication = null; switch (authContext.AuthProviderType) { case AuthProviderType.DeviceCodeProvider: case AuthProviderType.DeviceCodeProviderFallBack: clientApplication = (authProvider as DeviceCodeProvider).ClientApplication; break; case AuthProviderType.InteractiveAuthenticationProvider: { var interactiveProvider = (authProvider as InteractiveAuthenticationProvider).ClientApplication; //When User is not Interactive, Pre-Emptively Fallback and warn, to DeviceCode if (!interactiveProvider.IsUserInteractive()) { authContext.AuthProviderType = AuthProviderType.DeviceCodeProviderFallBack; fallBackWarning?.Invoke(); var fallBackAuthContext = await AuthenticateAsync(authContext, forceRefresh, cancellationToken, fallBackWarning); return(fallBackAuthContext); } break; } case AuthProviderType.ClientCredentialProvider: { clientApplication = (authProvider as ClientCredentialProvider).ClientApplication; break; } } try { // Incremental scope consent without re-instantiating the auth provider. We will use provided instance. GraphRequestContext graphRequestContext = new GraphRequestContext(); graphRequestContext.CancellationToken = cancellationToken; graphRequestContext.MiddlewareOptions = new Dictionary <string, IMiddlewareOption> { { typeof(AuthenticationHandlerOption).ToString(), new AuthenticationHandlerOption { AuthenticationProviderOption = new AuthenticationProviderOption { Scopes = authContext.Scopes, ForceRefresh = forceRefresh } } } }; // Trigger consent. HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/me"); httpRequestMessage.Properties.Add(typeof(GraphRequestContext).ToString(), graphRequestContext); await authProvider.AuthenticateRequestAsync(httpRequestMessage); IAccount account = null; if (clientApplication != null) { // Only get accounts when we are using MSAL to get an access token. IEnumerable <IAccount> accounts = clientApplication.GetAccountsAsync().GetAwaiter().GetResult(); account = accounts.FirstOrDefault(); } JwtHelpers.DecodeJWT(httpRequestMessage.Headers.Authorization?.Parameter, account, ref authContext); return(authContext); } catch (AuthenticationException authEx) { //Interactive Authentication Failure: Could Not Open Browser, fallback to DeviceAuth if (IsUnableToOpenWebPageError(authEx)) { authContext.AuthProviderType = AuthProviderType.DeviceCodeProviderFallBack; //ReAuthenticate using DeviceCode as fallback. var fallBackAuthContext = await AuthenticateAsync(authContext, forceRefresh, cancellationToken); //Indicate that this was a Fallback if (fallBackWarning != null && fallBackAuthContext.AuthProviderType == AuthProviderType.DeviceCodeProviderFallBack) { fallBackWarning(); } return(fallBackAuthContext); } if (authEx.InnerException is TaskCanceledException && cancellationToken.IsCancellationRequested) { // Authentication requets timeout. throw new Exception(string.Format(CultureInfo.CurrentCulture, ErrorConstants.Message.DeviceCodeTimeout, Constants.MaxDeviceCodeTimeOut)); } else if (authEx.InnerException is MsalServiceException msalServiceEx && msalServiceEx.StatusCode == 400 && msalServiceEx.ErrorCode == "invalid_scope" && string.IsNullOrWhiteSpace(authContext.TenantId) && (authContext.AuthProviderType == AuthProviderType.DeviceCodeProvider || authContext.AuthProviderType == AuthProviderType.DeviceCodeProviderFallBack)) { // MSAL scope validation error. Ask customer to specify sign-in audience or tenant Id. throw new MsalClientException(msalServiceEx.ErrorCode, $"{msalServiceEx.Message}.\r\n{ErrorConstants.Message.InvalidScope}", msalServiceEx); } //Something Unknown Went Wrong throw authEx.InnerException ?? authEx; } catch (Exception ex) { throw ex.InnerException ?? ex; } }
/// <summary> /// Authenticates the client using the provided <see cref="IAuthContext"/>. /// </summary> /// <param name="authContext">The <see cref="IAuthContext"/> to authenticate.</param> /// <param name="forceRefresh">Whether or not to force refresh a token if one exists.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns></returns> public static async Task <IAuthContext> AuthenticateAsync(IAuthContext authContext, bool forceRefresh, CancellationToken cancellationToken) { try { // Gets a static instance of IAuthenticationProvider when the client app hasn't changed. IAuthenticationProvider authProvider = AuthenticationHelpers.GetAuthProvider(authContext); IClientApplicationBase clientApplication = null; if (authContext.AuthType == AuthenticationType.Delegated) { clientApplication = (authProvider as DeviceCodeProvider).ClientApplication; } if (authContext.AuthType == AuthenticationType.AppOnly) { clientApplication = (authProvider as ClientCredentialProvider).ClientApplication; } // Incremental scope consent without re-instantiating the auth provider. We will use a static instance. GraphRequestContext graphRequestContext = new GraphRequestContext(); graphRequestContext.CancellationToken = cancellationToken; graphRequestContext.MiddlewareOptions = new Dictionary <string, IMiddlewareOption> { { typeof(AuthenticationHandlerOption).ToString(), new AuthenticationHandlerOption { AuthenticationProviderOption = new AuthenticationProviderOption { Scopes = authContext.Scopes, ForceRefresh = forceRefresh } } } }; // Trigger consent. HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/me"); httpRequestMessage.Properties.Add(typeof(GraphRequestContext).ToString(), graphRequestContext); await authProvider.AuthenticateRequestAsync(httpRequestMessage); IAccount account = null; if (clientApplication != null) { // Only get accounts when we are using MSAL to get an access token. IEnumerable <IAccount> accounts = clientApplication.GetAccountsAsync().GetAwaiter().GetResult(); account = accounts.FirstOrDefault(); } JwtHelpers.DecodeJWT(httpRequestMessage.Headers.Authorization?.Parameter, account, ref authContext); return(authContext); } catch (AuthenticationException authEx) { if ((authEx.InnerException is TaskCanceledException) && cancellationToken.IsCancellationRequested) { // DeviceCodeTimeout throw new Exception(string.Format( CultureInfo.CurrentCulture, ErrorConstants.Message.DeviceCodeTimeout, Constants.MaxDeviceCodeTimeOut)); } else { throw authEx.InnerException ?? authEx; } } catch (Exception ex) { throw ex.InnerException ?? ex; } }