コード例 #1
0
        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);
        }
コード例 #2
0
        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);
        }
コード例 #3
0
        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));
        }
コード例 #4
0
        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);
        }
コード例 #5
0
    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));
    }
コード例 #6
0
    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));
    }
コード例 #7
0
        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));
        }
コード例 #9
0
    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));
        }
    }
コード例 #10
0
        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));
        }
コード例 #11
0
        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));
        }
コード例 #12
0
    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));
    }
コード例 #13
0
ファイル: OpenIddictServer.cs プロジェクト: montr/montr
        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));
        }
コード例 #14
0
        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."
            }));
        }
コード例 #15
0
        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));
        }
コード例 #16
0
    /// <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));
    }
コード例 #17
0
        /// <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));
        }
コード例 #18
0
    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));
        }
    }
コード例 #19
0
        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));
        }
コード例 #20
0
    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
            })
コード例 #22
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));
    }
コード例 #23
0
    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;
    }
コード例 #24
0
        private async Task <ClaimsPrincipal> CreatePrincipalAsync(OpenIddictRequest request, IUser user)
        {
            var principal = await SignInManager.CreateUserPrincipalAsync((IdentityUser)user.Identity);

            return(await EnrichPrincipalAsync(principal, request, false));
        }
コード例 #25
0
        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.");
        }
コード例 #26
0
        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));
        }
コード例 #27
0
        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));
        }
コード例 #28
0
 /// <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);
コード例 #29
0
        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);
        }
コード例 #30
0
        /// <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));
        }