GitHub OAuth 重定向通过被 CORS 阻止的前端代理

GitHub OAuth redirect through frontend proxy blocked by CORS

我想做什么

我有一个带有 webpack 的 JS 前端,我正在将 /api 请求路由到后端服务器,如下所示:

proxy: {
    '/api': {
        target: 'https://[::1]:5001',
        secure: false,
        changeOrigin: true,
    }
}

还有一个 .NET 5 后端,它为所有来源和 headers 和方法启用了 cors。

app.UseCors(x => x
    .AllowAnyOrigin()
    .AllowAnyMethod()
    .AllowAnyHeader());

此后端正在尝试对使用 GitHub 的人进行身份验证。

services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = "GitHub";
            })
            .AddCookie()
            .AddOAuth("GitHub", options =>
            {
                options.ClientId = GitHubConfiguration.ClientId;
                options.ClientSecret = GitHubConfiguration.ClientSecret;
                options.CallbackPath = new PathString("/api/signin-github");
                options.CorrelationCookie.SameSite = SameSiteMode.Lax;

                options.AuthorizationEndpoint = "https://github.com/login/oauth/authorize";
                options.TokenEndpoint = "https://github.com/login/oauth/access_token";
                options.UserInformationEndpoint = "https://api.github.com/user";

                options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "id");
                options.ClaimActions.MapJsonKey(ClaimTypes.Name, "name");
                options.ClaimActions.MapJsonKey("urn:github:login", "login");
                options.ClaimActions.MapJsonKey("urn:github:url", "html_url");
                options.ClaimActions.MapJsonKey("urn:github:avatar", "avatar_url");

                options.Events = new OAuthEvents
                {
                    OnCreatingTicket = async context =>
                    {
                        var request = new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint);
                        request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken);

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

                        var user = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
                        context.RunClaimActions(user.RootElement);
                    }
                };
            });

据我所知,相当标准。

为了测试它,我有这个端点

    [ApiController]
    [Route("api/[controller]/[action]")]
    public class AccountController : ControllerBase
    {
        [HttpGet]
        public IActionResult Login()
        {
            return Challenge(new AuthenticationProperties { RedirectUri = "/" });
        }

当我在

的浏览器中直接调用它时,效果很好
https://localhost:5001/api/account/login

但是当我尝试 git 来自前端的端点时,我得到

Access to fetch at 'https://github.com/login/oauth/authorize?client_id=<CLIENT_ID>&scope=&response_type=code&redirect_uri=https%3A%2F%2Flocalhost%3A5001%2Fapi%2Fsignin-github&state=<OAUTH_STATE>' (redirected from 'https://localhost:5001/api/account/login') from origin 'http://localhost:8081' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

这是来自 fiddler 的基本摘要:

GET https://localhost:5001/api/account/login
302 Redirect to https://github.com/login/oauth/authorize?etc

GET https://github.com/login/oauth/authorize?etc
302 Redirect to https://github.com/login?etc

服务器日志无任何异常。

似乎正在发生的事情是前端 -> 后端工作正常,但随后后端试图重定向由于跨源限制而不允许的前端。

我是否正确地认为我需要以某种方式获得重定向以命中服务器而不是它仍然认为是原点的东西(因为 webpack 开发服务器代理将原点保持为 localhost:8081 即使在请求是代理)?如果是这样,我该如何响应前端?

我已经为此兜圈子几个小时了,所以任何朝着正确方向的推动都会很棒。

干杯,

编辑下面是 headers 从导致它的最后一个重定向返回的详细信息:

GET https://github.com/login/oauth/authorize?client_id=<oauthinfo>
Host: github.com
Connection: keep-alive
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="90", "Google Chrome";v="90"
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36
Accept: */*
Origin: null
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://localhost:8081/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9


HTTP/1.1 302 Found
Server: GitHub.com
Date: Mon, 26 Apr 2021 21:27:40 GMT
Content-Type: text/html; charset=utf-8
Vary: X-PJAX
permissions-policy: interest-cohort=()
Location: https://github.com/login?client_id=<oauthinfo>
Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
X-Frame-Options: deny
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
Expect-CT: max-age=2592000, report-uri="https://api.github.com/_private/browser/errors"
Content-Security-Policy: default-src 'none'; base-uri 'self'; block-all-mixed-content; connect-src 'self'
Vary: Accept-Encoding, Accept, X-Requested-With
X-GitHub-Request-Id: D17E:70F9:663C530:5A21FA1:7097304C
Content-Length: 545

您的应用程序似乎正在从 http=>https 来回重定向 (302),并且再次进入同一循环。发出 https 请求并为您的本地端点启用 https。然后它应该工作

我不知道您不能使用常规获取 GET 执行身份验证流程。

配置正确,正在使用

window.location.href('https://localhost:5001/api/account/login')

在 JS 中或使用 link 例如

<a href='https://localhost:5001/api/account/login'>
  Login to GitHub
</a>

解决了我的问题。

原因很简单,我忽略了一个。您需要直接向服务器发出请求,这样您就可以独立于前端完成整个身份验证流程,从而可以放弃前端中安全性较低的任何密钥。

在流程结束时,您将被重定向回前端,并通过完全身份验证。