I am working on a side project for learning purpose and I have a .Net solution containing a .Net Core Web App and .Net Core Web Api. In the Web Api, I am letting the user to log in and create a JWT token and then sending it back to the front end MVC Application (same solution). Using this token I want the user to access other resources, for example a User List (GetUsers).
Problem is when I am accessing this protected API Endpoint GetUsers() from POSTMAN using the generated JWT Token, it is authorizing it properly and returning the results as expected. But when I am trying to access the same endpoint from my .Net core MVC Web Application, by passing the Bearer token in the Header of the HttpClient, it is always giving 401 Unauthorized response and not entering the Web Api controller of GetUsers.
This is my Startup.cs of the API application -
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddDbContextPool<DBContext>(options => options.UseSqlServer(Configuration.GetConnectionString("UserContextConnectionString")));
services.AddIdentity<IdentityUser, IdentityRole>().AddEntityFrameworkStores<DBContext>();
//services.AddSingleton<IUserData, MockUserData>();
services.AddScoped<IUserData, SQLUserData>();
services.AddSwaggerGen();
services.AddApplicationInsightsTelemetry("e713bf5b-60e1-4fa8-a450-6fe640dbbcff");
services.AddAuthentication(
cfg =>
{
cfg.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
cfg.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}
)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Audience"],
RequireExpirationTime = false,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
};
});
services.AddMvc();
//services.AddMvcCore(options =>
// {
// var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
// options.Filters.Add(new AuthorizeFilter(policy));
// }
// ).AddXmlSerializerFormatters();
//services.Configure<IdentityOptions>(options =>
//{
// options.Password.RequireDigit = false;
// options.Password.RequiredLength = 5;
// options.Password.RequireLowercase = false;
// options.Password.RequireNonAlphanumeric = true;
// options.Password.RequireUppercase = false;
//});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseSwagger();
app.UseSwaggerUI(
c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json","User API V1");
c.RoutePrefix = string.Empty;
});
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseRouting();
app.UseAuthorization();
app.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
This is my AppSettings.json of API -
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"UserContextConnectionString": "server=(localdb)\\LocalDB;database=UserDB;Trusted_Connection=true"
},
"Jwt": {
"Key": "aRDmMQU7oxDKxn6xHNbG",
"Issuer": "https://localhost:44372/",
"Audience": "https://localhost:44372/"
}
}
This is my Token Generation -
private string GenerateJWTToken(Login user)
{
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var claims = new[]
{
new Claim(ClaimTypes.Email, user.Email)
};
var token = new JwtSecurityToken(_config["Jwt:Issuer"],
_config["Jwt:Audience"],
claims,
expires: DateTime.Now.AddMinutes(120),
signingCredentials: credentials);
return new JwtSecurityTokenHandler().WriteToken(token);
}
This is my MVC Web App Login Controller where I am setting the JWT Token in Session -
[HttpPost]
public async Task<IActionResult> Login(Login userLogin)
{
string baseURL = _configuration.GetSection("AppSettings").GetSection("baseURLAPI").Value;
string loginAPIURL = _configuration.GetSection("AppSettings").GetSection("getLoginEndpoint").Value;
var requestToSend = JsonConvert.SerializeObject(userLogin);
StringContent content = new StringContent(requestToSend, Encoding.UTF8, "application/json");
HttpClient client = _userAPI.Initial();
HttpResponseMessage res = await client.PostAsync(loginAPIURL, content);
Response response = new Response();
if (res.IsSuccessStatusCode)
{
string token = await res.Content.ReadAsStringAsync();
HttpContext.Session.SetString("JwtToken",token);
return RedirectToAction("Index", "Home");
}
else
{
var result = res.Content.ReadAsStringAsync().Result;
response = JsonConvert.DeserializeObject<Response>(result);
ViewBag.Error = "*"+response.Message;
return View();
}
}
This is my code for consuming the API from the MVC Web App -
[HttpGet]
public async Task<IActionResult> Index(bool userNotFound)
{
var accessToken = HttpContext.Session.GetString("JwtToken");
List<Users> users = new List<Users>();
HttpClient client = _userAPI.Initial();
string baseURL= _configuration.GetSection("AppSettings").GetSection("baseURLAPI").Value;
string usersAPIURL = _configuration.GetSection("AppSettings").GetSection("getUsersEndpoint").Value;
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
HttpResponseMessage res = await client.GetAsync(usersAPIURL);
if(res.IsSuccessStatusCode)
{
var result = res.Content.ReadAsStringAsync().Result;
users = JsonConvert.DeserializeObject<List<Users>>(result);
}
if(userNotFound)
{
ViewBag.userNotFound = "Could Not Delete User !";
}
if(users==null || users.Count==0)
{
ViewBag.NoUserMessage = "No Users Found";
}
return View(users);
}
This is my Startup.cs file of the .net core MVC Web App -
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace TheSampleProjectWeb
{
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.AddControllersWithViews();
services.AddSession(options=>
{
options.IdleTimeout = TimeSpan.FromMinutes(60);
});
services.AddCors();
}
// 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.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthentication();
app.UseRouting();
app.UseAuthorization();
app.UseSession();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Registration}/{action=Login}/{id?}");
});
}
}
}
This is my AppSettings.json for MVC Web App -
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"AppSettings": {
"baseURLAPI": "http://localhost:44372/",
//"baseURLAPI": "https://thesampleprojectapi.azurewebsites.net/",
"getUsersEndpoint": "api/users",
"getRegistrationEndpoint": "api/login",
"getLoginEndpoint": "api/login/LoginJWT",
"getLogOutEndpoint": "api/login/LogOutUser"
}
}
Please help. I am completely stuck in this. I am not understanding what am I missing.
Edit this line to add
Bearerwith theaccessTokenAnd try to add the middlewares this order in
startup.cs