User has logged out from the Site but APIs are accessible from POSTMAN with cookies in the header?
Logout
public async Task OnPost(string returnUrl = null)
{
await _httpContextAccessor.HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme,
new AuthenticationProperties
{
RedirectUri = returnUrl
});
await _signInManager.SignOutAsync();
HttpContext.Response.Cookies.Delete(".AspNetCore.Cookies");
}
Startup
public class Startup
{
public Startup(IConfiguration configuration, IHostingEnvironment env)
{
Configuration = configuration;
HostingEnvironment = env;
}
public IConfiguration Configuration { get; }
public IHostingEnvironment HostingEnvironment { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//Https
services.AddHsts(options =>
{
options.Preload = true;
options.IncludeSubDomains = true;
options.MaxAge = TimeSpan.FromDays(180);
options.ExcludedHosts.Add("admission.just.edu.bd");
options.ExcludedHosts.Add("www.admission.just.edu.bd");
});
services.AddHttpsRedirection(options =>
{
options.RedirectStatusCode = StatusCodes.Status301MovedPermanently;
options.HttpsPort = HostingEnvironment.IsDevelopment() ? 5001 : 443;
});
services.AddMemoryCache();
services.AddDbContext<AdmissionDbContext>(options =>
{
if (HostingEnvironment.IsDevelopment())
{
options.UseSqlServer(Configuration["DbConnection:Sql:Local"], x => x.MigrationsHistoryTable("__EFMigrationsHistory", Configuration["DbConnection:Sql:Schema"]));
}
else
{
options.UseSqlServer(Configuration["DbConnection:Sql:Cloud"], x => x.MigrationsHistoryTable("__EFMigrationsHistory", Configuration["DbConnection:Sql:Schema"]));
}
});
services.AddIdentity<ApplicationUser, IdentityRole>(
options =>
{
options.ClaimsIdentity.UserIdClaimType = ClaimTypes.NameIdentifier;
options.Password.RequireDigit = false;
options.Password.RequiredLength = 6;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequireLowercase = false;
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(2);
})
.AddEntityFrameworkStores<AdmissionDbContext>()
.AddDefaultTokenProviders();
services.AddAuthorization(opts =>
{
opts.AddPolicy("AuthenticatedUser", policy => policy.RequireAuthenticatedUser());
opts.AddPolicy("SystemAdminOnly", policy => policy.RequireRole(SystemRole.Administrator));
opts.AddPolicy("SupportOnly", policy => policy.RequireRole(SystemRole.Support));
opts.AddPolicy("ApplicantOnly", policy => policy.RequireRole(SystemRole.Applicant));
});
services.AddScoped<IClaimsTransformation, ClaimsTransformation>();
services.AddSession();
services.AddMvc(
options =>
{
options.Filters.Add<ErrorExceptionFilter>();
}
).SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddRazorPagesOptions(options =>
{
options.AllowAreas = true;
options.Conventions.AuthorizeAreaFolder("Identity", "/Account/Manage");
options.Conventions.AuthorizeAreaPage("Identity", "/Account/Logout");
})
.AddJsonOptions(opts =>
{
opts.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
opts.SerializerSettings.DateFormatHandling = DateFormatHandling.IsoDateFormat;
opts.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
opts.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
opts.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
if (HostingEnvironment.IsDevelopment())
{
opts.SerializerSettings.Formatting = Formatting.Indented;
}
});
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//app.UseDeveloperExceptionPage();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseHsts();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRequestLocalization();
// app.UsePendingMigrations();
app.UseDefaultRoles(SystemRole.All);
app.UseDefaultUsers();
//app.UseCookiePolicy();
app.UseAuthentication();
app.UseSession();
app.UseMvc(routes =>
{
routes.MapRoute("areaRoute", "{area:exists}/{controller}/{action=Index}/{id?}");
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
I don't know why, but for some reason Response.Cookies.Delete(cookieKey) was not working for me. What I did was create another cookie with the same cookie name with expiry set to a time in past. Eg:
var c = new HttpCookie("cookieKey");
c.Expires = DateTime.Now.AddDays(-1);
Response.Cookies.Add(c);
Related
I have been trying to figure this out all weekend, but can't seem to get it to work. Please help.
I have setup IdentityServer 4, an MVC application and an API.
I get an access token after login, but when I try to access the API it throws 500 internal server error (if I have an Authorize attribute)
this is my identityserver config file:
public static IEnumerable<IdentityResource> IdentityResources =>
new IdentityResource[]
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
};
public static IEnumerable<ApiScope> ApiScopes =>
new ApiScope[]
{
new ApiScope("api1", "my API")
};
public static IEnumerable<Client> Clients =>
new Client[]
{
new Client
{
ClientId = "mvc",
ClientSecrets = { new Secret("secret".Sha256()) },
AllowedGrantTypes = GrantTypes.Code,
RedirectUris = { "https://localhost:5002/signin-oidc" },
//FrontChannelLogoutUri = "https://localhost:5002/signout-oidc",
PostLogoutRedirectUris = { "https://localhost:5002/signout-callback-oidc" },
AllowOfflineAccess = true,
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api1"
}
}
};
This is the identityServer startup class:
public IWebHostEnvironment Environment { get; }
public IConfiguration Configuration { get; }
public Startup(IWebHostEnvironment environment, IConfiguration configuration)
{
Environment = environment;
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
if (System.Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Production")
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("MyDbConnection")));
else
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
var builder = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
// see https://identityserver4.readthedocs.io/en/latest/topics/resources.html
options.EmitStaticAudienceClaim = true;
})
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryClients(Config.Clients)
.AddAspNetIdentity<ApplicationUser>();
// not recommended for production - you need to store your key material somewhere secure
builder.AddDeveloperSigningCredential();
services.AddAuthentication()
.AddGoogle(options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.ClientId = "xxx.apps.googleusercontent.com";
options.ClientSecret = "xxx";
options.ReturnUrlParameter = "https://xxx.azurewebsites.net/signin-google";
}).AddFacebook(facebookOptions =>
{
facebookOptions.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
facebookOptions.ClientId = "xxx";
facebookOptions.ClientSecret = "xxx";
facebookOptions.ReturnUrlParameter = "https://xxx.azurewebsites.net/signin-facebook";
});
}
public void Configure(IApplicationBuilder app)
{
if (Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
This is my API startup class:
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using System.Configuration;
namespace SF.API
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration
{
get;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
// accepts any access token issued by identity server
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = ConfigurationManager.AppSettings["IdentityServerAddress"];
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false
};
});
// adds an authorization policy to make sure the token is for scope 'api1'
//services.AddAuthorization(options =>
//{
// options.AddPolicy("ApiScope", policy =>
// {
// policy.RequireAuthenticatedUser();
// policy.RequireClaim("scope", "api1");
// });
//});
}
public void Configure(IApplicationBuilder app)
{
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
});
app.UseStaticFiles();
}
}
}
this is the Controller class:
[Route("identity")]
[Authorize]
public class IdentityController : ControllerBase
{
public IActionResult Get()
{
return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
}
[Route("/Test")]
public IActionResult Test()
{
return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
}
}
This is the MVC application startup class:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.Authority = "https://xxx.azurewebsites.net";
options.ClientId = "mvc";
options.ClientSecret = "secret";
options.ResponseType = "code";
options.SaveTokens = true;
options.Scope.Add("api1");
options.Scope.Add("offline_access");
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute()
.RequireAuthorization();
});
}
This is how I call the API:
public async Task<IActionResult> CallApi()
{
var accessToken = await HttpContext.GetTokenAsync("access_token");
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var content = await client.GetStringAsync("https://localhost:6001/Test");
ViewBag.Json = JArray.Parse(content).ToString();
return View("json");
}
My access token looks like this:
"eyJhbGciOiJSUzI1NiIsImtpZCI6IkExNDYxOUUzOTAwNjM5ODA2NUU4RkUwQjJFMkU1RThFIiwidHlwIjoiYXQrand0In0.eyJuYmYiOjE2NDg5ODEyMjMsImV4cCI6MTY0ODk4NDgyMywiaXNzIjoiaHR0cHM6Ly9pZGVudGl0eXNlcnZlcmlkZW50aXR5cHJvdmlkZXJzZi5henVyZXdlYnNpdGVzLm5ldCIsImF1ZCI6Imh0dHBzOi8vaWRlbnRpdHlzZXJ2ZXJpZGVudGl0eXByb3ZpZGVyc2YuYXp1cmV3ZWJzaXRlcy5uZXQvcmVzb3VyY2VzIiwiY2xpZW50X2lkIjoibXZjIiwic3ViIjoiZDc5NGQxOGUtNGYyOC00MmE3LWFkMTQtZDdiMWMxMDcwOTE5IiwiYXV0aF90aW1lIjoxNjQ4OTgxMjIzLCJpZHAiOiJsb2NhbCIsImp0aSI6IjZBRTE3Q0RBMjBGQkNGNDExQzc3QUIyQkNBNTE3M0YzIiwic2lkIjoiRkMxREY5QUZCODQyRkZEN0JGRjk5MTY0RTYyN0M2ODYiLCJpYXQiOjE2NDg5ODEyMjMsInNjb3BlIjpbIm9wZW5pZCIsInByb2ZpbGUiLCJhcGkxIiwib2ZmbGluZV9hY2Nlc3MiXSwiYW1yIjpbInB3ZCJdfQ.Grcu-dbrLy6LHHxsW2FJsSIhmwQBEl1jQ2LRvJhBFzZ8j0HAqk129Q8JncJFSFBjQkEls8xBFN-OxyvhJ5o7dmgpkgYENbfjl7jC04yhvSh_MzLqG2h_mme1mwsC3xzuKbQR1yczei-j92WUMeP-CvzUtr2vbJd2lJv0YvpJvykGF4BbKrQMPLPZnlLFRkPm5LcdFfUsrHrCz3R0JZ7tpVSwGMjGMlDHlAMAR04Fzf6YQhbKUEydNdTIWFP2akyBoWuRwAvTXbOA8vm9GZpeTbo8S4At5X7RhOR_J-zIjk1QWKhqN9kVMnMLXpO_NmZ6iQ66pcnT0G75rtFEfFtISQ"
Is there anything wrong with how I access the token? I can't see the scopes for example?
What else could be wrong?
**Update: I added app.UseAuthorization() in the API.Startup class. Then I got 401 Unauthroized instead
One problem with the API is that it lacks
app.UseAuthentication();
app.UseAuthorization();
In the API startup class.
To debug the API in ASP.NET Core, you can try to set the log level top debug or trace, like this in appsetttings:
{
"Logging": {
"LogLevel": {
"Default": "Warning",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.AspNetCore.Authentication": "Trace",
"Microsoft.AspNetCore.Authorization": "Trace"
}
}
}
You should check inside the access token header:
{
"alg": "RS256",
"kid": "A14619E39006398065E8FE0B2E2E5E8E",
"typ": "at+jwt"
}
The kid key identifier must be present in the JWKS endpoint.
I have a api endpoints that allows me to login. Then I have a other api endpoint where I want to get data from.
Here is a exemple of data that I want to get from my postman As you can see when I use the login endpoint I get a token
Then I want to use the token to get the clients but it does not work I get A 401 here is a image of the problem. As you can see I have used the token but I am still not authorized why is that? Thank you for your help.
I have the following Controller to authenticate.
[Route("api/auth")]
[ApiController]
public class AuthController : ControllerBase
{
private readonly ILogger<AuthController> _logger;
private readonly IEmployeeService _employeeService;
public AuthController(ILogger<AuthController> logger, IEmployeeService employeeService)
{
_logger = logger;
_employeeService = employeeService;
}
[HttpPost, Route("login")]
public IActionResult Login([FromBody] LoginModel user)
{
Employee employeeFromDB = Task.Run(async () => await _employeeService.GetByUserName(user.UserName)).Result;
if (user == null)
{
return BadRequest("Invalid client request");
}
if(employeeFromDB == null)
{
return Unauthorized();
}
else if (user.UserName == employeeFromDB.UserName && user.Password == "123qwe")
{
var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("superSecretKey#45"));
var signingCreditials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256);
var tokenOptions = new JwtSecurityToken(
issuer: "https://localhost:44363/",
audience: "https://localhost:44363/",
claims: new List<Claim>(),
expires: DateTime.Now.AddMinutes(5),
signingCredentials: signingCreditials
);
var tokenString = new JwtSecurityTokenHandler().WriteToken(tokenOptions);
return Ok(new { Token = tokenString });
}
return Unauthorized();
}
I have the following controller that is secured
[Route("api/[controller]")]
[ApiController]
public class ClientsController : ControllerBase
{
private readonly ILogger<ClientsController> _logger;
private readonly IClientService _clientService;
public ClientsController(ILogger<ClientsController> logger, IClientService clientService)
{
_logger = logger;
_clientService = clientService;
}
// GET api/clients
[HttpGet]
[Authorize]
public IEnumerable<ClientViewModel> Get()
{
ClientViewModel clientViewModel;
List<ClientViewModel> listClientViewModels = new List<ClientViewModel>();
var clients = Task.Run(async () => await _clientService.GetAllClients()).Result;
foreach (var client in clients)
{
clientViewModel = new ClientViewModel();
clientViewModel.ClientId = client.ClientId;
clientViewModel.Active = client.Active;
clientViewModel.Address = client.Address;
clientViewModel.City = client.City;
clientViewModel.ClienteName = client.ClienteName;
clientViewModel.ComercialEmployeeId = client.ComercialEmployeeId;
clientViewModel.Confirmed = client.Confirmed;
clientViewModel.CountryId = client.CountryId;
clientViewModel.CreationDate = client.CreationDate;
clientViewModel.DANE = client.DANE;
clientViewModel.Department = client.Department;
clientViewModel.ElectronicBillingEmail = client.ElectronicBillingEmail;
clientViewModel.Eliminated = client.Eliminated;
clientViewModel.NIT = client.NIT;
clientViewModel.PostalCode = client.PostalCode;
clientViewModel.Phone = client.Phone;
listClientViewModels.Add(clientViewModel);
}
return listClientViewModels;
}
}
}
Here is my startup code
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("EnableCORS", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
});
services.AddAuthentication(opt =>
{
opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(opttions =>
{
opttions.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "https://localhost:44363",
ValidAudience = "https://localhost:44363",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("superSecretKey#45"))
};
});
services.AddControllersWithViews();
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<DatabaseMigrator>();
services.AddDbContext<erp_colombiaDbContext>(options => options.UseMySql(
Configuration.GetConnectionString("DefaultConnection"),
optionsBuilder => optionsBuilder.MigrationsAssembly(typeof(DesignTimeDbContextFactory).Assembly.FullName)));
services.AddDbContext<erp_colombiaDbContext>(options => options.UseMySql(
Configuration.GetConnectionString("DefaultConnection"),
optionsBuilder => optionsBuilder.MigrationsAssembly(typeof(DesignTimeDbContextFactory).Assembly.FullName)));
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseCors("EnableCORS");
app.UseStaticFiles();
if (!env.IsDevelopment())
{
app.UseSpaStaticFiles();
}
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
I need to add a Basic Authentication to a set of API.
DISCLAIMER: The APIs are inside an intranet and contains public data. They can be consumed from external users only through an API gateway where strong authentication and authorization are implemented. This Basic Authentication is needed only to avoid that internal development team calls directly this service. User and password are not the real one.
I've used the package ZNetCS.AspNetCore.Authentication.Basic, version 4.0.0
This is my startup.cs:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services
.AddAuthentication(BasicAuthenticationDefaults.AuthenticationScheme)
.AddBasicAuthentication(
options =>
{
options.Realm = "My Application";
options.Events = new BasicAuthenticationEvents
{
OnValidatePrincipal = context =>
{
if ((context.UserName.ToLower() == "gateway0107")
&& (context.Password == "jir6STt6437yMAQpl"))
{
var claims = new List<Claim>{
new Claim(ClaimTypes.Name,
context.UserName,
context.Options.ClaimsIssuer)
};
var ticket = new AuthenticationTicket(
new ClaimsPrincipal(new ClaimsIdentity(
claims,
BasicAuthenticationDefaults.AuthenticationScheme)),
new Microsoft.AspNetCore.Authentication.AuthenticationProperties(),
BasicAuthenticationDefaults.AuthenticationScheme);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
return Task.FromResult(AuthenticateResult.Fail("Authentication failed."));
}
};
});
services.AddMvcCore();
services.AddApiVersioning(
options =>
{
options.ReportApiVersions = true;
});
services.AddVersionedApiExplorer(
options =>
{
options.GroupNameFormat = "'v'VVV";
options.SubstituteApiVersionInUrl = true;
});
services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
services.AddSwaggerGen(
options =>
{
options.OperationFilter<SwaggerDefaultValues>();
options.IncludeXmlComments(XmlCommentsFilePath);
});
services.AddCors(options =>
{
options.AddPolicy(name: "AllowAllOrigins",
builder =>
{
builder.AllowAnyOrigin();
});
});
services.AddControllers(options =>
{
options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(Point)));
options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(Coordinate)));
options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(LineString)));
options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(MultiLineString)));
}).AddNewtonsoftJson(options =>
{
foreach (var converter in NetTopologySuite.IO.GeoJsonSerializer.Create(new GeometryFactory(new PrecisionModel(), 4326)).Converters)
{
options.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
options.SerializerSettings.Converters.Add(converter);
}
}).SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider provider)
{
app.UseForwardedHeaders();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(Directory.GetCurrentDirectory(), "swagger-ui")),
RequestPath = "/swagger-ui"
});
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors("AllowAllOrigins");
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.UseSwagger();
app.UseSwaggerUI(
options =>
{
// build a swagger endpoint for each discovered API version
foreach (var description in provider.ApiVersionDescriptions)
{
options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant());
}
});
}
static string XmlCommentsFilePath
{
get
{
var basePath = PlatformServices.Default.Application.ApplicationBasePath;
var fileName = typeof(Startup).GetTypeInfo().Assembly.GetName().Name + ".xml";
return Path.Combine(basePath, fileName);
}
}
}
But testing in Postman and from SwaggerUI I get always 410 Unauthorized.
Setting a breakpoint inside the OnValidatePrincipal, it never gets hit.
Should I have to add something else?
What do I miss to clear cookie to logout user .After logout when I click on backward arrow on the browser still my login username and password is saved and I am back to application.Do I need to remove services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
to clear cookie .
Logout Method
/// To logout user from Application
/// </summary>
/// <returns></returns>
[Obsolete]
public IActionResult Logout()
{
var model = new LoginViewModel();
HttpContext.Session.Clear();
HttpContext.Session.Remove(HttpContext.Session.Id);
Response.Cookies.Delete(".AspNetCore.Antiforgery.kLmeyaPL6YU");
foreach (var cookie in Request.Cookies.Keys)
{
Response.Cookies.Delete(cookie);
}
var cookies = Request.Cookies.Keys;
return Redirect("/");
}```
***startup.cs***
``` public void ConfigureServices(IServiceCollection services )
{
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.LoginPath = new PathString("/Account/Login/");
});
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
// var isEmployeeUserPolicy = new AuthorizationPolicyBuilder().RequireRole("Employee").Build();
/* services.AddMvc(options =>
{
options.Filters.Add(new ApplyPolicyOrAuthorizeFilter(isEmployeeUserPolicy));
});*/
// services.AddSignalR();
services.Configure<LdapConfig>(Configuration.GetSection("ldap"));
services.AddScoped<IAuthenticationService, LdapAuthenticationService>();
/* services.AddScoped<ECXSecurityAccess.ECXSecurityAccessClient>(provider => {
var client = new ECXSecurityAccessClient();
// Use configuration object to read it from appconfig.json
client.ClientCredentials.UserName.UserName = Configuration["MyService:Username"];
client.ClientCredentials.UserName.Password = Configuration["MyService:Password"];
client.Endpoint.Address = new EndpointAddress(Configuration["MyService:BaseUrl"]);
return client;
});
services.AddAuthentication(IISDefaults.AuthenticationScheme);
services.Configure<IISOptions>(options => {
options.AutomaticAuthentication = true;
options.ForwardClientCertificate = true;
});*/
services.AddDistributedMemoryCache();
services.AddHttpContextAccessor();
services.AddSession(options =>
{
// Set a short timeout for easy testing.
options.IdleTimeout = TimeSpan.FromSeconds(10);
options.Cookie.HttpOnly = true;
// Make the session cookie essential
options.Cookie.IsEssential = true;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.Configure<MySetting>(Configuration.GetSection("MySetting"));
services.AddSingleton(Configuration);
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.ToPagerList();
var MySetting = Configuration.GetSection("MySetting");
}
// 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.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseCookiePolicy(new CookiePolicyOptions
{
HttpOnly = HttpOnlyPolicy.Always,
Secure = CookieSecurePolicy.Always,
MinimumSameSitePolicy = SameSiteMode.None
});
app.UseAuthentication();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "images")),
RequestPath = "/MyImages"
});
app.UseDirectoryBrowser(new DirectoryBrowserOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "images")),
RequestPath = "/MyImages"
});
app.UseMiddleware<StackifyMiddleware.RequestTracerMiddleware>();
app.UseCookiePolicy();
app.UseSession();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "Login",
template: "{controller=Account}/{action=Login}/{id?}");
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}```
I am building a website in dotnet core and have recently started using claims based authentication and authorization.
In a view component I am checking if the user has access to a policy.
public NavigationViewComponent(
IContextManager contextManager,
IAuthorizationService authorizationService)
{
_contextManager = contextManager;
_authorizationService = authorizationService;
}
public async Task<IViewComponentResult> InvokeAsync()
{
var showAdmin = _contextManager.Principal != null &&
(await _authorizationService.AuthorizeAsync(_contextManager.Principal, "Admin")).Succeeded;
var vm = new NavigationViewModel
{
ShowAdmin = showAdmin
};
return View(vm);
}
However, I am receiving the Exception InvalidOperationException: No policy found: Admin..
My startup.cs contains the following inside the ConfigureServices method:
services.AddAuthorization(options =>
{
options.AddPolicy("Admin",
policy => policy.Requirements.Add(new HasPermissionRequirement("ADMIN")));
});
What else do I need to configure in order to get this to work correctly?
For reference I am registering 3 additional IAuthorizationHandler implementations and 1 IAuthorizationPolicyProvider.
Edit
For reference, the whole startup.cs looks something similar to this.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
// Name and policy settings
options.Cookie.Name = "account";
options.Cookie.SameSite = SameSiteMode.Strict;
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
options.Cookie.HttpOnly = true;
// Sign the user out after 28 days of inactivity
options.SlidingExpiration = true;
options.ExpireTimeSpan = TimeSpan.FromDays(28);
// Challenge actions
options.LoginPath = new PathString("/account/login");
options.ReturnUrlParameter = "returnUrl";
});
services.AddAuthorization(options =>
{
options.AddPolicy("Admin",
policy => policy.Requirements.Add(new HasPermissionRequirement("ADMIN")));
});
services.AddSingleton<IAuthorizationHandler, HasPermissionHandler>();
services.AddSingleton<IAuthorizationHandler, StrategyAuthorizationCrudHandler>();
services.AddSingleton<IAuthorizationHandler, UserAuthorizationCrudHandler>();
services.AddSingleton<IAuthorizationPolicyProvider, HasPermissionPolicyProvider>();
// AddAntiforgery, AddSession,AddDistributedRedisCache and AddDataProtection omitted
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddJsonOptions(options =>
{
options.SerializerSettings.ReferenceLoopHandling =
ReferenceLoopHandling.Ignore;
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
app.UseCookiePolicy(new CookiePolicyOptions
{
CheckConsentNeeded = httpContext => false,
MinimumSameSitePolicy = SameSiteMode.Strict,
Secure = CookieSecurePolicy.SameAsRequest
});
app.UseAuthentication();
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
app.UseStaticFiles();
var supportedCultures = new[]
{
new CultureInfo("en-GB")
};
app.UseRequestLocalization(new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture(supportedCultures[0]),
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures
});
app.UseSession();
app.UseMiddleware<UserMiddleware>();
app.UseMiddleware<LoggingMiddleware>();
app.UseMvc(routes =>
{
routes.MapRoute(
"default",
"{controller=Home}/{action=Index}/{id?}");
});
}
HasPermissionRequirement.cs
public class HasPermissionRequirement : IAuthorizationRequirement
{
public string Permission { get; private set; }
public HasPermissionRequirement(string permission)
{
Permission = permission;
}
}
HasPermissionHandler.cs
public class HasPermissionHandler : AuthorizationHandler<HasPermissionRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
HasPermissionRequirement requirement)
{
var hasPermission = context.User.HasClaim("Permission", requirement.Permission);
if (hasPermission)
context.Succeed(requirement);
return Task.CompletedTask;
}
}
HasPermissionPolicyProvider.cs
public class HasPermissionPolicyProvider : IAuthorizationPolicyProvider
{
private const string PolicyPrefix = "HasPermission";
public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
{
if (!policyName.StartsWith(PolicyPrefix, StringComparison.OrdinalIgnoreCase))
return Task.FromResult<AuthorizationPolicy>(null);
var permission = policyName.Substring(PolicyPrefix.Length);
var policy = new AuthorizationPolicyBuilder();
policy.AddRequirements(new HasPermissionRequirement(permission));
return Task.FromResult(policy.Build());
}
public Task<AuthorizationPolicy> GetDefaultPolicyAsync() =>
Task.FromResult(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build());
}
I've managed to solve this by taking a closer look at the Microsoft documentation after being pointed in the right direction.
https://github.com/aspnet/Docs/blob/master/aspnetcore/security/authorization/iauthorizationpolicyprovider.md#multiple-authorization-policy-providers
When using custom IAuthorizationPolicyProvider implementations, keep in mind that ASP.NET Core only uses one instance of IAuthorizationPolicyProvider. If a custom provider isn't able to provide authorization policies for all policy names, it should fall back to a backup provider.
As a result I changed the implementation of my HasPermissionPolicyProvider to CustomPolicyProvider and the content is below:
public class CustomPolicyProvider : DefaultAuthorizationPolicyProvider
{
private const string PermissionPolicyPrefix = "HasPermission";
public CustomPolicyProvider(IOptions<AuthorizationOptions> options) : base(options)
{
}
public override async Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
{
var policy = await base.GetPolicyAsync(policyName);
if (policy != null) return policy;
if (policyName.StartsWith(PermissionPolicyPrefix, StringComparison.OrdinalIgnoreCase))
{
var permission = policyName.Substring(PermissionPolicyPrefix.Length);
return new AuthorizationPolicyBuilder()
.AddRequirements(new HasPermissionRequirement(permission))
.Build();
}
return null;
}
}
This means that you can only have one PolicyProvider which must handle all your logic. The change is to ensure that it calls the default implementation if you require multiple handler logic.