private async Task <Option <GrantedToken> > HandleApprovedRequest(
            string issuerName,
            DeviceAuthorizationData authRequest,
            CancellationToken cancellationToken)
        {
            var scopes       = string.Join(" ", authRequest.Scopes);
            var grantedToken = await _tokenStore.GetValidGrantedToken(_jwksStore, scopes, authRequest.ClientId, cancellationToken)
                               //idTokenJwsPayload: result.AuthCode.IdTokenPayload,
                               //userInfoJwsPayload: result.AuthCode.UserInfoPayLoad)
                               .ConfigureAwait(false);

            if (grantedToken == null)
            {
                var client = await _clientStore.GetById(authRequest.ClientId, cancellationToken).ConfigureAwait(false);

                grantedToken = await client !.GenerateToken(
                    _jwksStore,
                    authRequest.Scopes,
                    issuerName,
                    //result.AuthCode.UserInfoPayLoad,
                    //result.AuthCode.IdTokenPayload,
                    cancellationToken: cancellationToken
                    //result.AuthCode.IdTokenPayload?.Claims.Where(
                    //        c => result.Client.UserClaimsToIncludeInAuthToken.Any(r => r.IsMatch(c.Type)))
                    //    .ToArray()
                    //?? Array.Empty<Claim>())
                    )
                               .ConfigureAwait(false);
                await _eventPublisher.Publish(
                    new TokenGranted(
                        Id.Create(),
                        grantedToken.UserInfoPayLoad?.Sub,
                        authRequest.ClientId,
                        string.Join(" ", authRequest.Scopes),
                        GrantTypes.AuthorizationCode,
                        DateTimeOffset.UtcNow))
                .ConfigureAwait(false);

                // Fill-in the id-token
                if (grantedToken.IdTokenPayLoad != null)
                {
                    grantedToken = grantedToken with
                    {
                        IdTokenPayLoad = JwtGenerator.UpdatePayloadDate(grantedToken.IdTokenPayLoad, client !.TokenLifetime),
                        IdToken        = await client.GenerateIdToken(grantedToken.IdTokenPayLoad, _jwksStore, cancellationToken)
                                         .ConfigureAwait(false)
                    };
                }

                await _tokenStore.AddToken(grantedToken !, cancellationToken).ConfigureAwait(false);
            }

            return(grantedToken !);
        }
    }
示例#2
0
        public static async Task <string?> GenerateIdToken(
            this IClientStore clientStore,
            string clientId,
            JwtPayload jwsPayload,
            IJwksStore jwksStore,
            CancellationToken cancellationToken)
        {
            var client = await clientStore.GetById(clientId, cancellationToken).ConfigureAwait(false);

            return(client == null
                ? null
                : await GenerateIdToken(client, jwsPayload, jwksStore, cancellationToken).ConfigureAwait(false));
        }
示例#3
0
        public async Task <IActionResult> RevokeSessionCallback([FromQuery] RevokeSessionRequest?request, CancellationToken cancellationToken)
        {
            var authenticatedUser = await _authenticationService.GetAuthenticatedUser(this, CookieNames.CookieName).ConfigureAwait(false);

            if (authenticatedUser == null || authenticatedUser.Identity?.IsAuthenticated != true)
            {
                return(Ok(new { Authenticated = false }));
            }

            Response.Cookies.Delete(CoreConstants.SessionId);
            await _authenticationService.SignOutAsync(HttpContext, CookieNames.CookieName, new AuthenticationProperties()).ConfigureAwait(false);

            if (request != null &&
                request.post_logout_redirect_uri != null &&
                !string.IsNullOrWhiteSpace(request.id_token_hint))
            {
                var handler       = new JwtSecurityTokenHandler();
                var jsonWebKeySet = await _jwksStore.GetPublicKeys(cancellationToken).ConfigureAwait(false);

                var tokenValidationParameters = new TokenValidationParameters
                {
                    ValidateActor     = false,
                    ValidateAudience  = false,
                    ValidateIssuer    = false,
                    IssuerSigningKeys = jsonWebKeySet.Keys
                };
                handler.ValidateToken(request.id_token_hint, tokenValidationParameters, out var token);
                var jws   = (token as JwtSecurityToken)?.Payload;
                var claim = jws?.GetClaimValue(StandardClaimNames.Azp);
                if (claim != null)
                {
                    var client = await _clientRepository.GetById(claim, cancellationToken).ConfigureAwait(false);

                    if (client?.PostLogoutRedirectUris != null && client.PostLogoutRedirectUris.Any(x => x == request.post_logout_redirect_uri))
                    {
                        var redirectUrl = request.post_logout_redirect_uri;
                        if (!string.IsNullOrWhiteSpace(request.state))
                        {
                            redirectUrl = new Uri($"{redirectUrl.AbsoluteUri}?state={request.state}");
                        }

                        return(Redirect(redirectUrl.AbsoluteUri));
                    }
                }
            }

            return(Ok(new { Authenticated = true }));
        }
示例#4
0
        public async Task <IActionResult> Index(string code, CancellationToken cancellationToken)
        {
            var request = _dataProtector.Unprotect <AuthorizationRequest>(code);

            if (request.client_id == null)
            {
                return(BadRequest());
            }
            var authenticatedUser = await SetUser().ConfigureAwait(false);

            var issuerName   = Request.GetAbsoluteUriWithVirtualPath();
            var actionResult = await _displayConsent.Execute(
                request.ToParameter(),
                authenticatedUser ?? new ClaimsPrincipal(),
                issuerName,
                cancellationToken)
                               .ConfigureAwait(false);

            var result = actionResult.EndpointResult.CreateRedirectionFromActionResult(request, _logger);

            if (result != null)
            {
                return(result);
            }

            var client = await _clientStore.GetById(request.client_id, cancellationToken).ConfigureAwait(false);

            if (client == null)
            {
                return(BadRequest());
            }
            var viewModel = new ConsentViewModel
            {
                ClientDisplayName        = client.ClientName,
                AllowedScopeDescriptions =
                    actionResult?.Scopes == null
                        ? new List <string>()
                        : actionResult.Scopes.Select(s => s.Description).ToList(),
                AllowedIndividualClaims = actionResult?.AllowedClaims ?? new List <string>(),
                LogoUri   = client.LogoUri?.AbsoluteUri,
                PolicyUri = client.PolicyUri?.AbsoluteUri,
                TosUri    = client.TosUri?.AbsoluteUri,
                Code      = code
            };

            return(Ok(viewModel));
        }
        public static async Task <Option <GrantedToken> > GenerateToken(
            this IClientStore clientStore,
            IJwksStore jwksStore,
            string clientId,
            string[] scope,
            string issuerName,
            CancellationToken cancellationToken,
            JwtPayload?userInformationPayload = null,
            JwtPayload?idTokenPayload         = null,
            params Claim[] additionalClaims)
        {
            if (string.IsNullOrWhiteSpace(clientId))
            {
                return(new Option <GrantedToken> .Error(new ErrorDetails
                {
                    Title = ErrorCodes.InvalidClient,
                    Detail = SharedStrings.TheClientDoesntExist,
                    Status = HttpStatusCode.NotFound
                }));
            }

            var client = await clientStore.GetById(clientId, cancellationToken).ConfigureAwait(false);

            if (client == null)
            {
                return(new Option <GrantedToken> .Error(new ErrorDetails
                {
                    Title = ErrorCodes.InvalidClient,
                    Detail = SharedStrings.TheClientDoesntExist,
                    Status = HttpStatusCode.NotFound
                }));
            }

            var token = await GenerateToken(
                client,
                jwksStore,
                scope,
                issuerName,
                userInformationPayload,
                idTokenPayload,
                cancellationToken,
                additionalClaims)
                        .ConfigureAwait(false);

            return(new Option <GrantedToken> .Result(token));
        }
        public async Task <Option <Client> > Validate(
            AuthorizationParameter parameter,
            CancellationToken cancellationToken)
        {
            // Check the required parameters. Read this RFC : http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
            if (string.IsNullOrWhiteSpace(parameter.Scope))
            {
                var message = string.Format(
                    Strings.MissingParameter,
                    CoreConstants.StandardAuthorizationRequestParameterNames.ScopeName);
                _logger.LogError(message);
                return(new Option <Client> .Error(
                           new ErrorDetails
                {
                    Title = ErrorCodes.InvalidRequest, Detail = message, Status = HttpStatusCode.BadRequest
                },
                           parameter.State));
            }

            if (string.IsNullOrWhiteSpace(parameter.ClientId))
            {
                var message = string.Format(
                    Strings.MissingParameter,
                    CoreConstants.StandardAuthorizationRequestParameterNames.ClientIdName);
                _logger.LogError(message);
                return(new Option <Client> .Error(
                           new ErrorDetails
                {
                    Title = ErrorCodes.InvalidRequest, Detail = message, Status = HttpStatusCode.BadRequest
                },
                           parameter.State));
            }

            if (parameter.RedirectUrl == null)
            {
                var message = string.Format(
                    Strings.MissingParameter,
                    CoreConstants.StandardAuthorizationRequestParameterNames.RedirectUriName);
                _logger.LogError(message);
                return(new Option <Client> .Error(
                           new ErrorDetails
                {
                    Title = ErrorCodes.InvalidRequest, Detail = message, Status = HttpStatusCode.BadRequest
                },
                           parameter.State));
            }

            if (string.IsNullOrWhiteSpace(parameter.ResponseType))
            {
                var message = string.Format(
                    Strings.MissingParameter,
                    CoreConstants.StandardAuthorizationRequestParameterNames.ResponseTypeName);
                _logger.LogError(message);
                return(new Option <Client> .Error(
                           new ErrorDetails
                {
                    Title = ErrorCodes.InvalidRequest, Detail = message, Status = HttpStatusCode.BadRequest
                },
                           parameter.State));
            }

            var validationResult = ValidateResponseTypeParameter(parameter.ResponseType, parameter.State);

            if (validationResult is Option.Error e)
            {
                return(new Option <Client> .Error(e.Details, e.State));
            }

            validationResult = ValidatePromptParameter(parameter.Prompt, parameter.State);
            if (validationResult is Option.Error e2)
            {
                return(new Option <Client> .Error(e2.Details, e2.State));
            }

            // With this instruction
            // The redirect_uri is considered well-formed according to the RFC-3986
            var redirectUrlIsCorrect = parameter.RedirectUrl.IsAbsoluteUri;

            if (!redirectUrlIsCorrect)
            {
                var message = Strings.TheRedirectionUriIsNotWellFormed;
                _logger.LogError(message);
                return(new Option <Client> .Error(
                           new ErrorDetails
                {
                    Title = ErrorCodes.InvalidRequest, Detail = message, Status = HttpStatusCode.BadRequest
                },
                           parameter.State));
            }

            var client = await _clientRepository.GetById(parameter.ClientId, cancellationToken).ConfigureAwait(false);

            if (client == null)
            {
                var message = string.Format(Strings.ClientIsNotValid, parameter.ClientId);
                _logger.LogError(message);
                return(new Option <Client> .Error(
                           new ErrorDetails
                {
                    Title = ErrorCodes.InvalidRequest, Detail = message, Status = HttpStatusCode.BadRequest
                },
                           parameter.State));
            }

            if (!client.GetRedirectionUrls(parameter.RedirectUrl).Any())
            {
                var message = string.Format(Strings.RedirectUrlIsNotValid, parameter.RedirectUrl);
                _logger.LogError(message);
                return(new Option <Client> .Error(
                           new ErrorDetails
                {
                    Title = ErrorCodes.InvalidRequest, Detail = message, Status = HttpStatusCode.BadRequest
                },
                           parameter.State));
            }

            return(new Option <Client> .Result(client));
        }
示例#7
0
        /// <summary>
        /// Fetch the scopes and client name from the ClientRepository and the parameter
        /// Those information are used to create the consent screen.
        /// </summary>
        /// <param name="authorizationParameter">Authorization code grant type parameter.</param>
        /// <param name="claimsPrincipal"></param>
        /// <param name="issuerName"></param>
        /// <param name="cancellationToken">The <see cref="CancellationToken"/> for the async operation.</param>
        /// <returns>Action resultKind.</returns>
        public async Task <DisplayContentResult> Execute(
            AuthorizationParameter authorizationParameter,
            ClaimsPrincipal claimsPrincipal,
            string issuerName,
            CancellationToken cancellationToken)
        {
            var client = authorizationParameter.ClientId == null
                ? null
                : await _clientRepository.GetById(authorizationParameter.ClientId, cancellationToken)
                         .ConfigureAwait(false);

            if (client == null)
            {
                return(new DisplayContentResult(
                           EndpointResult.CreateBadRequestResult(
                               new ErrorDetails
                {
                    Title = ErrorCodes.InvalidRequest,
                    Detail = string.Format(Strings.ClientIsNotValid, authorizationParameter.ClientId),
                    Status = HttpStatusCode.BadRequest
                })));
            }

            EndpointResult endpointResult;
            var            subject         = claimsPrincipal.GetSubject() !;
            var            assignedConsent = await _consentRepository
                                             .GetConfirmedConsents(subject, authorizationParameter, cancellationToken)
                                             .ConfigureAwait(false);

            // If there's already a consent then redirect to the callback
            if (assignedConsent != null)
            {
                endpointResult = await _generateAuthorizationResponse.Generate(
                    EndpointResult.CreateAnEmptyActionResultWithRedirectionToCallBackUrl(),
                    authorizationParameter,
                    claimsPrincipal,
                    client,
                    issuerName,
                    cancellationToken)
                                 .ConfigureAwait(false);

                var responseMode = authorizationParameter.ResponseMode;
                if (responseMode == ResponseModes.None)
                {
                    var responseTypes     = authorizationParameter.ResponseType.ParseResponseTypes();
                    var authorizationFlow = GetAuthorizationFlow(responseTypes, authorizationParameter.State);
                    switch (authorizationFlow)
                    {
                    case Option <AuthorizationFlow> .Error e:
                        return(new DisplayContentResult(EndpointResult.CreateBadRequestResult(e.Details)));

                    case Option <AuthorizationFlow> .Result r:
                        responseMode = GetResponseMode(r.Item);
                        break;
                    }
                }

                endpointResult = endpointResult with {
                    RedirectInstruction = endpointResult.RedirectInstruction !with {
                        ResponseMode = responseMode
                    }
                };
                return(new DisplayContentResult(endpointResult));
            }

            ICollection <string> allowedClaims = Array.Empty <string>();
            ICollection <Scope>  allowedScopes = Array.Empty <Scope>();
            var claimsParameter = authorizationParameter.Claims;

            if (claimsParameter.IsAnyIdentityTokenClaimParameter() || claimsParameter.IsAnyUserInfoClaimParameter())
            {
                allowedClaims = claimsParameter.GetClaimNames();
            }
            else
            {
                allowedScopes =
                    (await GetScopes(authorizationParameter.Scope !, cancellationToken).ConfigureAwait(false))
                    .Where(s => s.IsDisplayedInConsent)
                    .ToList();
            }

            endpointResult = EndpointResult.CreateAnEmptyActionResultWithOutput();
            return(new DisplayContentResult(client, allowedScopes, allowedClaims, endpointResult));
        }
示例#8
0
        /// <summary>
        /// Authenticates the specified instruction.
        /// </summary>
        /// <param name="instruction">The instruction.</param>
        /// <param name="issuerName">Name of the issuer.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException">instruction</exception>
        public async Task <AuthenticationResult> Authenticate(
            AuthenticateInstruction instruction,
            string issuerName,
            CancellationToken cancellationToken)
        {
            Client?client = null;
            // First we try to fetch the client_id
            // The different client authentication mechanisms are described here : http://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication
            var clientId = TryGettingClientId(instruction);

            if (!string.IsNullOrWhiteSpace(clientId))
            {
                client = await _clientRepository.GetById(clientId, cancellationToken).ConfigureAwait(false);
            }

            if (client == null)
            {
                return(new AuthenticationResult(null, SharedStrings.TheClientDoesntExist));
            }

            var errorMessage = string.Empty;

            switch (client.TokenEndPointAuthMethod)
            {
            case TokenEndPointAuthenticationMethods.ClientSecretBasic:
                client = instruction.AuthenticateClient(client);
                if (client == null)
                {
                    errorMessage = Strings.TheClientCannotBeAuthenticatedWithSecretBasic;
                }

                break;

            case TokenEndPointAuthenticationMethods.ClientSecretPost:
                client = ClientSecretPostAuthentication.AuthenticateClient(instruction, client);
                if (client == null)
                {
                    errorMessage = Strings.TheClientCannotBeAuthenticatedWithSecretPost;
                }

                break;

            case TokenEndPointAuthenticationMethods.ClientSecretJwt:
                if (client.Secrets.All(s => s.Type != ClientSecretTypes.SharedSecret))
                {
                    errorMessage = string.Format(
                        Strings.TheClientDoesntContainASharedSecret,
                        client.ClientId);
                    break;
                }

                return(await _clientAssertionAuthentication.AuthenticateClientWithClientSecretJwt(instruction, cancellationToken)
                       .ConfigureAwait(false));

            case TokenEndPointAuthenticationMethods.PrivateKeyJwt:
                return(await _clientAssertionAuthentication
                       .AuthenticateClientWithPrivateKeyJwt(instruction, issuerName, cancellationToken)
                       .ConfigureAwait(false));

            case TokenEndPointAuthenticationMethods.TlsClientAuth:
                client = AuthenticateTlsClient(instruction, client);
                if (client == null)
                {
                    errorMessage = Strings.TheClientCannotBeAuthenticatedWithTls;
                }

                break;
            }

            return(new AuthenticationResult(client, errorMessage));
        }
