В настоящее время работает над внедрением аутентификации AzureAD в нашем .NET Core Web API. Это должно взаимодействовать с интерфейсом Angular 7, с API в другом домене.
На предыдущей итерации приложения, написанной на PHP, наш процесс аутентификации был:
- Интерфейс перенаправляет неаутентифицированного пользователя на
<api host>/api/auth/login
- API перенаправит пользователя на страницу входа в систему клиента AzureAD
- Пользователь войдет в систему и будет возвращен на URL обратного вызова, который является
<client host>/auth/login/callback?{oauth params}
- Клиентское приложение отправит XHR на
<api host>/api/auth/login/callback?{oauth params}
и перенаправит все параметры OAuth GET, как
- Если предоставленные параметры аутентифицируются успешно, ответ XHR будет содержать действительный токен авторизации
Хотя в этот поток входа можно внести некоторые улучшения, он работает довольно хорошо и позволяет нам управлять сессиями / токенами. Проблема состоит в том, чтобы заставить тот же поток работать с .NET Core Web API.
Текущая проблема несколько двоякая, но:
Мы не можем переопределить обработчик, определенный CallbackPath
в методе AddOAuth(...)
. В идеале я хотел бы вернуть JWT здесь при успешной аутентификации, чтобы клиент мог сразу его использовать. Мы можем переопределить возвращаемый URL только после того, как AuthenticationMiddleware
закончит обработку:
[HttpGet(nameof(Login))]
[AllowAnonymous]
public IActionResult Login()
{
return Challenge(new AuthenticationProperties
{
RedirectUri = "/api/auth/postcallback"
}, "azuread");
}
/api/auth/postcallback
в данном случае это просто контроллер, который генерирует JWT с утверждениями, полученными при успешном входе в систему:
[HttpGet(nameof(PostCallback))]
[Authorize]
public IActionResult PostCallback()
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_configuration["Tokens:Key"]);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(HttpContext.User.Claims),
Expires = DateTime.UtcNow.AddHours(6),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha512Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return new JsonResult(new { token = tokenHandler.WriteToken(token) });
}
В браузере это будет работать нормально. Вы войдете в систему, а затем в конечном итоге окажетесь на странице, которая дает вам токен, который вы можете использовать для аутентификации, но мы не можем использовать это с SPA, потому что у нас нет способа вернуть токен, потому что мы не можем переопределить CallbackPath
для перенаправления обратно на клиентский домен / URL, поскольку он должен быть абсолютным URL-адресом в том же домене.
Я установил аутентификацию следующим образом:
var auth = services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme);
auth.AddCookie();
auth.AddJwtBearer(opt =>
{
opt.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey,
ClockSkew = TimeSpan.FromHours(6),
ValidateLifetime = true
};
});
auth.AddOAuth("azuread", opt =>
{
opt.ClientId = Configuration["Auth:ClientId"];
opt.ClientSecret = Configuration["Auth:ClientSecret"];
opt.CallbackPath = "/api/auth/callback";
opt.AuthorizationEndpoint = Configuration["Auth:AuthEndpoint"];
opt.TokenEndpoint = Configuration["Auth:TokenEndpoint"];
opt.UserInformationEndpoint = Configuration["Auth:UserInformationEndpoint"];
// claims/scopes/events removed from here
});
Если вы измените CallbackPath
на что-то другое, а затем вручную перейдете на страницу входа в AzureAD с нашим идентификатором клиента, областями действия и т. Д. (Но без действительного состояния / единицы, созданной .NET Core), функция обратного вызова будет всегда молча терпит неудачу, но если затем заменить CallbackPath
на обычное значение и обновить страницу, сохранив состояние, возвращаемое из AzureAD, вы получите недопустимое исключение состояния из обычного обработчика:
[HttpGet(nameof(Callback))]
[AllowAnonymous]
public async Task<IActionResult> Callback()
{
var result = await HttpContext.AuthenticateAsync();
await HttpContext.SignInAsync(result.Principal);
if(!result.Succeeded)
{
return Unauthorized();
}
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_configuration["Tokens:Key"]);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(HttpContext.User.Claims),
Expires = DateTime.UtcNow.AddHours(6),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha512Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return new JsonResult(new { token = tokenHandler.WriteToken(token) });
}
Итак, у меня есть несколько вопросов:
- Есть ли способ сгенерировать правильное состояние из веб-API (или клиента), который затем успешно подтвердит возврат, а затем мы сможем использовать XHR для входа в систему (если это проблема), в противном случае:
- Существует ли другой подход к проверке подлинности, который позволяет нам выполнять oauth-поток, основанный на коде авторизации, вместо неявного oauth-потока, который, как кажется, работает как типичные коннекторы AzureAD
- Это почти правильно, но я упускаю что-то критическое, что приводит к падению всего этого?
Я смотрел на .NET Core Внешнюю аутентификацию без ASP.NET Identity , но мне не очень повезло, что на его основе работала такая же идея.