public async Task <TokenRawResult> ProcessRawAsync(IFormCollection formCollection)
        {
            _logger.LogTrace("Processing token request.");

            var rawResult = new TokenRawResult();

            // validate HTTP
            if (formCollection.IsNullOrEmpty())
            {
                _logger.LogWarning($"Invalid {nameof(formCollection)} for token endpoint");
                rawResult.TokenErrorResult = Error(OidcConstants.TokenErrors.InvalidRequest);
                return(rawResult);
            }

            var clientId = formCollection[OidcConstants.TokenRequest.ClientId];
            var client   = await _clients.FindEnabledClientByIdAsync(clientId);

            if (client == null)
            {
                _logger.LogError($"No client with id '{clientId}' found. aborting");
                rawResult.TokenErrorResult = Error($"{OidcConstants.TokenRequest.ClientId} bad",
                                                   $"No client with id '{clientId}' found. aborting");
                return(rawResult);
            }

            var clientSecretValidationResult = new ClientSecretValidationResult
            {
                IsError = false,
                Client  = client,
                Secret  = null
            };

            // validate request
            var form = formCollection.AsNameValueCollection();

            _logger.LogTrace("Calling into token request validator: {type}", _requestValidator.GetType().FullName);
            var requestResult = await _requestValidator.ValidateRequestAsync(form, clientSecretValidationResult);

            if (requestResult.IsError)
            {
                await _events.RaiseAsync(new TokenIssuedFailureEvent(requestResult));

                rawResult.TokenErrorResult = Error(requestResult.Error, requestResult.ErrorDescription, requestResult.CustomResponse);
                return(rawResult);
            }

            // create response
            _logger.LogTrace("Calling into token request response generator: {type}", _responseGenerator.GetType().FullName);
            var response = await _responseGenerator.ProcessAsync(requestResult);

            await _events.RaiseAsync(new TokenIssuedSuccessEvent(response, requestResult));

            LogTokens(response, requestResult);

            // return result
            _logger.LogDebug("Token request success.");
            rawResult.TokenResult = new TokenResult(response);
            return(rawResult);
        }
예제 #2
0
        public async Task <ClientSecretValidationResult> ValidateAsync(HttpContext context)
        {
            var fail = new ClientSecretValidationResult()
            {
                IsError = true,
                Error   = OidcConstants.TokenErrors.InvalidClient,
            };

            if (!_plugins.Any())
            {
                await RaiseFailureEventAsync("unknown", "No client id found");

                fail.ErrorDescription = "No IClientSecretValidatorPlugin were found!";
                _logger.LogError(fail.ErrorDescription);
                return(fail);
            }
            var parsedSecret = await _parser.ParseAsync(context);

            if (parsedSecret == null)
            {
                await RaiseFailureEventAsync("unknown", "No client id found");

                fail.ErrorDescription = "No client identifier found";
                _logger.LogError(fail.ErrorDescription);
                return(fail);
            }

            // load client
            var client = await _clients.FindEnabledClientByIdAsync(parsedSecret.Id);

            if (client == null)
            {
                await RaiseFailureEventAsync(parsedSecret.Id, "Unknown client");

                fail.ErrorDescription = $"No client with id '{parsedSecret.Id}' found. aborting";
                _logger.LogError(fail.ErrorDescription);
                return(fail);
            }
            foreach (var plugin in _plugins)
            {
                var result = await plugin.ValidateAsync(context);

                if (result.IsError)
                {
                    return(result);
                }
            }

            var success = new ClientSecretValidationResult
            {
                IsError = false,
                Client  = client,
                Secret  = parsedSecret
            };

            return(success);
        }
        /// <summary>
        /// Validates the current request.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <returns></returns>
        public async Task <ClientSecretValidationResult> ValidateAsync(HttpContext context)
        {
            _logger.LogDebug("Start client validation");

            var fail = new ClientSecretValidationResult
            {
                IsError = true
            };

            var parsedSecret = await _parser.ParseAsync(context);

            if (parsedSecret == null)
            {
                await RaiseFailureEventAsync("unknown", "No client id found");

                _logger.LogError("No client identifier found");
                return(fail);
            }

            // load client
            var client = await _clients.FindEnabledClientByIdAsync(parsedSecret.Id);

            if (client == null)
            {
                await RaiseFailureEventAsync(parsedSecret.Id, "Unknown client");

                _logger.LogError("No client with id '{clientId}' found. aborting", parsedSecret.Id);
                return(fail);
            }

            var form      = (await context.Request.ReadFormAsync()).AsNameValueCollection();
            var grantType = form.Get(OidcConstants.TokenRequest.GrantType);

            if (grantType == OidcConstants.GrantTypes.RefreshToken)
            {
                // upcast;
                ClientExtra clientExtra = client as ClientExtra;
                if (!clientExtra.RequireRefreshClientSecret)
                {
                    _logger.LogDebug("Public Client - skipping secret validation success");
                    _logger.LogDebug("Client validation success");

                    var success = new ClientSecretValidationResult
                    {
                        IsError = false,
                        Client  = client,
                        Secret  = parsedSecret
                    };

                    await RaiseSuccessEventAsync(client.ClientId, parsedSecret.Type);

                    return(success);
                }
            }

            return(await StockClientSecretValidator.ValidateAsync(context));
        }
예제 #4
0
        public async Task <ClientSecretValidationResult> ValidateAsync(HttpContext context)
        {
            var fail = new ClientSecretValidationResult {
                IsError = true
            };

            var parsedSecret = await ParseAsync(context);

            if (parsedSecret == null)
            {
                await RaiseFailureEventAsync("unknown", "No client id found");

                return(fail);
            }

            var client = await _clients.FindEnabledClientByIdAsync(parsedSecret.Id);

            if (client == null)
            {
                await RaiseFailureEventAsync(parsedSecret.Id, "Unknown client");

                return(fail);
            }

            SecretValidationResult secretValidationResult = null;

            if (client.RequireClientSecret || !client.IsImplicitOnly())
            {
                secretValidationResult = await _validator.ValidateAsync(client.ClientSecrets, parsedSecret);

                if (secretValidationResult.Success == false)
                {
                    await RaiseFailureEventAsync(client.ClientId, "Invalid client secret");

                    return(fail);
                }
            }

            var success = new ClientSecretValidationResult
            {
                IsError      = false,
                Client       = client,
                Secret       = parsedSecret,
                Confirmation = secretValidationResult?.Confirmation
            };

            await RaiseSuccessEventAsync(client.ClientId, parsedSecret.Type);

            return(success);
        }