示例#9
0
        public async Task <EndpointResult> ProcessRedirection(
            AuthorizationParameter authorizationParameter,
            string?code,
            string subject,
            Claim[] claims,
            string?issuerName,
            CancellationToken cancellationToken)
        {
            var client = authorizationParameter.ClientId == null
                ? null
                : await _clientRepository.GetById(authorizationParameter.ClientId, cancellationToken)
                         .ConfigureAwait(false);

            if (client == null)
            {
                throw new InvalidOperationException(SharedStrings.TheClientDoesntExist);
            }

            // Redirect to the consent page if the prompt parameter contains "consent"
            EndpointResult result;
            var            prompts = authorizationParameter.Prompt.ParsePrompts();

            if (prompts.Contains(PromptParameters.Consent) && code != null)
            {
                return(EndpointResult.CreateAnEmptyActionResultWithRedirection(
                           SimpleAuthEndPoints.ConsentIndex,
                           new Parameter("code", code)));
            }

            var assignedConsent = await _consentRepository
                                  .GetConfirmedConsents(subject, authorizationParameter, cancellationToken)
                                  .ConfigureAwait(false);

            // If there's already one consent then redirect to the callback
            if (assignedConsent != null)
            {
                var claimsIdentity  = new ClaimsIdentity(claims, "SimpleAuth");
                var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
                result = await _generateAuthorizationResponse.Generate(
                    EndpointResult.CreateAnEmptyActionResultWithRedirectionToCallBackUrl(),
                    authorizationParameter,
                    claimsPrincipal,
                    client,
                    issuerName,
                    cancellationToken)
                         .ConfigureAwait(false);

                var responseMode = authorizationParameter.ResponseMode;
                if (responseMode != ResponseModes.None)
                {
                    return(result with
                    {
                        RedirectInstruction = result.RedirectInstruction !with {
                            ResponseMode = responseMode
                        }
                    });
                }

                var responseTypes     = authorizationParameter.ResponseType.ParseResponseTypes();
                var authorizationFlow = GetAuthorizationFlow(authorizationParameter.State, responseTypes);
                switch (authorizationFlow)
                {
                case Option <AuthorizationFlow> .Error e:
                    return(EndpointResult.CreateBadRequestResult(e.Details));

                case Option <AuthorizationFlow> .Result r:
                    responseMode = GetResponseMode(r.Item);
                    break;
                }

                return(result with
                {
                    RedirectInstruction = result.RedirectInstruction !with {
                        ResponseMode = responseMode
                    }
                });
            }

            // If there's no consent & there's no consent prompt then redirect to the consent screen.
            return(EndpointResult.CreateAnEmptyActionResultWithRedirection(
                       SimpleAuthEndPoints.ConsentIndex,
                       new Parameter("code", code ?? "")));
        }
