.NET core JWT authorization failed? - c#

I'm trying to get JWT working here, the token is successfully received on my client end after login but when I request user info at the /info route, the authorization fails. Any help would be much appreciated, thanks in advance.
I get the error:
Route matched with {action = "GetInfo", controller = "Accounts", page = ""}. Executing controller action with signature System.Threading.Tasks.Task`1[ProjectConker.Controllers.AccountsInfo] GetInfo() on controller ProjectConker.Controllers.AccountsController (ProjectConker).
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
Authorization failed.
This is where the token is issued.
[HttpPost("login")]
public async Task<IActionResult> Post([FromBody]LoginInfo credentials)
{
if (credentials == null)
{
return BadRequest("Invalid client request");
}
var user = await UserManager.FindByNameAsync(credentials.Username);
await SignInManager.SignInAsync(user, isPersistent: false);
var result = await SignInManager.PasswordSignInAsync(user,
credentials.Password, isPersistent: false, lockoutOnFailure: false);
if (result.Succeeded)
{
var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("**********"));
var signinCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256);
var tokeOptions = new JwtSecurityToken(
issuer: "http://localhost:5000",
audience: "http://localhost:5000",
claims: new List<Claim>(){
new Claim("username", credentials.Username)
},
expires: DateTime.Now.AddMinutes(5),
signingCredentials: signinCredentials
);
var tokenString = new JwtSecurityTokenHandler().WriteToken(tokeOptions);
return Ok(new { Token = tokenString, UserName = user.UserName });
}
else
{
return Unauthorized();
}
}
Save token to local storage
public Login(loginForm : ILoginForm) : Observable<ILoginForm>
{
return this.http.post<ILoginForm>(this.accountsUrl + "/login", loginForm, httpOptions)
.pipe(map<any, any>((data, index) => {
localStorage.setItem("auth_token", data.token);
this.username = data.username;
this.loggedIn = true;
console.log(data);
return data;
}));
}
Gets user information
public GetAccountInfo() : Observable<any>
{
httpOptions.headers.set('Authorization', localStorage.getItem('auth_token'));
return this.http.get(this.accountsUrl + "/info", httpOptions);
}
returns user info, but authorization fails here
[HttpGet]
[Route("info")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public async Task<AccountsInfo> GetInfo()
{
var usernameClaim = User.Claims.SingleOrDefault(c => c.Type == "username");
Console.WriteLine(usernameClaim.Value, ConsoleColor.Red);
var user = await UserManager.FindByNameAsync(usernameClaim.Value);
return new AccountsInfo{ DisplayName = user.UserName };
}
My startup.cs
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.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "http://localhost:5000",
ValidAudience = "http://localhost:5000",
IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes("superSecretKey#345"))
};
});
//services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
services.AddHttpClient();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddCors(options => options.AddPolicy("CorsPolicy",
builder =>
{
builder.AllowAnyMethod().AllowAnyHeader()
.WithOrigins("*")
.AllowCredentials();
}));
services.AddSignalR();
services.AddEntityFrameworkSqlServer();
services.AddDbContext<ConkerDbContext>(
options => options.UseQueryTrackingBehavior(QueryTrackingBehavior.TrackAll));
services.AddIdentity<IdentityUser, IdentityRole>().AddEntityFrameworkStores<ConkerDbContext>();
services.AddScoped<SearchEngine>();
services.AddTransient<RoadmapService>();
}
// 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("/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.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseAuthentication();
app.UseSignalR(routes =>
{
routes.MapHub<ChatHub>("/api/chat");
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
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");
}
});
}

Since Angular's HttpHeaders are immutable, you can't just use httpOptions.headers.set('Authorization', localStorage.getItem('auth_token'));, because it would have no effect on the original object.
First thing is the header is invalid, use the Bearer method provided by Ashique, then your GetAccountInfo call would look like this:
public GetAccountInfo() : Observable<any> {
const headers = new HttpHeaders({'Authorization': 'Bearer ' + localStorage.getItem('auth_token')});
return this.http.get(this.accountsUrl + "/info", {headers});
}
Here I assumed that you don't have other HttpOptions set, so I'm just passing the header to the HttpClient. Try it this way and let us know if it's still not working.

The default way to add authorization header in HTTP request for ASP.NET core token authentication is to add Bearer before the token. So the code should be like this-
httpOptions.headers.set('Authorization', "Bearer " + localStorage.getItem('auth_token'));
You can override the default behavior to remove the need of Bearer.
Please read the below post to help understand the reason for using bearer before token.
https://www.quora.com/Why-is-Bearer-required-before-the-token-in-Authorization-header-in-a-HTTP-request
Also try this,
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Expires = DateTime.Now.AddDays(1),
SigningCredentials = creds,
IssuedAt = DateTime.Now,
NotBefore = DateTime.Now
};
var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDescriptor);
What i mean is instead of creating a new JWTSecurityToken, Create a SecuritTokenDescriptor, instead of using function WriteToken, use CreateToken. I have used JWT in this way and it worked.

In the login end point where you create the token you are using key "**********" and in the Setup class you are using key "superSecretKey#345" , this is the problem , the authentication middleware is trying to validate the incoming JWT tokens with key different from the key used to issue the token , you have to use the same key for both issuing and validating , also put the key in somewhere else like "appsettings" to avoid this conflict
var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("**********"));
IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes("superSecretKey#345"))

Related

What is the Problems of my Code in Claim base Authorization ASP.NET CORE WEB API?

I want to use claim base Authorization in ASP.NET CORE Web API, I think my code is true but, it does not work for me and gives me the error 403 forbidden however I use the Right Token which has the right claim value in my AspNetUserClaims table
here is my Program.cs code
//Get Connection String
builder.Services.AddDbContext<AppDbContext>(opts =>
opts.UseNpgsql(builder.Configuration["connection:connectionString"]));
builder.Services.Configure<ApplicationSettings>(builder.Configuration.GetSection("ApplicationSettings"));
builder.Services.AddMvc();
builder.Services.AddIdentity<ApplicationUser, IdentityRole>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<AppDbContext>();
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("VIEW",
policy => policy.RequireClaim("VIEW"));
});
builder.Services.Configure<IdentityOptions>(options =>
options.User.RequireUniqueEmail = true);
//JWT Token Setup
var key = Encoding.UTF8.GetBytes(builder.Configuration["ApplicationSettings:JWT_Secret"]);
builder.Services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(x => {
x.RequireHttpsMetadata = false;
x.SaveToken = false;
x.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false,
ClockSkew = TimeSpan.Zero
};
});
here is my UsersLoginController when the user login, this controller generates the Token after successful login
[HttpPost]
[Route("Login")]
public async Task<IActionResult> Login(LoginModel model)
{
var user = await _userManager.FindByNameAsync(model.UserName);
if (user != null && await _userManager.CheckPasswordAsync(user, model.Password))
{
//Get role assigned to the user
var role = await _userManager.GetRolesAsync(user);
IdentityOptions _options = new IdentityOptions();
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim("UserID",user.Id.ToString()),
new Claim(_options.ClaimsIdentity.RoleClaimType,role.FirstOrDefault())
}),
Expires = DateTime.UtcNow.AddDays(1),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_appSettings.JWT_Secret)), SecurityAlgorithms.HmacSha256Signature)
};
var tokenHandler = new JwtSecurityTokenHandler();
var securityToken = tokenHandler.CreateToken(tokenDescriptor);
var token = tokenHandler.WriteToken(securityToken);
return Ok(new { token });
}
else
return BadRequest(new { message = "Username or password is incorrect." });
}
}
here is my UsersProfileDetailsController
[HttpGet]
[Authorize(Policy = "VIEW")]
public async Task<Object> GetUserProfile()
{
string userId = User.Claims.First(c => c.Type == "UserID").Value;
var user = await _userManager.FindByIdAsync(userId);
var role = await _userManager.GetRolesAsync(user);
return new
{
user.Id,
user.FirstName,
user.LastName,
user.Email,
user.UserName,
user.PhotoPath,
role,
};
}
Now when I login with Admin User which has the right Claimvalue VIEW in the Table AspNetUserClaims, it generates the Token for me but when I use this Token to access UserDetails from UsersProfileDetailsController it gives me the code 403 forbidden.
Can anyone help me with how to implement Claims base Authorization and what are my problems?
I use Postman for testing.
because you configured authoriztion as below:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("VIEW",
policy => policy.RequireClaim("VIEW"));
});
If you Press F12 and go to definition,you'll find the explaination of
the method
you added [Authorize(Policy = "VIEW")] on your controller, if you want authorize successfully, when you generate the token,you have to add the claim VIEW
Subject = new ClaimsIdentity(new Claim[]
{
new Claim("UserID",user.Id.ToString()),
//add a new claim named View here
new Claim("View",.......),
new Claim(_options.ClaimsIdentity.RoleClaimType,role.FirstOrDefault())
}),

Read asp.net core http response header in angular app with http interceptor

I try to do a simple appversion check by sending the current server version to the client in the http header and if there's a newer version, it should notify the user to reload. I thought this will be an easy task but my angular interceptor doesn't get my new response header sent by asp.net core. I use .Net6 and Angular 13.
Startup.cs
app.Use(async (context, next) =>
{
context.Response.Headers.Add("X-AppVersion", Assembly.GetEntryAssembly()!.GetName().Version!.ToString());
await next();
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
spa.Options.DefaultPageStaticFileOptions = new StaticFileOptions
{
OnPrepareResponse = context =>
{
var headers = context.Context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue
{
NoCache = true,
NoStore = true,
MustRevalidate = true,
MaxAge = TimeSpan.Zero
};
// Not sure about this one
headers.Append("X-AppVersion", Assembly.GetEntryAssembly()!.GetName().Version!.ToString());
}
};
});
Angular interceptor
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const reqWithCredentials = req.clone({ withCredentials: true });
return next.handle(reqWithCredentials).pipe(
map(resp => {
if (resp instanceof HttpResponse) {
if (resp.headers.get('x-appversion')) {
console.log(resp.headers.get('x-appversion'));
} else {
console.log(null); // I always get null
}
return resp;
}
})
)
}
I had to move my app.Use() middleware configuration before app.UseEndpoints() for this to work.
related: https://github.com/dotnet/aspnetcore/issues/18768

User.Identity is always null

I need to check if an user is logged in or not. In View I'm checking it like this;
#if (User.Identity.IsAuthenticated)
{
//links...
}
else
{
//links...
}
always returns false and Identity is empty although I SignInAsync in my login function. I tried to change the order of the configure method usings but it didn't work.
Here is my startup and login function.
public void ConfigureServices(IServiceCollection services)
{
var key = Encoding.ASCII.GetBytes(Configuration.GetSection("Appsettings:Secret").Value);
services.AddDbContext<BiHaberContext>();
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<BiHaberContext>()
.AddDefaultTokenProviders();
services.Configure<IdentityOptions>(options =>
{
options.Password.RequireDigit = false;
options.Password.RequiredLength = 3;
options.Password.RequireLowercase = false;
options.Password.RequireUppercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Lockout.MaxFailedAccessAttempts = 3;
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
options.User.RequireUniqueEmail = false;
options.SignIn.RequireConfirmedEmail = false;
options.SignIn.RequireConfirmedPhoneNumber = false;
options.SignIn.RequireConfirmedAccount = false;
});
services.AddAutoMapper(typeof(Startup));
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
//options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromDays(30);
options.LoginPath = "/Login";
options.AccessDeniedPath = "/Identity/Account/AccessDenied";
options.SlidingExpiration = true;
});
services.AddAuthentication();
services.AddAuthorization();
services.AddControllersWithViews();
services.AddScoped<ISemesterService, SemesterManager>();
services.AddScoped<IDepartmentService, DepartmentManager>();
services.AddScoped<ICourseService, CourseManager>();
services.AddScoped<IAnnouncementService, AnnouncementManager>();
services.AddCors();
services.AddResponseCaching();
services.AddMemoryCache();
}
// 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.UseCors(x => x.AllowAnyHeader().AllowAnyOrigin().AllowAnyHeader());
app.UseResponseCaching();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseCookiePolicy();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}");
});
}
Login:
[HttpPost("/login")]
public async Task<IActionResult> Login(LoginModel model)
{
if (!ModelState.IsValid)
return View(model);
var myContent = JsonConvert.SerializeObject(model);
var stringContent = new StringContent(myContent, System.Text.Encoding.UTF8, MediaTypeNames.Application.Json);
using (var postTask = await ApiHelper.ApiClient.PostAsync("Auth/Login", stringContent))
{
string jwt = await postTask.Content.ReadAsStringAsync();
var handler = new JwtSecurityTokenHandler();
var token = handler.ReadJwtToken(JwtExtension.CorrectJwtFormat(jwt));
var claims = token.Payload.Claims.ToList();
var claimsIdentity = new ClaimsIdentity(
claims, CookieAuthenticationDefaults.AuthenticationScheme);
var authProperties = new AuthenticationProperties()
{
AllowRefresh = true, ExpiresUtc = DateTimeOffset.Now.AddMonths(1), IsPersistent = true
};
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),authProperties);
return RedirectToAction("index", "home");
}
}
API works well and brings me 7 claims and claimsIdentity contains them as well. And redirecting to index. What did I do wrong I just couldn't figure out.
Added: Also I can not use Authorize attribute. So there is no authorizing anywhere.
When I delete this line
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<BiHaberContext>();
It worked. Identity is overriding my own claims. Thanks to #David Liang

Token and JWT with dotnet Core 3.1 User Identification, localStorage.getItem("token") is null

I can not get 'token' value from Angular App. it returns null.
Please any one pinpoint what I have done wrong.
Here is the code in startup.cs
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
//Jwt Authentication
var key = System.Text.Encoding.UTF8.GetBytes(Configuration["ApplicationSettings:JWT_Secret"].ToString());
//services.AddAuthentication()
services.AddAuthentication(auth =>
{
auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
auth.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(token =>
{
token.RequireHttpsMetadata = false;
token.SaveToken = true;
token.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = false,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false,
ClockSkew = TimeSpan.Zero
};
})
.AddIdentityServerJwt();
//End of Jwt Authentication
//services.AddControllersWithViews();
services.AddRazorPages();
//here is the code from login.cshtml.cs
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
if (ModelState.IsValid)
{
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
//
// add JWT to create Token
{
_logger.LogInformation("Yitong Create Token.");
// logic to get token from GenerateJSONWebToken
var tokenString = GenerateJSONWebToken();
// end changed
//_logger.LogInformation(tokenString);
_logger.LogInformation("User logged in.");
return LocalRedirect(returnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe });
}
if (result.IsLockedOut)
{
_logger.LogWarning("User account locked out.");
return RedirectToPage("./Lockout");
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return Page();
}
}
private string GenerateJSONWebToken()
{
var sec = Encoding.UTF8.GetBytes("1234567890123456");
var securityKey = new SymmetricSecurityKey(sec);
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var claims = new[] {
new Claim(ClaimTypes.UserData, User.Identity.Name)
};
var token = new JwtSecurityToken(
"self", // issuer
"self", // Audience
null, // replace null with claims
expires: DateTime.Now.AddMinutes(120),
signingCredentials: credentials);
return new JwtSecurityTokenHandler().WriteToken(token);
}
here is my code in angular
to get data from sql server
the token is = null
getForeCast() {
const token = localStorage.getItem("token");
const httpOptions = {
headers: new HttpHeaders({
'Authorization': 'Bearer ' + token
})
};
return this.http.get<forecastModel.weatherForecast_custom[]>(this.url, httpOptions)
.pipe(
tap(() => console.log("HTTP request executed")),
catchError(err => {
console.log('Handling error locally and rethrowing it...', err);
return throwError(err);
}),
finalize(() => console.log("first finalize() block executed")),
)
};
Anyone please?
I don't know why I can not post this it says
It looks like your post is mostly code; please add some more details.'
This is the best I can describe the issue I have had.
Thank you

How to manually validate JWT based on username in the url asp.net core

I am trying to use asp.net identity framework for mvc and JWT for APIs. Requirement is that api accessing username/device is in the url, for example, api/v1/username/accounts. The user or the device that JWT was issues has username in it. Can I do it in the startup.cs file. The following code was working fine until recently then it started doing strange thing by allowing asp.net identity to use JWT protected APIs. I want to check if username in the url api/v1/username/accounts matches the token one .Following is my code. Thanks for your insights.
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
Log.Logger = new LoggerConfiguration()
.MinimumLevel
.Warning()
.WriteTo.RollingFile("Logs/GateKeeperLog-{Date}.txt")
.CreateLogger();
}
public static IConfigurationRoot Configuration { get; set; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(Configuration);
services.AddDbContext<GkEnterpriseContext>(options =>
options.UseSqlServer(Configuration["Database:Connection"]));
services.AddTransient<IUnitOfWork, UnitOfWork>();
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<GkEnterpriseContext>()
.AddDefaultTokenProviders();
services.AddMvc()
.AddJsonOptions(options =>
{
options.SerializerSettings.Formatting = Formatting.Indented;
}).AddJsonOptions(options =>
{
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
}
// 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)
{
loggerFactory.AddSerilog();
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseWhen(context => context.Request.Path.Value.Contains("/api")
, builder =>
{
builder.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = Configuration["Tokens:Issuer"],
ValidAudience = Configuration["Tokens:Audidence"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes
(JwtTokenIssuer.PrivateKey)),
ValidateLifetime = true,
NameClaimType = JwtRegisteredClaimNames.FamilyName
}
});
app.UseWhen(context => context.Request.Path.Value.StartsWith("/api/v2/computers/")
, builder1 =>
builder1.MapWhen((ctx) =>
{
var deviceName = ctx.User.Claims.SingleOrDefault(c => c.Type == System.Security.Claims.ClaimTypes.Name)?.Value ?? "";
var testPath = new Microsoft.AspNetCore.Http.PathString($"/api/v2/computers/{deviceName}");
var pathMatch = ctx.Request.Path.StartsWithSegments(testPath);
return String.IsNullOrWhiteSpace(deviceName) || !pathMatch;
}, cfg =>
{
cfg.Run((req) =>
{
req.Response.StatusCode = 403;
return req.Response.WriteAsync("Sorry , you cant access this resource...");
});
}));
});
app.UseIdentity();
app.UseStatusCodePagesWithReExecute("/StatusCodes/{0}");
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapRoute(
name: "defaultApi",
template: "api/v2/{controller}/{id?}");
});
}
}
// JWT issung code block, it is now issuing tokens as expected, only validating is the problem.
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub,computer),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.FamilyName,"GkDevice")
};
var key = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(PrivateKey));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: Startup.Configuration["Tokens:Issuer"],
audience: Startup.Configuration["Tokens:Audidence"],
claims: claims,
notBefore: DateTime.Now,
expires: DateTime.Now.AddYears(10),
signingCredentials: creds
);
var data = new Token
{
Message = "New Token was issued",
Jwt = new JwtSecurityTokenHandler().WriteToken(token),
Iat = GkHelpers.ConvertTimeToEpoch(token.ValidFrom) ,
Exp = GkHelpers.ConvertTimeToEpoch(token.ValidTo)
};
return data;
Something like this might help you --
app.UseWhen(context => context.Request.Path.Value.StartsWith("/api"), builder =>
{
...jwt code...
builder.MapWhen((ctx) =>
{
var userName = ctx.User.Claims.SingleOrDefault(c => c.Type == System.Security.Claims.ClaimTypes.Name)?.Value ?? "";
var testPath = new Microsoft.AspNetCore.Http.PathString($"/api/v2/computers/{userName}/");
var pathMatch = ctx.Request.Path.StartsWithSegments(testPath);
return String.IsNullOrWhiteSpace(userName) || !pathMatch;
}, cfg =>
{
cfg.Run((req) =>
{
req.Response.StatusCode = 403;
return req.Response.WriteAsync("");
});
});
});
The inner MapWhen will trigger when the username in the "Name" claim (configure how to get the username here) does not match the given Path. It will then immediately execute the following request pipeline which will return a 403 code with an empty response body.
I am unsure, however, if you can process Identity-related items in the same request pipeline in which you actually add identity. You might have to extract that MapWhen code outside of the UseWhen.

Categories

Resources