예제 #5
0
        /// <summary>
        /// Validates the current request.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <returns></returns>
        public async Task <ClientSecretValidationResult> ValidateAsync(IDictionary <string, string> form)
        {
            _logger.LogDebug("Start client validation");

            var fail = new ClientSecretValidationResult
            {
                IsError = true
            };

            var parsedSecret = await _parser.ParseAsync(form);

            if (parsedSecret == null)
            {
                await RaiseFailureEventAsync("unknown", "No client id found");

                _logger.LogError("No client identifier found");
                return(fail);
            }

            // load client
            var client = await _clients.FindEnabledClientByIdAsync(parsedSecret.Id);

            if (client == null)
            {
                await RaiseFailureEventAsync(parsedSecret.Id, "Unknown client");

                _logger.LogError("No client with id '{clientId}' found. aborting", parsedSecret.Id);
                return(fail);
            }

            if (!client.RequireClientSecret || client.IsImplicitOnly())
            {
                _logger.LogDebug("Public Client - skipping secret validation success");
            }
            else
            {
                var result = await _validator.ValidateAsync(parsedSecret, client.ClientSecrets);

                if (result.Success == false)
                {
                    await RaiseFailureEventAsync(client.ClientId, "Invalid client secret");

                    _logger.LogError("Client secret validation failed for client: {clientId}.", client.ClientId);

                    return(fail);
                }
            }

            _logger.LogDebug("Client validation success");

            var success = new ClientSecretValidationResult
            {
                IsError = false,
                Client  = client,
                Secret  = parsedSecret
            };

            await RaiseSuccessEventAsync(client.ClientId, parsedSecret.Type);

            return(success);
        }
        public async Task <TokenRequestValidationResult> ValidateRequestAsync(NameValueCollection parameters, ClientSecretValidationResult clientValidationResult)
        {
            var grantType = parameters.Get(OidcConstants.TokenRequest.GrantType);

            _validatedRequest = new ValidatedTokenRequest
            {
                Raw = parameters ?? throw new ArgumentNullException(nameof(parameters)),
                            Options = _options
            };

            switch (grantType)
            {
            case OidcConstants.GrantTypes.AuthorizationCode:
                return(await RunValidationAsync(ValidateAuthorizationCodeRequestAsync, parameters));

            case OidcConstants.GrantTypes.ClientCredentials:
                return(await RunValidationAsync(ValidateClientCredentialsRequestAsync, parameters));

            case OidcConstants.GrantTypes.Password:
                return(await RunValidationAsync(ValidateResourceOwnerCredentialRequestAsync, parameters));

            case OidcConstants.GrantTypes.RefreshToken:
                return(await RunValidationAsync(ValidateRefreshTokenRequestAsync, parameters));

            case OidcConstants.GrantTypes.DeviceCode:
                return(await RunValidationAsync(ValidateDeviceCodeRequestAsync, parameters));

            default:
                return(await RunValidationAsync(ValidateExtensionGrantRequestAsync, parameters));
            }
        }
예제 #7
0
        /// <summary>
        /// Validates the request.
        /// </summary>
        /// <param name="parameters">The parameters.</param>
        /// <param name="clientValidationResult">The client validation result.</param>
        /// <returns></returns>
        /// <exception cref="System.ArgumentNullException">
        /// parameters
        /// or
        /// client
        /// </exception>
        public async Task <TokenRequestValidationResult> ValidateRequestAsync(NameValueCollection parameters, ClientSecretValidationResult clientValidationResult)
        {
            var result = await _inner.ValidateRequestAsync(parameters, clientValidationResult);

            if (!result.IsError)
            {
                if (result.ValidatedRequest.Subject?.IsAnonymous() == true)
                {
                    result.ValidatedRequest.AccessTokenLifetime = _options.AccessTokenLifetime;
                }
            }

            return(result);
        }
예제 #8
0
        public virtual async Task <IdentityUtilsResult <TokenResponse> > GetToken(TokenRequest request)
        {
            var config = HttpContext.RequestServices.GetService(typeof(IIdentityUtilsAuthenticationConfig)) as IIdentityUtilsAuthenticationConfig;

            request.ClientId = config?.ClientId ?? request.ClientId;

            HttpContext.Request.Form = new FormCollection(new Dictionary <string, StringValues>
            {
                { "client_id", request.ClientId },
                { "grant_type", request.GrantType },
                { "username", request.Username },
                { "password", request.Password },
                { "refresh_token", request.RefreshToken },
            });

            // validate client
            var client = await clientStore.FindEnabledClientByIdAsync(request.ClientId);

            var invalidClient = !client.AllowedGrantTypes.Intersect(GrantTypes.ResourceOwnerPassword).Any() && !(request.GrantType == AuthenticationConstants.GrantTypeRefreshToken);

            if (client == null || invalidClient)
            {
                return(IdentityUtilsResult <TokenResponse> .ErrorResult($"Client id not found or invalid grant type"));
            }
            var clientResult = new ClientSecretValidationResult
            {
                IsError = false,
                Client  = client
            };

            var form = new NameValueCollection
            {
                { "client_id", request.ClientId },
                { "grant_type", request.GrantType },
                { "refresh_token", request.RefreshToken },
                { "username", request.Username },
                { "password", request.Password },
            };

            // validate request
            var requestResult = await requestValidator.ValidateRequestAsync(form, clientResult);

            if (requestResult.IsError)
            {
                await events.RaiseAsync(new TokenIssuedFailureEvent(requestResult));

                return(IdentityUtilsResult <TokenResponse> .ErrorResult($"{requestResult.Error} - {requestResult.ErrorDescription}"));
            }

            // create response
            var response = await responseGenerator.ProcessAsync(requestResult);

            await events.RaiseAsync(new TokenIssuedSuccessEvent(response, requestResult));

            // return result
            var tokenResponse = new TokenResponse
            {
                AccessToken  = response.AccessToken,
                RefreshToken = response.RefreshToken,
                Lifetime     = response.AccessTokenLifetime,
                Scopes       = response.Scope.Split(' ')
            };

            return(IdentityUtilsResult <TokenResponse> .SuccessResult(tokenResponse));
        }
예제 #9
0
        public async Task <RevocationRawResult> ProcessRawRevocationAsync(IFormCollection formCollection)
        {
            _logger.LogTrace("Processing token request.");

            var rawResult = new RevocationRawResult()
            {
                StatusCodeResult = new StatusCodeResult(HttpStatusCode.BadRequest)
            };

            // validate HTTP
            if (formCollection.IsNullOrEmpty())
            {
                _logger.LogWarning($"Invalid {nameof(formCollection)} for token endpoint");
                rawResult.ErrorResult = ErrorRevocation(OidcConstants.TokenErrors.InvalidRequest);
                return(rawResult);
            }

            var clientId = formCollection[OidcConstants.TokenRequest.ClientId];
            var client   = await _clients.FindEnabledClientByIdAsync(clientId);

            if (client == null)
            {
                _logger.LogError($"No client with id '{clientId}' found. aborting");
                rawResult.ErrorResult = ErrorRevocation($"{OidcConstants.TokenRequest.ClientId} bad");
                return(rawResult);
            }

            var clientValidationResult = new ClientSecretValidationResult
            {
                IsError = false,
                Client  = client,
                Secret  = null
            };

            // validate request
            var form = formCollection.AsNameValueCollection();

            _logger.LogTrace("Calling into token revocation request validator: {type}", _requestValidator.GetType().FullName);
            var requestValidationResult = await _revocationRequestValidator.ValidateRequestAsync(form, clientValidationResult.Client);

            if (requestValidationResult.IsError)
            {
                rawResult.ErrorResult = new TokenRevocationErrorResult(requestValidationResult.Error);
                return(rawResult);
            }

            _logger.LogTrace("Calling into token revocation response generator: {type}", _responseGenerator.GetType().FullName);
            var response = await _revocationResponseGenerator.ProcessAsync(requestValidationResult);

            if (response.Success)
            {
                _logger.LogInformation("Token successfully revoked");
                await _events.RaiseAsync(new TokenRevokedSuccessEvent(requestValidationResult, requestValidationResult.Client));
            }
            else
            {
                _logger.LogInformation("No matching token found");
            }

            if (response.Error.IsPresent())
            {
                rawResult.ErrorResult = new TokenRevocationErrorResult(response.Error);
                return(rawResult);
            }

            rawResult.StatusCodeResult = new StatusCodeResult(HttpStatusCode.OK);
            return(rawResult);
        }
예제 #10
0
    /// <summary>
    /// Validates the current request.
    /// </summary>
    /// <param name="context">The context.</param>
    /// <returns></returns>
    public async Task <ClientSecretValidationResult> ValidateAsync(HttpContext context)
    {
        using var activity = Tracing.ValidationActivitySource.StartActivity("ClientSecretValidator.Validate");

        _logger.LogDebug("Start client validation");

        var fail = new ClientSecretValidationResult
        {
            IsError = true
        };

        var parsedSecret = await _parser.ParseAsync(context);

        if (parsedSecret == null)
        {
            await RaiseFailureEventAsync("unknown", "No client id found");

            _logger.LogError("No client identifier found");
            return(fail);
        }

        // load client
        var client = await _clients.FindEnabledClientByIdAsync(parsedSecret.Id);

        if (client == null)
        {
            await RaiseFailureEventAsync(parsedSecret.Id, "Unknown client");

            _logger.LogError("No client with id '{clientId}' found. aborting", parsedSecret.Id);
            return(fail);
        }

        SecretValidationResult secretValidationResult = null;

        if (!client.RequireClientSecret || client.IsImplicitOnly())
        {
            _logger.LogDebug("Public Client - skipping secret validation success");
        }
        else
        {
            secretValidationResult = await _validator.ValidateAsync(client.ClientSecrets, parsedSecret);

            if (secretValidationResult.Success == false)
            {
                await RaiseFailureEventAsync(client.ClientId, "Invalid client secret");

                _logger.LogError("Client secret validation failed for client: {clientId}.", client.ClientId);

                return(fail);
            }
        }

        _logger.LogDebug("Client validation success");

        var success = new ClientSecretValidationResult
        {
            IsError      = false,
            Client       = client,
            Secret       = parsedSecret,
            Confirmation = secretValidationResult?.Confirmation
        };

        await RaiseSuccessEventAsync(client.ClientId, parsedSecret.Type);

        return(success);
    }
        /// <summary>
        /// Validates the current request.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <returns></returns>
        public async Task <ClientSecretValidationResult> ValidateAsync(HttpContext context)
        {
            _logger.LogDebug("Start client validation");

            var fail = new ClientSecretValidationResult
            {
                IsError = true
            };

            var parsedSecret = await _parser.ParseAsync(context);

            if (parsedSecret == null)
            {
                await RaiseFailureEventAsync("unknown", "No client id found");

                _logger.LogError("No client identifier found");
                return(fail);
            }

            // load client
            var client = await _clients.FindEnabledClientByIdAsync(parsedSecret.Id) as ClientExtra;

            if (client == null)
            {
                await RaiseFailureEventAsync(parsedSecret.Id, $"Unknown client for tenant: '{_scopedTenantRequestContext.Context.TenantName}'");

                _logger.LogError($"No client with id '{parsedSecret.Id}' for tenant: '{_scopedTenantRequestContext.Context.TenantName}' found. aborting");
                return(fail);
            }


            SecretValidationResult secretValidationResult = null;

            if (!client.RequireClientSecret || client.IsImplicitOnly())
            {
                _logger.LogDebug("Public Client - skipping secret validation success");
            }
            else
            {
                ////////////////////////////////////////
                // Check if this is a refresh_token
                ////////////////////////////////////////
                bool continueValidation = true;
                if (!client.RequireRefreshClientSecret)
                {
                    try
                    {
                        var parameters = (await context.Request.ReadFormAsync()).AsNameValueCollection();
                        var grantType  = parameters.Get(OidcConstants.TokenRequest.GrantType);
                        if (!string.IsNullOrWhiteSpace(grantType) && grantType == OidcConstants.GrantTypes.RefreshToken)
                        {
                            // let it through
                            _logger.LogDebug("RequireRefreshClientSecret == false - skipping secret validation success");
                            continueValidation = false;
                        }
                    }
                    catch (Exception ex)
                    {
                        // let it through
                        _logger.LogDebug("RequireRefreshClientSecret == false - skipping secret validation success");
                        continueValidation = false;
                    }
                }
                if (continueValidation)
                {
                    secretValidationResult = await _validator.ValidateAsync(client.ClientSecrets, parsedSecret);

                    if (secretValidationResult.Success == false)
                    {
                        await RaiseFailureEventAsync(client.ClientId, "Invalid client secret");

                        _logger.LogError("Client secret validation failed for client: {clientId}.", client.ClientId);

                        return(fail);
                    }
                }
            }

            _logger.LogDebug("Client validation success");

            var success = new ClientSecretValidationResult
            {
                IsError      = false,
                Client       = client,
                Secret       = parsedSecret,
                Confirmation = secretValidationResult?.Confirmation
            };

            await RaiseSuccessEventAsync(client.ClientId, parsedSecret.Type);

            return(success);
        }
