IdentityServer4源码解析_4_令牌发放接口

目录

协议

Token接口

oidc服务需要提供token接口,提供AccessToken,IdToken,以及RefreshToken(可选)。在授权码模式下,token接口必须使用https。

请求

必须使用POST方法,使用x-www-form-urlencoded序列化参数,clientId:clientSecret使用Basic加密放在Authorization头中

POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW

grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb

请求校验

认证服务必须校验下列内容:

成功响应

在收到token请求,并校验通过之后,认证服务返回成功报文,报文包含了身份令牌和通行令牌。数据格式使用application/json。token_type必须返回Bearer,其他类型token不在本协议范围内。在OAuth2.0响应报文基础上,oidc增加了id_tken。所有token包含了token或者其他敏感信息的响应报文,必须包含以下响应头。

Cache-Control no-store
Pragma no-cache

失败响应

如果认证失败返回application/json格式错误消息,状态码400

  HTTP/1.1 400 Bad Request
  Content-Type: application/json
  Cache-Control: no-store
  Pragma: no-cache

  {
   "error": "invalid_request"
  }

id token校验

客户端必须校验返回的id token, 校验条件如下。对照这些条件,就可以更懂Microsoft.Authentication.OpenIdConnect里面的代码了,要做的事情很多。

  1. 如果id token被加密,使用客户端注册时候约定的秘钥和算法解密。如果约定了加密方式,id token未被加密,客户端应该拒绝。
  2. 签发方标识必须与iss声明一致
  3. 客户端必须校验aud声明包含了它的客户端id,如果id token未返回正确的audience或者反悔了不被新人的audience,应该拒绝
  4. 如果id token包含多个audience,需要校验是否有azp声明。azp即Authorized party,标识被授权的client。
  5. 如果包含azp声明,客户端需要校验其值是否为自己的客户端id
  6. 如果id token由token接口直接颁发给客户端(授权码模式就是如此),客户端必须根据alg参数值的算法验证签名。客户端必须使用签发方提供的秘钥。
  7. alg值默认为RS256,客户端可以在注册的时候使用id_token_signed_response_alg参数指定配置。
  8. 如果jwt的alg头使用了基于mac地址的加密算法,如HS256, HS384,HS512,aud声明中的字节会用作验签。(意思是会把mac地址相关信息写在aud声明上?)
  9. The current time MUST be before the time represented by the exp Claim. 当前时间必须早于exp(token过期时间)。
  10. iat(签发时间)可以用于拒绝过早、或者过于频繁签发的token,可以用于预防重放攻击。可接受时间范围由客户端自行决定。
  11. 如果认证请求包含了nonce参数,客户端必须交验认证响应中返回的nonce值是否一致。防止重放攻击。
  12. 如果客户端请求了acr声明(Authentication Context Class Reference,认证会话上下文,用于表示当前认证会话),必须交验acr值是否合法。
  13. 如果客户端请求了auth_time声明,客户端应该校验认证时间是否已经超出,是否需要重新认证。

access token校验

如果id_token中包含了at_hash声明,需要做下面的校验。at_hash标明了access_token和id_token之间的会话关联关系,做这个校验可以防跨站伪造。

  1. 用idtoken的alg头标明的算法加密access_token,比如alg位RS256,则是用HSA-256算法加密。
  2. 取hash值左边一般使用base64url加密
  3. id token中的at_hash值必须跟上个步骤得到的值一致

校验规则很多,了解一下即可,绝大部分属于客户端需要做的部分,绝大部分跟安全有关。这一块的实现可以参考Microsoft.Authentication.OpenIdConnect,这是客户端的实现。我们现在看的IdentityServer是认证服务端的实现。

源码

五种授权模式

有下面几种授权模式可以请求token接口

注意:简化模式所有的token都是由认证接口(authorize)一次性返回的,不能使用token接口。

校验请求方法

token接口仅允许POST方法,Content-Type必须为application/x-www-form-urlencoded,否则抛出InvalidRequest错误。

public async Task<IEndpointResult> ProcessAsync(HttpContext context)
    {
        _logger.LogTrace("Processing token request.");

        // validate HTTP
        if (!HttpMethods.IsPost(context.Request.Method) || !context.Request.HasFormContentType)
        {
            _logger.LogWarning("Invalid HTTP request for token endpoint");
            return Error(OidcConstants.TokenErrors.InvalidRequest);
        }

        return await ProcessTokenRequestAsync(context);
    }

处理流程

private async Task<IEndpointResult> ProcessTokenRequestAsync(HttpContext context)
{
    _logger.LogDebug("Start token request.");

    // validate client
    var clientResult = await _clientValidator.ValidateAsync(context);

    if (clientResult.Client == null)
    {
        return Error(OidcConstants.TokenErrors.InvalidClient);
    }

    // validate request
    var form = (await context.Request.ReadFormAsync()).AsNameValueCollection();
    _logger.LogTrace("Calling into token request validator: {type}", _requestValidator.GetType().FullName);
    var requestResult = await _requestValidator.ValidateRequestAsync(form, clientResult);

    if (requestResult.IsError)
    {
        await _events.RaiseAsync(new TokenIssuedFailureEvent(requestResult));
        return Error(requestResult.Error, requestResult.ErrorDescription, requestResult.CustomResponse);
    }

    // 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.");
    return new TokenResult(response);
}

校验客户端

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;
    }

    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 success = new ClientSecretValidationResult
    {
        IsError = false,
        Client = client,
        Secret = parsedSecret,
        Confirmation = secretValidationResult?.Confirmation
    };

    await RaiseSuccessEventAsync(client.ClientId, parsedSecret.Type);
    return success;
}

校验请求参数

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);
    }
}

参数校验 - 授权码模式

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())
        {
            LogError("Authorization code is missing");
            return Invalid(OidcConstants.TokenErrors.InvalidGrant);
        }

        if (code.Length > _options.InputLengthRestrictions.AuthorizationCode)
        {
            LogError("Authorization code is too long");
            return Invalid(OidcConstants.TokenErrors.InvalidGrant);
        }

        _validatedRequest.AuthorizationCodeHandle = code;

        var authZcode = await _authorizationCodeStore.GetAuthorizationCodeAsync(code);
        if (authZcode == null)
        {
            LogError("Invalid authorization code", new { code });
            return Invalid(OidcConstants.TokenErrors.InvalidGrant);
        }

        await _authorizationCodeStore.RemoveAuthorizationCodeAsync(code);

        if (authZcode.CreationTime.HasExceeded(authZcode.Lifetime, _clock.UtcNow.UtcDateTime))
        {
            LogError("Authorization code expired", new { code });
            return Invalid(OidcConstants.TokenErrors.InvalidGrant);
        }

        /////////////////////////////////////////////
        // populate session id
        /////////////////////////////////////////////
        if (authZcode.SessionId.IsPresent())
        {
            _validatedRequest.SessionId = authZcode.SessionId;
        }

        /////////////////////////////////////////////
        // validate client binding
        /////////////////////////////////////////////
        if (authZcode.ClientId != _validatedRequest.Client.ClientId)
        {
            LogError("Client is trying to use a code from a different client", new { clientId = _validatedRequest.Client.ClientId, codeClient = authZcode.ClientId });
            return Invalid(OidcConstants.TokenErrors.InvalidGrant);
        }

        /////////////////////////////////////////////
        // validate code expiration
        /////////////////////////////////////////////
        if (authZcode.CreationTime.HasExceeded(_validatedRequest.Client.AuthorizationCodeLifetime, _clock.UtcNow.UtcDateTime))
        {
            LogError("Authorization code is expired");
            return Invalid(OidcConstants.TokenErrors.InvalidGrant);
        }

        _validatedRequest.AuthorizationCode = authZcode;
        _validatedRequest.Subject = authZcode.Subject;

        /////////////////////////////////////////////
        // validate redirect_uri
        /////////////////////////////////////////////
        var redirectUri = parameters.Get(OidcConstants.TokenRequest.RedirectUri);
        if (redirectUri.IsMissing())
        {
            LogError("Redirect URI is missing");
            return Invalid(OidcConstants.TokenErrors.UnauthorizedClient);
        }

        if (redirectUri.Equals(_validatedRequest.AuthorizationCode.RedirectUri, StringComparison.Ordinal) == false)
        {
            LogError("Invalid redirect_uri", new { redirectUri, expectedRedirectUri = _validatedRequest.AuthorizationCode.RedirectUri });
            return Invalid(OidcConstants.TokenErrors.UnauthorizedClient);
        }

        /////////////////////////////////////////////
        // validate scopes are present
        /////////////////////////////////////////////
        if (_validatedRequest.AuthorizationCode.RequestedScopes == null ||
            !_validatedRequest.AuthorizationCode.RequestedScopes.Any())
        {
            LogError("Authorization code has no associated scopes");
            return Invalid(OidcConstants.TokenErrors.InvalidRequest);
        }

        /////////////////////////////////////////////
        // validate PKCE parameters
        /////////////////////////////////////////////
        var codeVerifier = parameters.Get(OidcConstants.TokenRequest.CodeVerifier);
        if (_validatedRequest.Client.RequirePkce || _validatedRequest.AuthorizationCode.CodeChallenge.IsPresent())
        {
            _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}. This happens when the client is trying to use PKCE, but it is not enabled. Set RequirePkce to true.", 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", new { subjectId = _validatedRequest.AuthorizationCode.Subject.GetSubjectId() });
            return Invalid(OidcConstants.TokenErrors.InvalidGrant);
        }

        _logger.LogDebug("Validation of authorization code token request success");

        return Valid();
    }

