【问题标题】:OAuth with custom JWT authentication带有自定义 JWT 身份验证的 OAuth
【发布时间】:2019-08-19 07:45:51
【问题描述】:

我正在努力使用 OAuth 和 JWT 实现自定义身份验证流程。 基本上应该是这样的:

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

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

到目前为止我做了什么:

配置了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);
        }
    };
});

授权控制器

    [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 工作和一切的流程。我不会在这里发帖,因为我的问题在此之前。

我想要什么

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

额外问题:我对 OAuth 了解不多(现在肯定很清楚)。您可能已经注意到我的options.CallbackPathChallengelogin 端点处传递的RedirectUri 不同。我预计 option.CallbackPath 会被第 3 部分 OAuth 提供程序调用,但这不是发生的事情(显然)。我确实必须将CallbackPath 设置为我在 OAuth 提供程序配置中设置的相同值(如使用 GitHub 的 Jerries 教程)才能正常工作。那正确吗?回调仅用于匹配配置?我什至可以评论 CallbackPath 指向的端点,它仍然以相同的方式工作......

谢谢!

【问题讨论】:

    标签: c# authentication oauth oauth-2.0 .net-core


    【解决方案1】:

    更改[AllowAnonymous]

    [Authorize]

    'loginCallback' 端点上(AuthController.IamCallback 方法)

    【讨论】:

      【解决方案2】:

      授权

      正如 Jerrie 在他的帖子中所链接的,关于 auth 中间件有一个很好的解释: https://digitalmccullough.com/posts/aspnetcore-auth-system-demystified.html

      您可以在身份验证和授权流程

      部分查看流程图

      第二步认证中间件调用Default Handler的Authenticate

      由于您的默认身份验证处理程序是 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 提供程序配置中设置的相同值(如 Jerries 教程与 GitHub)才能正常工作。是这样吗?”

      是的。 出于安全原因,注册的回调 uri(在授权服务器上)和提供的回调 uri(由客户端发送)必须匹配。 所以不能随意改,改了也得在鉴权服务器上改。

      如果不存在此限制,则 f.e.带有 mailformed 链接(带有修改后的回调 url)的电子邮件可以获得授权。 这称为 Open Redirect,rfc 也引用它:https://www.rfc-editor.org/rfc/rfc6749#section-10.15

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

      我什至可以评论 CallbackPath 指向的端点,并且它一直以相同的方式工作......"

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

      https://www.rfc-editor.org/rfc/rfc6749#section-4.1.1

      redirect_uri 可选的。如第 3.1.2 节所述。

      https://www.rfc-editor.org/rfc/rfc6749#section-3.1.2

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

      https://www.rfc-editor.org/rfc/rfc6749#section-3.1.2.2

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

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

      您的客户是机密的并使用授权码授予类型 (https://www.rfc-editor.org/rfc/rfc6749#section-1.3.1)

      https://www.rfc-editor.org/rfc/rfc6749#section-3.1.2.3

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

      您已经注册了重定向 uri,这就是身份验证服务器不会引发错误的原因。

      【讨论】:

      • 谢谢@LeBoucher,这很好:) 很快就会阅读你的所有链接,因为赏金即将到期,所以已经给出了正确的答案
      猜你喜欢
      • 2015-12-03
      • 2020-04-22
      • 1970-01-01
      • 2017-01-13
      • 1970-01-01
      • 2023-01-22
      • 2019-11-20
      • 1970-01-01
      • 2017-02-20
      相关资源
      最近更新 更多