简体   繁体   English

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

[英]OAuth with custom JWT authentication

I'm struggling to implement a custom auth flow with OAuth and JWT. 我正在努力用OAuth和JWT实现自定义身份验证流程。 Basically it should go as follows: 基本上它应该如下:

  • User clicks in login 用户点击登录
  • User is redirect to 3rd party OAuth login page 用户重定向到第三方OAuth登录页面
  • User logins into the page 用户登录页面
  • I get the access_token and request for User Info 我得到access_token并请求用户信息
  • I get the user info and create my own JWT Token to be sent back and forth 我获取用户信息并创建自己的JWT令牌来回发送

I have been following this great tutorial on how to build an OAuth authentication, the only part that differs is that Jerrie is using Cookies . 我一直在关注如何构建OAuth身份验证的这个很棒的教程 ,唯一不同的部分是Jerrie正在使用Cookies

What I Have done so far: 到目前为止我做了什么:

Configured the AuthenticationService 配置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 Auth控制器

    [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();
    }

Disclaimer: This OAuth flow is being incorporated now. 免责声明:此OAuth流程现已纳入。 I have a flow for creating and using my own JWT working and everything. 我有一个创建和使用我自己的JWT工作和一切的流程。 I will not post here because my problem is before that. 我不会在这里发帖,因为我的问题就在此之前。

What I want 我想要的是

In Jerrie's post you can see that he sets DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; 在Jerrie的帖子中,您可以看到他设置了DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; . With that, when the /auth/loginCallback is reached I have the user claims in the HttpContext . 有了它,当达到/auth/loginCallback时,我在HttpContext有用户声明。 The problem is my DefaultAuthenticateScheme is set to JwtBearersDefault and when the loginCallback is called I can't see the user claims nowhere in the Request . 问题是我的DefaultAuthenticateScheme设置为JwtBearersDefaultloginCallback叫我不能看到用户无处索赔的Request How can I have access to the information gained on the OnCreatingTicketEvent in my callback in this scenario? 在这种情况下,如何在回调中访问OnCreatingTicketEvent上获得的信息?

Bonus question: I don't know much about OAuth (sure that is clear now). 奖金问题:我对OAuth了解不多(现在确定很清楚)。 You may have noted that my options.CallbackPath differs from the RedirectUri passed in the Challenge at the login endpoint. 您可能已经注意到我的options.CallbackPathlogin端点上的Challenge中传递的RedirectUri不同。 I expected the option.CallbackPath to be called by the 3rd Part OAuth provider but this is not what happens (apparently). 我希望第三部分OAuth提供程序可以调用option.CallbackPath ,但这不会发生(显然)。 I did have to set the CallbackPath to the same value I have set in the OAuth provider configuration (like Jerries tutorial with GitHub) for it to work. 我必须将CallbackPath设置为我在OAuth提供程序配置中设置的相同值(如使用GitHub的Jerries教程)才能工作。 Is that right? 是对的吗? The Callback is used for nothing but a match configuration? Callback只用于匹配配置吗? I can even comment the endpoint CallbackPath points to and it keep working the same way... 我甚至可以评论端点CallbackPath指向它并继续以相同的方式工作......

Thanks! 谢谢!

change [AllowAnonymous] 改变[AllowAnonymous]

to [Authorize] [Authorize]

on the 'loginCallback' endpoint ( AuthController.IamCallback method) 'loginCallback'端点( AuthController.IamCallback方法)

Auth 验证

As Jerrie linked in his post, there is a great explanation about auth middlewares: https://digitalmccullough.com/posts/aspnetcore-auth-system-demystified.html 正如Jerrie在帖子中所说的那样,对auth中间件有一个很好的解释: https//digitalmccullough.com/posts/aspnetcore-auth-system-demystified.html

You can see a flowchart in the section Authentication and Authorization Flow 您可以在“ 身份验证和授权流 ”部分中查看流程图