参数校验 - 客户端秘钥模式

private async Task<TokenRequestValidationResult> ValidateClientCredentialsRequestAsync(NameValueCollection parameters)
    {
        _logger.LogDebug("Start client credentials token request validation");

        /////////////////////////////////////////////
        // check if client is authorized for grant type
        /////////////////////////////////////////////
        if (!_validatedRequest.Client.AllowedGrantTypes.ToList().Contains(GrantType.ClientCredentials))
        {
            LogError("Client not authorized for client credentials flow, check the AllowedGrantTypes setting", new { clientId = _validatedRequest.Client.ClientId });
            return Invalid(OidcConstants.TokenErrors.UnauthorizedClient);
        }

        /////////////////////////////////////////////
        // check if client is allowed to request scopes
        /////////////////////////////////////////////
        if (!await ValidateRequestedScopesAsync(parameters, ignoreImplicitIdentityScopes: true, ignoreImplicitOfflineAccess: true))
        {
            return Invalid(OidcConstants.TokenErrors.InvalidScope);
        }

        if (_validatedRequest.ValidatedScopes.ContainsOpenIdScopes)
        {
            LogError("Client cannot request OpenID scopes in client credentials flow", new { clientId = _validatedRequest.Client.ClientId });
            return Invalid(OidcConstants.TokenErrors.InvalidScope);
        }

        if (_validatedRequest.ValidatedScopes.ContainsOfflineAccessScope)
        {
            LogError("Client cannot request a refresh token in client credentials flow", new { clientId = _validatedRequest.Client.ClientId });
            return Invalid(OidcConstants.TokenErrors.InvalidScope);
        }

        _logger.LogDebug("{clientId} credentials token request validation success", _validatedRequest.Client.ClientId);
        return Valid();
    }

参数校验 - 用户名密码模式

