|
|
|
@ -59,9 +59,9 @@ namespace OpenIddict.Infrastructure { |
|
|
|
// and https://tools.ietf.org/html/rfc6749#section-6 for more information.
|
|
|
|
|
|
|
|
// Skip client authentication if the client identifier is missing.
|
|
|
|
// Note: ASOS will automatically ensure that the calling application
|
|
|
|
// cannot use an authorization code or a refresh token if it's not
|
|
|
|
// the intended audience, even if client authentication was skipped.
|
|
|
|
// Note: the OpenID Connect server middleware will automatically ensure that
|
|
|
|
// the calling application cannot use an authorization code or a refresh token
|
|
|
|
// if it's not the intended audience, even if client authentication was skipped.
|
|
|
|
if (string.IsNullOrEmpty(context.ClientId)) { |
|
|
|
services.Logger.LogInformation("The token request validation process was skipped " + |
|
|
|
"because the client_id parameter was missing or empty."); |
|
|
|
@ -126,236 +126,263 @@ namespace OpenIddict.Infrastructure { |
|
|
|
context.Validate(); |
|
|
|
} |
|
|
|
|
|
|
|
public override async Task GrantClientCredentials([NotNull] GrantClientCredentialsContext context) { |
|
|
|
public override async Task HandleTokenRequest([NotNull] HandleTokenRequestContext context) { |
|
|
|
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>(); |
|
|
|
|
|
|
|
// Retrieve the application details corresponding to the requested client_id.
|
|
|
|
// Note: this call shouldn't return a null instance, but a race condition may occur
|
|
|
|
// if the application was removed after the initial check made by ValidateTokenRequest.
|
|
|
|
var application = await services.Applications.FindByClientIdAsync(context.ClientId); |
|
|
|
if (application == null) { |
|
|
|
throw new InvalidOperationException("The token request was aborted because the client application corresponding " + |
|
|
|
$"to the '{context.ClientId}' identifier was not found in the database."); |
|
|
|
} |
|
|
|
Debug.Assert(context.Request.IsAuthorizationCodeGrantType() || |
|
|
|
context.Request.IsClientCredentialsGrantType() || |
|
|
|
context.Request.IsPasswordGrantType() || |
|
|
|
context.Request.IsRefreshTokenGrantType(), "The grant_type parameter should be a supported value."); |
|
|
|
|
|
|
|
var identity = new ClaimsIdentity(context.Options.AuthenticationScheme); |
|
|
|
// Note: the OpenID Connect server middleware automatically reuses the authentication ticket
|
|
|
|
// stored in the authorization code to create a new identity. To ensure the user was not removed
|
|
|
|
// after the authorization code was issued, a new check is made before validating the request.
|
|
|
|
if (context.Request.IsAuthorizationCodeGrantType()) { |
|
|
|
Debug.Assert(context.Ticket.Principal != null, "The authentication ticket shouldn't be null."); |
|
|
|
|
|
|
|
// Note: the name identifier is always included in both identity and
|
|
|
|
// access tokens, even if an explicit destination is not specified.
|
|
|
|
identity.AddClaim(ClaimTypes.NameIdentifier, context.ClientId); |
|
|
|
var user = await services.Users.GetUserAsync(context.Ticket.Principal); |
|
|
|
if (user == null) { |
|
|
|
services.Logger.LogError("The token request was rejected because the user profile associated " + |
|
|
|
"with the authorization code was not found in the database: '{Identifier}'.", |
|
|
|
context.Ticket.Principal.GetClaim(ClaimTypes.NameIdentifier)); |
|
|
|
|
|
|
|
identity.AddClaim(ClaimTypes.Name, await services.Applications.GetDisplayNameAsync(application), |
|
|
|
OpenIdConnectConstants.Destinations.AccessToken, |
|
|
|
OpenIdConnectConstants.Destinations.IdentityToken); |
|
|
|
context.Reject( |
|
|
|
error: OpenIdConnectConstants.Errors.InvalidGrant, |
|
|
|
description: "The authorization code is no longer valid."); |
|
|
|
|
|
|
|
// Create a new authentication ticket
|
|
|
|
// holding the application identity.
|
|
|
|
var ticket = new AuthenticationTicket( |
|
|
|
new ClaimsPrincipal(identity), |
|
|
|
new AuthenticationProperties(), |
|
|
|
context.Options.AuthenticationScheme); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
ticket.SetResources(context.Request.GetResources()); |
|
|
|
ticket.SetScopes(context.Request.GetScopes()); |
|
|
|
context.Validate(context.Ticket); |
|
|
|
} |
|
|
|
|
|
|
|
context.Validate(ticket); |
|
|
|
} |
|
|
|
// Note: the OpenID Connect server middleware automatically reuses the authentication ticket
|
|
|
|
// stored in the refresh token to create a new identity. To ensure the user was not removed
|
|
|
|
// after the refresh token was issued, a new check is made before validating the request.
|
|
|
|
else if (context.Request.IsRefreshTokenGrantType()) { |
|
|
|
Debug.Assert(context.Ticket.Principal != null, "The authentication ticket shouldn't be null."); |
|
|
|
|
|
|
|
public override async Task GrantAuthorizationCode([NotNull] GrantAuthorizationCodeContext context) { |
|
|
|
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>(); |
|
|
|
var user = await services.Users.GetUserAsync(context.Ticket.Principal); |
|
|
|
if (user == null) { |
|
|
|
services.Logger.LogError("The token request was rejected because the user profile associated " + |
|
|
|
"with the refresh token was not found in the database: '{Identifier}'.", |
|
|
|
context.Ticket.Principal.GetClaim(ClaimTypes.NameIdentifier)); |
|
|
|
|
|
|
|
var user = await services.Users.GetUserAsync(context.Ticket.Principal); |
|
|
|
if (user == null) { |
|
|
|
services.Logger.LogError("The token request was rejected because the user profile associated " + |
|
|
|
"with the authorization code was not found in the database: '{Identifier}'.", |
|
|
|
context.Ticket.Principal.GetClaim(ClaimTypes.NameIdentifier)); |
|
|
|
context.Reject( |
|
|
|
error: OpenIdConnectConstants.Errors.InvalidGrant, |
|
|
|
description: "The refresh token is no longer valid."); |
|
|
|
|
|
|
|
context.Reject( |
|
|
|
error: OpenIdConnectConstants.Errors.InvalidGrant, |
|
|
|
description: "The authorization code is no longer valid."); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
return; |
|
|
|
} |
|
|
|
// Extract the token identifier from the refresh token.
|
|
|
|
var identifier = context.Ticket.GetTicketId(); |
|
|
|
Debug.Assert(!string.IsNullOrEmpty(identifier), |
|
|
|
"The refresh token should contain a ticket identifier."); |
|
|
|
|
|
|
|
context.Validate(context.Ticket); |
|
|
|
} |
|
|
|
// Retrieve the token from the database and ensure it is still valid.
|
|
|
|
var token = await services.Tokens.FindByIdAsync(identifier); |
|
|
|
if (token == null) { |
|
|
|
services.Logger.LogError("The token request was rejected because the refresh token was revoked."); |
|
|
|
|
|
|
|
public override async Task GrantRefreshToken([NotNull] GrantRefreshTokenContext context) { |
|
|
|
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>(); |
|
|
|
context.Reject( |
|
|
|
error: OpenIdConnectConstants.Errors.InvalidGrant, |
|
|
|
description: "The refresh token is no longer valid."); |
|
|
|
|
|
|
|
var user = await services.Users.GetUserAsync(context.Ticket.Principal); |
|
|
|
if (user == null) { |
|
|
|
services.Logger.LogError("The token request was rejected because the user profile associated " + |
|
|
|
"with the refresh token was not found in the database: '{Identifier}'.", |
|
|
|
context.Ticket.Principal.GetClaim(ClaimTypes.NameIdentifier)); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
context.Reject( |
|
|
|
error: OpenIdConnectConstants.Errors.InvalidGrant, |
|
|
|
description: "The refresh token is no longer valid."); |
|
|
|
// When sliding expiration is enabled, immediately
|
|
|
|
// revoke the refresh token to prevent future reuse.
|
|
|
|
// See https://tools.ietf.org/html/rfc6749#section-6.
|
|
|
|
if (context.Options.UseSlidingExpiration) { |
|
|
|
await services.Tokens.RevokeAsync(token); |
|
|
|
} |
|
|
|
|
|
|
|
return; |
|
|
|
// Note: the "scopes" property stored in context.AuthenticationTicket is automatically updated by the
|
|
|
|
// OpenID Connect server middleware when the client application requests a restricted scopes collection.
|
|
|
|
var identity = await services.Users.CreateIdentityAsync(user, context.Ticket.GetScopes()); |
|
|
|
if (identity == null) { |
|
|
|
throw new InvalidOperationException("The token request was aborted because the user manager returned a null " + |
|
|
|
$"identity for user '{await services.Users.GetUserNameAsync(user)}'."); |
|
|
|
} |
|
|
|
|
|
|
|
// Create a new authentication ticket holding the user identity but
|
|
|
|
// reuse the authentication properties stored in the refresh token.
|
|
|
|
var ticket = new AuthenticationTicket( |
|
|
|
new ClaimsPrincipal(identity), |
|
|
|
context.Ticket.Properties, |
|
|
|
context.Options.AuthenticationScheme); |
|
|
|
|
|
|
|
context.Validate(ticket); |
|
|
|
} |
|
|
|
|
|
|
|
// Extract the token identifier from the refresh token.
|
|
|
|
var identifier = context.Ticket.GetTicketId(); |
|
|
|
Debug.Assert(!string.IsNullOrEmpty(identifier), |
|
|
|
"The refresh token should contain a ticket identifier."); |
|
|
|
else if (context.Request.IsPasswordGrantType()) { |
|
|
|
// Note: at this stage, the client credentials cannot be null as the OpenID Connect server middleware
|
|
|
|
// automatically rejects grant_type=password requests that don't specify a username/password couple.
|
|
|
|
Debug.Assert(!string.IsNullOrEmpty(context.Request.Username) && |
|
|
|
!string.IsNullOrEmpty(context.Request.Password), "The user credentials shouldn't be null."); |
|
|
|
|
|
|
|
// Retrieve the token from the database and ensure it is still valid.
|
|
|
|
var token = await services.Tokens.FindByIdAsync(identifier); |
|
|
|
if (token == null) { |
|
|
|
services.Logger.LogError("The token request was rejected because the refresh token was revoked."); |
|
|
|
var user = await services.Users.FindByNameAsync(context.Request.Username); |
|
|
|
if (user == null) { |
|
|
|
services.Logger.LogError("The token request was rejected because no user profile corresponding to " + |
|
|
|
"the specified username was found: '{Username}'.", context.Request.Username); |
|
|
|
|
|
|
|
context.Reject( |
|
|
|
error: OpenIdConnectConstants.Errors.InvalidGrant, |
|
|
|
description: "The refresh token is no longer valid."); |
|
|
|
context.Reject( |
|
|
|
error: OpenIdConnectConstants.Errors.InvalidGrant, |
|
|
|
description: "Invalid credentials."); |
|
|
|
|
|
|
|
return; |
|
|
|
} |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// When sliding expiration is enabled, immediately
|
|
|
|
// revoke the refresh token to prevent future reuse.
|
|
|
|
// See https://tools.ietf.org/html/rfc6749#section-6.
|
|
|
|
if (context.Options.UseSlidingExpiration) { |
|
|
|
await services.Tokens.RevokeAsync(token); |
|
|
|
} |
|
|
|
// Ensure the user is allowed to sign in.
|
|
|
|
if (!await services.SignIn.CanSignInAsync(user)) { |
|
|
|
services.Logger.LogError("The token request was rejected because the user '{Username}' " + |
|
|
|
"was not allowed to sign in.", context.Request.Username); |
|
|
|
|
|
|
|
// Note: the "scopes" property stored in context.AuthenticationTicket is automatically
|
|
|
|
// updated by ASOS when the client application requests a restricted scopes collection.
|
|
|
|
var identity = await services.Users.CreateIdentityAsync(user, context.Ticket.GetScopes()); |
|
|
|
if (identity == null) { |
|
|
|
throw new InvalidOperationException("The token request failed because the user manager returned a null " + |
|
|
|
$"identity for user '{await services.Users.GetUserNameAsync(user)}'."); |
|
|
|
} |
|
|
|
context.Reject( |
|
|
|
error: OpenIdConnectConstants.Errors.InvalidGrant, |
|
|
|
description: "The user is not allowed to sign in."); |
|
|
|
|
|
|
|
// Create a new authentication ticket holding the user identity but
|
|
|
|
// reuse the authentication properties stored in the refresh token.
|
|
|
|
var ticket = new AuthenticationTicket( |
|
|
|
new ClaimsPrincipal(identity), |
|
|
|
context.Ticket.Properties, |
|
|
|
context.Options.AuthenticationScheme); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
context.Validate(ticket); |
|
|
|
} |
|
|
|
// Ensure the user is not already locked out.
|
|
|
|
if (services.Users.SupportsUserLockout && await services.Users.IsLockedOutAsync(user)) { |
|
|
|
services.Logger.LogError("The token request was rejected because the account '{Username}' " + |
|
|
|
"was locked out to prevent brute force attacks.", context.Request.Username); |
|
|
|
|
|
|
|
public override async Task GrantResourceOwnerCredentials([NotNull] GrantResourceOwnerCredentialsContext context) { |
|
|
|
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>(); |
|
|
|
context.Reject( |
|
|
|
error: OpenIdConnectConstants.Errors.InvalidGrant, |
|
|
|
description: "Account locked out."); |
|
|
|
|
|
|
|
var user = await services.Users.FindByNameAsync(context.UserName); |
|
|
|
if (user == null) { |
|
|
|
services.Logger.LogError("The token request was rejected because no user profile corresponding to " + |
|
|
|
"the specified username was found: '{Username}'.", context.UserName); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
context.Reject( |
|
|
|
error: OpenIdConnectConstants.Errors.InvalidGrant, |
|
|
|
description: "Invalid credentials."); |
|
|
|
// Ensure the password is valid.
|
|
|
|
if (!await services.Users.CheckPasswordAsync(user, context.Request.Password)) { |
|
|
|
services.Logger.LogError("The token request was rejected because the password didn't match " + |
|
|
|
"the password associated with the account '{Username}'.", context.Request.Username); |
|
|
|
|
|
|
|
return; |
|
|
|
} |
|
|
|
context.Reject( |
|
|
|
error: OpenIdConnectConstants.Errors.InvalidGrant, |
|
|
|
description: "Invalid credentials."); |
|
|
|
|
|
|
|
// Ensure the user is allowed to sign in.
|
|
|
|
if (!await services.SignIn.CanSignInAsync(user)) { |
|
|
|
services.Logger.LogError("The token request was rejected because the user '{Username}' " + |
|
|
|
"was not allowed to sign in.", context.UserName); |
|
|
|
if (services.Users.SupportsUserLockout) { |
|
|
|
await services.Users.AccessFailedAsync(user); |
|
|
|
|
|
|
|
context.Reject( |
|
|
|
error: OpenIdConnectConstants.Errors.InvalidGrant, |
|
|
|
description: "The user is not allowed to sign in."); |
|
|
|
// Ensure the user is not locked out.
|
|
|
|
if (await services.Users.IsLockedOutAsync(user)) { |
|
|
|
services.Logger.LogError("The token request was rejected because the account '{Username}' " + |
|
|
|
"was locked out to prevent brute force attacks.", context.Request.Username); |
|
|
|
|
|
|
|
return; |
|
|
|
} |
|
|
|
context.Reject( |
|
|
|
error: OpenIdConnectConstants.Errors.InvalidGrant, |
|
|
|
description: "Account locked out."); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Ensure the user is not already locked out.
|
|
|
|
if (services.Users.SupportsUserLockout && await services.Users.IsLockedOutAsync(user)) { |
|
|
|
services.Logger.LogError("The token request was rejected because the account '{Username}' " + |
|
|
|
"was locked out to prevent brute force attacks.", context.UserName); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
context.Reject( |
|
|
|
error: OpenIdConnectConstants.Errors.InvalidGrant, |
|
|
|
description: "Account locked out."); |
|
|
|
if (services.Users.SupportsUserLockout) { |
|
|
|
await services.Users.ResetAccessFailedCountAsync(user); |
|
|
|
} |
|
|
|
|
|
|
|
return; |
|
|
|
} |
|
|
|
// Reject the token request if two-factor authentication has been enabled by the user.
|
|
|
|
if (services.Users.SupportsUserTwoFactor && await services.Users.GetTwoFactorEnabledAsync(user)) { |
|
|
|
services.Logger.LogError("The token request was rejected because two-factor authentication " + |
|
|
|
"was required for the account '{Username}.", context.Request.Username); |
|
|
|
|
|
|
|
// Ensure the password is valid.
|
|
|
|
if (!await services.Users.CheckPasswordAsync(user, context.Password)) { |
|
|
|
services.Logger.LogError("The token request was rejected because the password didn't match " + |
|
|
|
"the password associated with the account '{Username}'.", context.UserName); |
|
|
|
context.Reject( |
|
|
|
error: OpenIdConnectConstants.Errors.InvalidGrant, |
|
|
|
description: "Two-factor authentication is required for this account."); |
|
|
|
|
|
|
|
context.Reject( |
|
|
|
error: OpenIdConnectConstants.Errors.InvalidGrant, |
|
|
|
description: "Invalid credentials."); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (services.Users.SupportsUserLockout) { |
|
|
|
await services.Users.AccessFailedAsync(user); |
|
|
|
// Return an error if the username corresponds to the registered
|
|
|
|
// email address and if the "email" scope has not been requested.
|
|
|
|
if (services.Users.SupportsUserEmail && context.Request.HasScope(OpenIdConnectConstants.Scopes.Profile) && |
|
|
|
!context.Request.HasScope(OpenIdConnectConstants.Scopes.Email)) { |
|
|
|
// Retrieve the username and the email address associated with the user.
|
|
|
|
var username = await services.Users.GetUserNameAsync(user); |
|
|
|
var email = await services.Users.GetEmailAsync(user); |
|
|
|
|
|
|
|
// Ensure the user is not locked out.
|
|
|
|
if (await services.Users.IsLockedOutAsync(user)) { |
|
|
|
services.Logger.LogError("The token request was rejected because the account '{Username}' " + |
|
|
|
"was locked out to prevent brute force attacks.", context.UserName); |
|
|
|
if (!string.IsNullOrEmpty(email) && string.Equals(username, email, StringComparison.OrdinalIgnoreCase)) { |
|
|
|
services.Logger.LogError("The token request was rejected because the 'email' scope was not requested: " + |
|
|
|
"to prevent data leakage, the 'email' scope must be granted when the username " + |
|
|
|
"is identical to the email address associated with the user profile."); |
|
|
|
|
|
|
|
context.Reject( |
|
|
|
error: OpenIdConnectConstants.Errors.InvalidGrant, |
|
|
|
description: "Account locked out."); |
|
|
|
error: OpenIdConnectConstants.Errors.InvalidRequest, |
|
|
|
description: "The 'email' scope is required."); |
|
|
|
|
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (services.Users.SupportsUserLockout) { |
|
|
|
await services.Users.ResetAccessFailedCountAsync(user); |
|
|
|
} |
|
|
|
var identity = await services.Users.CreateIdentityAsync(user, context.Request.GetScopes()); |
|
|
|
if (identity == null) { |
|
|
|
throw new InvalidOperationException("The token request was aborted because the user manager returned a null " + |
|
|
|
$"identity for user '{await services.Users.GetUserNameAsync(user)}'."); |
|
|
|
} |
|
|
|
|
|
|
|
// Reject the token request if two-factor authentication has been enabled by the user.
|
|
|
|
if (services.Users.SupportsUserTwoFactor && await services.Users.GetTwoFactorEnabledAsync(user)) { |
|
|
|
services.Logger.LogError("The token request was rejected because two-factor authentication " + |
|
|
|
"was required for the account '{Username}.", context.UserName); |
|
|
|
// Create a new authentication ticket holding the user identity.
|
|
|
|
var ticket = new AuthenticationTicket( |
|
|
|
new ClaimsPrincipal(identity), |
|
|
|
new AuthenticationProperties(), |
|
|
|
context.Options.AuthenticationScheme); |
|
|
|
|
|
|
|
context.Reject( |
|
|
|
error: OpenIdConnectConstants.Errors.InvalidGrant, |
|
|
|
description: "Two-factor authentication is required for this account."); |
|
|
|
ticket.SetResources(context.Request.GetResources()); |
|
|
|
ticket.SetScopes(context.Request.GetScopes()); |
|
|
|
|
|
|
|
return; |
|
|
|
context.Validate(ticket); |
|
|
|
} |
|
|
|
|
|
|
|
// Return an error if the username corresponds to the registered
|
|
|
|
// email address and if the "email" scope has not been requested.
|
|
|
|
if (services.Users.SupportsUserEmail && context.Request.HasScope(OpenIdConnectConstants.Scopes.Profile) && |
|
|
|
!context.Request.HasScope(OpenIdConnectConstants.Scopes.Email)) { |
|
|
|
// Retrieve the username and the email address associated with the user.
|
|
|
|
var username = await services.Users.GetUserNameAsync(user); |
|
|
|
var email = await services.Users.GetEmailAsync(user); |
|
|
|
else if (context.Request.IsClientCredentialsGrantType()) { |
|
|
|
// Note: at this stage, the client credentials cannot be null or invalid, as client authentication is required
|
|
|
|
// to use the client credentials grant and is automatically enforced by the OpenID Connect server middleware.
|
|
|
|
Debug.Assert(!string.IsNullOrEmpty(context.Request.ClientId) && |
|
|
|
!string.IsNullOrEmpty(context.Request.ClientSecret), "The client credentials shouldn't be null."); |
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(email) && string.Equals(username, email, StringComparison.OrdinalIgnoreCase)) { |
|
|
|
services.Logger.LogError("The token request was rejected because the 'email' scope was not requested: " + |
|
|
|
"to prevent data leakage, the 'email' scope must be granted when the username" + |
|
|
|
"is identical to the email address associated with the user ({Username}).", username); |
|
|
|
// Retrieve the application details corresponding to the requested client_id.
|
|
|
|
// Note: this call shouldn't return a null instance, but a race condition may occur
|
|
|
|
// if the application was removed after the initial check made by ValidateTokenRequest.
|
|
|
|
var application = await services.Applications.FindByClientIdAsync(context.Request.ClientId); |
|
|
|
if (application == null) { |
|
|
|
services.Logger.LogError("The token request was aborted because the client application " + |
|
|
|
"was not found in the database: '{ClientId}'.", context.Request.ClientId); |
|
|
|
|
|
|
|
context.Reject( |
|
|
|
error: OpenIdConnectConstants.Errors.InvalidRequest, |
|
|
|
description: "The 'email' scope is required."); |
|
|
|
error: OpenIdConnectConstants.Errors.InvalidClient, |
|
|
|
description: "Application not found in the database: ensure that your client_id is correct."); |
|
|
|
|
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
var identity = await services.Users.CreateIdentityAsync(user, context.Request.GetScopes()); |
|
|
|
if (identity == null) { |
|
|
|
throw new InvalidOperationException("The token request failed because the user manager returned a null " + |
|
|
|
$"identity for user '{await services.Users.GetUserNameAsync(user)}'."); |
|
|
|
} |
|
|
|
var identity = new ClaimsIdentity(context.Options.AuthenticationScheme); |
|
|
|
|
|
|
|
// Note: the name identifier is always included in both identity and
|
|
|
|
// access tokens, even if an explicit destination is not specified.
|
|
|
|
identity.AddClaim(ClaimTypes.NameIdentifier, context.Request.ClientId); |
|
|
|
|
|
|
|
// Create a new authentication ticket holding the user identity.
|
|
|
|
var ticket = new AuthenticationTicket( |
|
|
|
new ClaimsPrincipal(identity), |
|
|
|
new AuthenticationProperties(), |
|
|
|
context.Options.AuthenticationScheme); |
|
|
|
identity.AddClaim(ClaimTypes.Name, await services.Applications.GetDisplayNameAsync(application), |
|
|
|
OpenIdConnectConstants.Destinations.AccessToken, |
|
|
|
OpenIdConnectConstants.Destinations.IdentityToken); |
|
|
|
|
|
|
|
ticket.SetResources(context.Request.GetResources()); |
|
|
|
ticket.SetScopes(context.Request.GetScopes()); |
|
|
|
// Create a new authentication ticket
|
|
|
|
// holding the application identity.
|
|
|
|
var ticket = new AuthenticationTicket( |
|
|
|
new ClaimsPrincipal(identity), |
|
|
|
new AuthenticationProperties(), |
|
|
|
context.Options.AuthenticationScheme); |
|
|
|
|
|
|
|
context.Validate(ticket); |
|
|
|
ticket.SetResources(context.Request.GetResources()); |
|
|
|
ticket.SetScopes(context.Request.GetScopes()); |
|
|
|
|
|
|
|
context.Validate(ticket); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |