ExternalIdentity.BootstrapContext 始终为空

ExternalIdentity.BootstrapContext always null

在我当前的应用程序中,我使用 Owin + Aspnet Identity 以及 Microsoft Live OAuth 提供程序来处理身份验证。

到目前为止一切正常,除了我尝试检索远程令牌以便将其存储在我的数据库中。

我在网上找到一些文档说要在 web.config 中启用 "saveBootstrapContext",所以我这样做了:

<system.identityModel>
<identityConfiguration saveBootstrapContext="true">
  <securityTokenHandlers>
    <securityTokenHandlerConfiguration saveBootstrapContext="true"></securityTokenHandlerConfiguration>
  </securityTokenHandlers>
</identityConfiguration>
</system.identityModel>

我只在 identityConfiguration 上试过,然后在 securityTokenHandlerConfiguration 上试过,然后两者都试过,但结果总是一样的。在以下代码中,externalData.ExternalIdentity.BootstrapContext 始终为空。

SignIn 方法在中间件调用的 "ExternalLoginCallback" 方法中被调用。

using System.IdentityModel.Tokens;
using System.Security.Claims;
using System.Web;

// custom namespaces redacted
using Microsoft.AspNet.Identity;
using Microsoft.Owin.Security;

public class AuthManager : IAuthManager
{
    private readonly IUserBusinessLogic userBusinessLogic;

    public AuthManager(IUserBusinessLogic userBusinessLogic)
    {
        this.userBusinessLogic = userBusinessLogic;
    }

    public void SignIn()
    {
        IAuthenticationManager manager = HttpContext.Current.GetOwinContext().Authentication;
        var externalData = manager.GetExternalLoginInfo();

        UserDto user = this.userBusinessLogic.GetUser(externalData.Login.LoginProvider, externalData.Login.ProviderKey);
        var token = ((BootstrapContext)externalData.ExternalIdentity.BootstrapContext).Token;

        if (user == null)
        {
            user = this.userBusinessLogic.AddUser(new UserDto(), externalData.Login.LoginProvider, externalData.Login.ProviderKey, token);
        }

        user.Token = token;

        var claims = new Claim[]
        {
            new Claim(ClaimTypes.NameIdentifier, user.ID.ToString()),
            new Claim(ClaimTypes.UserData, UserData.FromUserDto(user).ToString())
        };

        var identity = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
        var properties = new AuthenticationProperties
        {
            AllowRefresh = true,
            IsPersistent = true
        };

        manager.SignIn(properties, identity);
    }

SO上的一些其他帖子说尝试重新启动IIS,重新启动机器,清空浏览器cookie并重新启动浏览器。我尝试了所有这些,但仍然一无所获。如果我模拟令牌字符串,其他一切正常。

现在我显然遗漏了一些东西,但我在网上找不到任何明确的文档。

非常感谢任何帮助。

谢谢。

有时候没有帮助就是最好的帮助,因为我不得不越来越深入地挖掘,最终找到解决方案。

前提是我当时一头雾水,在不理解所有含义的情况下混合了三种不同的技术。

我的示例在 web.config 中使用了 WIF 配置,但随后在代码端使用了 OWIN 之上的 Aspnet Identity(根本不使用 web.config)。

当我理清思路后,我意识到以下几点:

  • WIF 完全不需要,因此我摆脱了所有配置(和 WIF 一起)
  • 由于我的 MS 身份验证是由处理它的特定 OWIN 中间件执行的,因此我必须了解如何配置它以检索令牌
  • Aspnet Identity 仅用于 DefaultAuthenticationTypes 静态 class,它提供了一些字符串常量。为了简单起见,我保留了它,但我也可以删除它。

所以我的重构(和工作)代码看起来像这样。首先,中间件配置需要让 MS 身份验证与令牌一起工作,在 Startup.cs

app.UseMicrosoftAccountAuthentication(new MicrosoftAccountAuthenticationOptions
{
    ClientId = "myClientId",
    ClientSecret = "myClientSecret",
    Provider = new MicrosoftAccountAuthenticationProvider
    {
        OnAuthenticated = context =>
        {
            // here's the token
            context.Identity.AddClaim(new System.Security.Claims.Claim("AccessToken", context.AccessToken));
            context.Identity.AddClaim(new System.Security.Claims.Claim("FirstName", context.FirstName));
            context.Identity.AddClaim(new System.Security.Claims.Claim("LastName", context.LastName));
            return Task.FromResult(true);
        }
    }
});

然后重温SignIn方法:

public void SignIn()
{
    IAuthenticationManager manager = HttpContext.Current.GetOwinContext().Authentication;
    var externalData = manager.GetExternalLoginInfo();

    UserDto user = this.userBusinessLogic.GetUser(externalData.Login.LoginProvider, externalData.Login.ProviderKey);

    if (user == null)
    {
        user = this.userBusinessLogic.AddUser(
            new UserDto
            { 
                FirstName = externalData.ExternalIdentity.Claims.Single(c => c.Type == "FirstName").Value,
                LastName = externalData.ExternalIdentity.Claims.Single(c => c.Type == "LastName").Value
            },
            externalData.Login.LoginProvider,
            externalData.Login.ProviderKey,
            // here's the token claim that I set in the middleware configuration
            externalData.ExternalIdentity.Claims.Single(c => c.Type == "AccessToken").Value);
    }

    var claims = new Claim[]
    {
        new Claim(ClaimTypes.NameIdentifier, user.ID.ToString()),
        new Claim(ClaimTypes.UserData, UserData.FromUserDto(user).ToString()),
        new Claim("AccessToken", user.Token),
        new Claim("FirstName", user.FirstName),
        new Claim("LastName", user.LastName)
    };

    var identity = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
    var properties = new AuthenticationProperties
    {
        AllowRefresh = true,
        IsPersistent = true
    };

    manager.SignIn(properties, identity);
}

也许这对我来说很难,但无论如何我都会在这里发布我的解决方案,希望它可以避免一些头痛和一些开发人员发誓的日子。

编码愉快^^