private async Task<TokenRequestValidationResult> ValidateResourceOwnerCredentialRequestAsync(NameValueCollection parameters)
{
    _logger.LogDebug("Start resource owner password token request validation");

    /////////////////////////////////////////////
    // check if client is authorized for grant type
    /////////////////////////////////////////////
    if (!_validatedRequest.Client.AllowedGrantTypes.Contains(GrantType.ResourceOwnerPassword))
    {
        LogError("Client not authorized for resource owner flow, check the AllowedGrantTypes setting", new { client_id = _validatedRequest.Client.ClientId });
        return Invalid(OidcConstants.TokenErrors.UnauthorizedClient);
    }

    /////////////////////////////////////////////
    // check if client is allowed to request scopes
    /////////////////////////////////////////////
    if (!(await ValidateRequestedScopesAsync(parameters)))
    {
        return Invalid(OidcConstants.TokenErrors.InvalidScope);
    }

    /////////////////////////////////////////////
    // check resource owner credentials
    /////////////////////////////////////////////
    var userName = parameters.Get(OidcConstants.TokenRequest.UserName);
    var password = parameters.Get(OidcConstants.TokenRequest.Password);

    if (userName.IsMissing())
    {
        LogError("Username is missing");
        return Invalid(OidcConstants.TokenErrors.InvalidGrant);
    }

    if (password.IsMissing())
    {
        password = "";
    }

    if (userName.Length > _options.InputLengthRestrictions.UserName ||
        password.Length > _options.InputLengthRestrictions.Password)
    {
        LogError("Username or password too long");
        return Invalid(OidcConstants.TokenErrors.InvalidGrant);
    }

    _validatedRequest.UserName = userName;


    /////////////////////////////////////////////
    // authenticate user
    /////////////////////////////////////////////
    var resourceOwnerContext = new ResourceOwnerPasswordValidationContext
    {
        UserName = userName,
        Password = password,
        Request = _validatedRequest
    };
    await _resourceOwnerValidator.ValidateAsync(resourceOwnerContext);

    if (resourceOwnerContext.Result.IsError)
    {
        // protect against bad validator implementations
        resourceOwnerContext.Result.Error = resourceOwnerContext.Result.Error ?? OidcConstants.TokenErrors.InvalidGrant;

        if (resourceOwnerContext.Result.Error == OidcConstants.TokenErrors.UnsupportedGrantType)
        {
            LogError("Resource owner password credential grant type not supported");
            await RaiseFailedResourceOwnerAuthenticationEventAsync(userName, "password grant type not supported", resourceOwnerContext.Request.Client.ClientId);

            return Invalid(OidcConstants.TokenErrors.UnsupportedGrantType, customResponse: resourceOwnerContext.Result.CustomResponse);
        }

        var errorDescription = "invalid_username_or_password";

        if (resourceOwnerContext.Result.ErrorDescription.IsPresent())
        {
            errorDescription = resourceOwnerContext.Result.ErrorDescription;
        }

        LogInformation("User authentication failed: ", errorDescription ?? resourceOwnerContext.Result.Error);
        await RaiseFailedResourceOwnerAuthenticationEventAsync(userName, errorDescription, resourceOwnerContext.Request.Client.ClientId);

        return Invalid(resourceOwnerContext.Result.Error, errorDescription, resourceOwnerContext.Result.CustomResponse);
    }

    if (resourceOwnerContext.Result.Subject == null)
    {
        var error = "User authentication failed: no principal returned";
        LogError(error);
        await RaiseFailedResourceOwnerAuthenticationEventAsync(userName, error, resourceOwnerContext.Request.Client.ClientId);

        return Invalid(OidcConstants.TokenErrors.InvalidGrant);
    }

    /////////////////////////////////////////////
    // make sure user is enabled
    /////////////////////////////////////////////
    var isActiveCtx = new IsActiveContext(resourceOwnerContext.Result.Subject, _validatedRequest.Client, IdentityServerConstants.ProfileIsActiveCallers.ResourceOwnerValidation);
    await _profile.IsActiveAsync(isActiveCtx);

    if (isActiveCtx.IsActive == false)
    {
        LogError("User has been disabled", new { subjectId = resourceOwnerContext.Result.Subject.GetSubjectId() });
        await RaiseFailedResourceOwnerAuthenticationEventAsync(userName, "user is inactive", resourceOwnerContext.Request.Client.ClientId);

        return Invalid(OidcConstants.TokenErrors.InvalidGrant);
    }

    _validatedRequest.UserName = userName;
    _validatedRequest.Subject = resourceOwnerContext.Result.Subject;

    await RaiseSuccessfulResourceOwnerAuthenticationEventAsync(userName, resourceOwnerContext.Result.Subject.GetSubjectId(), resourceOwnerContext.Request.Client.ClientId);
    _logger.LogDebug("Resource owner password token request validation success.");
    return Valid(resourceOwnerContext.Result.CustomResponse);
}

