努力使身份服务器4使用具有多个Web客户端的JWT令牌进行身份验证

问题描述 投票:0回答:1

我们在我们的Web应用程序中使用oidc-client客户端,并且zupa.apps.market.web应用程序正常运行,并且可以获取可以在我们的身份验证后端进行验证的jwt令牌,但是zupa.apps.admin始终会失败,并收到令牌。使用oidc客户端时,我们会收到一个令牌。管理应用程序中收到的令牌接收到正确的范围和受众,但始终无法验证。

Startup.cs

using System;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Cosmos.Table;
using Microsoft.Azure.ServiceBus;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Zupa.Authentication.AuthService.AppInsights;
using Zupa.Authentication.AuthService.Configuration;
using Zupa.Authentication.AuthService.Data;
using Zupa.Authentication.AuthService.EmailSending;
using Zupa.Authentication.AuthService.Models.Account.Entities;
using Zupa.Authentication.AuthService.Models.Entity;
using Zupa.Authentication.AuthService.Security;
using Zupa.Authentication.AuthService.Services.FailedLoginAttempts;
using Zupa.Authentication.AuthService.Services.Password;
using Zupa.Authentication.AuthService.Services.Registration;
using Zupa.Authentication.AuthService.Validators.Password;
using Zupa.Authentication.AuthService.Validators.User;
using Zupa.Authentication.Common;
using Zupa.Authentication.Common.Data;
using Zupa.Authentication.Common.Data.Migrations.IdentityServer.ApplicationDb;
using Zupa.Libraries.CosmosTableStorageClient;
using Zupa.Libraries.ServiceBus.ServiceBusClient;
using Zupa.Libraries.ServiceBus.ServiceBusClient.Configuration;

namespace Zupa.Authentication.AuthService
{
    public class Startup
    {
        private static readonly string CorsPolicy = nameof(CorsPolicy);
        public IConfiguration Configuration { get; }
        public IHostingEnvironment CurrentEnv { get; }

        public Startup(IHostingEnvironment env, IConfiguration configuration)
        {
            Configuration = configuration;
            CurrentEnv = env;
        }

