如何让基于OpenIddict的纯API授权服务器支持多外部身份提供商?
基于OpenIddict实现纯API型多外部身份提供商授权服务器
场景描述
- 桌面富客户端支持用户选择Google、GitHub等外部身份提供商完成登录
- 授权服务器不提供登录页面,仅通过API端点处理认证请求
- 用户点击客户端登录按钮后,调用授权服务器
authorize端点,服务器自动重定向至所选外部提供商的登录页面 - 用户在本地浏览器完成认证后,授权服务器向客户端返回OpenID Connect访问令牌,供其调用后端服务
核心问题
已实现将身份提供商类型通过授权请求传递至authorize端点,但不知如何触发对应外部提供商的登录重定向;同时需要修改Mimban示例应用,支持客户端通过URI查询参数指定身份提供商完成认证。
修改Mimban示例实现多外部身份提供商的步骤
1. 配置多外部身份认证方案
在Program.cs中添加Google、GitHub等外部提供商的认证配置:
builder.Services.AddAuthentication() // 配置Google认证 .AddGoogle(options => { options.ClientId = builder.Configuration["Authentication:Google:ClientId"]; options.ClientSecret = builder.Configuration["Authentication:Google:ClientSecret"]; options.CallbackPath = "/signin-google"; // 接管重定向逻辑,避免默认跳转冲突 options.Events.OnRedirectToAuthorizationEndpoint = context => { context.Response.Redirect(context.RedirectUri); return Task.CompletedTask; }; }) // 配置GitHub认证 .AddGitHub(options => { options.ClientId = builder.Configuration["Authentication:GitHub:ClientId"]; options.ClientSecret = builder.Configuration["Authentication:GitHub:ClientSecret"]; options.CallbackPath = "/signin-github"; options.Events.OnRedirectToAuthorizationEndpoint = context => { context.Response.Redirect(context.RedirectUri); return Task.CompletedTask; }; });
2. 修改Authorize端点逻辑,处理提供商参数
在AuthorizationController的Authorize方法中,从请求参数提取指定的身份提供商,触发对应认证挑战:
[HttpGet("~/connect/authorize")] [HttpPost("~/connect/authorize")] [IgnoreAntiforgeryToken] public async Task<IActionResult> Authorize() { var request = HttpContext.GetOpenIddictServerRequest() ?? throw new InvalidOperationException("无法获取OpenIddict请求上下文。"); // 从查询参数获取指定的身份提供商 var provider = request.GetParameter("provider").ToString(); if (string.IsNullOrEmpty(provider)) { return BadRequest(new OpenIddictResponse { Error = OpenIddictConstants.Errors.InvalidRequest, ErrorDescription = "必须指定身份提供商参数(provider)。" }); } // 验证是否为支持的提供商类型 var supportedProviders = new[] { "google", "github" }; if (!supportedProviders.Contains(provider, StringComparison.OrdinalIgnoreCase)) { return BadRequest(new OpenIddictResponse { Error = OpenIddictConstants.Errors.InvalidRequest, ErrorDescription = $"不支持的身份提供商:{provider}。" }); } // 存储OpenIddict请求状态,用于后续回调处理 var properties = new AuthenticationProperties { RedirectUri = Url.Action("Callback"), Items = { ["OpenIddict.Server.AuthenticationProperties"] = HttpContext.Features.Get<OpenIddictServerFeature>()?.Properties } }; // 触发对应提供商的认证挑战 return Challenge(properties, provider); }
3. 实现回调端点处理认证结果
添加Callback方法,接收外部提供商的认证回调,完成OpenID Connect授权流程:
[HttpGet("~/signin-callback")] public async Task<IActionResult> Callback() { // 获取外部认证的结果 var result = await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); if (!result.Succeeded) { return Forbid(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); } // 从外部认证主体中提取用户核心声明 var claims = new List<Claim> { new Claim(OpenIddictConstants.Claims.Subject, result.Principal.FindFirst(ClaimTypes.NameIdentifier)!.Value), new Claim(OpenIddictConstants.Claims.Name, result.Principal.FindFirst(ClaimTypes.Name)!.Value) // 可根据需求添加邮箱、头像等其他声明 }; // 创建用于生成令牌的身份标识 var identity = new ClaimsIdentity(claims, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); var principal = new ClaimsPrincipal(identity); // 标记授权成功,触发OpenIddict生成并返回令牌 return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); }
4. 配置OpenIddict允许自定义参数
在Program.cs的OpenIddict服务配置中,允许传递provider自定义参数:
builder.Services.AddOpenIddict() .AddServer(options => { // 保留原有配置,新增允许自定义参数 options.AllowRequestParameters("provider"); // 配置授权端点与令牌端点 options.SetAuthorizationEndpointUris("/connect/authorize") .SetTokenEndpointUris("/connect/token"); });
5. 客户端请求示例
客户端调用授权端点时,通过查询参数指定身份提供商:
GET /connect/authorize?client_id=desktop-client&redirect_uri=myapp://callback&response_type=code&scope=openid profile&provider=google
内容的提问来源于stack exchange,提问作者Jonny Dee
相关产品推荐
相关产品推荐

