diff --git a/src/Microsoft.Identity.Web/ITokenAcquisition.cs b/src/Microsoft.Identity.Web/ITokenAcquisition.cs index 69a8c387d..599f21b9d 100644 --- a/src/Microsoft.Identity.Web/ITokenAcquisition.cs +++ b/src/Microsoft.Identity.Web/ITokenAcquisition.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.Collections.Generic; +using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.Identity.Client; @@ -22,8 +23,15 @@ public interface ITokenAcquisition /// Enables to override 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. /// Azure AD B2C UserFlow to target. + /// Optional claims principal representing the user. If not provided, will use the signed-in + /// user (in a web app), or the user for which the token was received (in a web API) + /// 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. /// An access token to call on behalf of the user, the downstream API characterized by its scopes. - Task GetAccessTokenForUserAsync(IEnumerable scopes, string? tenantId = null, string? userFlow = null); + Task GetAccessTokenForUserAsync( + IEnumerable scopes, + string? tenantId = null, + string? userFlow = null, + ClaimsPrincipal? user = null); /// /// Acquires a token from the authority configured in the app, for the confidential client itself (not on behalf of a user) diff --git a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml index af462f1b4..569d2d0d5 100644 --- a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml +++ b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml @@ -477,6 +477,8 @@ Name Identifier ID claim: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier". + + General constants. @@ -620,7 +622,7 @@ Interface for the token acquisition service (encapsulating MSAL.NET). - + Typically used from an ASP.NET Core Web App or Web API controller, this method gets an access token for a downstream API on behalf of the user account which claims are provided in the @@ -630,6 +632,9 @@ Enables to override 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. Azure AD B2C UserFlow to target. + Optional claims principal representing the user. If not provided, will use the signed-in + user (in a web app), or the user for which the token was received (in a web API) + 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. An access token to call on behalf of the user, the downstream API characterized by its scopes. @@ -1397,7 +1402,7 @@ you have previously called AddAccountToCacheFromAuthorizationCodeAsync from a method called by OpenIdConnectOptions.Events.OnAuthorizationCodeReceived. - + Typically used from a Web App or WebAPI controller, this method retrieves an access token for a downstream API using; @@ -1410,8 +1415,11 @@ 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. Azure AD B2C user flow to target. + Optional claims principal representing the user. If not provided, will use the signed-in + user (in a web app), or the user for which the token was received (in a Web API) + 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. An access token to call the downstream API and populated with this downstream API's scopes. - Calling this method from a Web API supposes that you have previously called, + 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 diff --git a/src/Microsoft.Identity.Web/TokenAcquisition.cs b/src/Microsoft.Identity.Web/TokenAcquisition.cs index b27d85f14..797c4b664 100644 --- a/src/Microsoft.Identity.Web/TokenAcquisition.cs +++ b/src/Microsoft.Identity.Web/TokenAcquisition.cs @@ -191,8 +191,11 @@ public async Task GetAccessTokenOnBehalfOfUserAsync( /// 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. /// Azure AD B2C user flow to target. + /// Optional claims principal representing the user. If not provided, will use the signed-in + /// user (in a web app), or the user for which the token was received (in a Web API) + /// 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. /// An access token to call the downstream API and populated with this downstream API's scopes. - /// Calling this method from a Web API supposes that you have previously called, + /// 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 @@ -200,7 +203,8 @@ public async Task GetAccessTokenOnBehalfOfUserAsync( public async Task GetAccessTokenForUserAsync( IEnumerable scopes, string? tenant = null, - string? userFlow = null) + string? userFlow = null, + ClaimsPrincipal? user = null) { if (scopes == null) { @@ -215,7 +219,7 @@ public async Task GetAccessTokenForUserAsync( { accessToken = await GetAccessTokenOnBehalfOfUserFromCacheAsync( _application, - CurrentHttpContext.User, + user ?? CurrentHttpContext.User, scopes, tenant, userFlow) @@ -334,12 +338,16 @@ private async Task GetOrBuildConfidentialClientA /// private async Task BuildConfidentialClientApplicationAsync() { - var request = CurrentHttpContext.Request; - string currentUri = UriHelper.BuildAbsolute( - request.Scheme, - request.Host, - request.PathBase, - _microsoftIdentityOptions.CallbackPath.Value ?? string.Empty); + var request = CurrentHttpContext?.Request; + string? currentUri = null; + if (request != null) + { + currentUri = UriHelper.BuildAbsolute( + request.Scheme, + request.Host, + request.PathBase, + _microsoftIdentityOptions.CallbackPath.Value ?? string.Empty); + } if (!_applicationOptions.Instance.EndsWith("/", StringComparison.InvariantCulture)) { @@ -354,9 +362,14 @@ private async Task BuildConfidentialClientApplic { var builder = ConfidentialClientApplicationBuilder .CreateWithApplicationOptions(_applicationOptions) - .WithRedirectUri(currentUri) .WithHttpClientFactory(_httpClientFactory); + // The redirect URI is not needed for OBO + if (!string.IsNullOrEmpty(currentUri)) + { + builder.WithRedirectUri(currentUri); + } + string authority; if (_microsoftIdentityOptions.IsB2C)