private async Task <string?> GetTokenForWebApiToCallDownstreamApiAsync( IConfidentialClientApplication application, string authority, IEnumerable <string> scopes) { try { // In web API, validatedToken will not be null JwtSecurityToken?validatedToken = CurrentHttpContext?.GetTokenUsedToCallWebAPI(); // Case of web APIs: we need to do an on-behalf-of flow, with the token used to call the API if (validatedToken != null) { // In the case the token is a JWE (encrypted token), we use the decrypted token. string tokenUsedToCallTheWebApi = validatedToken.InnerToken == null ? validatedToken.RawData : validatedToken.InnerToken.RawData; var result = await application .AcquireTokenOnBehalfOf(scopes.Except(_scopesRequestedByMsal), new UserAssertion(tokenUsedToCallTheWebApi)) .WithSendX5C(_microsoftIdentityOptions.SendX5C) .WithAuthority(authority) .ExecuteAsync() .ConfigureAwait(false); return(result.AccessToken); } return(null); } catch (MsalUiRequiredException ex) { _logger.LogInformation(string.Format(CultureInfo.InvariantCulture, LogMessages.ErrorAcquiringTokenForDownstreamWebApi, ex.Message)); throw ex; } }
public async Task <IActionResult> GetAsync([FromHeader] string authorization) { string sso_token = authorization.Substring("Bearer".Length + 1); IConfidentialClientApplication app = ConfidentialClientApplicationBuilder .Create(this.azureADOptions.Value.AppId) .WithClientSecret(this.azureADOptions.Value.AppPassword) .WithTenantId(this.azureADOptions.Value.TenantId) .WithAuthority($"https://login.microsoftonline.com/{this.azureADOptions.Value.TenantId}") .Build(); try { var onBehalfOfToken = await app.AcquireTokenOnBehalfOf(new List <string>() { "User.ReadBasic.All" }, new UserAssertion(sso_token)).ExecuteAsync(); return(this.Ok(onBehalfOfToken.AccessToken)); } catch (MsalUiRequiredException e) { string failureReason = "Failed to retrieve a Graph token for the user"; if (e.Classification == UiRequiredExceptionClassification.ConsentRequired) { failureReason = "The user or admin has not provided sufficient consent for the application"; } return(this.StatusCode(403, new UnsuccessfulResponse { Reason = failureReason })); } }
public async Task <AuthenticationResult> GenerateToken(OnBehalfOptions options) { AuthenticationResult result = null; try { X509Certificate2 certificate = this._certReader.ReadCertificate(options.CertThumbprint); IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(options.ClientId) .WithAuthority(new Uri(options.Authority), true) .WithCertificate(certificate) .Build(); var userAssertion = new UserAssertion( options.AccessToken, "urn:ietf:params:oauth:grant-type:jwt-bearer"); result = await app.AcquireTokenOnBehalfOf(new string[] { options.Scope }, userAssertion).ExecuteAsync(); } catch (MsalUiRequiredException ex) { var message = @"The application doesn't have sufficient permissions. - Did you declare enough app permissions during app creation? -Did the tenant admin grant permissions to the application?"; Console.WriteLine(message); Console.WriteLine(ex); } catch (MsalServiceException ex) when(ex.Message.Contains("AADSTS70011")) { var message = @"Invalid scope. The scope has to be in the form 'https://resourceurl/.default' Mitigation: Change the scope to be as expected."; Console.WriteLine(message); Console.WriteLine(ex); } return(result); }
/// <summary> /// Get token using client credentials flow /// </summary> /// <param name="configuration">IConfiguration instance.</param> /// <param name="httpClientFactory">IHttpClientFactory instance.</param> /// <param name="httpContextAccessor">IHttpContextAccessor instance.</param> /// <returns>App access token on behalf of user.</returns> public static async Task <string> GetAccessTokenOnBehalfUserAsync(IConfiguration configuration, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor) { var tenantId = configuration["AzureAd:TenantId"]; IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(configuration["AzureAd:MicrosoftAppId"]) .WithClientSecret(configuration["AzureAd:MicrosoftAppPassword"]) .WithAuthority($"https://login.microsoftonline.com/{tenantId}") .Build(); try { var httpContext = httpContextAccessor.HttpContext; httpContext.Request.Headers.TryGetValue("Authorization", out StringValues assertion); var idToken = assertion.ToString().Split(" ")[1]; UserAssertion assert = new UserAssertion(idToken); List <string> scopes = new List <string>(); scopes.Add("https://graph.microsoft.com/User.Read"); var responseToken = await app.AcquireTokenOnBehalfOf(scopes, assert).ExecuteAsync(); return(responseToken.AccessToken.ToString()); } catch (Exception ex) { return(ex.Message); } }
/// <summary> /// Retrieved the access token based on the permission level given to the respective application /// </summary> /// <param name="app"></param> /// <returns></returns> protected async Task <string> GetAccessToken(IConfidentialClientApplication app) { string[] scopes = { "user.read.all" }; UserAssertion userAssertion = new UserAssertion(BearerToken, "urn:ietf:params:oauth:grant-type:jwt-bearer"); AuthenticationResult result = await app.AcquireTokenOnBehalfOf(scopes, userAssertion).ExecuteAsync(); return(result.AccessToken); }
public async Task <string> GetAccessToken() { try { // First attempt to get token from the cache for this user // Check for a matching account in the cache var account = await _msalClient.GetAccountAsync(_tokenResult.MsalAccountId); if (account != null) { // Make a "silent" request for a token. This will // return the cached token if still valid, and will handle // refreshing the token if needed var cacheResult = await _msalClient .AcquireTokenSilent(_scopes, account) .ExecuteAsync(); _logger.LogInformation($"User access token: {cacheResult.AccessToken}"); return(cacheResult.AccessToken); } } catch (MsalUiRequiredException) { // This exception indicates that a new token // can only be obtained by invoking the on-behalf-of // flow. "UiRequired" isn't really accurate since the OBO // flow doesn't involve UI. // Catching the exception so code will continue to the // AcquireTokenOnBehalfOf call below. } catch (Exception exception) { _logger.LogError(exception, "Error getting access token via on-behalf-of flow"); return(null); } try { _logger.LogInformation("Token not found in cache, attempting OBO flow"); // Use the token sent by the calling client as a // user assertion var userAssertion = new UserAssertion(_tokenResult.Token); // Invoke on-behalf-of flow var result = await _msalClient .AcquireTokenOnBehalfOf(_scopes, userAssertion) .ExecuteAsync(); _logger.LogInformation($"User access token: {result.AccessToken}"); return(result.AccessToken); } catch (Exception exception) { _logger.LogError(exception, "Error getting access token from cache"); return(null); } }
/// <summary> /// Typically used from a Web App or WebAPI controller, this method retrieves an access token /// for a downstream API using; /// 1) the token cache (for Web Apps and Web APis) if a token exists in the cache /// 2) or the <a href='https://docs.microsoft.com/azure/active-directory/develop/v2-oauth2-on-behalf-of-flow'>on-behalf-of flow</a> /// in Web APIs, for the user account that is ascertained from claims are provided in the <see cref="HttpContext.User"/> /// instance of the current HttpContext. /// </summary> /// <param name="scopes">Scopes to request for the downstream API to call.</param> /// <param name="tenant">Enables overriding of the tenant/account for the same identity. This is useful in the /// cases where a given account is guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest in.</param> /// <returns>An access token to call the downstream API and populated with this downstream Api's scopes.</returns> /// <remarks>Calling this method from a Web API supposes that you have previously called, /// in a method called by JwtBearerOptions.Events.OnTokenValidated, the HttpContextExtensions.StoreTokenUsedToCallWebAPI method /// passing the validated token (as a JwtSecurityToken). Calling it from a Web App supposes that /// you have previously called AddAccountToCacheFromAuthorizationCodeAsync from a method called by /// OpenIdConnectOptions.Events.OnAuthorizationCodeReceived.</remarks> public async Task <string> GetAccessTokenForUserAsync( IEnumerable <string> scopes, string tenant = null) { if (scopes == null) { throw new ArgumentNullException(nameof(scopes)); } // Use MSAL to get the right token to call the API _application = await GetOrBuildConfidentialClientApplicationAsync().ConfigureAwait(false); string accessToken; try { accessToken = await GetAccessTokenOnBehalfOfUserFromCacheAsync(_application, CurrentHttpContext.User, scopes, tenant) .ConfigureAwait(false); } catch (MsalUiRequiredException ex) { // GetAccessTokenForUserAsync is an abstraction that can be called from a Web App or a Web API _logger.LogInformation(ex.Message); // to get a token for a Web API on behalf of the user, but not necessarily with the on behalf of OAuth2.0 // flow as this one only applies to Web APIs. JwtSecurityToken validatedToken = CurrentHttpContext.GetTokenUsedToCallWebAPI(); // Case of Web APIs: we need to do an on-behalf-of flow if (validatedToken != null) { // In the case the token is a JWE (encrypted token), we use the decrypted token. string tokenUsedToCallTheWebApi = validatedToken.InnerToken == null ? validatedToken.RawData : validatedToken.InnerToken.RawData; var result = await _application .AcquireTokenOnBehalfOf(scopes.Except(_scopesRequestedByMsal), new UserAssertion(tokenUsedToCallTheWebApi)) .ExecuteAsync() .ConfigureAwait(false); accessToken = result.AccessToken; } // Case of the Web App: we let the MsalUiRequiredException be caught by the // AuthorizeForScopesAttribute exception filter so that the user can consent, do 2FA, etc ... else { throw; } } return(accessToken); }
public HttpResponseMessage Token(Token body) { if (string.IsNullOrEmpty(body.UserToken)) { return(SendErrorToClient(HttpStatusCode.InternalServerError, null, "No token provided")); } UserAssertion userAssertion = new UserAssertion(body.UserToken); // Get the access token for MS Graph. ConfidentialClientApplicationBuilder b = ConfidentialClientApplicationBuilder.Create(ConfigurationManager.AppSettings["ida:ClientID"]); b.WithClientSecret(ConfigurationManager.AppSettings["ida:Password"]).WithRedirectUri(ConfigurationManager.AppSettings["ida:RedirectUri"]); IConfidentialClientApplication cca = b.Build(); //string[] graphScopes = { "user.read", "files.read.all", "mail.read", "calendars.read" }; string[] graphScopes = { "Mail.Read", "Files.Read.All", "Calendars.Read" }; AuthenticationResult result = null; try { // The AcquireTokenOnBehalfOfAsync method will first look in the MSAL in memory cache for a // matching access token. Only if there isn't one, does it initiate the "on behalf of" flow // with the Azure AD V2 endpoint. result = cca.AcquireTokenOnBehalfOf(graphScopes, userAssertion).ExecuteAsync().Result; } catch (MsalServiceException e) { if (e.Message.StartsWith("AADSTS50076")) { string responseMessage = String.Format("{{\"AADError\":\"AADSTS50076\",\"Claims\":{0}}}", e.Claims); return(SendErrorToClient(HttpStatusCode.InternalServerError, e, responseMessage)); } if ((e.Message.StartsWith("AADSTS65001")) || (e.Message.StartsWith("AADSTS70011: The provided value for the input parameter 'scope' is not valid."))) { return(SendErrorToClient(HttpStatusCode.InternalServerError, e, e.Message)); } return(SendErrorToClient(HttpStatusCode.InternalServerError, e, e.Message)); } HttpRequestMessage requestMessage = new HttpRequestMessage(); var configuration = new HttpConfiguration(); requestMessage.Properties[System.Web.Http.Hosting.HttpPropertyKeys.HttpConfigurationKey] = configuration; HttpResponseMessage tokenMessage = requestMessage.CreateResponse(HttpStatusCode.OK, result.AccessToken); return(tokenMessage); }
private async Task <AuthenticationResult?> GetAuthenticationResultForWebApiToCallDownstreamApiAsync( IConfidentialClientApplication application, string authority, IEnumerable <string> scopes, TokenAcquisitionOptions?tokenAcquisitionOptions) { try { // In web API, validatedToken will not be null JwtSecurityToken?validatedToken = CurrentHttpContext?.GetTokenUsedToCallWebAPI(); // Case of web APIs: we need to do an on-behalf-of flow, with the token used to call the API if (validatedToken != null) { // In the case the token is a JWE (encrypted token), we use the decrypted token. string tokenUsedToCallTheWebApi = validatedToken.InnerToken == null ? validatedToken.RawData : validatedToken.InnerToken.RawData; var builder = application .AcquireTokenOnBehalfOf( scopes.Except(_scopesRequestedByMsal), new UserAssertion(tokenUsedToCallTheWebApi)) .WithSendX5C(_microsoftIdentityOptions.SendX5C) .WithAuthority(authority); if (tokenAcquisitionOptions != null) { builder.WithExtraQueryParameters(tokenAcquisitionOptions.ExtraQueryParameters); builder.WithCorrelationId(tokenAcquisitionOptions.CorrelationId); builder.WithForceRefresh(tokenAcquisitionOptions.ForceRefresh); builder.WithClaims(tokenAcquisitionOptions.Claims); if (tokenAcquisitionOptions.PoPConfiguration != null) { builder.WithProofOfPossession(tokenAcquisitionOptions.PoPConfiguration); } } return(await builder.ExecuteAsync() .ConfigureAwait(false)); } return(null); } catch (MsalUiRequiredException ex) { _logger.LogInformation( string.Format( CultureInfo.InvariantCulture, LogMessages.ErrorAcquiringTokenForDownstreamWebApi, ex.Message)); throw; } }
public override async Task <AuthenticationResult> TokenForCurrentUser(string[] scopes) { if (accessor.HttpContext.Request.Headers.TryGetValue("Authorization", out Microsoft.Extensions.Primitives.StringValues value)) { var token = value.FirstOrDefault(); AzureAdOptions o = options.Value; IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(clientId: o.ClientId) .WithClientSecret(o.ClientSecret) .WithTenantId(o.TenantId) .Build(); AuthenticationResult t = await app.AcquireTokenOnBehalfOf(scopes, new UserAssertion(token.Split(" ")[1])).ExecuteAsync(); return(t); } else { ClaimsPrincipal user = accessor.HttpContext.User; if (user == null || !user.Identity.IsAuthenticated) { throw new ForbiddenException("Not authenticated"); } try { AuthenticationResult result = await tokenService.GetAccessTokenAsync(user, scopes); return(result); } catch (MsalUiRequiredException e) { if (e.Classification == UiRequiredExceptionClassification.ConsentRequired) { var message = $"Consent missing: The app does not yet have your consent to the scope '{scopes[0]}'." + $" Please first allow the app to act on your behalf, then try again." + $" Scopes can be granted under the account settings."; logger.LogError("Missing consent {@scopes}", scopes); throw new MissingConsentException(message, scopes[0]); } throw; } } }
public async Task <string> GetUserAccessTokenAsync(string jwtBearerToken) { var account = await _oboApp.AcquireTokenOnBehalfOf(_scopes, new UserAssertion(jwtBearerToken)).ExecuteAsync(); if (account == null) { throw new ServiceException(new Error { Code = "TokenNotFound", Message = "Unable to retrieve the access token from assertion" }); } return(account.AccessToken); }
public void EnsurePublicApiSurfaceExistsOnInterface() { IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId) .WithClientSecret("cats") .Build(); // This test is to ensure that the methods we want/need on the IConfidentialClientApplication exist and compile. This isn't testing functionality, that's done elsewhere. // It's solely to ensure we know that the methods we want/need are available where we expect them since we tend to do most testing on the concrete types. var authCodeBuilder = app.AcquireTokenByAuthorizationCode(TestConstants.s_scope, "authorizationcode"); PublicClientApplicationTests.CheckBuilderCommonMethods(authCodeBuilder); var clientBuilder = app.AcquireTokenForClient(TestConstants.s_scope) .WithForceRefresh(true) .WithSendX5C(true); PublicClientApplicationTests.CheckBuilderCommonMethods(clientBuilder); var onBehalfOfBuilder = app.AcquireTokenOnBehalfOf( TestConstants.s_scope, new UserAssertion("assertion", "assertiontype")) .WithSendX5C(true); PublicClientApplicationTests.CheckBuilderCommonMethods(onBehalfOfBuilder); var silentBuilder = app.AcquireTokenSilent(TestConstants.s_scope, "*****@*****.**") .WithForceRefresh(false); PublicClientApplicationTests.CheckBuilderCommonMethods(silentBuilder); silentBuilder = app.AcquireTokenSilent(TestConstants.s_scope, TestConstants.s_user) .WithForceRefresh(true); PublicClientApplicationTests.CheckBuilderCommonMethods(silentBuilder); var requestUrlBuilder = app.GetAuthorizationRequestUrl(TestConstants.s_scope) .WithAccount(TestConstants.s_user) .WithLoginHint("loginhint") .WithExtraScopesToConsent(TestConstants.s_scope) .WithRedirectUri(TestConstants.RedirectUri); PublicClientApplicationTests.CheckBuilderCommonMethods(requestUrlBuilder); var byRefreshTokenBuilder = ((IByRefreshToken)app).AcquireTokenByRefreshToken(TestConstants.s_scope, "refreshtoken") .WithRefreshToken("refreshtoken"); PublicClientApplicationTests.CheckBuilderCommonMethods(byRefreshTokenBuilder); }
/// <summary> /// Gets an access token for the requested resource and scope /// </summary> /// <param name="resource">Resource to request an access token for (unused)</param> /// <param name="scopes">Scopes to request</param> /// <returns>An access token</returns> public override async Task <string> GetAccessTokenAsync(Uri resource, string[] scopes) { if (resource == null) { throw new ArgumentNullException(nameof(resource)); } if (scopes == null) { throw new ArgumentNullException(nameof(scopes)); } if (UserTokenProvider == null) { throw new ConfigurationErrorsException( PnPCoreAuthResources.OnBehalfOfAuthenticationProvider_MissingUserTokenProvider); } AuthenticationResult tokenResult = null; try { // Define the user assertion based on the consumer access token var assertion = new UserAssertion(UserTokenProvider.Invoke()); // Try to get the token from the tokens cache tokenResult = await confidentialClientApplication .AcquireTokenOnBehalfOf(scopes, assertion) .ExecuteAsync().ConfigureAwait(false); } catch (MsalServiceException) { // Handle the various exceptions throw; } // Log the access token retrieval action Log?.LogInformation(PnPCoreAuthResources.AuthenticationProvider_LogAccessTokenRetrieval, GetType().Name, resource, scopes.Aggregate(string.Empty, (c, n) => c + ", " + n).TrimEnd(',')); // Return the Access Token, if we've got it // In case of any exception while retrieving the access token, // MSAL will throw an exception that we simply bubble up return(tokenResult.AccessToken); }
/// <summary> /// For web APIs, acquire token on-behalf-of flow with the token used to call the API /// </summary> /// <param name="requestedScopes"></param> /// <returns></returns> public async Task <AuthenticationResult> GetUserTokenOnBehalfOfAsync(IEnumerable <string> requestedScopes) { string authority = $"{AuthenticationConfig.AADInstance}{AuthenticationConfig.TenantId}/"; this.PrepareConfidentialClientInstanceAsync(); //IConfidentialClientApplication app = await BuildConfidentialClientApplicationAsync(); var bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext; string userAccessToken = (string)bootstrapContext; UserAssertion userAssertion = new UserAssertion(userAccessToken, "urn:ietf:params:oauth:grant-type:jwt-bearer"); var result = await _application.AcquireTokenOnBehalfOf(requestedScopes, userAssertion) .WithAuthority(authority) .ExecuteAsync(); return(result); }
public async Task <IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req) { var accessTokenForThis = req.Headers["Authorization"].ToString().Split(" ")[1]; _log.LogTrace("Got access token from header, using that in assertion"); _log.LogTrace(accessTokenForThis); var tokenRequest = _aadApp.AcquireTokenOnBehalfOf(_scopes.Split(" "), new UserAssertion(accessTokenForThis)); var token = await tokenRequest.ExecuteAsync(); _log.LogTrace($"Got new access token obo: {token.AccessToken}"); _httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token.AccessToken); var ping = await _httpClient.GetAsync(_apiUrl); if (ping.IsSuccessStatusCode) { return(new OkObjectResult(new { data = await ping.Content.ReadAsStringAsync() })); } return(new BadRequestObjectResult(new { data = ping.StatusCode, message = ping.ReasonPhrase })); }
public async Task <IActionResult> GetAsync([FromHeader] string authorization) { string sso_token = authorization.Substring("Bearer".Length + 1); IConfidentialClientApplication app = ConfidentialClientApplicationBuilder .Create(this.azureADOptions.Value.AppId) .WithClientSecret(this.azureADOptions.Value.AppPassword) .WithTenantId(this.azureADOptions.Value.TenantId) .WithAuthority($"https://login.microsoftonline.com/{this.azureADOptions.Value.TenantId}") .Build(); try { var onBehalfOfToken = await app.AcquireTokenOnBehalfOf(new List <string>() { "ChannelMessage.Send", "User.ReadBasic.All" }, new UserAssertion(sso_token)).ExecuteAsync(); return(this.Ok(onBehalfOfToken.AccessToken)); } catch (MsalUiRequiredException) { return(this.StatusCode(403)); } }
public async Task <string> Get( bool ccaPerRequest = true, Flow flow = Flow.S2S, int userForObo = 1, TID tenantId = TID.A, Scope scope = Scope.S1, bool staticL1 = false, bool useL2 = true) { char c = tenantId.ToString().ToCharArray()[0]; var tid = new string(Enumerable.Repeat(c, 16).ToArray()); IConfidentialClientApplication cca = GetOrCreateCCA(ccaPerRequest, staticL1, useL2); AuthenticationResult res; if (flow == Flow.S2S) { res = await cca.AcquireTokenForClient(new[] { scope.ToString() }) .WithAuthority($"https://login.microsoftonline.com/{tid}") .ExecuteAsync().ConfigureAwait(false); } else { var user = $"user_{userForObo}"; // must be in this format. MSAL will use {user} as the object id string fakeUpstreamToken = $"upstream_token_{user}"; res = await cca.AcquireTokenOnBehalfOf(new[] { scope.ToString() }, new UserAssertion(fakeUpstreamToken)) .WithAuthority($"https://login.microsoftonline.com/{tid}") .ExecuteAsync() .ConfigureAwait(false); } return(res.AuthenticationResultMetadata.TokenSource.ToString()); }
public override async ValueTask <AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken) { AuthenticationResult result = await _confidentialClient.AcquireTokenOnBehalfOf(requestContext.Scopes, _userAssertion).ExecuteAsync(); return(new AccessToken(result.AccessToken, result.ExpiresOn)); }
public async Task <string> GetAccessTokenForUserAsync(HttpRequestData req, IEnumerable <string> scopes, string?tenantId = null, string?userFlow = null) { var result = await _application.AcquireTokenOnBehalfOf(scopes, new UserAssertion(getAccessTokenFromHeaders(req))).ExecuteAsync(); return(result.AccessToken); }