You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 04:16:39