标签:dcl calling response cat 用户信息 urlencode ati ids source
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, 校验条件如下。对照这些条件,就可以更懂Microsoft.Authentication.OpenIdConnect里面的代码了,要做的事情很多。
如果id_token中包含了at_hash声明,需要做下面的校验。at_hash标明了access_token和id_token之间的会话关联关系,做这个校验可以防跨站伪造。
校验规则很多,了解一下即可,绝大部分属于客户端需要做的部分,绝大部分跟安全有关。这一块的实现可以参考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-刷新令牌。顾名思义,用于刷新通行令牌的凭证。拥有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()
};
}
标签:dcl calling response cat 用户信息 urlencode ati ids source
原文地址:https://www.cnblogs.com/holdengong/p/12589436.html