参数校验 - RefreshToken

RefreshToken-刷新令牌。顾名思义,用于刷新通行令牌的凭证。拥有offline_access权限的客户端可以使用刷新令牌。只有授权码、混合流程等由后端参与的授权模式才允许使用刷新令牌。

private async Task<TokenRequestValidationResult> ValidateRefreshTokenRequestAsync(NameValueCollection parameters)
{
    _logger.LogDebug("Start validation of refresh token request");

    var refreshTokenHandle = parameters.Get(OidcConstants.TokenRequest.RefreshToken);
    if (refreshTokenHandle.IsMissing())
    {
        LogError("Refresh token is missing");
        return Invalid(OidcConstants.TokenErrors.InvalidRequest);
    }

    if (refreshTokenHandle.Length > _options.InputLengthRestrictions.RefreshToken)
    {
        LogError("Refresh token too long");
        return Invalid(OidcConstants.TokenErrors.InvalidGrant);
    }

    var result = await _tokenValidator.ValidateRefreshTokenAsync(refreshTokenHandle, _validatedRequest.Client);

    if (result.IsError)
    {
        LogWarning("Refresh token validation failed. aborting");
        return Invalid(OidcConstants.TokenErrors.InvalidGrant);
    }

    _validatedRequest.RefreshToken = result.RefreshToken;
    _validatedRequest.RefreshTokenHandle = refreshTokenHandle;
    _validatedRequest.Subject = result.RefreshToken.Subject;

    _logger.LogDebug("Validation of refresh token request success");
    return Valid();
}

生成响应报文 - 授权码模式

protected virtual async Task<TokenResponse> ProcessAuthorizationCodeRequestAsync(TokenRequestValidationResult request)
    {
        Logger.LogTrace("Creating response for authorization code request");

        //////////////////////////
        // access token
        /////////////////////////
        (var accessToken, var refreshToken) = await CreateAccessTokenAsync(request.ValidatedRequest);
        var response = new TokenResponse
        {
            AccessToken = accessToken,
            AccessTokenLifetime = request.ValidatedRequest.AccessTokenLifetime,
            Custom = request.CustomResponse,
            Scope = request.ValidatedRequest.AuthorizationCode.RequestedScopes.ToSpaceSeparatedString(),
        };

        //////////////////////////
        // refresh token
        /////////////////////////
        if (refreshToken.IsPresent())
        {
            response.RefreshToken = refreshToken;
        }

        //////////////////////////
        // id token
        /////////////////////////
        if (request.ValidatedRequest.AuthorizationCode.IsOpenId)
        {
            // load the client that belongs to the authorization code
            Client client = null;
            if (request.ValidatedRequest.AuthorizationCode.ClientId != null)
            {
                client = await Clients.FindEnabledClientByIdAsync(request.ValidatedRequest.AuthorizationCode.ClientId);
            }
            if (client == null)
            {
                throw new InvalidOperationException("Client does not exist anymore.");
            }

            var resources = await Resources.FindEnabledResourcesByScopeAsync(request.ValidatedRequest.AuthorizationCode.RequestedScopes);

            var tokenRequest = new TokenCreationRequest
            {
                Subject = request.ValidatedRequest.AuthorizationCode.Subject,
                Resources = resources,
                Nonce = request.ValidatedRequest.AuthorizationCode.Nonce,
                AccessTokenToHash = response.AccessToken,
                StateHash = request.ValidatedRequest.AuthorizationCode.StateHash,
                ValidatedRequest = request.ValidatedRequest
            };

            var idToken = await TokenService.CreateIdentityTokenAsync(tokenRequest);
            var jwt = await TokenService.CreateSecurityTokenAsync(idToken);
            response.IdentityToken = jwt;
        }

        return response;
    }

