我们在我们的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"
]
}
对此的任何帮助将是巨大的,因为一切看起来都应该正常工作,但是由于事物的到期时间,令牌永远无法验证。
期待您的回音,克里斯。
似乎是最初从范围中丢失的是缺少的Zupa.Authentication范围。这是在发布此评论之前几分钟添加的,显然需要重新购买机器才能清除旧的缓存或其他内容,但似乎可以使用身份验证api进行预期的身份验证]