I have two different clients with diffrent domains. And i have identity server hosting different domains. Like this;
client1.com, client2.com (client domains) , auth.client1.com (identity server domain).
Silent renew works on client1.com. But on client2.com when try to silent renew it's gives an error and whole page become empty.
Error is;
"Error: AuthCallback AuthResult came with error: login_required".
Am i missing something?
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.ConfigureNonBreakingSameSiteCookies();
services.AddControllersWithViews();
string migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
string connectionString = "<connectionString>"
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
var builder = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
})
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = b => b.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
})
.AddOperationalStore(options =>
{
options.ConfigureDbContext = b => b.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
options.EnableTokenCleanup = true;
}).AddAspNetIdentity<ApplicationUser>(); ;
string cerFileName = "<cerFileName>";
string cerPassword = "<cerPassword>"
if (Environment.IsDevelopment())
{
string fileName = Path.Combine(Environment.ContentRootPath, "tempkey.rsa");
builder.AddDeveloperSigningCredential(filename: fileName);
}
else
{
// string fileName = Path.Combine(Environment.ContentRootPath, "<file>");
if (!File.Exists(cerFileName))
{
throw new FileNotFoundException("Signing Certificate is missing!");
}
X509Certificate2 cert = new X509Certificate2(cerFileName, cerPassword, X509KeyStorageFlags.MachineKeySet);
//X509Certificate2 cert = new X509Certificate2(fileName, "", X509KeyStorageFlags.MachineKeySet);
builder.AddSigningCredential(cert);
Console.WriteLine("###Certificate is done");
}
string authorityUrl = Configuration.GetValue<string>("<AuthorityUrl>");
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, jwt =>
{
jwt.Authority = authorityUrl;
jwt.TokenValidationParameters = new TokenValidationParameters()
{
ValidateAudience = false,
};
jwt.RequireHttpsMetadata = false;
});
// Add application services.
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
services.Configure<EmailSettings>(Configuration.GetSection("EmailSettings"));
}
public void Configure(IApplicationBuilder app)
{
using (var scope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
SeedData.EnsureSeedData(scope.ServiceProvider, Configuration);
}
app.UseCookiePolicy();
if (Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
///app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
Related
I need to add custom middleware between authentication and authorization that will add ClaimsIdentity to User that in context.
public class PermissionsMiddleware
{
private readonly RequestDelegate _next;
public PermissionsMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context, IAccountService accountService)
{
if (context.User.Identity == null || !context.User.Identity.IsAuthenticated)
{
await _next(context);
return;
}
var userSub = context.User.FindFirst(ClaimConstants.Subject)?.Value;
if (string.IsNullOrEmpty(userSub))
{
context.Response.StatusCode = 401;
await context.Response.WriteAsync("User 'sub' claim is required");
return;
}
var permissionsIdentity = await accountService.GetUserPermissionsIdentity(userSub);
if (permissionsIdentity == null)
{
context.Response.StatusCode = 401;
return;
}
context.User.AddIdentity(permissionsIdentity);
await _next(context);
}
}
But context.User.Identity.IsAuthenticated is always false.
My ConfigureServicesmethod
string connectionString = Configuration["ConnectionStrings:DefaultConnection"];
string migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
services.AddCoreServices();
services.AddControllers();
services.AddDbContext<AppDbContext>(
options => options.UseMySql(
connectionString,
ServerVersion.AutoDetect(connectionString),
b => b.MigrationsAssembly(migrationsAssembly))
.UseSnakeCaseNamingConvention());
services.AddIdentity<ApplicationUser, ApplicationRole>().AddEntityFrameworkStores<AppDbContext>().AddDefaultTokenProviders();
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
services.AddScoped<IUnitOfWork, UnitOfWork>();
services.AddScoped<IEmailSender, EmailSender>();
services.AddScoped<IUnitOfWork, HttpUnitOfWork>();
services.AddScoped<IAccountManager, AccountManager>();
services.AddTransient<IDatabaseInitializer, AppDbInitializer>();
services.AddScoped<IGrantValidationService, DelegationGrantValidationService>();
services.Configure<AppSettings>(Configuration);
services.Configure<IdentityOptions>(options =>
{
options.User.RequireUniqueEmail = false;
options.SignIn.RequireConfirmedEmail = false;
options.Password.RequireDigit = false;
options.Password.RequiredLength = 1;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequireLowercase = false;
});
var applicationUrl = Configuration["ApplicationUrl"].TrimEnd('/');
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = builder => builder.UseMySql(
connectionString,
ServerVersion.AutoDetect(connectionString),
b => b.MigrationsAssembly(migrationsAssembly))
.UseSnakeCaseNamingConvention();
})
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder => builder.UseMySql(
connectionString,
ServerVersion.AutoDetect(connectionString),
b => b.MigrationsAssembly(migrationsAssembly))
.UseSnakeCaseNamingConvention();
options.EnableTokenCleanup = true;
options.TokenCleanupInterval = 30;
})
.AddAspNetIdentity<ApplicationUser>()
.AddProfileService<ProfileService<ApplicationUser>>()
.AddDelegationGrant<ApplicationUser, string>();
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
options.Authority = applicationUrl;
options.SupportedTokens = SupportedTokens.Jwt;
options.RequireHttpsMetadata = false;
options.ApiName = IdentityServerConfig.ApiName;
});
//services.AddSingleton<IAuthorizationHandler, ViewUserAuthorizationHandler>();
//services.AddSingleton<IAuthorizationHandler, CreateUserAuthorizationHandler>();
//services.AddSingleton<IAuthorizationHandler, EditUserAuthorizationHandler>();
//services.AddSingleton<IAuthorizationHandler, DeleteUserAuthorizationHandler>();
services.AddSingleton<IAuthorizationHandler, ViewRoleAuthorizationHandler>();
services.AddSingleton<IAuthorizationHandler, AssignRolesAuthorizationHandler>();
services.AddAuthorization(options =>
{
options.AddPolicy(Policies.ViewUsersPolicy, policy => policy.RequireClaim(ClaimConstants.Permission, ApplicationPermissions.ViewUsers));
options.AddPolicy(Policies.CreateUsersPolicy, policy => policy.RequireClaim(ClaimConstants.Permission, ApplicationPermissions.CreateUsers));
options.AddPolicy(Policies.EditUsersPolicy, policy => policy.RequireClaim(ClaimConstants.Permission, ApplicationPermissions.EditUsers));
options.AddPolicy(Policies.DeleteUsersPolicy, policy => policy.RequireClaim(ClaimConstants.Permission, ApplicationPermissions.DeleteUsers));
options.AddPolicy(Policies.ViewAllRolesPolicy, policy => policy.RequireClaim(ClaimConstants.Permission, ApplicationPermissions.ViewRoles));
options.AddPolicy(Policies.ViewRoleByRoleNamePolicy, policy => policy.Requirements.Add(new ViewRoleAuthorizationRequirement()));
options.AddPolicy(Policies.ManageAllRolesPolicy, policy => policy.RequireClaim(ClaimConstants.Permission, ApplicationPermissions.ManageRoles));
options.AddPolicy(Policies.AssignAllowedRolesPolicy, policy => policy.Requirements.Add(new AssignRolesAuthorizationRequirement()));
});
My Configure method
app.UseCors(x => x.SetIsOriginAllowed(origin => true)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
app.UseMiddleware<ErrorHandlerMiddleware>();
//app.UseSwagger();
//app.UseSwaggerUI();
app.UseRouting();
app.UseIdentityServer();
app.UseMiddleware<PermissionsMiddleware>();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
First off, make sure the Authority and ApiName are the same in the TOKEN as in your API. (I think this is case sensitive.) This is something you haven't provided so I cannot verify.
Also 1 line before this line:
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
add the following line:
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
EDIT: Also remove this line in your startup:
app.UseIdentityServer();
This most likely overrides your own settings for identityserver.
I am currently developing an application using Asp.net core 5.0 and Identity server 4.My OIDC authentication flow handled by Microsoft.AspNetCore.Authentication.OpenIdConnect. I deployed my application into IIS and I am getting my login screen. But after login I got the Http 400 Bad request error.Error. I checked my application cookie it contains many AspNetCore.OpenIdConnect.Nonce cookies. I deleted the cookies but doesn't solve my issue. What is the proper solution to handle this solution. I tried this but doesn't help me. I will share the code below
MVC Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
}).SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
services.AddScoped<RenewToken>();
services.AddAuthorization(options =>
{
options.AddPolicy("CreatePolicy", policy => policy.RequireAssertion(context => AuthorizeAccess(context, "RC", "UC")));
options.AddPolicy("ReadPolicy", policy => policy.RequireAssertion(context => AuthorizeAccess(context, "RR", "UR")));
});
services.AddControllersWithViews();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
}).AddCookie("Cookies", options =>
{
options.Cookie.Name = "Cookies";
options.ExpireTimeSpan = TimeSpan.FromMinutes(10);
options.SlidingExpiration = true;
}).AddOpenIdConnect("oidc", options =>
{
options.BackchannelHttpHandler = new HttpClientHandler { ServerCertificateCustomValidationCallback = delegate { return true; } };
options.Authority = Configuration.GetSection("API:IDS4").Value;
options.SignInScheme = "Cookies";
options.SignedOutRedirectUri = Configuration.GetSection("API:WebClient").Value + "/signout-callback-oidc";
options.RequireHttpsMetadata = true;
options.ClientId = "mvc";
options.ClientSecret = "*****";
options.ResponseType = "code";
options.UsePkce = true;
options.Scope.Add("profile");
options.Scope.Add("mcApi");
options.Scope.Add("Api1");
options.Scope.Add("Api2");
options.Scope.Add("Api3");
options.Scope.Add("Api4");
options.Scope.Add("Api5");
options.Scope.Add("Api6");
options.Scope.Add("Api7");
options.Scope.Add("offline_access");
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
options.Events.OnRedirectToIdentityProvider = context =>
{
context.ProtocolMessage.Prompt = "login";
return Task.CompletedTask;
};
options.Events = new OpenIdConnectEvents
{
OnRemoteFailure = (context) =>
{
context.Response.Redirect("/");
context.HandleResponse();
return Task.CompletedTask;
}
};
options.TokenValidationParameters = new TokenValidationParameters
{
RoleClaimType = JwtClaimTypes.Role
};
});
services.AddHttpClient<IAdminService, AdminService>();
services.AddSingleton<DataProtectionPurposeStrings>();
services.AddSingleton<GlobalConstants>();
}
private bool AuthorizeAccess(AuthorizationHandlerContext context, string roleClaim, string userClaim)
{
return context.User.HasClaim(claim => claim.Type == roleClaim && claim.Value == "True") &&
context.User.HasClaim(claim => claim.Type == userClaim && claim.Value == "True") ||
context.User.IsInRole("MyAdmin");
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
Identity Server Startup.cs
public class Startup
{
public IWebHostEnvironment Environment { get; }
public IConfiguration Configuration { get; }
public Startup(IWebHostEnvironment environment, IConfiguration configuration)
{
Environment = environment;
Configuration = configuration;
}
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
/****Register asp.net core Identity DBConetexts***/
var idenConnectionString = Configuration["DbContextSettings:IdentityConnectionString"];
var dbPassword = Configuration["DbContextSettings:DbPassword"];
var builder = new NpgsqlConnectionStringBuilder(idenConnectionString)
{
Password = dbPassword
};
services.AddDbContext<IdentityDBContext>(opts => opts.UseNpgsql(builder.ConnectionString));
services.AddIdentity<ApplicationUser, ApplicationRole>(options =>
{
options.Password.RequiredLength = 8;
options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._#+ ";
options.SignIn.RequireConfirmedEmail = false;
options.User.RequireUniqueEmail = false;
}).AddRoles<ApplicationRole>().AddEntityFrameworkStores<IdentityDBContext>()
.AddDefaultTokenProviders();
/****Identity Server implementation with asp.net core Identity***/
var idsServerConnectionString = Configuration["DbContextSettings:IdentityServer4ConnectionString"];
var migrationAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
var idsServerdbPassword = Configuration["DbContextSettings:DbPassword"];
var idsServerbuilder = new NpgsqlConnectionStringBuilder(idsServerConnectionString)
{
Password = dbPassword
};
var idBuilder = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
options.UserInteraction.LoginUrl = "/Account/Login";
options.UserInteraction.LogoutUrl = "/Account/Login";
options.Authentication = new AuthenticationOptions()
{
CookieLifetime = TimeSpan.FromMinutes(10),
CookieSlidingExpiration = true
};
})
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = b => b.UseNpgsql(idsServerbuilder.ConnectionString, sql => sql.MigrationsAssembly(migrationAssembly));
})
.AddOperationalStore(options =>
{
options.ConfigureDbContext = b => b.UseNpgsql(idsServerbuilder.ConnectionString, sql => sql.MigrationsAssembly(migrationAssembly));
options.EnableTokenCleanup = true;
}).AddAspNetIdentity<MembershipUser>()
.AddProfileService<ProfileService>();
X509Certificate2 cert = null;
using (var certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine))
{
certStore.Open(OpenFlags.ReadOnly);
var certCollection = certStore.Certificates.Find(
X509FindType.FindByThumbprint,
"thumbprint",
false);
if (certCollection.Count > 0)
{
cert = certCollection[0];
}
}
if (Environment.IsDevelopment())
{
idBuilder.AddDeveloperSigningCredential();
}
else
{
idBuilder.AddSigningCredential(cert);
}
idBuilder.Services.ConfigureExternalCookie(options =>
{
options.Cookie.IsEssential = true;
options.Cookie.SameSite = (SameSiteMode)(-1);
});
idBuilder.Services.ConfigureApplicationCookie(options =>
{
options.Cookie.IsEssential = true;
options.Cookie.SameSite = (SameSiteMode)(-1);
});
services.AddMediatR(typeof(Startup));
RegisterServices(services);
}
private void RegisterServices(IServiceCollection services)
{
services.AddSingleton<IEventBus, RabbitMQBus>(sp =>
{
var scopeFactory = sp.GetRequiredService<IServiceScopeFactory>();
return new RabbitMQBus(sp.GetService<IMediator>(), scopeFactory);
});
services.AddTransient<UserDBContext>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
if (Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// uncomment if you want to add MVC
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
}
Can I store the cookies into SessionStore using MemoryCacheTicketStore ? Kindly share your thoughts.
A potential thing could be that IIS thinks the cookie header is too long.
By default ASP.NET Core chunks up the cookie in 4Kb chunks, like this picture shows:
So either you try to reduce the size of the cookie or look at the IIS settings, if you can increase the max header length?
Alternatively, you stop saving the tokens inside the cookie, by setting:
options.SaveTokens = false;
Now you of course need top store it somewhere else, like in a tokenstore.
After migrating from .net core 2.2 to .net core 3.1, and after taking some time to fix all the bugs depending on documentations, after running the app, the authorization method get's hit before it requires the user to be authenticated.
I am using OpenId Connect with AD FS for authentication and a custom method called "PermissionGranted" for authorization. And this method get's hit before it requires for the user to be authenticated.
This is the code of my Startup.cs file:
public class Startup
{
private readonly ConfigSettings settings;
private readonly GetSettings configSettings;
private readonly MyMemoryCache myMemoryCache;
public Startup(IWebHostEnvironment env, IConfiguration configuration)
{
configSettings = new GetSettings();
settings = configSettings.getSettings();
Configuration = configuration;
Environment = env;
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
myMemoryCache = new MyMemoryCache();
Log.Logger = new LoggerConfiguration().Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.File(settings.LogFile, rollingInterval: RollingInterval.Day)
.WriteTo.ApplicationInsights(new TelemetryConfiguration(settings.AppInsightsConnection), TelemetryConverter.Events)
.CreateLogger();
}
public IConfiguration Configuration { get; }
private IWebHostEnvironment Environment { get; }
public IServiceProvider ServiceProvider { get; set; }
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(Log.Logger);
services.AddSingleton(settings);
services.AddHttpContextAccessor();
services.AddRouting(options => options.LowercaseUrls = true);
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => false;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies", options =>
{
options.AccessDeniedPath = "/users/denied";
})
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.MetadataAddress = settings.MetadataAddress;
options.ClientId = settings.ClientId;
options.GetClaimsFromUserInfoEndpoint = true;
options.ResponseType = OpenIdConnectResponseType.IdToken;
options.SignedOutRedirectUri = settings.PostLogoutUri;
options.SaveTokens = true;
options.UseTokenLifetime = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = true,
ValidAudience = settings.ClientId,
ValidateIssuer = true,
ValidIssuer = settings.Issuer,
ValidateLifetime = true,
RequireExpirationTime = true
};
options.Events = new OpenIdConnectEvents
{
OnRemoteFailure = context =>
{
context.HandleResponse();
context.Response.Redirect("/Login");
return Task.FromResult(0);
},
OnTicketReceived = context =>
{
// Configuration to make the Authentication cookie persistent for 8 hours (after 8 hours it expires and the user has to re-authenticate)
context.Properties.IsPersistent = true;
context.Properties.ExpiresUtc = DateTimeOffset.UtcNow.AddHours(8);
return Task.FromResult(0);
}
};
options.Events.OnRedirectToIdentityProvider = context =>
{
context.ProtocolMessage.Prompt = "login";
return Task.CompletedTask;
};
});
Audit.Core.Configuration.Setup()
.UseSqlServer(config => config
.ConnectionString(settings.ConnectionString)
.TableName("AuditEvents")
.IdColumnName("EventId")
.JsonColumnName("AuditData")
.LastUpdatedColumnName("LastUpdatedDate"));
services.AddAntiforgery(options => options.HeaderName = "X-CSRF-TOKEN");
services.AddApplicationInsightsTelemetry(settings.AppInsightsConnection);
services.AddMvc(options =>
{
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
options.Filters.Add(new AuthorizeFilter(policy));
})
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
// Use NewtonsoftJson and custom date format
services.AddControllers().AddNewtonsoftJson(options =>
{
options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
});
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(15);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true; // Make the session cookie essential
});
services.AddAuthorization(options =>
{
options.AddPolicy("Permissions", policy => policy.RequireAssertion(context => PermissionGranted(context)));
});
services.AddDbContext<PasswordManagerContext>(options =>
options.UseSqlServer(settings.ConnectionString));
services.AddSingleton<MyMemoryCache>();
services.AddPaging(options =>
{
options.ViewName = "PagerUI";
options.HtmlIndicatorDown = " <span>↓</span>";
options.HtmlIndicatorUp = " <span>↑</span>";
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IAntiforgery antiforgery)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSession();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Login}/{action=Index}/{id?}");
//endpoints.MapFallbackToController("Index", "Home");
});
ServiceProvider = app.ApplicationServices;
}
According to official docs, the order of these:
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints
matters but in my code they are ordered correctly.
What could be missing or what could possibly be wrong in the configuration?
Thanks in advance
I follow the tutorial link below.
https://fullstackmark.com/post/13/jwt-authentication-with-aspnet-core-2-web-api-angular-5-net-core-identity-and-facebook-login
I am trying to understand how it works and I want to use role-based authentication using this token. so I made another policy in the Startup.cs file as below.
And I tried to use it like [Authorize(Policy = "admin")] the controller but every time I try I get unauthenticated using postman.
What am I missing? how to make roles-based authentication based on the tutorial?
Configure
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.WithOrigins("http://localhost:4200", "http://localhost:44318")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
var jwtAppSettingOptions = Configuration.GetSection(nameof(JwtIssuerOptions));
// Configure JwtIssuerOptions
services.Configure<JwtIssuerOptions>(options =>
{
options.Issuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
options.Audience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)];
options.SigningCredentials = new SigningCredentials(_signingKey, SecurityAlgorithms.HmacSha256);
});
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)],
ValidateAudience = true,
ValidAudience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)],
ValidateIssuerSigningKey = true,
IssuerSigningKey = _signingKey,
RequireExpirationTime = false,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(configureOptions =>
{
configureOptions.ClaimsIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
configureOptions.TokenValidationParameters = tokenValidationParameters;
configureOptions.SaveToken = true;
});
// api user claim policy
services.AddAuthorization(options =>
{
options.AddPolicy("ApiUser", policy => policy.RequireClaim(Constants.Strings.JwtClaimIdentifiers.Rol, Constants.Strings.JwtClaims.ApiAccess));
});
services.AddAuthorization(options =>
options.AddPolicy("admin", policy => policy.RequireRole("admin"))
);
var builder = services.AddIdentityCore<AppUser>(o =>
{
// configure identity options
o.Password.RequireDigit = false;
o.Password.RequireLowercase = false;
o.Password.RequireUppercase = false;
o.Password.RequireNonAlphanumeric = false;
o.Password.RequiredLength = 6;
});
builder = new IdentityBuilder(builder.UserType, typeof(IdentityRole), builder.Services);
builder.AddEntityFrameworkStores<CDSPORTALContext>().AddDefaultTokenProviders().AddRoles<IdentityRole>();
//.AddRoles<IdentityRole>()
services.AddControllers();
services.AddAutoMapper(typeof(Startup));
services.AddSingleton<IJwtFactory, JwtFactory>();
services.TryAddTransient<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<IRegionRepository, RegionRepository>();
services.AddScoped<IRegionService, RegionService>();
services.AddScoped<IEmailHelper, EmailHelper>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseCors("CorsPolicy");
app.UseExceptionHandler(
builder =>
{
builder.Run(
async context =>
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
var error = context.Features.Get<IExceptionHandlerFeature>();
if (error != null)
{
context.Response.AddApplicationError(error.Error.Message);
await context.Response.WriteAsync(error.Error.Message).ConfigureAwait(false);
}
});
});
app.UseRouting();
app.UseStaticFiles();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "Client";
spa.UseAngularCliServer(npmScript: "start");
});
}
}
Auth Controller
// POST api/auth/login
[HttpPost("login")]
public async Task<IActionResult> Post([FromBody]CredentialsViewModel credentials)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var identity = await GetClaimsIdentity(credentials.UserName, credentials.Password);
if (identity == null)
{
//return null;
return BadRequest(Error.AddErrorToModelState("login_failure", "Invalid username or password.", ModelState));
}
var id = identity.Claims.Single(c => c.Type == "id").Value;
var user = await _userManager.FindByIdAsync(id);
IList<string> role = await _userManager.GetRolesAsync(user);
var jwt = await Tokens.GenerateJwt(identity, role[0], _jwtFactory, credentials.UserName, _jwtOptions, new JsonSerializerSettings { Formatting = Formatting.Indented });
return new OkObjectResult(jwt);
}
I tried with all method and none of them working
[Authorize(Policy = "ApiUser")]
[HttpGet("getPolicy")]
public string GetPolicy()
{
return "policyWorking";
}
[Authorize(Roles = "admin")]
[HttpGet("getAdmin")]
public string GetAdmin()
{
return "adminWorking";
}
[Authorize ]
[HttpGet("getAuthorize")]
public string GetAuthorize()
{
return "normal authorize Working";
}
Decoded Token
"jti": "840d507d-b2d0-454b-bd1f-007890d3e669",
"iat": 1587699300,
"rol": "api_access",
"id": "1ac370e2-f5e9-4404-b017-8a3c087e2196",
"nbf": 1587699299,
"exp": 1587706499
I forgot to add this to appsetting.
"JwtIssuerOptions": {
"Issuer": "webApi",
"Audience": "http://localhost:4200/"
}
I encounter this error in my API IDX10214: Audience validation failed. Audiences: 'https://localhost:44337/resources'. Did not match: validationParameters.ValidAudience: 'joborderingapi' or validationParameters.ValidAudiences: 'null'
I've been trying to solve this for 2 days already and can't figure out yet about how to solve it.
I have the following applications:
Client App (Angular 7)
Identity Server
API
I was able to login successfully to Identity Server in my Client app and was able to get the token but when I used the token to connect to the API method it throws this error IDX10214: Audience validation failed. Audiences: 'https://localhost:44337/resources'. Did not match: validationParameters.ValidAudience: 'joborderingapi' or validationParameters.ValidAudiences: 'null'.
I followed the answer from Identity Server 4 with EF Identity DB Issue and checked the three tables (ApiResources, ApiScopes, ClientScopes), the values are correct, joborderingapiis enabled in ApiResources, in ApiScopes it is linked to ApiResource and in ClientScopes it is linked to the Client
Here is my API Startup.cs
public void ConfigureServices(IServiceCollection services)
{
var apiIdpAuthority = Configuration["AppSettings:IdpAuthority"];
var apiName = Configuration["AppSettings:ApiName"];
var apiSecret = Configuration["AppSettings:ApiSecret"];
var requireHttps = Convert.ToBoolean(Configuration["AppSettings:RequireHttps"]);
var httpsPort = Configuration["AppSettings:HttpsPort"];
var applicationUrl = Configuration["AppSettings:ApplicationUrl"];
services.Configure<ClientAppSettings>(Configuration.GetSection("ClientAppSettings"));
services.AddDbContext<JobOrderingDataContext>(options => options.UseSqlServer(Configuration.GetConnectionString("JobOrderingDB")));
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
IdentityModelEventSource.ShowPII = true;
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
options.Authority = apiIdpAuthority;
options.RequireHttpsMetadata = requireHttps;
options.ApiName = apiName;
options.ApiSecret = apiSecret;
});
services.AddCors(options =>
{
// this defines a CORS policy called "default"
options.AddPolicy("default", policy =>
{
policy.WithOrigins(apiIdpAuthority, applicationUrl)
.AllowAnyHeader()
.AllowAnyMethod()
.AllowAnyOrigin()
.AllowCredentials();
});
});
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
services.Configure<MvcOptions>(options =>
{
options.Filters.Add(new RequireHttpsAttribute());
});
services.AddHsts(options =>
{
options.Preload = true;
options.IncludeSubDomains = true;
options.MaxAge = TimeSpan.FromDays(60);
});
services.AddHttpsRedirection(options =>
{
options.RedirectStatusCode = StatusCodes.Status307TemporaryRedirect;
options.HttpsPort = Convert.ToInt32(httpsPort);
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseCors("default");
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseHttpsRedirection();
app.UseCookiePolicy();
var locale = Configuration["SiteLocale"];
var supportedCultures = new List<CultureInfo> { new CultureInfo("en-US") };
if (supportedCultures.Where(x => x.Name == locale).Count() == 0)
{
supportedCultures.Add(new CultureInfo(locale));
}
RequestLocalizationOptions localizationOptions = new RequestLocalizationOptions()
{
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures,
DefaultRequestCulture = new RequestCulture(locale)
};
app.UseRequestLocalization(localizationOptions);
app.UseAuthentication();
app.UseMvc();
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
Identity Server Startup.cs
public void ConfigureServices(IServiceCollection services)
{
//var microsoftClientId = Configuration["MicrosoftClientId"];
// var microsoftClientSecret = Configuration["MircosoftClientSecret"];
var azureADClientId = Configuration["AzureADClientId"];
var azureADClientSecret = Configuration["AzureADClientSecret"];
var azureADEndPoint = Configuration["AzureADEndPoint"];
var issuerUri = Configuration["IssuerUri"];
string connectionString = Configuration.GetConnectionString("DefaultConnection");
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
services.AddDbContext<IdentityServerDataContext>(options => options.UseSqlServer(connectionString));
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddAuthentication()
// .AddCookie()
.AddOpenIdConnect("AAD", "Azure AD", options =>
{
options.Authority = string.Format("https://login.microsoftonline.com/{0}", azureADEndPoint);
options.ClientId = azureADClientId;
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("email");
});
IdentityModelEventSource.ShowPII = true;
services.AddTransient<IProfileService, IdentityWithAdditionalClaimsProfileService>();
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddIdentityServer() .AddSigninCredentialFromConfig(Configuration.GetSection("SigninKeyCredentials"), _logger)
// 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;
})
.AddAspNetIdentity<ApplicationUser>()
.AddProfileService<IdentityWithAdditionalClaimsProfileService>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// this will do the initial DB population
// InitializeDatabase(app);
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseIdentityServer();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
Note: I only encounter this issue when I use the local login. It is working fine if I use the Azure AD login, I was able to connect to the API using the authorization token from the client app