如何通过Duende Identity Server令牌端点返回外部提供商令牌给客户端
在Duende Identity Server中返回外部身份提供商的令牌到Custom属性
核心实现思路
要将Google的API令牌加入Duende令牌端点响应的Custom属性,需要分两步操作:
- 捕获外部登录过程中获取的Google访问令牌
- 通过Duende的扩展点自定义令牌响应,将令牌注入
Custom字段
步骤1:配置Google认证并捕获令牌
首先在Google认证配置中,确保保存外部令牌并将其暴露为可访问的数据源:
方式A:通过AuthenticationProperties保存令牌
builder.Services.AddAuthentication() .AddGoogle(options => { options.ClientId = "你的Google客户端ID"; options.ClientSecret = "你的Google客户端密钥"; // 开启令牌保存,将Google令牌存入AuthenticationProperties options.SaveTokens = true; // 添加需要调用的Google API权限范围 options.Scope.Add("https://www.googleapis.com/auth/drive.readonly"); });
方式B:将令牌添加到用户Claims中
如果希望令牌随用户身份主体一起传递,可以在Google登录的OnCreatingTicket事件中添加自定义Claim:
builder.Services.AddAuthentication() .AddGoogle(options => { options.ClientId = "你的Google客户端ID"; options.ClientSecret = "你的Google客户端密钥"; options.Scope.Add("https://www.googleapis.com/auth/drive.readonly"); options.Events.OnCreatingTicket = context => { // 将Google访问令牌添加到用户身份Claims context.Identity.AddClaim(new Claim("google_access_token", context.AccessToken)); return Task.CompletedTask; }; });
步骤2:自定义令牌响应生成器
实现Duende提供的ICustomTokenResponseGenerator接口,在令牌响应生成时将Google令牌注入Custom属性:
public class CustomTokenResponseGenerator : ICustomTokenResponseGenerator { private readonly ITokenResponseGenerator _innerGenerator; // 如果用方式A需要依赖IHttpContextAccessor,方式B则不需要 private readonly IHttpContextAccessor? _httpContextAccessor; // 构造函数:方式B可移除IHttpContextAccessor参数 public CustomTokenResponseGenerator(ITokenResponseGenerator innerGenerator, IHttpContextAccessor? httpContextAccessor = null) { _innerGenerator = innerGenerator; _httpContextAccessor = httpContextAccessor; } public async Task<TokenResponse> GenerateAsync(TokenResponseGenerationContext context) { // 先执行默认的令牌响应生成逻辑 var response = await _innerGenerator.GenerateAsync(context); // 仅针对授权码流程的令牌请求处理(可根据实际需求调整) if (context.Request.GrantType == OidcConstants.GrantTypes.AuthorizationCode) { string? googleAccessToken = null; // 方式A:从AuthenticationProperties中提取令牌 if (_httpContextAccessor != null) { var externalAuthResult = await _httpContextAccessor.HttpContext .AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); if (externalAuthResult.Succeeded) { googleAccessToken = externalAuthResult.Properties.GetTokenValue("access_token"); } } // 方式B:从用户主体Claims中提取令牌(优先级高于方式A) if (string.IsNullOrEmpty(googleAccessToken)) { googleAccessToken = context.Subject.FindFirstValue("google_access_token"); } // 将令牌加入Custom属性 if (!string.IsNullOrEmpty(googleAccessToken)) { response.Custom ??= new Dictionary<string, object>(); response.Custom["google_access_token"] = googleAccessToken; } } return response; } }
步骤3:注册自定义生成器
将自定义的令牌响应生成器注册到Duende的依赖注入容器中,替换默认实现:
builder.Services.AddIdentityServer() // 其他配置(如AddInMemoryClients、AddInMemoryIdentityResources等) .AddCustomTokenResponseGenerator<CustomTokenResponseGenerator>();
注意事项
- 确保
IdentityServerConstants.ExternalCookieAuthenticationScheme的Cookie在令牌请求阶段仍有效(授权码流程中,code交换token时该Cookie通常会保留) - 若使用方式B,需确保
google_access_tokenClaim不会被Duende默认的Claims过滤规则移除,可在AddIdentityServer配置中调整Claims映射或保留规则 - 根据实际业务场景,可扩展支持其他外部身份提供商的令牌注入
内容的提问来源于stack exchange,提问作者Kiran B
相关产品推荐
相关产品推荐

