private async Task <TokenRequestValidationResult> ValidateAuthorizationCodeRequestAsync(NameValueCollection parameters) { _logger.LogDebug("Start validation of authorization code token request"); ///////////////////////////////////////////// // check if client is authorized for grant type ///////////////////////////////////////////// if (!_validatedRequest.Client.AllowedGrantTypes.ToList().Contains(GrantType.AuthorizationCode) && !_validatedRequest.Client.AllowedGrantTypes.ToList().Contains(GrantType.Hybrid)) { LogError("Client not authorized for code flow"); return(Invalid(OidcConstants.TokenErrors.UnauthorizedClient)); } ///////////////////////////////////////////// // validate authorization code ///////////////////////////////////////////// var code = parameters.Get(OidcConstants.TokenRequest.Code); if (code.IsMissing()) { var error = "Authorization code is missing"; LogError(error); await RaiseFailedAuthorizationCodeRedeemedEventAsync(null, error); return(Invalid(OidcConstants.TokenErrors.InvalidGrant)); } if (code.Length > _options.InputLengthRestrictions.AuthorizationCode) { var error = "Authorization code is too long"; LogError(error); await RaiseFailedAuthorizationCodeRedeemedEventAsync(null, error); return(Invalid(OidcConstants.TokenErrors.InvalidGrant)); } _validatedRequest.AuthorizationCodeHandle = code; var authZcode = await _grants.GetAuthorizationCodeAsync(code); if (authZcode == null) { LogError("Authorization code cannot be found in the store: {code}", code); await RaiseFailedAuthorizationCodeRedeemedEventAsync(code, "Invalid handle"); return(Invalid(OidcConstants.TokenErrors.InvalidGrant)); } await _grants.RemoveAuthorizationCodeAsync(code); ///////////////////////////////////////////// // populate session id ///////////////////////////////////////////// if (authZcode.SessionId.IsPresent()) { _validatedRequest.SessionId = authZcode.SessionId; } ///////////////////////////////////////////// // validate client binding ///////////////////////////////////////////// if (authZcode.ClientId != _validatedRequest.Client.ClientId) { LogError("Client {clientId} is trying to use a code from client {clientId}", _validatedRequest.Client.ClientId, authZcode.ClientId); await RaiseFailedAuthorizationCodeRedeemedEventAsync(code, "Invalid client binding"); return(Invalid(OidcConstants.TokenErrors.InvalidGrant)); } ///////////////////////////////////////////// // validate code expiration ///////////////////////////////////////////// if (authZcode.CreationTime.HasExceeded(_validatedRequest.Client.AuthorizationCodeLifetime)) { var error = "Authorization code is expired"; LogError(error); await RaiseFailedAuthorizationCodeRedeemedEventAsync(code, error); return(Invalid(OidcConstants.TokenErrors.InvalidGrant)); } _validatedRequest.AuthorizationCode = authZcode; ///////////////////////////////////////////// // validate redirect_uri ///////////////////////////////////////////// var redirectUri = parameters.Get(OidcConstants.TokenRequest.RedirectUri); if (redirectUri.IsMissing()) { var error = "Redirect URI is missing"; LogError(error); await RaiseFailedAuthorizationCodeRedeemedEventAsync(code, error); return(Invalid(OidcConstants.TokenErrors.UnauthorizedClient)); } if (redirectUri.Equals(_validatedRequest.AuthorizationCode.RedirectUri, StringComparison.Ordinal) == false) { LogError("Invalid redirect_uri: {redirectUri}", redirectUri); var error = "Invalid redirect_uri: " + redirectUri; await RaiseFailedAuthorizationCodeRedeemedEventAsync(code, error); return(Invalid(OidcConstants.TokenErrors.UnauthorizedClient)); } ///////////////////////////////////////////// // validate scopes are present ///////////////////////////////////////////// if (_validatedRequest.AuthorizationCode.RequestedScopes == null || !_validatedRequest.AuthorizationCode.RequestedScopes.Any()) { var error = "Authorization code has no associated scopes"; LogError(error); await RaiseFailedAuthorizationCodeRedeemedEventAsync(code, error); return(Invalid(OidcConstants.TokenErrors.InvalidRequest)); } ///////////////////////////////////////////// // validate PKCE parameters ///////////////////////////////////////////// var codeVerifier = parameters.Get(OidcConstants.TokenRequest.CodeVerifier); if (_validatedRequest.Client.RequirePkce) { _logger.LogDebug("Client required a proof key for code exchange. Starting PKCE validation"); var proofKeyResult = ValidateAuthorizationCodeWithProofKeyParameters(codeVerifier, _validatedRequest.AuthorizationCode); if (proofKeyResult.IsError) { return(proofKeyResult); } _validatedRequest.CodeVerifier = codeVerifier; } else { if (codeVerifier.IsPresent()) { LogError("Unexpected code_verifier: {codeVerifier}", codeVerifier); return(Invalid(OidcConstants.TokenErrors.InvalidGrant)); } } ///////////////////////////////////////////// // make sure user is enabled ///////////////////////////////////////////// var isActiveCtx = new IsActiveContext(_validatedRequest.AuthorizationCode.Subject, _validatedRequest.Client, IdentityServerConstants.ProfileIsActiveCallers.AuthorizationCodeValidation); await _profile.IsActiveAsync(isActiveCtx); if (isActiveCtx.IsActive == false) { LogError("User has been disabled: {subjectId}", _validatedRequest.AuthorizationCode.Subject.GetSubjectId()); var error = "User has been disabled: " + _validatedRequest.AuthorizationCode.Subject.GetSubjectId(); await RaiseFailedAuthorizationCodeRedeemedEventAsync(code, error); return(Invalid(OidcConstants.TokenErrors.InvalidGrant)); } _logger.LogDebug("Validation of authorization code token request success"); await RaiseSuccessfulAuthorizationCodeRedeemedEventAsync(); return(Valid()); }