最近,我使用Identity Server 3为我的Web API实现了2FA。如果登录是在本地进行的(使用IUserService),一切都按预期工作。 现在,我希望能够通过发出部分登录cookie来进行此登录。这意味着我有一个API方法(POST),可以让用户在不进入主页面的情况下进行部分登录。 要发出身份验证cookie,这就是我所做的(基于IS3扩展方法):
_owinContext.Environment.IssueLoginCookie(new AuthenticatedLogin
{
IdentityProvider = Constants.ExternalAuthenticationType,
Subject = userId,
Name = identityId,
Claims = new[] { new Claim(ClaimTypes.NameIdentifier, identityId) },
AuthenticationMethod = Constants.AuthenticationMethods.TwoFactorAuthentication
});
在此之后,我将用户重定向回登录页面,并完全登录到绕过2FA步骤的应用程序。我正在跳跃,这将使用户部分登录,但它完全登录用户。
注意:我实施Two Factor的方式基于IUserService中的 AuthenticateLocalAsync 方法。在这里,我更新 AuthenticateResult 以使用带有重定向路径的构造函数。 API方法不会调用IUserService。它很简单地发布了登录cookie。
修改 因此,在检查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代码后,用户应在输入简历网址后完全登录:
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);
}
你们有没有尝试过这样的事情,或者甚至可能做到这一点?
答案 0 :(得分:1)
您可以将 OwinEnvironmentService 注入用户服务的ctor中,然后用 OwinContext 包装。
请点击以下链接查看行为。 https://identityserver.github.io/Documentation/docsv2/advanced/owin.html
答案 1 :(得分:1)
似乎您对身份验证的授权感到困惑。您使用Cookie是为了进行身份验证。例如,他们说的是那个用户。这些角色用于阻止对特定路径的授权。
尚不清楚您将收到500个错误,但是我认为,如果我分解MFA的工作流程,可能对每个人都有用。
在您的api中创建必填声明。 (注意:您可以改用角色)例如
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthorization(options =>
{
options.AddPolicy("Default",
policy => policy.RequireClaim("API.Access"));
});
}
现在,这将隐式阻止没有该角色的用户登录。现在,在登录期间,您需要允许访问用户控制器。因此,用AllowAnonymous装饰Authentication控制器,您可以在特定的方法或控制器上执行此操作,为简单起见,我将其放在控制器上
[AllowAnonymous]
public class AuthenticationController : ControllerBase {
}
如果用户需要2FA,则可以“登录”,但不要要求他们声明API.Access(理想情况下,您应该为MFA创建策略,并在控制器上要求该策略,但是为了简单起见,我会让你知道)
成功通过MFA授予他们Api.Access的所有权,但他们没有访问权限,例如
//Pseudo Code
public async Task<IIdentityUser> VerifyMFA(string challenge, string response)
{
var challenge = EncryptionTools.DecryptChallenge(challenge);
challenge.VerifyOrThrowAuthorizationError(response);
var user = this.GetUserFromService();
await user.AddClaimsAsync("API.Access");
return user;
}