具有自定义JWT身份验证的OAuth

问题描述 投票:6回答:2

我正在努力用OAuth和JWT实现自定义身份验证流程。基本上它应该如下:

  • 用户点击登录
  • 用户重定向到第三方OAuth登录页面
  • 用户登录页面
  • 我得到access_token并请求用户信息
  • 我获取用户信息并创建自己的JWT令牌来回发送

我一直关注this great tutorial如何构建OAuth身份验证,唯一不同的部分是Jerrie正在使用Cookies

到目前为止我做了什么:

Configured the AuthenticationService

services.AddAuthentication(options => 
{
    options.DefaultChallengeScheme = "3rdPartyOAuth";
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie() // Added only because of the DefaultSignInScheme
.AddJwtBearer(options =>
{
    options.TokenValidationParameters = // Ommited for brevity
})
.AddOAuth("3rdPartyOAuth", options =>
{
    options.ClientId = securityConfig.ClientId;
    options.ClientSecret = securityConfig.ClientSecret;
    options.CallbackPath = new PathString("/auth/oauthCallback");

    options.AuthorizationEndpoint = securityConfig.AuthorizationEndpoint;
    options.TokenEndpoint = securityConfig.TokenEndpoint;
    options.UserInformationEndpoint = securityConfig.UserInfoEndpoint;

    // Only this for testing for now
    options.ClaimActions.MapJsonKey("sub", "sub");

    options.Events = new OAuthEvents
    {
        OnCreatingTicket = async context =>
        {
            // Request for user information
            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 = JObject.Parse(await response.Content.ReadAsStringAsync());

            context.RunClaimActions(user);
        }
    };
});

Auth Controller

    [AllowAnonymous]
    [HttpGet("login")]
    public IActionResult LoginIam(string returnUrl = "/auth/loginCallback")
    {
        return Challenge(new AuthenticationProperties() {RedirectUri = returnUrl});
    }

    [AllowAnonymous]
    [DisableRequestSizeLimit]
    [HttpGet("loginCallback")]
    public IActionResult IamCallback()
    {
        // Here is where I expect to get the user info, create my JWT and send it back to the client
        return Ok();
    }

免责声明:此OAuth流程现已纳入。我有一个创建和使用我自己的JWT工作和一切的流程。我不会在这里发帖,因为我的问题就在此之前。

What I want

在Jerrie的帖子中你可以看到他设置了DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;。有了这个,当达到/auth/loginCallback时,我有HttpContext的用户声明。问题是我的DefaultAuthenticateScheme设置为JwtBearersDefault,当loginCallback被调用时,我无法看到用户声称在Request无处可去。在这种情况下,如何在回调中访问OnCreatingTicketEvent上获得的信息?

奖金问题:我对OAuth知之甚少(确定现在很清楚)。您可能已经注意到我的options.CallbackPathRedirectUriChallenge端点传递的login不同。我期望第三部分OAuth提供商调用option.CallbackPath,但这不是发生的事情(显然)。我确实必须将CallbackPath设置为我在OAuth提供程序配置中设置的相同值(如使用GitHub的Jerries教程)才能生效。是对的吗? Callback只用于匹配配置吗?我甚至可以评论端点CallbackPath指向它并继续以相同的方式工作......

谢谢!

c# authentication oauth oauth-2.0 .net-core
2个回答
0
投票

验证

正如Jerrie在帖子中所说的那样,对auth中间件有一个很好的解释:https://digitalmccullough.com/posts/aspnetcore-auth-system-demystified.html

您可以在“身份验证和授权流”部分中查看流程图

第二步是身份验证中间件调用默认处理程序的身份验证。

由于您的默认身份验证处理程序是Jwt,因此在oauth流之后上下文不会被用户数据限制,因为它使用CookieAuthenticationDefaults.AuthenticationScheme

尝试:

[AllowAnonymous]
[DisableRequestSizeLimit]
[HttpGet("loginCallback")]
public IActionResult IamCallback()
{
    //
    // Read external identity from the temporary cookie
    //
    var result = await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);

    if (result?.Succeeded != true)
    {
        throw new Exception("Nein");
    }

    var oauthUser = result.Principal;

    ...

    return Ok();
}

伟大的计划摘要:ASP.NET Core 2 AuthenticationSchemes

您可以使用https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.authenticationhttpcontextextensions.signinasync?view=aspnetcore-2.2保留您的用户

奖金

我必须将CallbackPath设置为我在OAuth提供程序配置中设置的相同值(如使用GitHub的Jerries教程)才能工作。是对的吗?”

是。出于安全原因,注册的回调uri(在授权服务器上)和提供的回调uri(由客户端发送)必须匹配。因此您无法随机更改它,或者如果您更改它,您也必须在auth服务器上更改它。

如果没有这种限制,f.e。带有mailformed链接的电子邮件(带有修改后的回调网址)可以获得授权。这称为Open Redirect,rfc也指它:https://tools.ietf.org/html/rfc6749#section-10.15

OWASP有一个很好的描述:https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md

我甚至可以评论端点CallbackPath指向并继续以相同的方式工作......“

这是因为您的客户端是可信任的(您提供了您的秘密,而且您不是一个完全正面的单页应用程序)。因此,您可以选择发送回调uri。但是如果你发送它,它必须与服务器上注册的那个匹配。如果您不发送它,auth服务器将重定向到其侧面注册的URL。

https://tools.ietf.org/html/rfc6749#section-4.1.1

redirect_uri可选。如3.1.2节所述。

https://tools.ietf.org/html/rfc6749#section-3.1.2

授权服务器在客户端注册过程中或在发出授权请求时,将用户代理重定向到先前使用授权服务器建立的客户端重定向端点。

https://tools.ietf.org/html/rfc6749#section-3.1.2.2

授权服务器必须要求以下客户端注册其重定向端点:

  • 公共客户。
  • 机密客户端使用隐式授权类型。

您的客户是保密的,并使用授权代码授予类型(https://tools.ietf.org/html/rfc6749#section-1.3.1

https://tools.ietf.org/html/rfc6749#section-3.1.2.3

如果已注册了多个重定向URI,如果只注册了部分重定向URI,或者没有注册重定向URI,则客户端必须使用“redirect_uri”请求参数包含带有授权请求的重定向URI。

您已注册重定向uri,这就是auth服务器不会引发错误的原因。


0
投票

改变[AllowAnonymous]

[Authorize]

'loginCallback'终点(AuthController.IamCallback方法)

© www.soinside.com 2019 - 2024. All rights reserved.