创建外部认证的部分登录cookie

55

最近,我使用IdentityServer3为我的WebAPI实现了2FA。如果使用进行本地登录,则一切按预期工作。 现在,我希望能够通过发出部分登录cookie来进行此登录。这意味着我有一个API方法(POST),让用户在不输入主页面的情况下进行部分登录。 为了发出身份验证cookie,我执行以下操作(基于IdentityServer3 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步骤进行登录。我本来希望这会使用户部分登录,但相反,它让用户完全登录了。

注意: 我实现双重认证的方式是基于 IUserServiceAuthenticateLocalAsync 方法。在这里,我更新了 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);

在输入双因素身份验证代码后,用户应该在输入简历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 working 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);
}

你们有没有尝试过类似这样的事情,或者这是否可能?


另一个需要考虑的事情是在 github 上询问此问题。 - huysentruitw
9
在第一个代码示例中,你创建了一个完整的登录并带有idsrv.external身份验证类型。在最后一个代码示例中,你期望进行部分登录,但这是不存在的,因为默认情况下部分登录具有idsrv.partial身份验证类型,因此会出现500错误。 - laika
2个回答

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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接