【问题标题】:IdentityServer4 - sub claim is missingIdentityServer4 - 子声明丢失
【发布时间】:2018-04-03 17:13:28
【问题描述】:

我有一个 IdentityServer4 实例,我试图在 nginx 代理后面的 Docker 容器中运行它。我基于 Git 存储库中的 AspNet 身份示例,但在用户成功注册新帐户后,我从 IdentityServer 收到“发生错误”并且日志显示

[07:46:39 ERR] An unhandled exception has occurred: sub claim is missing System.InvalidOperationException: sub claim is missing at IdentityServer4.IdentityServerPrincipal.AssertRequiredClaims(ClaimsPrincipal principal at IdentityServer4.Hosting.IdentityServerAuthenticationService.AugmentPrincipal(ClaimsPrincipal principal at IdentityServer4.Hosting.IdentityServerAuthenticationService.<SignInAsync>d__7.MoveNext

我的 Startup.cs 看起来像这样

var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().
var connectionString = Configuration.GetConnectionString("DefaultConnection");
var issuerUri = Configuration.GetSection("IssuerUri").Value;

services.AddDbContext<ApplicationDbContext>(options => 
    options.UseSqlServer(connectionString));

services.AddIdentity<ApplicationUser, IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

services.AddTransient<IEmailSender, EmailSender>();

services.AddMvc();

services.AddCors(o => o.AddPolicy("CorsPolicy", b =>
{
    b.AllowAnyOrigin()
        .AllowAnyMethod()
        .AllowAnyHeader();
}));

services.AddIdentityServer(options =>
{
    options.IssuerUri = issuerUri;
    options.PublicOrigin = issuerUri;
})
.AddDeveloperSigningCredential()

// this adds the config data from DB (clients, resources)
.AddConfigurationStore(options =>
{
    options.ConfigureDbContext = builder =>
        builder.UseSqlServer(connectionString,
            sql => sql.MigrationsAssembly(migrationsAssembly));
})

// this adds the operational data from DB (codes, tokens, consents)
.AddOperationalStore(options =>
{
    options.ConfigureDbContext = builder =>
        builder.UseSqlServer(connectionString,
            sql => sql.MigrationsAssembly(migrationsAssembly));

    // this enables automatic token cleanup. this is optional.
    //options.EnableTokenCleanup = true;
    //options.TokenCleanupInterval = 30;
});

我一定错过了一些明显的配置,但我看不到在哪里。有什么想法吗?

更新 我在这方面取得了一些进展,似乎已经克服了最初的错误。用户现在已通过身份验证,但 signin-oidc 页面抛出错误

[11:33:21 INF] Request starting HTTP/1.1 POST http://mvcportal.co.uk/signin-oidc application/x-www-form-urlencoded 1565
[11:33:21 INF] AuthenticationScheme: Cookies signed in.
[11:33:21 INF] Request finished in 684.8425ms 302
[11:33:27 INF] Request starting HTTP/1.1 POST http://mvcportal.co.uk/signin-oidc application/x-www-form-urlencoded 1565
[11:33:27 ERR] Message contains error: 'invalid_grant', error_description: 'error_description is null', error_uri: 'error_uri is null', status code '400'.

我有一个有效的 JWT,但我注意到 idp 不等于颁发者。对吗?

{
  "nbf": 1508758474,
  "exp": 1508758774,
  "iss": "http://myproxiedlogonsitebehindnginx.co.uk",
  "aud": "mvc.portal",
  "nonce": "636443552746808541.MGVjMzk2NTEtYmYwNS00NmQwLTllOTQtZDVjNjdlYTA2YWVlYTQ3Zjg1NjgtZDA1Yi00NDE0LWJiYmYtMjM4YzI1NjZlYTcx",
  "iat": 1508758474,
  "c_hash": "kG7wG8vSgRe5zdriHQ6iMA",
  "sid": "c9410ee8f27b69c32e43d5ac3d407f37",
  "sub": "e80fb854-cab2-4381-8057-19de0fea73f4",
  "auth_time": 1508757008,
  "idp": "local",
  "amr": [
    "pwd"
  ]
}

更新 2 如果有帮助,这是 idsrv 上的客户端配置

new Client
{
    ClientId = "mvc.portal",
    ClientName = "Customer Portal",
    ClientUri = customerPortalBaseUri,

    ClientSecrets =
    {
        new Secret("21f51463-f436-4a84-92ce-1b520dd63a81".Sha256())
    },

    AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
    AllowAccessTokensViaBrowser = false,

    RedirectUris = { $"{customerPortalBaseUri}/signin-oidc"},
    FrontChannelLogoutUri = $"{customerPortalBaseUri}/signout-oidc",
    PostLogoutRedirectUris = { $"{customerPortalBaseUri}/signout-callback-oidc" },

    AllowOfflineAccess = true,

    RequireConsent = false,

    AllowedScopes =
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        IdentityServerConstants.StandardScopes.Email
    }
}

这是客户端/门户配置

services.AddAuthentication(options =>
    {
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = "oidc";
    })
    .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddOpenIdConnect("oidc", options =>
    {
        options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.Authority = "http://myproxiedlogonsitebehindnginx.co.uk";
        options.RequireHttpsMetadata = false;
        options.ClientId = "mvc.portal";
        options.ClientSecret = "21f51463-f436-4a84-92ce-1b520dd63a81";
        options.ResponseType = "code id_token";
        options.SaveTokens = true;
        options.GetClaimsFromUserInfoEndpoint = true;
    });

更新 3 所以现在我确信它与部署有关,因为如果我在本地机器上运行 mvc 应用程序但在容器中使用部署的 idsvr(在 nginx 后面),我可以毫无问题地进行身份验证,但是如果我尝试容器化门户的版本我仍然得到一个未处理的 500 没有被记录,然后如果我重试该操作,我会得到这个记录:

[11:22:51 INF] Request starting HTTP/1.1 POST http://mvcportal.co.uk/signin-oidc application/x-www-form-urlencoded 1559
[11:22:51 ERR] Message contains error: 'invalid_grant', error_description: 'error_description is null', error_uri: 'error_uri is null', status code '400'.
[11:22:51 ERR] Exception occurred while processing message.
Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectProtocolException: Message contains error: 'invalid_grant'], error_description: 'error_description is null', error_uri: 'error_uri is null'.

