private async Task <ClaimsPrincipal> CreateApplicationPrinicpalAsync(OpenIddictRequest request, object application) { var identity = new ClaimsIdentity( TokenValidationParameters.DefaultAuthenticationType, Claims.Name, Claims.Role); var principal = new ClaimsPrincipal(identity); var clientId = request.ClientId; var clientName = await applicationManager.GetDisplayNameAsync(application); if (clientId != null) { identity.AddClaim(Claims.Subject, clientId, Destinations.AccessToken, Destinations.IdentityToken); } if (clientName != null) { identity.AddClaim(Claims.Name, clientName, Destinations.AccessToken, Destinations.IdentityToken); } var properties = await applicationManager.GetPropertiesAsync(application); foreach (var claim in properties.Claims()) { identity.AddClaim(claim); } await EnrichPrincipalAsync(request, principal, true); return(principal); }
public static async Task <string> IsUserAuthorized( OpenIddictAuthorizationManager <BTCPayOpenIdAuthorization> authorizationManager, OpenIddictRequest request, string userId, string applicationId) { var authorizations = await authorizationManager.ListAsync(queryable => queryable.Where(authorization => authorization.Subject.Equals(userId, StringComparison.OrdinalIgnoreCase) && applicationId.Equals(authorization.Application.Id, StringComparison.OrdinalIgnoreCase) && authorization.Status.Equals(OpenIddictConstants.Statuses.Valid, StringComparison.OrdinalIgnoreCase))).ToArrayAsync(); if (authorizations.Length > 0) { var scopeTasks = authorizations.Select(authorization => (authorizationManager.GetScopesAsync(authorization).AsTask(), authorization.Id)); await Task.WhenAll(scopeTasks.Select((tuple) => tuple.Item1)); var authorizationsWithSufficientScopes = scopeTasks .Select((tuple) => (Id: tuple.Id, Scopes: tuple.Item1.Result)) .Where((tuple) => !request.GetScopes().Except(tuple.Scopes).Any()); if (authorizationsWithSufficientScopes.Any()) { return(authorizationsWithSufficientScopes.First().Id); } } return(null); }
private async Task <ClaimsPrincipal> CreateApplicationPrincipalAsync(OpenIddictRequest request, object application) { var identity = new ClaimsIdentity( TokenValidationParameters.DefaultAuthenticationType, Claims.Name, Claims.Role); var principal = new ClaimsPrincipal(identity); if (request.ClientId != null) { identity.AddClaim(Claims.Subject, request.ClientId, Destinations.AccessToken, Destinations.IdentityToken); } var properties = await applicationManager.GetPropertiesAsync(application, HttpContext.RequestAborted); foreach (var claim in properties.Claims()) { identity.AddClaim(claim); } return(await EnrichPrincipalAsync(principal, request, true)); }
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); }
public void PropertyGetter_ReturnsExpectedParameter(string property, string name, OpenIddictParameter value) { // Arrange var request = new OpenIddictRequest(); request.SetParameter(name, value); // Act and assert Assert.Equal(value.Value, typeof(OpenIddictRequest).GetProperty(property) !.GetValue(request)); }
public void PropertySetter_AddsExpectedParameter(string property, string name, OpenIddictParameter value) { // Arrange var request = new OpenIddictRequest(); // Act typeof(OpenIddictRequest).GetProperty(property) !.SetValue(request, value.Value); // Assert Assert.Equal(value, request.GetParameter(name)); }
private async Task EnrichPrincipalAsync(OpenIddictRequest request, ClaimsPrincipal principal, bool alwaysDeliverPermissions) { var scopes = request.GetScopes(); principal.SetScopes(scopes); principal.SetResources(await scopeManager.ListResourcesAsync(scopes, HttpContext.RequestAborted).ToListAsync(HttpContext.RequestAborted)); foreach (var claim in principal.Claims) { claim.SetDestinations(GetDestinations(claim, principal, alwaysDeliverPermissions)); } }
/// <summary> /// Sends a generic OpenID Connect request to the given endpoint using POST /// and converts the returned response to an OpenID Connect response. /// </summary> /// <param name="uri">The endpoint to which the request is sent.</param> /// <param name="request">The OpenID Connect request to send.</param> /// <returns>The OpenID Connect response returned by the server.</returns> public Task <OpenIddictResponse> PostAsync(string uri, OpenIddictRequest request) { if (request == null) { throw new ArgumentNullException(nameof(request)); } if (string.IsNullOrEmpty(uri)) { throw new ArgumentException("The URL cannot be null or empty.", nameof(uri)); } return(PostAsync(new Uri(uri, UriKind.RelativeOrAbsolute), request)); }
protected virtual async Task <IActionResult> HandleTwoFactorLoginAsync(OpenIddictRequest request, IdentityUser user) { var twoFactorProvider = request.GetParameter("TwoFactorProvider")?.ToString(); var twoFactorCode = request.GetParameter("TwoFactorCode")?.ToString();; if (!twoFactorProvider.IsNullOrWhiteSpace() && !twoFactorCode.IsNullOrWhiteSpace()) { var providers = await UserManager.GetValidTwoFactorProvidersAsync(user); if (providers.Contains(twoFactorProvider) && await UserManager.VerifyTwoFactorTokenAsync(user, twoFactorProvider, twoFactorCode)) { return(await SetSuccessResultAsync(request, user)); } Logger.LogInformation("Authentication failed for username: {username}, reason: InvalidAuthenticatorCode", request.Username); var properties = new AuthenticationProperties(new Dictionary <string, string> { [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant, [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "Invalid authenticator code!" }); return(Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)); } else { Logger.LogInformation("Authentication failed for username: {username}, reason: RequiresTwoFactor", request.Username); var twoFactorToken = await UserManager.GenerateUserTokenAsync(user, TokenOptions.DefaultProvider, nameof(SignInResult.RequiresTwoFactor)); await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext { Identity = OpenIddictSecurityLogIdentityConsts.OpenIddict, Action = OpenIddictSecurityLogActionConsts.LoginRequiresTwoFactor, UserName = request.Username, ClientId = request.ClientId }); var properties = new AuthenticationProperties(new Dictionary <string, string> { [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant, [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = nameof(SignInResult.RequiresTwoFactor), ["userId"] = user.Id.ToString("N"), ["twoFactorToken"] = twoFactorToken }); return(Forbid(properties, 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)); }
private async Task <IActionResult> PasswordFlow(OpenIddictRequest request) { if (!request.IsPasswordGrantType()) { return(BadRequest("The specified grant type is not implemented.")); } var user = await _user.FindByNameAsync(request.Username); if (user == null || !await _sign.CanSignInAsync(user)) { var properties = new AuthenticationProperties(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. var result = await _sign.CheckPasswordSignInAsync(user, request.Password, true); if (!result.Succeeded) { var properties = new AuthenticationProperties(new Dictionary <string, string> { [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant, [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The username/password couple is invalid" }); return(Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)); } var principal = await _sign.CreateUserPrincipalAsync(user); 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)); }
protected virtual async Task <IActionResult> HandleClientCredentialsAsync(OpenIddictRequest request) { // Note: the client credentials are automatically validated by OpenIddict: // if client_id or client_secret are invalid, this action won't be invoked. var application = await ApplicationManager.FindByClientIdAsync(request.ClientId); if (application == null) { throw new InvalidOperationException(L["TheApplicationDetailsCannotBeFound"]); } // Create a new ClaimsIdentity containing the claims that // will be used to create an id_token, a token or a code. var identity = new ClaimsIdentity( TokenValidationParameters.DefaultAuthenticationType, OpenIddictConstants.Claims.PreferredUsername, OpenIddictConstants.Claims.Role); // The Subject and PreferredUsername will be removed by <see cref="RemoveClaimsFromClientCredentialsGrantType"/>. // Use the client_id as the subject identifier. identity.AddClaim(OpenIddictConstants.Claims.Subject, await ApplicationManager.GetClientIdAsync(application), OpenIddictConstants.Destinations.AccessToken, OpenIddictConstants.Destinations.IdentityToken); identity.AddClaim(OpenIddictConstants.Claims.PreferredUsername, await ApplicationManager.GetDisplayNameAsync(application), OpenIddictConstants.Destinations.AccessToken, OpenIddictConstants.Destinations.IdentityToken); // Note: In the original OAuth 2.0 specification, the client credentials grant // doesn't return an identity token, which is an OpenID Connect concept. // // As a non-standardized extension, OpenIddict allows returning an id_token // to convey information about the client application when the "openid" scope // is granted (i.e specified when calling principal.SetScopes()). When the "openid" // scope is not explicitly set, no identity token is returned to the client application. // Set the list of scopes granted to the client application in access_token. var principal = new ClaimsPrincipal(identity); principal.SetScopes(request.GetScopes()); principal.SetResources(await GetResourcesAsync(request.GetScopes())); foreach (var claim in principal.Claims) { claim.SetDestinations(GetDestinations(claim)); } return(SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)); }
private async Task <IActionResult> AuthorizeImplicitFlow( OpenIddictRequest oidcRequest, HttpContext httpContext, CancellationToken cancellationToken) { if (httpContext.User.Identity?.IsAuthenticated == false) { // If the client application request promptless authentication, // return an error indicating that the user is not logged in. if (oidcRequest.HasPrompt(Prompts.None)) { var properties = new AuthenticationProperties(new Dictionary <string, string> { [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.LoginRequired, [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is not logged in." }); // Ask OpenIddict to return a login_required error to the client application. return(new ForbidResult(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, properties)); } return(new ChallengeResult()); } // Retrieve the profile of the logged in user. var dbUser = await _userManager.GetUserAsync(httpContext.User) ?? throw new InvalidOperationException("The user details cannot be retrieved."); // Create a new ClaimsPrincipal containing the claims that // will be used to create an id_token, a token or a code. var principal = await _signInManager.CreateUserPrincipalAsync(dbUser); // Set the list of scopes granted to the client application. var scopes = oidcRequest.GetScopes(); var resources = await _scopeManager.ListResourcesAsync(scopes, cancellationToken).ToListAsync(); principal.SetScopes(scopes); principal.SetResources(resources); foreach (var claim in principal.Claims) { claim.SetDestinations(GetDestinations(claim, principal)); } // Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens. return(new SignInResult(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, principal)); }
public async Task <IActionResult> Exchange() { //Change based on: https://github.com/openiddict/openiddict-core/blob/dev/samples/Mvc.Server/Controllers/AuthorizationController.cs#L59 //If you try to do it as a parameter then you'll get a grant_type failure for some reason. OpenIddictRequest authRequest = HttpContext.GetOpenIddictServerRequest() ?? throw new InvalidOperationException("The OpenID Connect request cannot be retrieved."); if (authRequest.IsPasswordGrantType()) { return(await Authenticate(authRequest.Username, authRequest.Password, authRequest.GetScopes())); } return(BadRequest(new OpenIddictResponse() { Error = OpenIddictConstants.Errors.UnsupportedGrantType, ErrorDescription = "The specified grant type is not supported." })); }
private async Task <IActionResult> ExchangeClientCredentialsGrantType(OpenIddictRequest request) { // Note: client authentication is always enforced by OpenIddict before this action is invoked. var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ?? throw new InvalidOperationException("The application details cannot be found."); var identity = new ClaimsIdentity( OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, Claims.Name, Claims.Role); identity.AddClaim(OpenIdConstants.Claims.EntityType, OpenIdConstants.EntityTypes.Application, Destinations.AccessToken, Destinations.IdentityToken); identity.AddClaim(Claims.Subject, request.ClientId, Destinations.AccessToken, Destinations.IdentityToken); identity.AddClaim(Claims.Name, await _applicationManager.GetDisplayNameAsync(application), Destinations.AccessToken, Destinations.IdentityToken); // If the role service is available, add all the role claims // associated with the application roles in the database. var roleService = HttpContext.RequestServices.GetService <IRoleService>(); foreach (var role in await _applicationManager.GetRolesAsync(application)) { identity.AddClaim(identity.RoleClaimType, role, Destinations.AccessToken, Destinations.IdentityToken); if (roleService != null) { foreach (var claim in await roleService.GetRoleClaimsAsync(role)) { identity.AddClaim(claim.SetDestinations(Destinations.AccessToken, Destinations.IdentityToken)); } } } var principal = new ClaimsPrincipal(identity); principal.SetResources(await GetResourcesAsync(request.GetScopes())); return(SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)); }
/// <summary> /// Sends a generic OpenID Connect request to the given endpoint and /// converts the returned response to an OpenID Connect response. /// </summary> /// <param name="method">The HTTP method used to send the OpenID Connect request.</param> /// <param name="uri">The endpoint to which the request is sent.</param> /// <param name="request">The OpenID Connect request to send.</param> /// <returns>The OpenID Connect response returned by the server.</returns> public Task <OpenIddictResponse> SendAsync(HttpMethod method, string uri, OpenIddictRequest request) { if (method is null) { throw new ArgumentNullException(nameof(method)); } if (request is null) { throw new ArgumentNullException(nameof(request)); } if (string.IsNullOrEmpty(uri)) { throw new ArgumentException("The URL cannot be null or empty.", nameof(uri)); } return(SendAsync(method, new Uri(uri, UriKind.RelativeOrAbsolute), request)); }
/// <summary> /// Sends a generic OpenID Connect request to the given endpoint and /// converts the returned response to an OpenID Connect response. /// </summary> /// <param name="method">The HTTP method used to send the OpenID Connect request.</param> /// <param name="uri">The endpoint to which the request is sent.</param> /// <param name="request">The OpenID Connect request to send.</param> /// <returns>The OpenID Connect response returned by the server.</returns> public Task <OpenIddictResponse> SendAsync(string method, string uri, OpenIddictRequest request) { if (request == null) { throw new ArgumentNullException(nameof(request)); } if (string.IsNullOrEmpty(method)) { throw new ArgumentException("The HTTP method cannot be null or empty.", nameof(method)); } if (string.IsNullOrEmpty(uri)) { throw new ArgumentException("The URL cannot be null or empty.", nameof(uri)); } return(SendAsync(new HttpMethod(method), uri, request)); }
protected virtual async Task <IActionResult> HandleDeviceCodeAsync(OpenIddictRequest request) { // Retrieve the claims principal stored in the authorization code/device code/refresh token. var principal = (await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)).Principal; using (CurrentTenant.Change(principal.FindTenantId())) { // Retrieve the user profile corresponding to the authorization code/refresh token. // Note: if you want to automatically invalidate the authorization code/refresh token // when the user password/roles change, use the following line instead: // var user = _signInManager.ValidateSecurityStampAsync(info.Principal); var user = await UserManager.GetUserAsync(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." }))); } // Ensure the user is still allowed to sign in. if (!await SignInManager.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." }))); } await SetClaimsDestinationsAsync(principal); // Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens. return(SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)); } }
private async Task <ActionResult> SignInAsync(OpenIddictRequest request, ApplicationUser user) { // Create a new ClaimsPrincipal containing the claims that // will be used to create an id_token, a token or a code. ClaimsPrincipal principal = await _signInManager.CreateUserPrincipalAsync(user); // Set the list of scopes granted to the client application. // Note: the offline_access scope must be granted // to allow OpenIddict to return a refresh token. principal.SetScopes(AllowedRefreshTokenScopes.Intersect(request.GetScopes())); // Note: by default, claims are NOT automatically included in the access and identity tokens. // To allow OpenIddict to serialize them, you must attach them a destination, that specifies // whether they should be included in access tokens, in identity tokens or in both. foreach (Claim claim in principal.Claims) { // Never include the security stamp in the access and identity tokens, as it's a secret value. if (claim.Type == _identityOptions.Value.ClaimsIdentity.SecurityStampClaimType) { continue; } List <string> destinations = new() { OpenIddictConstants.Destinations.AccessToken, }; // Only add the iterated claim to the id_token if the corresponding scope was granted to the client application. // The other claims will only be added to the access_token, which is encrypted when using the default format. if ((claim.Type == OpenIddictConstants.Claims.Name && principal.HasScope(OpenIddictConstants.Scopes.Profile)) || (claim.Type == OpenIddictConstants.Claims.Email && principal.HasScope(OpenIddictConstants.Scopes.Email)) || (claim.Type == OpenIddictConstants.Claims.Role && principal.HasScope(OpenIddictConstants.Scopes.Roles))) { destinations.Add(OpenIddictConstants.Destinations.IdentityToken); } claim.SetDestinations(destinations); } return(SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)); }
public async Task <ClaimsPrincipal> Handle(OpenIddictRequest request) { if (!request.IsClientCredentialsGrantType()) { throw new NotImplementedException("The specified grant type is not implemented"); } var application = await _applicationManager.FindByClientIdAsync(request.ClientId !) ?? throw new NotFoundException <string?>("Admin API Client", request.ClientId); if (!await _applicationManager.ValidateClientSecretAsync(application, request.ClientSecret !)) { throw new AuthenticationException("Invalid Admin API Client key and secret"); } var requestedScopes = request.GetScopes(); var appScopes = (await _applicationManager.GetPermissionsAsync(application)) .Where(p => p.StartsWith(OpenIddictConstants.Permissions.Prefixes.Scope)) .Select(p => p.Substring(OpenIddictConstants.Permissions.Prefixes.Scope.Length)) .ToList(); var missingScopes = requestedScopes.Where(s => !appScopes.Contains(s)).ToList(); if (missingScopes.Any()) { throw new AuthenticationException($"Client is not allowed access to requested scope(s): {string.Join(", " , missingScopes)}"); } var displayName = await _applicationManager.GetDisplayNameAsync(application); var identity = new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme); identity.AddClaim(OpenIddictConstants.Claims.Subject, request.ClientId !, OpenIddictConstants.Destinations.AccessToken); identity.AddClaim(OpenIddictConstants.Claims.Name, displayName !, OpenIddictConstants.Destinations.AccessToken); var principal = new ClaimsPrincipal(identity); principal.SetScopes(requestedScopes); return(principal); }
private HttpRequestMessage CreateRequestMessage(OpenIddictRequest request, HttpMethod method, Uri uri) { // Note: a dictionary is deliberately not used here to allow multiple parameters with the // same name to be specified. While initially not allowed by the core OAuth2 specification, // this is required for derived drafts like the OAuth2 token exchange specification. var parameters = new List <KeyValuePair <string?, string?> >(); foreach (var parameter in request.GetParameters()) { // If the parameter is null or empty, send an empty value. if (OpenIddictParameter.IsNullOrEmpty(parameter.Value)) { parameters.Add(new KeyValuePair <string?, string?>(parameter.Key, string.Empty)); continue; } var values = (string?[]?)parameter.Value; if (values is not { Length: > 0 })
protected virtual async Task <IActionResult> SetSuccessResultAsync(OpenIddictRequest request, IdentityUser user) { // Create a new ClaimsPrincipal containing the claims that // will be used to create an id_token, a token or a code. var principal = await SignInManager.CreateUserPrincipalAsync(user); principal.SetScopes(request.GetScopes()); principal.SetResources(await GetResourcesAsync(request.GetScopes())); await SetClaimsDestinationsAsync(principal); await IdentitySecurityLogManager.SaveAsync( new IdentitySecurityLogContext { Identity = OpenIddictSecurityLogIdentityConsts.OpenIddict, Action = OpenIddictSecurityLogActionConsts.LoginSucceeded, UserName = request.Username, ClientId = request.ClientId } ); return(SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)); }
protected virtual async Task ReplaceEmailToUsernameOfInputIfNeeds(OpenIddictRequest request) { if (!ValidationHelper.IsValidEmailAddress(request.Username)) { return; } var userByUsername = await UserManager.FindByNameAsync(request.Username); if (userByUsername != null) { return; } var userByEmail = await UserManager.FindByEmailAsync(request.Username); if (userByEmail == null) { return; } request.Username = userByEmail.UserName; }
private async Task <ClaimsPrincipal> CreatePrincipalAsync(OpenIddictRequest request, IUser user) { var principal = await SignInManager.CreateUserPrincipalAsync((IdentityUser)user.Identity); return(await EnrichPrincipalAsync(principal, request, false)); }
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."); }
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] = S["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> ExchangePasswordGrantType(OpenIddictRequest request) { var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ?? throw new InvalidOperationException("The application details cannot be found."); // By design, the password flow requires direct username/password validation, which is performed by // the user service. If this service is not registered, prevent the password flow from being used. var service = HttpContext.RequestServices.GetService <IUserService>(); if (service == null) { return(Forbid(new AuthenticationProperties(new Dictionary <string, string> { [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.UnsupportedGrantType, [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = S["The resource owner password credentials grant is not supported."] }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)); } string error = null; var user = await service.AuthenticateAsync(request.Username, request.Password, (key, message) => error = message); if (user == null) { return(Forbid(new AuthenticationProperties(new Dictionary <string, string> { [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant, [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = error }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)); } var principal = await service.CreatePrincipalAsync(user); var authorizations = await _authorizationManager.FindAsync( subject : principal.GetUserIdentifier(), client : await _applicationManager.GetIdAsync(application), status : Statuses.Valid, type : AuthorizationTypes.Permanent, scopes : request.GetScopes()).ToListAsync(); // If the application is configured to use external consent, // reject the request if no existing authorization can be found. switch (await _applicationManager.GetConsentTypeAsync(application)) { case ConsentTypes.External when !authorizations.Any(): return(Forbid(new AuthenticationProperties(new Dictionary <string, string> { [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired, [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = S["The logged in user is not allowed to access this client application."] }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)); } 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())); } principal.SetScopes(request.GetScopes()); principal.SetResources(await GetResourcesAsync(request.GetScopes())); // Automatically create a permanent authorization to avoid requiring explicit consent // for future authorization or token requests containing the same scopes. var authorization = authorizations.FirstOrDefault(); if (authorization == null) { authorization = await _authorizationManager.CreateAsync( principal : principal, subject : principal.GetUserIdentifier(), client : await _applicationManager.GetIdAsync(application), type : AuthorizationTypes.Permanent, scopes : principal.GetScopes()); } principal.SetAuthorizationId(await _authorizationManager.GetIdAsync(authorization)); foreach (var claim in principal.Claims) { claim.SetDestinations(GetDestinations(claim, principal)); } return(SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)); }
/// <summary> /// Sends a generic OpenID Connect request to the given endpoint using POST /// and converts the returned response to an OpenID Connect response. /// </summary> /// <param name="uri">The endpoint to which the request is sent.</param> /// <param name="request">The OpenID Connect request to send.</param> /// <returns>The OpenID Connect response returned by the server.</returns> public Task <OpenIddictResponse> PostAsync(Uri uri, OpenIddictRequest request) => SendAsync(HttpMethod.Post, uri, request);
private HttpRequestMessage CreateRequestMessage(OpenIddictRequest request, HttpMethod method, Uri uri) { // Note: a dictionary is deliberately not used here to allow multiple parameters with the // same name to be specified. While initially not allowed by the core OAuth2 specification, // this is required for derived drafts like the OAuth2 token exchange specification. var parameters = new List <KeyValuePair <string, string> >(); foreach (var parameter in request.GetParameters()) { // If the parameter is null or empty, send an empty value. if (OpenIddictParameter.IsNullOrEmpty(parameter.Value)) { parameters.Add(new KeyValuePair <string, string>(parameter.Key, string.Empty)); continue; } var values = (string[])parameter.Value; if (values == null || values.Length == 0) { continue; } foreach (var value in values) { parameters.Add(new KeyValuePair <string, string>(parameter.Key, value)); } } if (method == HttpMethod.Get && parameters.Count != 0) { var builder = new StringBuilder(); foreach (var parameter in parameters) { if (builder.Length != 0) { builder.Append('&'); } builder.Append(UrlEncoder.Default.Encode(parameter.Key)); builder.Append('='); builder.Append(UrlEncoder.Default.Encode(parameter.Value)); } if (!uri.IsAbsoluteUri) { uri = new Uri(HttpClient.BaseAddress, uri); } uri = new UriBuilder(uri) { Query = builder.ToString() }.Uri; } var message = new HttpRequestMessage(method, uri); if (method != HttpMethod.Get) { message.Content = new FormUrlEncodedContent(parameters); } return(message); }
/// <summary> /// Sends a generic OpenID Connect request to the given endpoint and /// converts the returned response to an OpenID Connect response. /// </summary> /// <param name="method">The HTTP method used to send the OpenID Connect request.</param> /// <param name="uri">The endpoint to which the request is sent.</param> /// <param name="request">The OpenID Connect request to send.</param> /// <returns>The OpenID Connect response returned by the server.</returns> public virtual async Task <OpenIddictResponse> SendAsync(HttpMethod method, Uri uri, OpenIddictRequest request) { if (method == null) { throw new ArgumentNullException(nameof(method)); } if (uri == null) { throw new ArgumentNullException(nameof(uri)); } if (request == null) { throw new ArgumentNullException(nameof(request)); } if (HttpClient.BaseAddress == null && !uri.IsAbsoluteUri) { throw new ArgumentException("The address cannot be a relative URI when no base address " + "is associated with the HTTP client.", nameof(uri)); } using var message = CreateRequestMessage(request, method, uri); using var response = await HttpClient.SendAsync(message); return(await GetResponseAsync(response)); }