I have an ASP.NET Core 2.1 Web API that uses an Angular 5 front-end. When I call the Login()
method, it saves a Cookie to the session.
Response.Cookies.Append("UserRole", userFromRepo.UserRole.ToString());
Then when accessing the Hangfire dashboard, I check for that cookie, and authorize if the user is an Admin (See Startup.cs below).
However, since I am using JWT tokens, and the front-end token doesn't expire right away, if the user closes the browser and re-opens my site, they don't have to log in, but it is a new session, so the cookie no longer exists.
Is there a more persistent way to store the user's role? (no, I'm not using Identity) Or is there a way to re-instantiate the cookie at the startup of the new session?
AuthController.cs
[HttpPost("login")]
public async Task<IActionResult> Login([FromBody] UserForLoginDto userForLoginDto)
{
var userFromRepo = await _repo.Login(userForLoginDto.Username.ToLower(), userForLoginDto.Password);
//stuff to generate tokenString
Response.Cookies.Append("UserRole", userFromRepo.UserRole.ToString());
return Ok(new { tokenString, user });
}
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddHangfire(config =>
config.UseSqlServerStorage(Configuration.GetConnectionString("DefaultConnection")));
services.AddMvc()
.AddJsonOptions(opt =>
{
opt.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddScoped<IAuthRepository, AuthRepository>();
services.AddScoped<IBaseRepository, BaseRepository>();
services.AddSingleton(Configuration);
var key = Encoding.ASCII.GetBytes(Configuration.GetSection("AppSettings:Token").Value);
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseAuthentication();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler(builder =>
{
builder.Run(async context =>
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
var error = context.Features.Get<IExceptionHandlerFeature>();
if (error != null)
{
context.Response.AddApplicationError(error.Error.Message);
await context.Response.WriteAsync(error.Error.Message);
}
});
});
}
app.UseCors(x => x.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin().AllowCredentials());
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseHangfireDashboard("/hangfire", new DashboardOptions()
{
Authorization = new[] {new HangfireAuthorizationFilter()}
});
app.UseHangfireServer();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseProxyToSpaDevelopmentServer("http://localhost:4200");
}
});
}
}
public class HangfireAuthorizationFilter : IDashboardAuthorizationFilter
{
public bool Authorize([NotNull] DashboardContext context)
{
try
{
var httpContext = context.GetHttpContext();
var userRole = httpContext.Request.Cookies["UserRole"];
return userRole == UserRole.Admin.ToString();
}
catch
{
return false;
}
}
}
AuthController.cs
Ok, a couple months later, I came back to this... here's how I got it to work!
AddCookie()
to save the value within request's cookies (within ConfigureServices
)HangfireAuthorizationFilter
that searches for the UserRole cookie and compares it to my UserRoles enum.Configure
)public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DataContext>(x => x
.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
b =>
{
b.MigrationsAssembly(("MyApp"));
b.EnableRetryOnFailure();
})
.ConfigureWarnings(warnings => warnings.Ignore(CoreEventId.IncludeIgnoredWarning)));
services.AddMvc()
.AddJsonOptions(opt =>
{
opt.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Events = new JwtBearerEvents
{
OnTokenValidated = async ctx =>
{
var clientId = ctx.Principal.FindFirstValue(ClaimTypes.NameIdentifier);
var db = ctx.HttpContext.RequestServices.GetRequiredService<DataContext>();
var user = await db.Users.FirstOrDefaultAsync(u => u.Id == int.Parse(clientId));
if (user != null)
{
var userRole = user.UserRole;
var claims = new List<Claim>
{
new Claim(ClaimTypes.Role, userRole.ToString())
};
var appIdentity = new ClaimsIdentity(claims);
ctx.Principal.AddIdentity(appIdentity);
}
}
};
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
}).AddCookie();
//...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseAuthentication();
app.UseHangfireDashboard("/hangfire", new DashboardOptions()
{
Authorization = new[] { new HangfireAuthorizationFilter() }
});
app.UseHangfireServer();
//...
}
}
public class HangfireAuthorizationFilter : ControllerBase, IDashboardAuthorizationFilter
{
public bool Authorize([NotNull] DashboardContext context)
{
try
{
var httpContext = context.GetHttpContext();
var userRole = httpContext.Request.Cookies["UserRole"];
return userRole == UserRole.Admin.ToString();
}
catch
{
return false;
}
}
}