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

Blazor Server集成LinkedIn OAuth2报redirect_uri不匹配及重定向过多错误

问题根因

两个问题均由路径配置冲突导致:

  • 重定向URI不匹配:你通过app.Map("/login")创建了独立路径分支,进入该分支时请求路径基元会被自动追加/login前缀,OAuth中间件默认基于当前请求上下文生成回调地址时,会自动拼接该路径基元,最终生成的redirect_uri就变成了https://localhost:7167/login/signin-linkedin,和你最初在LinkedIn后台配置的根路径回调地址不一致。
  • 循环重定向:你将带/login前缀的地址加入LinkedIn回调配置后,LinkedIn授权完成回跳该地址,该地址属于/login路径分支,分支内的逻辑是直接发起LinkedIn登录挑战,完全没有走到OAuth中间件的回调处理逻辑,流程变成「进入登录页→跳LinkedIn授权→回跳登录页→再跳LinkedIn授权」的死循环。
修复步骤

1. 修正LinkedIn后台配置

在LinkedIn应用开发者后台,保留唯一的授权重定向URL:https://localhost:7167/signin-linkedin,删除之前添加的带/login前缀的错误回调地址。

2. 调整中间件执行顺序

确保认证、授权中间件的注册位置在自定义路由分支之前,避免OAuth回调请求被自定义登录分支拦截,正确顺序如下:

// 先注册认证、授权中间件
app.UseAuthentication();
app.UseAuthorization();

// 再注册自定义的登录、登出路由分支
app.Map("/login", applicationBuilder =>
{
   applicationBuilder.Run(async context =>
   {
       await context.ChallengeAsync("LinkedIn",
           properties: new AuthenticationProperties { RedirectUri = "/" });
   });
});

app.Map("/logout", applicationBuilder =>
{
   applicationBuilder.Run(async context =>
   {
       await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
       context.Response.Redirect("/");
   });
});

3. 强制OAuth中间件生成正确的回调地址

在LinkedIn OAuth配置块中,新增跳转授权端点的事件处理,避免路径基元影响回调地址拼接,同时修复原有代码中的字段判断bug:

.AddOAuth("LinkedIn", options =>
{
    options.ClientId = "sampletest";
    options.ClientSecret = "sampletest";
    options.ClaimsIssuer = LinkedInAuthenticationDefaults.Issuer;
    options.CallbackPath = new PathString("/signin-linkedin");
    options.AuthorizationEndpoint = LinkedInAuthenticationDefaults.AuthorizationEndpoint;
    options.TokenEndpoint = LinkedInAuthenticationDefaults.TokenEndpoint;
    options.UserInformationEndpoint = LinkedInAuthenticationDefaults.UserInformationEndpoint;
    options.Scope.Add("r_liteprofile");
    options.Scope.Add("r_emailaddress");
    
    // 强制生成不带/login前缀的正确回调地址
    options.Events.OnRedirectToAuthorizationEndpoint = context =>
    {
        var query = QueryHelpers.ParseQuery(context.RedirectUri.Split('?')[1]);
        query["redirect_uri"] = $"{context.Request.Scheme}://{context.Request.Host}{options.CallbackPath}";
        var authUrl = QueryHelpers.AddQueryString(options.AuthorizationEndpoint, query);
        context.Response.Redirect(authUrl);
        return Task.CompletedTask;
    };

    options.Events.OnCreatingTicket = async context =>
    {
        var request = new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint);
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken);
        request.Headers.Add("x-li-format", "json");

        var response = await context.Backchannel.SendAsync(request, context.HttpContext.RequestAborted);
        response.EnsureSuccessStatusCode();

        var user = JObject.Parse(await response.Content.ReadAsStringAsync());
        var userId = user.Value<string>("id");

        if (!string.IsNullOrEmpty(userId))
            context.Identity?.AddClaim(new Claim(ClaimTypes.NameIdentifier, userId, ClaimValueTypes.String,
                context.Options.ClaimsIssuer));

        var formattedName = user.Value<string>("formattedName");
        if (!string.IsNullOrEmpty(formattedName))
            context.Identity?.AddClaim(new Claim(ClaimTypes.Name, formattedName, ClaimValueTypes.String,
                context.Options.ClaimsIssuer));

        var email = user.Value<string>("emailAddress");
        if (!string.IsNullOrEmpty(email))
            context.Identity?.AddClaim(new Claim(ClaimTypes.Email, email, ClaimValueTypes.String,
                context.Options.ClaimsIssuer));

        var pictureUrl = user.Value<string>("pictureUrl");
        // 修复原代码判断条件bug:原逻辑误判email字段,导致头像声明写入逻辑异常
        if (!string.IsNullOrEmpty(pictureUrl))
            context.Identity?.AddClaim(new Claim("profile-picture", pictureUrl, ClaimValueTypes.String,
                context.Options.ClaimsIssuer));
    };
});

注意:需要在代码顶部引入Microsoft.AspNetCore.WebUtilities命名空间,才能正常使用QueryHelpers类。


内容的提问来源于stack exchange,提问作者N0Sp4m

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 14:27:18