        public void ConfigureServices(IServiceCollection services)
        {
            var databaseConnectionString = Configuration.GetValue<string>("Authentication:DatabaseConnectionStrings:DefaultConnection");

            if (CurrentEnv.IsEnvironment("Testing"))
            {
                services.AddDbContext<ApplicationDbContext>(options =>
                options.UseInMemoryDatabase("TestDb"));
            }
            else
            {
                services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(databaseConnectionString));
            }

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
            services.AddAuthorization(options =>
            {
                options.AddPolicy("apipolicy", policy =>
                {
                    policy.RequireAuthenticatedUser();
                    policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
                });
            });
            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            })
            .AddCookie()
            .AddJwtBearer(options =>
            {
                var identityServerConfiguration = Configuration
                    .GetSection("Authentication:IdentityServer")
                    .Get<IdentityServerConfiguration>();

                options.Audience = identityServerConfiguration.Audience;
                options.Authority = identityServerConfiguration.Authority;
                options.RequireHttpsMetadata = CurrentEnv.IsProduction();
            });

            services.AddApplicationInsightsTelemetry(Configuration);

            services.Configure<LockoutSettings>(options => Configuration.GetSection("Authentication:LockoutRules").Bind(options));
            services.Configure<AppInsightsSettings>(options => Configuration.GetSection("Authentication:ApplicationInsights").Bind(options));
            services.Configure<TransactionalTemplateConfiguration>(options => Configuration.GetSection("Authentication:TransactionalTemplates").Bind(options));
            services.Configure<MessageConstants>(options => Configuration.GetSection("Authentication:MessageConstants").Bind(options));
            services.Configure<BlacklistedPasswordsSettings>(options => Configuration.GetSection("Authentication:PasswordRules:BlacklistedPasswords").Bind(options));
            services.Configure<PasswordRulesSettings>(options => Configuration.GetSection("Authentication:PasswordRules").Bind(options));
            services.Configure<PasswordHashingConfiguration>(options => Configuration.GetSection("Authentication:PasswordHashing").Bind(options));
            services.Configure<FailedLoginAttemptsTableStorageConfiguration>(options => Configuration.GetSection("Authentication:FailedLoginAttemptsTableStorageConfiguration").Bind(options));
            services.Configure<FailedLoginAttemptsSettings>(options => Configuration.GetSection("Authentication:FailedLoginAttemptsSettings").Bind(options));

            var failedLoginAttemptsConfiguration = Configuration
                .GetSection("Authentication:FailedLoginAttemptsTableStorageConfiguration")
                .Get<FailedLoginAttemptsTableStorageConfiguration>();

            services.AddSingleton<ICosmosTableStorageClient<FailedAttemptEntity>>
            (provider => new CosmosTableStorageClient<FailedAttemptEntity>(
                new CosmosTableFactory(),
                failedLoginAttemptsConfiguration.Connection, failedLoginAttemptsConfiguration.TableName));
            services.AddTransient<ICosmosTableCommand<FailedAttemptEntity>, CosmosTableCommand<FailedAttemptEntity>>();
            services.AddTransient<ICosmosTableQuery<TableQuery<FailedAttemptEntity>>, CosmosTableQuery<FailedAttemptEntity>>();

            services.AddTransient<IFailedLoginAttemptsService, FailedLoginAttemptsService>();
            services.AddTransient<IFailedLoginAttemptsRepository, FailedLoginAttemptsRepository>();

            var whitelistTableStorageSettings = Configuration.GetSection("Authentication:WhitelistTableStorageSettings")
                .Get<WhitelistTableStorageSettings>();

            services.AddSingleton<ICosmosTableStorageClient<WhitelistEntity>>(
                provider => new CosmosTableStorageClient<WhitelistEntity>(
                    new CosmosTableFactory(),
                    whitelistTableStorageSettings.Connection,
                    whitelistTableStorageSettings.TableName));
            services.AddTransient<ICosmosTableQuery<TableQuery<WhitelistEntity>>, CosmosTableQuery<WhitelistEntity>>();

            services.AddTransient<IWhitelistRepository, WhitelistRepository>();
            services.AddTransient<ISendEmail, SendGridEmailSender>();
            services.AddTransient<IRegisterService, RegisterService>();
            services.AddSingleton<ITrackTelemetry, TrackTelemetry>();
            services.TryAddSingleton<IServiceBusClientFactory, ServiceBusClientFactory>();
            services.AddScoped<IDatabaseInitialiser, DatabaseInitialiser>();
            services.AddSingleton(Configuration);
            services.AddScoped<IPasswordHasher<IdentityUser>, ArgonHash<IdentityUser>>();

            var authenticationTopicClientSettings = Configuration.GetSection("Authentication:AuthenticationTopicClientSettings")
               .Get<TopicClientConfiguration>();

            services.TryAddTransient<IServiceBusClient<ITopicClient>>(provider =>
            {
                var factory = provider.GetRequiredService<IServiceBusClientFactory>();
                var telemetryTracker = provider.GetRequiredService<ITrackTelemetry>();
                return factory.GetClient(authenticationTopicClientSettings, telemetryTracker.TrackException);
            });

            services.AddHttpClient<IPwnedPasswordsService, PwnedPasswordsService>();
            services.AddIdentity<IdentityUser, IdentityRole>(options =>
                {
                    options.Password.RequireDigit = false;
                    options.Password.RequireLowercase = false;
                    options.Password.RequireUppercase = false;
                    options.Password.RequireNonAlphanumeric = false;
                    options.Password.RequiredLength = Configuration.GetValue<int>("Authentication:PasswordRules:RequiredLength");

                    options.Lockout.MaxFailedAccessAttempts = Configuration.GetValue<int>("Authentication:LockoutRules:MaxFailedAccessAttempts");
                    options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(Configuration.GetValue<int>("Authentication:LockoutRules:DefaultLockoutTimeSpan"));

                    options.SignIn.RequireConfirmedEmail = Configuration.GetValue<bool>("Authentication:EmailRequirements:RequireConfirmedEmail");
                    options.User.RequireUniqueEmail = Configuration.GetValue<bool>("Authentication:EmailRequirements:RequireUniqueEmail");
                })
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders()
                .AddPasswordValidator<BlacklistedPasswordValidator<IdentityUser>>()
                .AddPasswordValidator<MaxLengthPasswordValidator<IdentityUser>>()
                .AddPasswordValidator<ExpectedInformationPasswordValidator<IdentityUser>>()
                .AddUserValidator<WhitelistedEmailValidator<IdentityUser>>();

            var migrationsAssembly = typeof(RoleConstants).GetTypeInfo().Assembly.GetName().Name;

            services
                .AddIdentityServer()
                .AddDeveloperSigningCredential(CurrentEnv.IsDevelopment())
                .AddAspNetIdentity<IdentityUser>()
                .AddConfigurationStore(options =>
                {
                    if (CurrentEnv.IsEnvironment("Testing"))
                    {
                        options.ConfigureDbContext = builder =>
                            builder.UseInMemoryDatabase("TestDb");
                    }
                    else
                    {
                        options.ConfigureDbContext = builder =>
                            builder.UseSqlServer(databaseConnectionString,
                                sql => sql.MigrationsAssembly(migrationsAssembly));
                    }
                })
                .AddOperationalStore(options =>
                {
                    if (CurrentEnv.IsEnvironment("Testing"))
                    {
                        options.ConfigureDbContext = builder =>
                            builder.UseInMemoryDatabase("TestDb");
                    }
                    else
                    {
                        options.ConfigureDbContext = builder =>
                            builder.UseSqlServer(databaseConnectionString,
                                sql => sql.MigrationsAssembly(migrationsAssembly));
                    }
                    options.EnableTokenCleanup = Configuration.GetValue<bool>("Authentication:TokenCleanup:EnableTokenCleanup");
                    options.TokenCleanupInterval = TimeSpan.FromSeconds(Configuration.GetValue<int>("Authentication:TokenCleanup:TokenCleanupInterval")).Seconds;
                });

            var allowedOrigins = Configuration
                .GetSection("Authentication:Cors")
                .GetValue<string>("AllowedOrigins")
                .Split(";")
                .Select(o => o.TrimEnd('/'))
                .ToArray();

            services.AddCors(options =>
            {
                options.AddPolicy(CorsPolicy,
                    policy => policy.WithOrigins(allowedOrigins)
                        .AllowAnyMethod()
                        .AllowAnyHeader());
            });
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, UserManager<IdentityUser> userManager, IDatabaseInitialiser databaseInitialiser)
        {
            app.UseHsts();

            databaseInitialiser.Initialise(app, env);
            app.UseMiddleware<AppInsightsRequestTrackingMiddleware>();

            app.UseIdentityServer()
                .UseHttpsRedirection()
                .UseAuthentication()
                .UseCors(CorsPolicy)
                .UseStaticFiles()
                .UseMvc(routes =>
                {
                    routes.MapRoute(
                        name: "default",
                        template: "{controller=Account}/{action=Login}/{id?}");
                });
        }
    }
}

