Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ITokenAcquisition.GetAuthenticationResultForUserAsync #553

Merged
merged 7 commits into from
Sep 8, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/Microsoft.Identity.Web/ITokenAcquisition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,25 @@ Task<string> GetAccessTokenForUserAsync(
string? userFlow = null,
ClaimsPrincipal? user = null);

/// <summary>
/// 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 <see cref="HttpContext.User"/>
/// member of the controller's <see cref="HttpContext"/> parameter.
/// </summary>
/// <param name="scopes">Scopes to request for the downstream API to call.</param>
/// <param name="tenantId">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.</param>
jmprieur marked this conversation as resolved.
Show resolved Hide resolved
/// <param name="userFlow">Azure AD B2C UserFlow to target.</param>
/// <param name="user">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.</param>
jmprieur marked this conversation as resolved.
Show resolved Hide resolved
/// <returns>An <see cref="AuthenticationResult"/> to call on behalf of the user, the downstream API characterized by its scopes.</returns>
Task<AuthenticationResult> GetAuthenticationResultForUserAsync(
IEnumerable<string> scopes,
string? tenantId = null,
string? userFlow = null,
ClaimsPrincipal? user = null);

/// <summary>
/// Acquires a token from the authority configured in the app, for the confidential client itself (not on behalf of a user)
/// using the client credentials flow. See https://aka.ms/msal-net-client-credentials.
Expand Down
44 changes: 41 additions & 3 deletions src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 44 additions & 13 deletions src/Microsoft.Identity.Web/TokenAcquisition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ public async Task<string> GetAccessTokenOnBehalfOfUserAsync(
/// 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(
public async Task<AuthenticationResult> GetAuthenticationResultForUserAsync(
IEnumerable<string> scopes,
string? tenant = null,
string? userFlow = null,
Expand All @@ -231,23 +231,23 @@ public async Task<string> GetAccessTokenForUserAsync(

_application = await GetOrBuildConfidentialClientApplicationAsync().ConfigureAwait(false);
string authority = CreateAuthorityBasedOnTenantIfProvided(_application, tenant);
string? accessToken;
AuthenticationResult? authenticationResult;

try
{
// Access token will return if call is from a web API
accessToken = await GetTokenForWebApiToCallDownstreamApiAsync(
authenticationResult = await GetAuthenticationResultForWebApiToCallDownstreamApiAsync(
_application,
authority,
scopes).ConfigureAwait(false);

if (!string.IsNullOrEmpty(accessToken))
if (authenticationResult != null)
{
return accessToken;
return authenticationResult;
}

// If access token is null, this is a web app
return await GetAccessTokenForWebAppWithAccountFromCacheAsync(
return await GetAuthenticationResultForWebAppWithAccountFromCacheAsync(
_application,
user,
scopes,
Expand All @@ -266,7 +266,7 @@ public async Task<string> GetAccessTokenForUserAsync(
}
}

private async Task<string?> GetTokenForWebApiToCallDownstreamApiAsync(
private async Task<AuthenticationResult?> GetAuthenticationResultForWebApiToCallDownstreamApiAsync(
IConfidentialClientApplication application,
string authority,
IEnumerable<string> scopes)
Expand All @@ -288,7 +288,7 @@ public async Task<string> GetAccessTokenForUserAsync(
.WithAuthority(authority)
.ExecuteAsync()
.ConfigureAwait(false);
return result.AccessToken;
return result;
}

return null;
Expand Down Expand Up @@ -471,7 +471,7 @@ private async Task<IConfidentialClientApplication> BuildConfidentialClientApplic
/// <param name="authority">(optional) Authority based on a specific tenant for which to acquire a token to access the scopes
/// on behalf of the user described in the claimsPrincipal.</param>
/// <param name="userFlow">Azure AD B2C user flow to target.</param>
private async Task<string> GetAccessTokenForWebAppWithAccountFromCacheAsync(
private async Task<AuthenticationResult> GetAuthenticationResultForWebAppWithAccountFromCacheAsync(
IConfidentialClientApplication application,
ClaimsPrincipal? claimsPrincipal,
IEnumerable<string> scopes,
Expand All @@ -496,7 +496,7 @@ private async Task<string> GetAccessTokenForWebAppWithAccountFromCacheAsync(
}
}

return await GetAccessTokenForWebAppWithAccountFromCacheAsync(
return await GetAuthenticationResultForWebAppWithAccountFromCacheAsync(
application,
account,
scopes,
Expand All @@ -513,7 +513,7 @@ private async Task<string> GetAccessTokenForWebAppWithAccountFromCacheAsync(
/// <param name="authority">Authority based on a specific tenant for which to acquire a token to access the scopes
/// on behalf of the user.</param>
/// <param name="userFlow">Azure AD B2C user flow.</param>
private async Task<string> GetAccessTokenForWebAppWithAccountFromCacheAsync(
private async Task<AuthenticationResult> GetAuthenticationResultForWebAppWithAccountFromCacheAsync(
IConfidentialClientApplication application,
IAccount? account,
IEnumerable<string> scopes,
Expand All @@ -540,7 +540,7 @@ private async Task<string> GetAccessTokenForWebAppWithAccountFromCacheAsync(
.ExecuteAsync()
.ConfigureAwait(false);

return result.AccessToken;
return result;
}

result = await application
Expand All @@ -549,7 +549,7 @@ private async Task<string> GetAccessTokenForWebAppWithAccountFromCacheAsync(
.WithSendX5C(_microsoftIdentityOptions.SendX5C)
.ExecuteAsync()
.ConfigureAwait(false);
return result.AccessToken;
return result;
}

/// <summary>
Expand Down Expand Up @@ -657,5 +657,36 @@ private static bool AcceptedTokenVersionMismatch(MsalUiRequiredException msalSer

return authority;
}

/// <summary>
/// Typically used from a web app or web API 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>
jmprieur marked this conversation as resolved.
Show resolved Hide resolved
/// <param name="userFlow">Azure AD B2C user flow to target.</param>
/// <param name="user">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.</param>
jmprieur marked this conversation as resolved.
Show resolved Hide resolved
/// <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,
string? userFlow = null,
ClaimsPrincipal? user = null)
{
AuthenticationResult result = await GetAuthenticationResultForUserAsync(scopes, tenant, userFlow, user).ConfigureAwait(false);
return result.AccessToken;
}
}
}