Я изо всех сил пытаюсь реализовать собственный поток аутентификации с помощью OAuth и JWT. В основном это должно происходить следующим образом:
Я следил за этот отличный учебник о том, как создать аутентификацию OAuth, единственная часть, которая отличается, это то, что Джерри использует 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 и всего остального. Я не буду писать здесь, потому что моя проблема до этого.
В посте Джерри видно, что он ставит DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;. При этом, когда достигается /auth/loginCallback, у меня есть претензии пользователя в HttpContext.
Проблема в том, что мой DefaultAuthenticateScheme установлен на JwtBearersDefault, и когда вызывается loginCallback, я не вижу утверждений пользователя нигде в Request.
Как я могу получить доступ к информации, полученной на OnCreatingTicketEvent в моем обратном вызове в этом сценарии?
Бонусный вопрос: Я мало что знаю об OAuth (конечно, теперь все ясно). Вы могли заметить, что мой options.CallbackPath отличается от RedirectUri, переданного в Challenge в конечной точке login. Я ожидал, что option.CallbackPath будет вызываться поставщиком OAuth 3-й части, но это не так (очевидно). Мне пришлось установить для CallbackPath то же значение, которое я установил в конфигурации провайдера OAuth (например, учебник Jerries с GitHub), чтобы он работал. Это правильно? Обратный вызов используется только для настройки соответствия? Я даже могу прокомментировать конечную точку CallbackPath, и она продолжит работать так же...
Спасибо!





изменить [AllowAnonymous]
в [Authorize]
на конечной точке 'loginCallback' (метод AuthController.IamCallback)
Авторизация
Как Джерри упомянул в своем посте, есть отличное объяснение промежуточного программного обеспечения аутентификации: 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
Вы можете сохранить своего пользователя с помощью https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.authenticationhttpcontextextensions.signinasync?view=aspnetcore-2.2
Бонус
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. Is that right?"
да. По соображениям безопасности зарегистрированный uri обратного вызова (на сервере авторизации) и предоставленный uri обратного вызова (отправленный клиентом) ДОЛЖНЫ совпадать. Таким образом, вы не можете изменить его случайным образом, или, если вы его измените, вы также должны изменить его на сервере авторизации.
Если этого ограничения не было, т.е. электронное письмо со ссылкой, сформированной по электронной почте (с измененным 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
I can even comment the endpoint CallbackPath points to and it keep working the same way..."
Это потому, что вашему клиенту доверяют (вы предоставляете свой секрет, и вы не являетесь полноценным одностраничным приложением). Поэтому вам необязательно отправлять 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, это сработало отлично :) Скоро прочитаю все ваши ссылки, уже дам правильный ответ, потому что срок действия награды истекает