生成响应报文 - 客户端密钥模式

protected virtual Task<TokenResponse> ProcessClientCredentialsRequestAsync(TokenRequestValidationResult request)
{
    Logger.LogTrace("Creating response for client credentials request");

    return ProcessTokenRequestAsync(request);
}

protected virtual async Task<TokenResponse> ProcessTokenRequestAsync(TokenRequestValidationResult validationResult)
    {
        (var accessToken, var refreshToken) = await CreateAccessTokenAsync(validationResult.ValidatedRequest);
        var response = new TokenResponse
        {
            AccessToken = accessToken,
            AccessTokenLifetime = validationResult.ValidatedRequest.AccessTokenLifetime,
            Custom = validationResult.CustomResponse,
            Scope = validationResult.ValidatedRequest.Scopes.ToSpaceSeparatedString()
        };

        if (refreshToken.IsPresent())
        {
            response.RefreshToken = refreshToken;
        }

        return response;
    }

生成响应报文 - 用户名密码模式

protected virtual Task<TokenResponse> ProcessPasswordRequestAsync(TokenRequestValidationResult request)
    {
        Logger.LogTrace("Creating response for password request");

        return ProcessTokenRequestAsync(request);
    }

生成响应报文 - 刷新令牌

protected virtual async Task<TokenResponse> ProcessRefreshTokenRequestAsync(TokenRequestValidationResult request)
    {
        Logger.LogTrace("Creating response for refresh token request");

        var oldAccessToken = request.ValidatedRequest.RefreshToken.AccessToken;
        string accessTokenString;

        if (request.ValidatedRequest.Client.UpdateAccessTokenClaimsOnRefresh)
        {
            var subject = request.ValidatedRequest.RefreshToken.Subject;

            var creationRequest = new TokenCreationRequest
            {
                Subject = subject,
                ValidatedRequest = request.ValidatedRequest,
                Resources = await Resources.FindEnabledResourcesByScopeAsync(oldAccessToken.Scopes)
            };

            var newAccessToken = await TokenService.CreateAccessTokenAsync(creationRequest);
            accessTokenString = await TokenService.CreateSecurityTokenAsync(newAccessToken);
        }
        else
        {
            oldAccessToken.CreationTime = Clock.UtcNow.UtcDateTime;
            oldAccessToken.Lifetime = request.ValidatedRequest.AccessTokenLifetime;

            accessTokenString = await TokenService.CreateSecurityTokenAsync(oldAccessToken);
        }

        var handle = await RefreshTokenService.UpdateRefreshTokenAsync(request.ValidatedRequest.RefreshTokenHandle, request.ValidatedRequest.RefreshToken, request.ValidatedRequest.Client);

        return new TokenResponse
        {
            IdentityToken = await CreateIdTokenFromRefreshTokenRequestAsync(request.ValidatedRequest, accessTokenString),
            AccessToken = accessTokenString,
            AccessTokenLifetime = request.ValidatedRequest.AccessTokenLifetime,
            RefreshToken = handle,
            Custom = request.CustomResponse,
            Scope = request.ValidatedRequest.RefreshToken.Scopes.ToSpaceSeparatedString()
        };
    }