DbInitialiser.cs包含两个Web客户端

                new Client
                {
                    ClientId = "Zupa.Apps.Admin",
                    ClientName = "Zupa Apps Admin",
                    UpdateAccessTokenClaimsOnRefresh = true,
                    AllowedGrantTypes = GrantTypes.Implicit,
                    AllowAccessTokensViaBrowser = true,
                    RequireConsent = false,
                    RedirectUris = {"http://localhost:1337/callback.html", "http://localhost:1337/silent-refresh.html"},
                    PostLogoutRedirectUris = {"http://localhost:1337/index.html"},
                    AllowedCorsOrigins = {"http://localhost:1337"},
                    AllowedScopes = new List<string>
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                        IdentityServerConstants.StandardScopes.Email,
                        "Zupa.Authentication",
                        "Zupa.Organisations",
                        "Zupa.Contacts",
                    }
                },
                new Client
                {
                    ClientId = "Zupa.Apps.Market.Web",
                    ClientName = "Zupa Apps Market Web",
                    UpdateAccessTokenClaimsOnRefresh = true,
                    AllowedGrantTypes = GrantTypes.Implicit,
                    AllowAccessTokensViaBrowser = true,
                    RequireConsent = false,
                    RedirectUris = {"http://localhost:55435/callback.html", "http://localhost:55435/silent-refresh.html"},
                    PostLogoutRedirectUris = {"http://localhost:55435/index.html"},
                    AllowedCorsOrigins = {"http://localhost:55435"},
                    AllowedScopes = new List<string>
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                        IdentityServerConstants.StandardScopes.Email,
                        "Zupa.Organisations",
                        "Zupa.Contacts",
                        "Zupa.Recipes",
                        "Zupa.Products",
                        "Zupa.Media",
                        "Zupa.Orders",
                        "Zupa.Stock",
                        "Zupa.Safe",
                        "Zupa.Occasions",
                        "Zupa.Groups",
                        "Zupa.Messaging",
                        "Zupa.Authentication",
                        "Zupa.Agreements",
                        "Zupa.ProductCategories",
                        "Zupa.Notifications.NotificationsService"
                    }
                },

验证错误如下

