为外部身份验证创建部分登录Cookie

时间:2018-05-28 14:49:51

标签: c# asp.net-web-api identityserver3 two-factor-authentication

最近,我使用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);
}

你们有没有尝试过这样的事情,或者甚至可能做到这一点?

2 个答案:

答案 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;
}
相关问题