예제 #12
0
    private DeviceAuthorizationRequestValidationResult ValidateClient(ValidatedDeviceAuthorizationRequest request, ClientSecretValidationResult clientValidationResult)
    {
        //////////////////////////////////////////////////////////
        // set client & secret
        //////////////////////////////////////////////////////////
        if (clientValidationResult == null)
        {
            throw new ArgumentNullException(nameof(clientValidationResult));
        }
        request.SetClient(clientValidationResult.Client, clientValidationResult.Secret);

        //////////////////////////////////////////////////////////
        // check if client protocol type is oidc
        //////////////////////////////////////////////////////////
        if (request.Client.ProtocolType != IdentityServerConstants.ProtocolTypes.OpenIdConnect)
        {
            LogError("Invalid protocol type for OIDC authorize endpoint", request.Client.ProtocolType, request);
            return(Invalid(request, OidcConstants.AuthorizeErrors.UnauthorizedClient, "Invalid protocol"));
        }

        //////////////////////////////////////////////////////////
        // check if client allows device flow
        //////////////////////////////////////////////////////////
        if (!request.Client.AllowedGrantTypes.Contains(GrantType.DeviceFlow))
        {
            LogError("Client not configured for device flow", GrantType.DeviceFlow, request);
            return(Invalid(request, OidcConstants.AuthorizeErrors.UnauthorizedClient));
        }

        return(Valid(request));
    }
예제 #13
0
    public async Task <DeviceAuthorizationRequestValidationResult> ValidateAsync(NameValueCollection parameters, ClientSecretValidationResult clientValidationResult)
    {
        using var activity = Tracing.BasicActivitySource.StartActivity("DeviceAuthorizationRequestValidator.Validate");

        _logger.LogDebug("Start device authorization request validation");

        var request = new ValidatedDeviceAuthorizationRequest
        {
            Raw = parameters ?? throw new ArgumentNullException(nameof(parameters)),
                        Options = _options
        };

        var clientResult = ValidateClient(request, clientValidationResult);

        if (clientResult.IsError)
        {
            return(clientResult);
        }

        var scopeResult = await ValidateScopeAsync(request);

        if (scopeResult.IsError)
        {
            return(scopeResult);
        }

        _logger.LogDebug("{clientId} device authorization request validation success", request.Client.ClientId);
        return(Valid(request));
    }