示例#10
0
        /// <summary>
        /// This method is executed when the user confirm the consent
        /// 1). If there's already consent confirmed in the past by the resource owner
        /// 1).* then we generate an authorization code and redirects to the callback.
        /// 2). If there's no consent then we insert it and the authorization code is returned
        ///  2°.* to the callback url.
        /// </summary>
        /// <param name="authorizationParameter">Authorization code grant-type</param>
        /// <param name="claimsPrincipal">Resource owner's claims</param>
        /// <param name="issuerName"></param>
        /// <param name="cancellationToken">The <see cref="CancellationToken"/> for the async operation.</param>
        /// <returns>Redirects the authorization code to the callback.</returns>
        public async Task <EndpointResult> Execute(
            AuthorizationParameter authorizationParameter,
            ClaimsPrincipal claimsPrincipal,
            string issuerName,
            CancellationToken cancellationToken)
        {
            var client = authorizationParameter.ClientId == null
                ? null
                : await _clientRepository.GetById(authorizationParameter.ClientId, cancellationToken)
                         .ConfigureAwait(false);

            if (client == null)
            {
                throw new InvalidOperationException(
                          string.Format(
                              Strings.TheClientIdDoesntExist,
                              authorizationParameter.ClientId));
            }

            var subject         = claimsPrincipal.GetSubject() !;
            var assignedConsent = await _consentRepository
                                  .GetConfirmedConsents(subject, authorizationParameter, cancellationToken)
                                  .ConfigureAwait(false);

            // Insert a new consent.
            if (assignedConsent == null)
            {
                var claimsParameter = authorizationParameter.Claims;
                if (claimsParameter.IsAnyIdentityTokenClaimParameter() || claimsParameter.IsAnyUserInfoClaimParameter())
                {
                    // A consent can be given to a set of claims
                    assignedConsent = new Consent
                    {
                        Id         = Id.Create(),
                        ClientId   = client.ClientId,
                        ClientName = client.ClientName,
                        Subject    = subject,
                        Claims     = claimsParameter.GetClaimNames()
                    };
                }
                else
                {
                    // A consent can be given to a set of scopes
                    assignedConsent = new Consent
                    {
                        Id            = Id.Create(),
                        ClientId      = client.ClientId,
                        ClientName    = client.ClientName,
                        GrantedScopes =
                            authorizationParameter.Scope == null
                                ? Array.Empty <string>()
                                : (await GetScopes(authorizationParameter.Scope, cancellationToken)
                                   .ConfigureAwait(false)).ToArray(),
                        Subject = subject,
                    };
                }

                // A consent can be given to a set of claims
                await _consentRepository.Insert(assignedConsent, cancellationToken).ConfigureAwait(false);

                await _eventPublisher.Publish(
                    new ConsentAccepted(
                        Id.Create(),
                        subject,
                        client.ClientId,
                        assignedConsent.GrantedScopes,
                        DateTimeOffset.UtcNow))
                .ConfigureAwait(false);
            }

            var result = await _generateAuthorizationResponse.Generate(
                EndpointResult.CreateAnEmptyActionResultWithRedirectionToCallBackUrl(),
                authorizationParameter,
                claimsPrincipal,
                client,
                issuerName,
                cancellationToken)
                         .ConfigureAwait(false);

            // If redirect to the callback and the response mode has not been set.
            if (result.Type != ActionResultType.RedirectToCallBackUrl)
            {
                return(result);
            }

            var responseMode = authorizationParameter.ResponseMode;

            if (responseMode == ResponseModes.None)
            {
                var responseTypes     = authorizationParameter.ResponseType.ParseResponseTypes();
                var authorizationFlow = GetAuthorizationFlow(responseTypes, authorizationParameter.State);
                switch (authorizationFlow)
                {
                case Option <AuthorizationFlow> .Error e:
                    return(EndpointResult.CreateBadRequestResult(e.Details));

                case Option <AuthorizationFlow> .Result r:
                    responseMode = GetResponseMode(r.Item);
                    break;
                }
            }

            result = result with
            {
                RedirectInstruction = result.RedirectInstruction !with {
                    ResponseMode = responseMode
                }
            };

            return(result);
        }