public static async Task <ClaimsPrincipal> CreateClaimsPrincipalAsync(OpenIddictApplicationManager <BTCPayOpenIdClient> applicationManager, OpenIddictAuthorizationManager <BTCPayOpenIdAuthorization> authorizationManager, IdentityOptions identityOptions, SignInManager <ApplicationUser> signInManager, OpenIddictRequest request, ApplicationUser user) { var principal = await signInManager.CreateUserPrincipalAsync(user); if (!request.IsAuthorizationCodeGrantType() && !request.IsRefreshTokenGrantType()) { principal.SetScopes(request.GetScopes().Restrict(principal)); } else if (request.IsAuthorizationCodeGrantType() && string.IsNullOrEmpty(principal.GetInternalAuthorizationId())) { var app = await applicationManager.FindByClientIdAsync(request.ClientId); var authorizationId = await IsUserAuthorized(authorizationManager, request, user.Id, app.Id); if (!string.IsNullOrEmpty(authorizationId)) { principal.SetInternalAuthorizationId(authorizationId); } } principal.SetDestinations(identityOptions); return(principal); }
private async Task <IActionResult> ExchangeAuthorizationCodeOrRefreshTokenGrantType(OpenIddictRequest request) { var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ?? throw new InvalidOperationException("The application details cannot be found."); // Retrieve the claims principal stored in the authorization code/refresh token. var info = await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme) ?? throw new InvalidOperationException("The user principal cannot be resolved."); if (request.IsRefreshTokenGrantType()) { var type = info.Principal.FindFirst(OpenIdConstants.Claims.EntityType)?.Value; if (!string.Equals(type, OpenIdConstants.EntityTypes.User)) { return(Forbid(new AuthenticationProperties(new Dictionary <string, string> { [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.UnauthorizedClient, [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The refresh token grant type is not allowed for refresh tokens retrieved using the client credentials flow." }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)); } } // By default, re-use the principal stored in the authorization code/refresh token. var principal = info.Principal; // If the user service is available, try to refresh the principal by retrieving // the user object from the database and creating a new claims-based principal. var service = HttpContext.RequestServices.GetService <IUserService>(); if (service != null) { var user = await service.GetUserByUniqueIdAsync(principal.GetUserIdentifier()); if (user != null) { principal = await service.CreatePrincipalAsync(user); } } var identity = (ClaimsIdentity)principal.Identity; identity.AddClaim(OpenIdConstants.Claims.EntityType, OpenIdConstants.EntityTypes.User, Destinations.AccessToken, Destinations.IdentityToken); // Note: while ASP.NET Core Identity uses the legacy WS-Federation claims (exposed by the ClaimTypes class), // OpenIddict uses the newer JWT claims defined by the OpenID Connect specification. To ensure the mandatory // subject claim is correctly populated (and avoid an InvalidOperationException), it's manually added here. if (string.IsNullOrEmpty(principal.FindFirst(Claims.Subject)?.Value)) { identity.AddClaim(new Claim(Claims.Subject, principal.GetUserIdentifier())); } foreach (var claim in principal.Claims) { claim.SetDestinations(GetDestinations(claim, principal)); } return(SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)); }
private async Task <IActionResult> CodeFlow(OpenIddictRequest request) { if (!request.IsAuthorizationCodeGrantType() && !request.IsRefreshTokenGrantType()) { throw new InvalidOperationException("The specified grant type is not supported."); } var principal = (await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)).Principal; if (principal == null) { return(NotFound()); } var user = await _sign.ValidateSecurityStampAsync(principal); if (user == null) { return(Forbid( authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, properties: new AuthenticationProperties(new Dictionary <string, string> { [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant, [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The token is no longer valid." }))); } if (!await _sign.CanSignInAsync(user)) { return(Forbid( authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, properties: new AuthenticationProperties(new Dictionary <string, string> { [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant, [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is no longer allowed to sign in." }))); } principal.SetScopes(request.GetScopes()); principal.SetResources(await _scope.ListResourcesAsync(request.GetScopes()).ToListAsync()); foreach (var claim in principal.Claims) { claim.SetDestinations(GetDestinations(claim, principal)); } return(SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)); }
public async Task <ActionResult> ExchangeAsync() { OpenIddictRequest request = HttpContext.GetOpenIddictServerRequest(); if (request.IsPasswordGrantType()) { // Allow the user to log in with their email address too. // We already check that usernames are only "word" chars (\w+), so this check is sufficient. ApplicationUser user = request.Username.Contains("@", StringComparison.Ordinal) ? await _userManager.FindByEmailAsync(request.Username) : await _userManager.FindByNameAsync(request.Username); if (user == null) { AuthenticationProperties properties = new(new Dictionary <string, string> { [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant, [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The username/password couple is invalid.", }); return(Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)); } // Validate the username/password parameters and ensure the account is not locked out. SignInResult result = await _signInManager.CheckPasswordSignInAsync(user, request.Password, lockoutOnFailure : false); if (!result.Succeeded) { AuthenticationProperties properties = new(new Dictionary <string, string> { [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant, [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The username/password couple is invalid.", }); return(Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)); } return(await SignInAsync(request, user)); } if (request.IsRefreshTokenGrantType()) { // Retrieve the claims principal stored in the refresh token. AuthenticateResult result = await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); // Retrieve the user profile corresponding to the refresh token. ApplicationUser user = await _signInManager.ValidateSecurityStampAsync(result.Principal); if (user == null) { AuthenticationProperties properties = new(new Dictionary <string, string> { [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant, [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The refresh token is no longer valid.", }); return(Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)); } // Ensure the user is still allowed to sign in. if (!await _signInManager.CanSignInAsync(user)) { AuthenticationProperties properties = new(new Dictionary <string, string> { [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant, [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is no longer allowed to sign in.", }); return(Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)); } // Reuse the properties stored in the refresh token, including the scopes originally granted. return(await SignInAsync(request, user)); } IAssertionGrantHandler assertionGrantHandler = _assertionGrantHandlerProvider.GetHandler(request.GrantType); if (assertionGrantHandler != null) { // Reject the request if the "assertion" parameter is missing. if (string.IsNullOrEmpty(request.Assertion)) { AuthenticationProperties properties = new(new Dictionary <string, string> { [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidRequest, [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The mandatory 'assertion' parameter was missing.", }); return(Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)); } AssertionGrantResult validationResult = await assertionGrantHandler.ValidateAsync(request.Assertion); if (!validationResult.IsSuccessful) { AuthenticationProperties properties = new(new Dictionary <string, string> { [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant, [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = validationResult.Error, }); return(Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)); } // Find the user associated with this external log in ApplicationUser user = await _userManager.FindByLoginAsync(assertionGrantHandler.Name, validationResult.ExternalUserId); if (user == null) { if (!string.IsNullOrEmpty(request.Username)) { // They provided a user name, so try to implicitly create an account for them user = new ApplicationUser { UserName = request.Username, Email = validationResult.ExternalUserEmail }; IdentityResult creationResult = await _userManager.CreateAsync(user); if (!creationResult.Succeeded) { AuthenticationProperties properties = new(new Dictionary <string, string> { [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant, [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = string.Join(" ", creationResult.Errors.Select(error => error.Description)), }); return(Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)); } } if (user == null) { // If the user is already logged in, use the current user user = await _userManager.GetUserAsync(User); } // Add the login if we found a user if (user != null) { UserLoginInfo login = new(assertionGrantHandler.Name, validationResult.ExternalUserId, assertionGrantHandler.Name); IdentityResult addLoginResult = await _userManager.AddLoginAsync(user, login); if (!addLoginResult.Succeeded) { AuthenticationProperties properties = new(new Dictionary <string, string> { [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant, [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = string.Join(" ", addLoginResult.Errors.Select(error => error.Description)), }); return(Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)); } } else { // Ask the user to create an account. AuthenticationProperties properties = new(new Dictionary <string, string> { [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.AccountSelectionRequired, }); return(Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)); } } // Ensure the user is allowed to sign in. if (!await _signInManager.CanSignInAsync(user)) { AuthenticationProperties properties = new(new Dictionary <string, string> { [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant, [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is no longer allowed to sign in.", }); return(Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)); } return(await SignInAsync(request, user)); } throw new NotImplementedException("The specified grant type is not implemented."); }