{
  "name": "Microsoft.ApplicationInsights.Exception",
  "time": "2019-12-07T19:11:47.7093552Z",
  "tags": {
    "ai.application.ver": "1.25.2.0",
    "ai.cloud.roleInstance": "ZUPLTCMA01",
    "ai.operation.parentId": "|16a57b38-4a0d3e8c68664495.",
    "ai.internal.nodeName": "ZUPLTCMA01",
    "ai.internal.sdkVersion": "aspnet5c:2.1.1",
    "ai.operation.id": "16a57b38-4a0d3e8c68664495",
    "ai.location.ip": "127.0.0.1",
    "ai.operation.name": "GET User/FindClaimsByUserId [userId]"
  },
  "data": {
    "baseType": "ExceptionData",
    "baseData": {
      "ver": 2,
      "properties": {
        "AspNetCoreEnvironment": "Development",
        "Exception": "Microsoft.IdentityModel.Tokens.SecurityTokenExpiredException: IDX10223: Lifetime validation failed. The token is expired. ValidTo: '[PII is hidden]', Current time: '[PII is hidden]'.\r\n   at Microsoft.IdentityModel.Tokens.Validators.ValidateLifetime(Nullable`1 notBefore, Nullable`1 expires, SecurityToken securityToken, TokenValidationParameters validationParameters) in C:\\agent2\\_work\\15\\s\\src\\Microsoft.IdentityModel.Tokens\\Validators.cs:line 269\r\n   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateLifetime(Nullable`1 notBefore, Nullable`1 expires, JwtSecurityToken jwtToken, TokenValidationParameters validationParameters) in C:\\agent2\\_work\\15\\s\\src\\System.IdentityModel.Tokens.Jwt\\JwtSecurityTokenHandler.cs:line 1259\r\n   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateTokenPayload(JwtSecurityToken jwtToken, TokenValidationParameters validationParameters) in C:\\agent2\\_work\\15\\s\\src\\System.IdentityModel.Tokens.Jwt\\JwtSecurityTokenHandler.cs:line 785\r\n   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken) in C:\\agent2\\_work\\15\\s\\src\\System.IdentityModel.Tokens.Jwt\\JwtSecurityTokenHandler.cs:line 769\r\n   at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync()",
        "CategoryName": "Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler",
        "{OriginalFormat}": "Failed to validate the token."
      },
      "exceptions": [
        {
          "id": 41633851,
          "typeName": "Microsoft.IdentityModel.Tokens.SecurityTokenExpiredException",
          "message": "Failed to validate the token.",
          "hasFullStack": true,
          "parsedStack": [
            {
              "level": 0,
              "method": "Microsoft.IdentityModel.Tokens.Validators.ValidateLifetime",
              "assembly": "Microsoft.IdentityModel.Tokens, Version=5.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
              "fileName": "C:\\agent2\\_work\\15\\s\\src\\Microsoft.IdentityModel.Tokens\\Validators.cs",
              "line": 269
            },
            {
              "level": 1,
              "method": "System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateLifetime",
              "assembly": "System.IdentityModel.Tokens.Jwt, Version=5.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
              "fileName": "C:\\agent2\\_work\\15\\s\\src\\System.IdentityModel.Tokens.Jwt\\JwtSecurityTokenHandler.cs",
              "line": 1259
            },
            {
              "level": 2,
              "method": "System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateTokenPayload",
              "assembly": "System.IdentityModel.Tokens.Jwt, Version=5.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
              "fileName": "C:\\agent2\\_work\\15\\s\\src\\System.IdentityModel.Tokens.Jwt\\JwtSecurityTokenHandler.cs",
              "line": 785
            },
            {
              "level": 3,
              "method": "System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken",
              "assembly": "System.IdentityModel.Tokens.Jwt, Version=5.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
              "fileName": "C:\\agent2\\_work\\15\\s\\src\\System.IdentityModel.Tokens.Jwt\\JwtSecurityTokenHandler.cs",
              "line": 769
            },
            {
              "level": 4,
              "method": "Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler+<HandleAuthenticateAsync>d__6.MoveNext",
              "assembly": "Microsoft.AspNetCore.Authentication.JwtBearer, Version=2.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60"
            }
          ]
        }
      ],
      "severityLevel": "Information"
    }
  }
}

并且管理客户端收到的jwt令牌如下

{
  "nbf": 1575745821,
  "exp": 1575749421,
  "iss": "http://localhost:2662",
  "aud": [
    "http://localhost:2662/resources",
    "Zupa.Organisations",
    "Zupa.Contacts",
    "Zupa.Authentication"
  ],
  "client_id": "Zupa.Apps.Admin",
  "sub": "513D4B88-D066-41F9-8F58-8142BDE8B828",
  "auth_time": 1575745521,
  "idp": "local",
  "scope": [
    "openid",
    "profile",
    "Zupa.Organisations",
    "Zupa.Contacts",
    "Zupa.Authentication"
  ],
  "amr": [
    "pwd"
  ]
}

对此的任何帮助将是巨大的,因为一切看起来都应该正常工作,但是由于事物的到期时间,令牌永远无法验证。

期待您的回音,克里斯。

c# security jwt identityserver4 openid-connect
1个回答
0
投票

似乎是最初从范围中丢失的是缺少的Zupa.Authentication范围。这是在发布此评论之前几分钟添加的,显然需要重新购买机器才能清除旧的缓存或其他内容,但似乎可以使用身份验证api进行预期的身份验证]

© www.soinside.com 2019 - 2024. All rights reserved.