我正在努力用OAuth和JWT实现自定义身份验证流程。基本上它应该如下:
我一直关注this great tutorial如何构建OAuth身份验证,唯一不同的部分是Jerrie正在使用Cookies
。
到目前为止我做了什么:
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.CallbackPath
与RedirectUri
在Challenge
端点传递的login
不同。我期望第三部分OAuth提供商调用option.CallbackPath
,但这不是发生的事情(显然)。我确实必须将CallbackPath
设置为我在OAuth提供程序配置中设置的相同值(如使用GitHub的Jerries教程)才能生效。是对的吗? Callback只用于匹配配置吗?我甚至可以评论端点CallbackPath
指向它并继续以相同的方式工作......
谢谢!
验证
正如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
奖金
我必须将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服务器不会引发错误的原因。
改变[AllowAnonymous]
到[Authorize]
在'loginCallback'
终点(AuthController.IamCallback
方法)