The second step is Authentication middleware calls Default Handler's Authenticate . 第二步是身份验证中间件调用默认处理程序的身份验证

As your default auth handler is Jwt, the context is not pupulated with the user data after the oauth flow, since it uses the CookieAuthenticationDefaults.AuthenticationScheme 由于您的默认身份验证处理程序是Jwt,因此在oauth流之后上下文不会被用户数据限制,因为它使用CookieAuthenticationDefaults.AuthenticationScheme

Try: 尝试:

[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();
}

Great schemes summary: ASP.NET Core 2 AuthenticationSchemes 伟大的方案摘要: ASP.NET Core 2 AuthenticationSchemes

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

Bonus 奖金

I did have to set the CallbackPath to the same value I have set in the OAuth provider configuration (like Jerries tutorial with GitHub) for it to work. 我必须将CallbackPath设置为我在OAuth提供程序配置中设置的相同值(如使用GitHub的Jerries教程)才能工作。 Is that right?" 是对的吗?”

Yes. 是。 For security reasons, the registered callback uri (on authorization server) and the provided callback uri (sent by the client) MUST match. 出于安全原因,注册的回调uri(在授权服务器上)和提供的回调uri(由客户端发送)必须匹配。 So you cannot change it randomly, or if you change it, you have to change it on the auth server too. 因此您无法随机更改它,或者如果您更改它,您也必须在auth服务器上更改它。

If this restriction was not present, fe an email with a mailformed link (with modified callback url) could obtain grant. 如果没有此限制,那么带有mailformed链接(带有修改后的回调网址)的电子邮件可以获得授权。 This is called Open Redirect, the rfc refers to it too: https://tools.ietf.org/html/rfc6749#section-10.15 这称为Open Redirect,rfc也引用它: https//tools.ietf.org/html/rfc6749#section-10.15

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

I can even comment the endpoint CallbackPath points to and it keep working the same way..." 我甚至可以评论端点CallbackPath指向并继续以相同的方式工作......“

That is because your client is trusted (you provide your secret, and you are not a fully-frontend Single Page App). 这是因为您的客户端是可信任的(您提供了您的秘密,而且您不是一个完全正面的单页应用程序)。 So it is optional for you to send the callback uri. 因此,您可以选择发送回调uri。 But IF you send it, it MUST match with the one registered on the server. 但是如果你发送它,它必须与服务器上注册的那个匹配。 If you don't send it, the auth server will redirect to the url, that is registered on its side. 如果您不发送它,auth服务器将重定向到其侧面注册的URL。

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

redirect_uri OPTIONAL. redirect_uri可选。 As described in Section 3.1.2. 如3.1.2节所述。

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

The authorization server redirects the user-agent to the client's redirection endpoint previously established with the authorization server during the client registration process or when making the authorization request. 授权服务器在客户端注册过程中或在发出授权请求时,将用户代理重定向到先前使用授权服务器建立的客户端重定向端点。

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

The authorization server MUST require the following clients to register their redirection endpoint: 授权服务器必须要求以下客户端注册其重定向端点:

  • Public clients. 公共客户。
  • Confidential clients utilizing the implicit grant type. 机密客户端使用隐式授权类型。

Your client is confidential and uses authorization code grant type ( https://tools.ietf.org/html/rfc6749#section-1.3.1 ) 您的客户是保密的,并使用授权代码授予类型( https://tools.ietf.org/html/rfc6749#section-1.3.1

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

If multiple redirection URIs have been registered, if only part of the redirection URI has been registered, or if no redirection URI has been registered, the client MUST include a redirection URI with the authorization request using the "redirect_uri" request parameter. 如果已注册了多个重定向URI,如果只注册了部分重定向URI,或者没有注册重定向URI,则客户端必须使用“redirect_uri”请求参数包含带有授权请求的重定向URI。

You have registered your redirect uri, that's why the auth server does not raise an error. 您已注册重定向uri,这就是auth服务器不会引发错误的原因。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM