Недавно я реализовал 2FA для своего веб-API с помощью Identity Server 3. Все работает так, как ожидалось, если логин сделан локально (с использованием IUserService). Теперь, я хочу, чтобы иметь возможность сделать это логин, выпустив частичный файл cookie для входа. Это означает, что у меня есть метод API (POST), который позволит пользователям делать частичный вход без входа на главную страницу. Чтобы выпустить файл cookie для проверки подлинности, это то, что я делаю (на основе метода IS3 Extensions):
_owinContext.Environment.IssueLoginCookie(new AuthenticatedLogin
{
IdentityProvider = Constants.ExternalAuthenticationType,
Subject = userId,
Name = identityId,
Claims = new[] { new Claim(ClaimTypes.NameIdentifier, identityId) },
AuthenticationMethod = Constants.AuthenticationMethods.TwoFactorAuthentication
});
После этого я перенаправляю пользователя обратно на страницу входа в систему, и он полностью входит в приложение, минуя шаг 2FA. Я надеялся, что это заставит пользователя частично войти в систему, но вместо этого полностью выполнит вход пользователя.
Примечание. Способ реализации двух факторов основан на методе AuthenticateLocalAsync из IUserService. Здесь я обновляю AuthenticateResult, чтобы использовать конструктор с пути перенаправления. Метод API не вызывает IUserService. Это простое решение для файлов cookie для входа.
Edit: Итак, после проверки внутренней реализации IdentityServer3 я теперь могу заставить пользователя пройти через экраны 2FA. Теперь проблема заключается в том, что частичный вход в систему является успешным (совпадают коды аутентификации). Я перенаправляю пользователя на URL-адрес резюме, и это приводит к странице с ошибкой 500 (при этом не отображаются исключения или журналы). Если это происходит при обычном входе в страницу, все работает.
Обращайтесь с запросом пользователя на вход:
var messageId = clientIdentifier;
var claims = new List<Claim>();
(...)
var authenticationContext = new ExternalAuthenticationContext
{
ExternalIdentity = new ExternalIdentity() { Provider = "API", Claims = claims },
};
await _userService.AuthenticateExternalAsync(authenticationContext);
var authResult = authenticationContext.AuthenticateResult;
var ctx = new PostAuthenticationContext
{
AuthenticateResult = authResult
};
var id = authResult.User.Identities.FirstOrDefault();
var props = new AuthenticationProperties();
var resumeId = CryptoRandom.CreateUniqueId();
var resumeLoginUrl = _owinContext.GetPartialLoginResumeUrl(resumeId);
var resumeLoginClaim = new Claim(Constants.ClaimTypes.PartialLoginReturnUrl, resumeLoginUrl);
id.AddClaim(resumeLoginClaim);
id.AddClaim(new Claim(GetClaimTypeForResumeId(resumeId), messageId));
// add url to start login process over again (which re-triggers preauthenticate)
var restartUrl = _owinContext.GetPartialLoginRestartUrl(messageId);
id.AddClaim(new Claim(Constants.ClaimTypes.PartialLoginRestartUrl, restartUrl));
_owinContext.Authentication.SignIn(props, id);
// Sends the user to the 2FA pages (where he needs to insert the validation code).
// At this point the user is successfuly partially logged in.
var redirectUrl = GetRedirectUrl(authResult);
return Redirect(redirectUrl);
После ввода кода 2FA пользователь должен полностью войти в систему после ввода URL-адреса резюме:
if (isAuthCodeValid)
{
var resumeUrl = await owinContext.Environment.GetPartialLoginResumeUrlAsync();
// Redirects the user to resume url. This is not working if authentication is done by API but is works with normal local authentication.
// With API it redirects to a page which eventually will have 500 error (no logs or exceptions being shown)
return Redirect(resumeUrl);
}
Вы, ребята, когда-либо пытались сделать что-то подобное или если это возможно?