예제 #14
0
 public Task <TokenRequestValidationResult> ValidateRequestAsync(NameValueCollection parameters, ClientSecretValidationResult clientValidationResult)
 {
     throw new NotImplementedException();
 }
    /// <summary>
    /// Validates the request.
    /// </summary>
    /// <param name="parameters">The parameters.</param>
    /// <param name="clientValidationResult">The client validation result.</param>
    /// <returns></returns>
    /// <exception cref="System.ArgumentNullException">
    /// parameters
    /// or
    /// client
    /// </exception>
    public async Task <TokenRequestValidationResult> ValidateRequestAsync(NameValueCollection parameters, ClientSecretValidationResult clientValidationResult)
    {
        using var activity = Tracing.BasicActivitySource.StartActivity("TokenRequestValidator.ValidateRequest");

        _logger.LogDebug("Start token request validation");

        _validatedRequest = new ValidatedTokenRequest
        {
            IssuerName                          = await _issuerNameService.GetCurrentAsync(),
            Raw                                 = parameters ?? throw new ArgumentNullException(nameof(parameters)),
                                        Options = _options
        };

        if (clientValidationResult == null)
        {
            throw new ArgumentNullException(nameof(clientValidationResult));
        }

        _validatedRequest.SetClient(clientValidationResult.Client, clientValidationResult.Secret, clientValidationResult.Confirmation);

        /////////////////////////////////////////////
        // check client protocol type
        /////////////////////////////////////////////
        if (_validatedRequest.Client.ProtocolType != IdentityServerConstants.ProtocolTypes.OpenIdConnect)
        {
            LogError("Invalid protocol type for client",
                     new
            {
                clientId             = _validatedRequest.Client.ClientId,
                expectedProtocolType = IdentityServerConstants.ProtocolTypes.OpenIdConnect,
                actualProtocolType   = _validatedRequest.Client.ProtocolType
            });

            return(Invalid(OidcConstants.TokenErrors.InvalidClient));
        }

        /////////////////////////////////////////////
        // check grant type
        /////////////////////////////////////////////
        var grantType = parameters.Get(OidcConstants.TokenRequest.GrantType);

        if (grantType.IsMissing())
        {
            LogError("Grant type is missing");
            return(Invalid(OidcConstants.TokenErrors.UnsupportedGrantType));
        }

        if (grantType.Length > _options.InputLengthRestrictions.GrantType)
        {
            LogError("Grant type is too long");
            return(Invalid(OidcConstants.TokenErrors.UnsupportedGrantType));
        }

        _validatedRequest.GrantType = grantType;

        //////////////////////////////////////////////////////////
        // check for resource indicator and basic formatting
        //////////////////////////////////////////////////////////
        var resourceIndicators = parameters.GetValues(OidcConstants.TokenRequest.Resource) ?? Enumerable.Empty <string>();

        if (resourceIndicators?.Any(x => x.Length > _options.InputLengthRestrictions.ResourceIndicatorMaxLength) == true)
        {
            return(Invalid(OidcConstants.AuthorizeErrors.InvalidTarget, "Resource indicator maximum length exceeded"));
        }

        if (!resourceIndicators.AreValidResourceIndicatorFormat(_logger))
        {
            return(Invalid(OidcConstants.AuthorizeErrors.InvalidTarget, "Invalid resource indicator format"));
        }

        if (resourceIndicators.Count() > 1)
        {
            return(Invalid(OidcConstants.AuthorizeErrors.InvalidTarget, "Multiple resource indicators not supported on token endpoint."));
        }

        _validatedRequest.RequestedResourceIndicator = resourceIndicators.SingleOrDefault();


        //////////////////////////////////////////////////////////
        // run specific logic for grants
        //////////////////////////////////////////////////////////

        switch (grantType)
        {
        case OidcConstants.GrantTypes.AuthorizationCode:
            return(await RunValidationAsync(ValidateAuthorizationCodeRequestAsync, parameters));

        case OidcConstants.GrantTypes.ClientCredentials:
            return(await RunValidationAsync(ValidateClientCredentialsRequestAsync, parameters));

        case OidcConstants.GrantTypes.Password:
            return(await RunValidationAsync(ValidateResourceOwnerCredentialRequestAsync, parameters));

        case OidcConstants.GrantTypes.RefreshToken:
            return(await RunValidationAsync(ValidateRefreshTokenRequestAsync, parameters));

        case OidcConstants.GrantTypes.DeviceCode:
            return(await RunValidationAsync(ValidateDeviceCodeRequestAsync, parameters));

        case OidcConstants.GrantTypes.Ciba:
            return(await RunValidationAsync(ValidateCibaRequestRequestAsync, parameters));

        default:
            return(await RunValidationAsync(ValidateExtensionGrantRequestAsync, parameters));
        }
    }
        /// <summary>
        /// 登录校验密钥
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task <ClientSecretValidationResult> ValidateAsync(HttpContext context)
        {
            var _signInManager = (SignInManager <ApplicationUser>)context.RequestServices.GetService(typeof(SignInManager <ApplicationUser>));

            _logger.LogDebug("Start client validation");
            //错误返回
            var fail = new ClientSecretValidationResult
            {
                IsError = true
            };
            //根据上下文查找密钥
            var parsedSecret = await _parser.ParseAsync(context);

            if (parsedSecret == null)
            {
                await RaiseFailureEventAsync("unknown", "No client id found");

                _logger.LogError("No client identifier found");
                return(fail);
            }

            // 加载客户端配置
            var client = await _clients.FindEnabledClientByIdAsync(parsedSecret.Id);

            if (client == null)
            {
                await RaiseFailureEventAsync(parsedSecret.Id, "Unknown client");

                _logger.LogError("No client with id '{clientId}' found. aborting", parsedSecret.Id);
                return(fail);
            }

            SecretValidationResult secretValidationResult = null;

            if (!client.RequireClientSecret || client.IsImplicitOnly())
            {
                _logger.LogDebug("Public Client - skipping secret validation success");
            }
            else
            {
                //验证客户端密钥
                secretValidationResult = await _validator.ValidateAsync(parsedSecret, client.ClientSecrets);

                if (secretValidationResult.Success == false)
                {
                    await RaiseFailureEventAsync(client.ClientId, "Invalid client secret");

                    _logger.LogError("Client secret validation failed for client: {clientId}.", client.ClientId);

                    return(fail);
                }
            }

            _logger.LogDebug("Client validation success");
            //var user = await _signInManager.UserManager.FindByNameAsync(context.User.Identity.Name);
            //var  claimsPrincipal = await _signInManager.ClaimsFactory.CreateAsync(user);
            var success = new ClientSecretValidationResult
            {
                IsError      = false,
                Client       = client,
                Secret       = parsedSecret,
                Confirmation = secretValidationResult?.Confirmation
            };

            await RaiseSuccessEventAsync(client.ClientId, parsedSecret.Type);

            return(success);
        }
        /// <summary>
        /// Validates the request.
        /// </summary>
        /// <param name="parameters">The parameters.</param>
        /// <param name="clientValidationResult">The client validation result.</param>
        /// <returns></returns>
        /// <exception cref="System.ArgumentNullException">
        /// parameters
        /// or
        /// client
        /// </exception>
        public async Task <TokenRequestValidationResult> ValidateRequestAsync(NameValueCollection parameters, ClientSecretValidationResult clientValidationResult)
        {
            _logger.LogDebug("Start token request validation");

            _validatedRequest = new ValidatedTokenRequest
            {
                Raw = parameters ?? throw new ArgumentNullException(nameof(parameters)),
                            Options = _options
            };

            if (clientValidationResult == null)
            {
                throw new ArgumentNullException(nameof(clientValidationResult));
            }

            _validatedRequest.SetClient(clientValidationResult.Client, clientValidationResult.Secret, clientValidationResult.Confirmation);

            /////////////////////////////////////////////
            // check client protocol type
            /////////////////////////////////////////////
            if (_validatedRequest.Client.ProtocolType != IdentityServerConstants.ProtocolTypes.OpenIdConnect)
            {
                LogError("Invalid protocol type for client",
                         new
                {
                    clientId             = _validatedRequest.Client.ClientId,
                    expectedProtocolType = IdentityServerConstants.ProtocolTypes.OpenIdConnect,
                    actualProtocolType   = _validatedRequest.Client.ProtocolType
                });

                return(Invalid(OidcConstants.TokenErrors.InvalidClient));
            }

            /////////////////////////////////////////////
            // check grant type
            /////////////////////////////////////////////
            var grantType = parameters.Get(OidcConstants.TokenRequest.GrantType);

            if (grantType.IsMissing())
            {
                LogError("Grant type is missing");
                return(Invalid(OidcConstants.TokenErrors.UnsupportedGrantType));
            }

            if (grantType.Length > _options.InputLengthRestrictions.GrantType)
            {
                LogError("Grant type is too long");
                return(Invalid(OidcConstants.TokenErrors.UnsupportedGrantType));
            }

            _validatedRequest.GrantType = grantType;

            switch (grantType)
            {
            //case OidcConstants.GrantTypes.AuthorizationCode:
            //    return await RunValidationAsync(ValidateAuthorizationCodeRequestAsync, parameters);
            case OidcConstants.GrantTypes.ClientCredentials:
                return(await RunValidationAsync(ValidateClientCredentialsRequestAsync, parameters));

            case OidcConstants.GrantTypes.Password:
                return(await RunValidationAsync(ValidateResourceOwnerCredentialRequestAsync, parameters));

            case OidcConstants.GrantTypes.RefreshToken:
                return(await RunValidationAsync(ValidateRefreshTokenRequestAsync, parameters));

            case OidcConstants.GrantTypes.DeviceCode:
                return(await RunValidationAsync(ValidateDeviceCodeRequestAsync, parameters));

            default:
                return(await RunValidationAsync(ValidateExtensionGrantRequestAsync, parameters));
            }
        }
        /// <summary>
        /// Validates the current request.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <returns></returns>
        public async Task <ClientSecretValidationResult> ValidateAsync(NameValueCollection nvc)
        {
            _logger.LogDebug("Start client validation");

            var fail = new ClientSecretValidationResult
            {
                IsError = true
            };

            var          id           = nvc.Get("client_id");
            var          secret       = nvc.Get("client_secret");
            ParsedSecret parsedSecret = null;

            // client id must be present
            if (id.IsPresent())
            {
                if (id.Length > _options.InputLengthRestrictions.ClientId)
                {
                    _logger.LogError("Client ID exceeds maximum length.");
                    return(null);
                }

                if (secret.IsPresent())
                {
                    if (secret.Length > _options.InputLengthRestrictions.ClientSecret)
                    {
                        _logger.LogError("Client secret exceeds maximum length.");
                        return(null);
                    }

                    parsedSecret = new ParsedSecret
                    {
                        Id         = id,
                        Credential = secret,
                        Type       = IdentityServerConstants.ParsedSecretTypes.SharedSecret
                    };
                }
            }

            if (parsedSecret == null)
            {
                await RaiseFailureEventAsync("unknown", "No client id found");

                _logger.LogError("No client identifier found");
                return(fail);
            }

            // load client
            var client = await _clients.FindEnabledClientByIdAsync(parsedSecret.Id);

            if (client == null)
            {
                await RaiseFailureEventAsync(parsedSecret.Id, "Unknown client");

                _logger.LogError("No client with id '{clientId}' found. aborting", parsedSecret.Id);
                return(fail);
            }

            if (!client.RequireClientSecret)
            {
                _logger.LogDebug("Public Client - skipping secret validation success");
            }
            else
            {
                var result = await _validator.ValidateAsync(parsedSecret, client.ClientSecrets);

                if (result.Success == false)
                {
                    await RaiseFailureEventAsync(client.ClientId, "Invalid client secret");

                    _logger.LogError("Client secret validation failed for client: {clientId}.", client.ClientId);

                    return(fail);
                }
            }

            _logger.LogDebug("Client validation success");

            var success = new ClientSecretValidationResult
            {
                IsError = false,
                Client  = client,
                Secret  = parsedSecret
            };

            await RaiseSuccessEventAsync(client.ClientId, parsedSecret.Type);

            return(success);
        }
    public async Task <BackchannelAuthenticationRequestValidationResult> ValidateRequestAsync(NameValueCollection parameters, ClientSecretValidationResult clientValidationResult)
    {
        using var activity = Tracing.BasicActivitySource.StartActivity("BackchannelAuthenticationRequestValidator.ValidateRequest");

        if (clientValidationResult == null)
        {
            throw new ArgumentNullException(nameof(clientValidationResult));
        }

        _logger.LogDebug("Start backchannel authentication request validation");

        _validatedRequest = new ValidatedBackchannelAuthenticationRequest
        {
            Raw = parameters ?? throw new ArgumentNullException(nameof(parameters)),
                        Options = _options
        };
        _validatedRequest.SetClient(clientValidationResult.Client, clientValidationResult.Secret, clientValidationResult.Confirmation);

        //////////////////////////////////////////////////////////
        // Client must be configured for CIBA
        //////////////////////////////////////////////////////////
        if (!clientValidationResult.Client.AllowedGrantTypes.Contains(OidcConstants.GrantTypes.Ciba))
        {
            LogError("Client {clientId} not configured with the CIBA grant type.", clientValidationResult.Client.ClientId);
            return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.UnauthorizedClient, "Unauthorized client"));
        }

        LicenseValidator.ValidateCiba();

        //////////////////////////////////////////////////////////
        // load request object
        //////////////////////////////////////////////////////////
        var jwtRequest = _validatedRequest.Raw.Get(OidcConstants.BackchannelAuthenticationRequest.Request);

        // check length restrictions
        if (jwtRequest.IsPresent())
        {
            if (jwtRequest.Length >= _options.InputLengthRestrictions.Jwt)
            {
                LogError("request value is too long");
                return(Invalid(OidcConstants.AuthorizeErrors.InvalidRequestObject, "Invalid request value"));
            }
        }

        _validatedRequest.RequestObject = jwtRequest;

        //////////////////////////////////////////////////////////
        // validate request object
        //////////////////////////////////////////////////////////
        var roValidationResult = await TryValidateRequestObjectAsync();

        if (!roValidationResult.Success)
        {
            return(roValidationResult.ErrorResult);
        }

        if (_validatedRequest.Client.RequireRequestObject &&
            !_validatedRequest.RequestObjectValues.Any())
        {
            LogError("Client is configured for RequireRequestObject but none present");
            return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest));
        }

        //////////////////////////////////////////////////////////
        // scope must be present
        //////////////////////////////////////////////////////////
        var scope = _validatedRequest.Raw.Get(OidcConstants.BackchannelAuthenticationRequest.Scope);

        if (scope.IsMissing())
        {
            LogError("Missing scope");
            return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest, "Missing scope"));
        }

        if (scope.Length > _options.InputLengthRestrictions.Scope)
        {
            LogError("scopes too long.");
            return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest, "Invalid scope"));
        }

        _validatedRequest.RequestedScopes = scope.FromSpaceSeparatedString().Distinct().ToList();

        //////////////////////////////////////////////////////////
        // openid scope required
        //////////////////////////////////////////////////////////
        if (!_validatedRequest.RequestedScopes.Contains(IdentityServerConstants.StandardScopes.OpenId))
        {
            LogError("openid scope missing.");
            return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest, "Missing the openid scope"));
        }

        //////////////////////////////////////////////////////////
        // check for resource indicators and valid format
        //////////////////////////////////////////////////////////
        var resourceIndicators = _validatedRequest.Raw.GetValues("resource") ?? Enumerable.Empty <string>();

        if (resourceIndicators?.Any(x => x.Length > _options.InputLengthRestrictions.ResourceIndicatorMaxLength) == true)
        {
            return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidTarget, "Resource indicator maximum length exceeded"));
        }

        if (!resourceIndicators.AreValidResourceIndicatorFormat(_logger))
        {
            return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidTarget, "Invalid resource indicator format"));
        }

        _validatedRequest.RequestedResourceIndiators = resourceIndicators?.ToList();

        //////////////////////////////////////////////////////////
        // check if scopes are valid/supported and check for resource scopes
        //////////////////////////////////////////////////////////
        var validatedResources = await _resourceValidator.ValidateRequestedResourcesAsync(new ResourceValidationRequest
        {
            Client                         = _validatedRequest.Client,
            Scopes                         = _validatedRequest.RequestedScopes,
            ResourceIndicators             = resourceIndicators,
            IncludeNonIsolatedApiResources = _validatedRequest.RequestedScopes.Contains(OidcConstants.StandardScopes.OfflineAccess),
        });

        if (!validatedResources.Succeeded)
        {
            if (validatedResources.InvalidResourceIndicators.Any())
            {
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidTarget, "Invalid resource indicator"));
            }

            if (validatedResources.InvalidScopes.Any())
            {
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidScope, "Invalid scope"));
            }
        }

        LicenseValidator.ValidateResourceIndicators(resourceIndicators);
        _validatedRequest.ValidatedResources = validatedResources;


        //////////////////////////////////////////////////////////
        // check requested_expiry
        //////////////////////////////////////////////////////////
        var requestLifetime = _validatedRequest.Client.CibaLifetime ?? _options.Ciba.DefaultLifetime;
        var requestedExpiry = _validatedRequest.Raw.Get(OidcConstants.BackchannelAuthenticationRequest.RequestedExpiry);

        if (requestedExpiry.IsPresent())
        {
            // Int32.MaxValue == 2147483647, which is 10 characters in length
            // so using 9 so we don't overflow below on the Int32.Parse
            if (requestedExpiry.Length > 9)
            {
                LogError("requested_expiry too long");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest, "Invalid requested_expiry"));
            }

            if (Int32.TryParse(requestedExpiry, out var expiryValue) &&
                expiryValue > 0 &&
                expiryValue <= requestLifetime)
            {
                _validatedRequest.Expiry = expiryValue;
            }
            else
            {
                LogError("requested_expiry value out of valid range");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest, "Invalid requested_expiry"));
            }
        }
        else
        {
            _validatedRequest.Expiry = requestLifetime;
        }


        //////////////////////////////////////////////////////////
        // check acr_values
        //////////////////////////////////////////////////////////
        var acrValues = _validatedRequest.Raw.Get(OidcConstants.BackchannelAuthenticationRequest.AcrValues);

        if (acrValues.IsPresent())
        {
            if (acrValues.Length > _options.InputLengthRestrictions.AcrValues)
            {
                LogError("Acr values too long");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest, "Invalid acr_values"));
            }

            _validatedRequest.AuthenticationContextReferenceClasses = acrValues.FromSpaceSeparatedString().Distinct().ToList();

            //////////////////////////////////////////////////////////
            // check custom acr_values: idp and tenant
            //////////////////////////////////////////////////////////
            var tenant = _validatedRequest.AuthenticationContextReferenceClasses.FirstOrDefault(x => x.StartsWith(KnownAcrValues.Tenant));
            if (tenant != null)
            {
                _validatedRequest.AuthenticationContextReferenceClasses.Remove(tenant);
                tenant = tenant.Substring(KnownAcrValues.Tenant.Length);
                _validatedRequest.Tenant = tenant;
            }

            var idp = _validatedRequest.AuthenticationContextReferenceClasses.FirstOrDefault(x => x.StartsWith(KnownAcrValues.HomeRealm));
            if (idp != null)
            {
                _validatedRequest.AuthenticationContextReferenceClasses.Remove(idp);
                idp = idp.Substring(KnownAcrValues.HomeRealm.Length);

                // check if idp is present but client does not allow it, and then ignore it
                if (_validatedRequest.Client.IdentityProviderRestrictions != null &&
                    _validatedRequest.Client.IdentityProviderRestrictions.Any())
                {
                    if (!_validatedRequest.Client.IdentityProviderRestrictions.Contains(idp))
                    {
                        _logger.LogWarning("idp requested ({idp}) is not in client restriction list.", idp);
                        idp = null;
                    }
                }

                _validatedRequest.IdP = idp;
            }
        }


        //////////////////////////////////////////////////////////
        // login hints
        //////////////////////////////////////////////////////////
        var loginHint      = _validatedRequest.Raw.Get(OidcConstants.BackchannelAuthenticationRequest.LoginHint);
        var loginHintToken = _validatedRequest.Raw.Get(OidcConstants.BackchannelAuthenticationRequest.LoginHintToken);
        var idTokenHint    = _validatedRequest.Raw.Get(OidcConstants.BackchannelAuthenticationRequest.IdTokenHint);

        var loginHintCount = 0;

        if (loginHint.IsPresent())
        {
            loginHintCount++;
        }
        if (loginHintToken.IsPresent())
        {
            loginHintCount++;
        }
        if (idTokenHint.IsPresent())
        {
            loginHintCount++;
        }

        if (loginHintCount == 0)
        {
            LogError("Missing login_hint_token, id_token_hint, or login_hint");
            return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest, "Missing login_hint_token, id_token_hint, or login_hint"));
        }
        else if (loginHintCount > 1)
        {
            LogError("Too many of login_hint_token, id_token_hint, or login_hint");
            return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest, "Too many of login_hint_token, id_token_hint, or login_hint"));
        }

        //////////////////////////////////////////////////////////
        // check login_hint
        //////////////////////////////////////////////////////////
        if (loginHint.IsPresent())
        {
            if (loginHint.Length > _options.InputLengthRestrictions.LoginHint)
            {
                LogError("Login hint too long");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest, "Invalid login_hint"));
            }

            _validatedRequest.LoginHint = loginHint;
        }

        //////////////////////////////////////////////////////////
        // check login_hint_token
        //////////////////////////////////////////////////////////
        if (loginHintToken.IsPresent())
        {
            if (loginHintToken.Length > _options.InputLengthRestrictions.LoginHintToken)
            {
                LogError("Login hint token too long");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest, "Invalid login_hint_token"));
            }

            _validatedRequest.LoginHintToken = loginHintToken;
        }

        //////////////////////////////////////////////////////////
        // check id_token_hint
        //////////////////////////////////////////////////////////
        if (idTokenHint.IsPresent())
        {
            if (idTokenHint.Length > _options.InputLengthRestrictions.IdTokenHint)
            {
                LogError("id token hint too long");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest, "Invalid id_token_hint"));
            }

            var idTokenHintValidationResult = await _tokenValidator.ValidateIdentityTokenAsync(idTokenHint, _validatedRequest.ClientId, false);

            if (idTokenHintValidationResult.IsError)
            {
                LogError("id token hint failed to validate: " + idTokenHintValidationResult.Error, idTokenHintValidationResult.ErrorDescription);
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest, "Invalid id_token_hint"));
            }

            _validatedRequest.IdTokenHint       = idTokenHint;
            _validatedRequest.IdTokenHintClaims = idTokenHintValidationResult.Claims;
        }

        //////////////////////////////////////////////////////////
        // check user_code
        //////////////////////////////////////////////////////////
        var userCode = _validatedRequest.Raw.Get(OidcConstants.BackchannelAuthenticationRequest.UserCode);

        if (userCode.IsPresent())
        {
            if (userCode.Length > _options.InputLengthRestrictions.UserCode)
            {
                LogError("user_code too long");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest, "Invalid user_code"));
            }

            _validatedRequest.UserCode = userCode;
        }

        //////////////////////////////////////////////////////////
        // check binding_message
        //////////////////////////////////////////////////////////
        var bindingMessage = _validatedRequest.Raw.Get(OidcConstants.BackchannelAuthenticationRequest.BindingMessage);

        if (bindingMessage.IsPresent())
        {
            if (bindingMessage.Length > _options.InputLengthRestrictions.BindingMessage)
            {
                LogError("binding_message too long");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidBindingMessage, "Invalid binding_message"));
            }

            _validatedRequest.BindingMessage = bindingMessage;
        }

        //////////////////////////////////////////////////////////
        // validate the login hint w/ custom validator
        //////////////////////////////////////////////////////////
        var userResult = await _backchannelAuthenticationUserValidator.ValidateRequestAsync(new BackchannelAuthenticationUserValidatorContext
        {
            Client            = _validatedRequest.Client,
            IdTokenHint       = _validatedRequest.IdTokenHint,
            LoginHint         = _validatedRequest.LoginHint,
            LoginHintToken    = _validatedRequest.LoginHintToken,
            IdTokenHintClaims = _validatedRequest.IdTokenHintClaims,
            UserCode          = _validatedRequest.UserCode,
            BindingMessage    = _validatedRequest.BindingMessage
        });

        if (userResult.IsError)
        {
            if (userResult.Error == OidcConstants.BackchannelAuthenticationRequestErrors.AccessDenied)
            {
                LogError("Request was denied access for that user");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.AccessDenied, userResult.ErrorDescription));
            }
            if (userResult.Error == OidcConstants.BackchannelAuthenticationRequestErrors.ExpiredLoginHintToken)
            {
                LogError("Expired login_hint_token");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.ExpiredLoginHintToken, userResult.ErrorDescription ?? "Expired login_hint_token"));
            }
            if (userResult.Error == OidcConstants.BackchannelAuthenticationRequestErrors.UnknownUserId)
            {
                LogError("Unknown user id");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.UnknownUserId, userResult.ErrorDescription));
            }
            if (userResult.Error == OidcConstants.BackchannelAuthenticationRequestErrors.MissingUserCode)
            {
                LogError("Missing user_code");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.MissingUserCode, userResult.ErrorDescription));
            }
            if (userResult.Error == OidcConstants.BackchannelAuthenticationRequestErrors.InvalidUserCode)
            {
                LogError("Invalid user_code");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidUserCode, userResult.ErrorDescription));
            }
            if (userResult.Error == OidcConstants.BackchannelAuthenticationRequestErrors.InvalidBindingMessage)
            {
                LogError("Invalid binding_message");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidBindingMessage, userResult.ErrorDescription));
            }

            LogError("Unexpected error from IBackchannelAuthenticationUserValidator: {error}", userResult.Error);
            return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.UnknownUserId));
        }

        if (userResult.Subject == null || !userResult.Subject.HasClaim(x => x.Type == JwtClaimTypes.Subject))
        {
            LogError("No subject or subject id returned from IBackchannelAuthenticationUserValidator");
            return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.UnknownUserId));
        }

        _validatedRequest.Subject = userResult.Subject;

        LogSuccess();
        return(new BackchannelAuthenticationRequestValidationResult(_validatedRequest));
    }