【问题讨论】:

  • 您能分享一下客户端配置以及客户端是如何连接到 IdentityServer4 的吗?
  • 当然。我已经更新了帖子
  • 两个版本都在看同一个数据库吗?如果不是,可能是配置有错误。
  • IdSvr 应用程序有它自己的数据库,但 MVC 客户端不需要查看它吗?
  • 您是如何从 Update3 中解决问题的?

标签: asp.net-identity identityserver4


【解决方案1】:

我遇到了同样的问题并通过添加解决了它:

.AddAspNetIdentity<ApplicationUser>();

services.AddIdentityServer()

在 Startup.cs 中

【讨论】:

    【解决方案2】:

    所以我终于明白了。似乎 nginx 中的默认标头限制效果不佳,我在日志中发现了这一点 upstream sent too big header while reading response header from upstream

    更新 nginx 配置以包含这些行

    proxy_buffer_size          128k;
    proxy_buffers              4 256k;
    proxy_busy_buffers_size    256k;
    

    防止了 502 错误,现在一切正常。

    【讨论】:

      【解决方案3】:

      感谢您的回复亲爱的丹, 另外,我得到了这个错误,我已经像这样解决了这个问题

      在 Startup.cs confgiureservices 中找到这一行

      services.AddIdentity<ApplicationUser, IdentityRole>()
      .AddEntityFrameworkStores()
      .AddIdentityServer()
      .AddDefaultTokenProviders();
      

      确保.AddIdentityServer() 在那里。

      【讨论】:

      • 我觉得这个答案是错误的。我在启动时也有这些配置,我的仍然抛出该错误。但是,我使用的是 MongoDb 而不是 EntityFramework。
      【解决方案4】:
              services.AddIdentityServer()
                    .AddAspNetIdentity<ApplicationUser>()
                    .AddConfigurationStore(options =>
                    {
                        options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString,
                            sqlServerOptionsAction: sqlOptions =>
                            {
                                sqlOptions.MigrationsAssembly(migrationsAssembly);
                            });
                    })
                   .AddOperationalStore(options =>
                   {
                       options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString,
                           sqlServerOptionsAction: sqlOptions =>
                           {
                               sqlOptions.MigrationsAssembly(migrationsAssembly);
                           });
                   }).Services.AddTransient<IProfileService, ProfileService>();
      

      ProfileService.cs

      public class ProfileService : IProfileService
       {
           private readonly UserManager<ApplicationUser> _userManager;
      
           public ProfileService(UserManager<ApplicationUser> userManager)
           {
               _userManager = userManager;
           }
      
           async public Task GetProfileDataAsync(ProfileDataRequestContext context)
           {
               var subject = context.Subject ?? throw new ArgumentNullException(nameof(context.Subject));
      
               var subjectId = subject.Claims.Where(x => x.Type == "sub").FirstOrDefault().Value;
      
               var user = await _userManager.FindByIdAsync(subjectId);
               if (user == null)
                   throw new ArgumentException("Invalid subject identifier");
      
               var claims = GetClaimsFromUser(user);
               context.IssuedClaims = claims.ToList();
           }
      
           async public Task IsActiveAsync(IsActiveContext context)
           {
               var subject = context.Subject ?? throw new ArgumentNullException(nameof(context.Subject));
      
               var subjectId = subject.Claims.Where(x => x.Type == "sub").FirstOrDefault().Value;
               var user = await _userManager.FindByIdAsync(subjectId);
      
               context.IsActive = false;
      
               if (user != null)
               {
                   if (_userManager.SupportsUserSecurityStamp)
                   {
                       var security_stamp = subject.Claims.Where(c => c.Type == "security_stamp").Select(c => c.Value).SingleOrDefault();
                       if (security_stamp != null)
                       {
                           var db_security_stamp = await _userManager.GetSecurityStampAsync(user);
                           if (db_security_stamp != security_stamp)
                               return;
                       }
                   }
      
                   context.IsActive =
                       !user.LockoutEnabled ||
                       !user.LockoutEnd.HasValue ||
                       user.LockoutEnd <= DateTime.Now;
               }
           }
      
           private IEnumerable<Claim> GetClaimsFromUser(ApplicationUser user)
           {
               var claims = new List<Claim>
               {
                   new Claim(JwtClaimTypes.Subject, user.Id),
                   new Claim(JwtClaimTypes.PreferredUserName, user.UserName),
                   new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName)
               };
      
               if (!string.IsNullOrWhiteSpace(user.Name))
                   claims.Add(new Claim("name", user.Name));
      
               if (!string.IsNullOrWhiteSpace(user.LastName))
                   claims.Add(new Claim("last_name", user.LastName));
      
      
               if (_userManager.SupportsUserEmail)
               {
                   claims.AddRange(new[]
                   {
                       new Claim(JwtClaimTypes.Email, user.Email),
                       new Claim(JwtClaimTypes.EmailVerified, user.EmailConfirmed ? "true" : "false", ClaimValueTypes.Boolean)
                   });
               }
      
               if (_userManager.SupportsUserPhoneNumber && !string.IsNullOrWhiteSpace(user.PhoneNumber))
               {
                   claims.AddRange(new[]
                   {
                       new Claim(JwtClaimTypes.PhoneNumber, user.PhoneNumber),
                       new Claim(JwtClaimTypes.PhoneNumberVerified, user.PhoneNumberConfirmed ? "true" : "false", ClaimValueTypes.Boolean)
                   });
               }
      
               return claims;
           }
       }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-04-08
        • 1970-01-01
        • 2019-09-30
        • 1970-01-01
        • 1970-01-01
        • 2015-07-07
        • 1970-01-01
        相关资源
        最近更新 更多