ASP.NET Core - can't get simple bearer token auth working - asp.net-core

I am building an API with ASP.NET Core 2, and I am trying to get a simple auth example that uses a Bearer token to work.
First, here is my Startup code...
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "mydomain.com",
ValidAudience = "mydomain.com",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("THESECRETKEY THESECRETKEY THESECRETKEY THESECRETKEY"))
};
});
// goes last
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler();
}
app.UseStatusCodePages();
// goes last
app.UseMvc();
}
}
Then I have an Auth controller, that returns the token...
[Route("api/[controller]")]
public class AuthController : Controller
{
[AllowAnonymous]
[HttpPost("RequestToken")]
public IActionResult RequestToken([FromBody] TokenRequest request)
{
if (request.Username == "theuser" && request.Password == "thepassword")
{
var claims = new[]
{
new Claim(ClaimTypes.Name, request.Username)
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("THESECRETKEY THESECRETKEY THESECRETKEY THESECRETKEY"));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: "mydomain.com",
audience: "mydomain.com",
claims: claims,
expires: DateTime.Now.AddMinutes(30),
signingCredentials: creds);
return Ok(new
{
token = new JwtSecurityTokenHandler().WriteToken(token)
});
}
return BadRequest("Could not verify username and password");
}
}
public class TokenRequest
{
public string Username { get; set; }
public string Password { get; set; }
}
So in Postman you can see I get a token back...
And then I try GET from a Values controller...
[Route("api/[controller]")]
[Authorize]
public class ValuesController : Controller
{
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
}
If I add the [Anonymous] attribute, it works fine. However, when it requires authorization, I get a 401...

You haven't referenced the Authentication middleware in your Startup.cs
You can reference it by adding app.UseAuthentication(); preferably just before the app.UseMvc();
Here's how your Startup.cs's Configure method should look like:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler();
}
app.UseStatusCodePages();
app.UseAuthentication();
app.UseMvc();
}
You can read more into middleware here and more into authorization with ASP.NET Core here.
You should also look here for everything else about ASP.NET Core

Related

.net core 5.0.2 and jwt => response 401 Unauthorized

I am following an video tutorial for identity server 4 with web api's.
And Im not sure when I went wrong.
Im getting 401 Unauthorized when I try to call api with bearer token.
In previos step, without authorization, my api worked.
This is my api controller in my TablesReach.API project:
...
namespace TablesReach.Controllers
{
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
private readonly DataContext _context;
public UsersController(DataContext context)
{
_context = context;
}
// GET: api/Users
[HttpGet]
public async Task<ActionResult<IEnumerable<User>>> GetUsers()
{
return await _context.Users.ToListAsync();
}
...
this is my Startup.cs of my api project:
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("Bearer")
.AddIdentityServerAuthentication(opts =>
{
opts.Authority = "http://localhost:5000";
opts.RequireHttpsMetadata = false;
opts.ApiName = "TablesReachApi";
});
services.AddDbContext<DataContext>(opts => opts.UseInMemoryDatabase("UNWDb"));
services.AddControllers();
}
// 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();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.UseAuthentication();
}
}
My other project TablesReach.IdentityServer is host on localhost:5000
and Im being able to get bearer token, so I assume that this project is quite OK.
identityServer startup.cs class:
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.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiScopes(Config.GetAllApiResources())
.AddInMemoryClients(Config.GetClients());
}
// 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.UseRouting();
//app.UseAuthorization();
//app.UseEndpoints(endpoints =>
//{
// endpoints.MapControllerRoute(
// name: "default",
// pattern: "{controller=Home}/{action=Index}/{id?}");
//});
app.UseIdentityServer();
}
}
and Config.cs:
public class Config
{
public static IEnumerable<ApiScope> GetAllApiResources()
{
return new List<ApiScope>
{
new ApiScope("TablesReachApi", "Api for solution")
};
}
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "client",
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = { "TablesReachApi" }
}
};
}
}
Note: When I remove annotation [Authorize] from my api controller I can reach my method.
For some middleware, order matters. Authentication and authorization, for example, can't go in the order that you have put them in the API. Microsoft has some clear documentation on this for you to read here..

Is there anyone who can offer me a solution to this error? I can't understand where I'm wrong in configuring IdentityServer4

The image is here for Postman Request
It's a problem that I can't understand in the end, which is that it always gets a {"error": "invalid_request"}.
I request from Postman, using ImplicitFlow and OAuth 2.0, but I always get the same error.
I attach below the settings I made for IdentityServer4. I hope it will be useful to someone and can give me a solution.
public static class Config
{
public static IEnumerable<ApiResource> ApiResources()
{
return new[] {
new ApiResource("SkyEye.API", "SkyEye.API")
};
}
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId="ReactClient",
ClientName="SkyEye.SPA",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RedirectUris = new List<string>
{
"https://localhost:5002",
},
PostLogoutRedirectUris = new []{
"https://localhost:5002"
},
AllowedScopes= {
// IdentityServerConstants.StandardScopes.Profile,
// IdentityServerConstants.StandardScopes.OpenId,
"SkyEye.API"
}
}
};
}
public static IEnumerable<TestUser> Users()
{
return new[]
{
new TestUser
{ SubjectId = "1",
Username = "lascodaniil",
Password="password"
}
};
}
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddTestUsers(Config.Users().ToList())
.AddInMemoryClients(Config.GetClients())
.AddInMemoryApiResources(Config.ApiResources());
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseDeveloperExceptionPage();
app.UseIdentityServer();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
I think your redirect URL is not valid. The URL in postman is different than the one provided in IDs configuration. The URL should be an exact match
Try changing this
RedirectUris = new List<string>
{
"https://localhost:5002",
},
to this
RedirectUris = new List<string>
{
"https://localhost:5002/connect/token",
},
Hope it helps

cant access JWT token because of CORS using .NetCore

I have a very strange issue on the back end side of my application,here is my Startup:
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.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
string securityKey = "My_First_Key_generated_by_myself";
var semetricSecurityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(securityKey));
services.AddCors(options =>
{
options.AddPolicy(
"EnableCORS",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials().Build());
});
services.AddAuthentication().AddJwtBearer(
options =>
{
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "morteza",
ValidAudience = "pass",
IssuerSigningKey = semetricSecurityKey
};
}
);
}
// 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();
}
app.UseAuthentication();
//app.UseHttpsRedirection();
app.UseCors("EnableCORS");
app.UseMvc();
}
My Athentication Controller to generate Token:
[HttpPost("token")]
// [Authorize]
public ActionResult GetToken(string username)
{
// return Ok("hi Morteza");
if (username == "morteza") {
string securityKey = "My_First_Key_generated_by_myself";
var semetricSecurityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(securityKey));
var claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.Role, "Admin"));
var signIncredentials = new SigningCredentials(semetricSecurityKey, SecurityAlgorithms.HmacSha256Signature);
var token = new JwtSecurityToken(
issuer: "morteza",
audience: "pass",
expires: DateTime.Now.AddHours(1),
claims: claims,
signingCredentials: signIncredentials);
return Ok(new JwtSecurityTokenHandler().WriteToken(token));
}
else
{
return null;
}
}
I have no idea why I get Access to XMLHttpRequest at 'https://localhost:44361/api/Auth/token/' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource when I consume it on the front end?
You need to call app.UseCors() before app.UseAuthentication() because your code at the moment is trying to authenticate before the CORS policy is being applied.
Your Configure method should look like this:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// Moved this line
app.UseCors("EnableCORS");
app.UseAuthentication();
//app.UseHttpsRedirection();
app.UseMvc();
}
See the documentation on middleware order and recommended order for common services

How to Authorize Controller .NET Core API

I'm able to generate tokens succesfully when user login the app.But after I added [Authorize] on my controller,that token comes from header cannot pass the authorization.On Postman returns Unauthorized even though sending up-to date token in header to controller.Before added [Authorize] that worked very well
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.AddDbContext<DataContext>(x => x.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));
services.AddControllers().AddNewtonsoftJson(opt => {
opt.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});
services.AddCors();
services.AddAutoMapper(typeof(AppointmentRepository).Assembly);
services.AddScoped<IHospitalRepository, HospitalRepository>();
services.AddScoped<IAppointmentRepository, AppointmentRepository>();
services.AddScoped<IPatientRepository, PatientRepository>();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII
.GetBytes(Configuration.GetSection("AppSettings:Token").Value)),
ValidateIssuer = false,
ValidateAudience = false
};
});
services.AddControllers();
}
// 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();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseCors(x => x.WithOrigins().AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
login Method in controller
[HttpPost("login")]
public async Task<IActionResult> Login(PatientLoginDto patientLoginDto)
{
//if user exists or not
var patientFromRepo = await _repo.Login(patientLoginDto.IdentityNumber, patientLoginDto.Password);
if (patientFromRepo == null)
{ return Unauthorized(); }
var claims = new[]
{
//Token has two claim username and id
new Claim(ClaimTypes.NameIdentifier,patientFromRepo.Id.ToString()),
new Claim(ClaimTypes.NameIdentifier,patientFromRepo.Name)
};
//key generated
var key = new SymmetricSecurityKey(Encoding.UTF8
.GetBytes(_config.GetSection("AppSettings:Token").Value));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha512Signature);
var tokenDescriptor = new SecurityTokenDescriptor
{
//passing claims
Subject = new ClaimsIdentity(claims),
//expiry date in hours
Expires = DateTime.Now.AddDays(1),
SigningCredentials = creds
};
var tokenHandler = new JwtSecurityTokenHandler();
//storing token here(based on token descriptor object)
var token = tokenHandler.CreateToken(tokenDescriptor);
var patient = _mapper.Map<PatientLoggedinDto>(patientFromRepo);
return Ok(new
{
//as response send back to the client
token = tokenHandler.WriteToken(token),
patient
});
}
}
You need register the AuthenticationMiddleware before app.UseAuthorization();:
app.UseRouting();
app.UseAuthentication(); // add this line. NOTE The order is important.
app.UseAuthorization();
// ... other middlewares

blazor webassembly System.Net.Http.HttpRequestException: Response status code does not indicate success: 400 (Bad Request)

I have a blazor project that is dotnet core hosted. I am working on blazor authentication following this tutorial. Everything is ok on the server side because I was able to use Postman to create users successfully. I have tried different suggestions online but non work for me. Some suggested CORS which I fixed, some route which I amended but problem still persist.
I have been having issue debugging as I cant get break point in the browser console. I have debugged some blazor project I could set break points and view local variables but I couldnt with the current project. I dont know if its a bug.
server startup.cs
namespace EssentialShopCoreBlazor.Server
{
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration _configuration)
{
Configuration = _configuration;
}
readonly string AllowSpecificOrigins = "allowSpecificOrigins";
// 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.AddDbContext<DbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<IdentityUsersModel, IdentityRole>()
.AddEntityFrameworkStores<DbContext>()
.AddDefaultTokenProviders();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["JwtIssuer"],
ValidAudience = Configuration["JwtAudience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtSecurityKey"]))
};
});
services.AddCors(options =>
{
options.AddPolicy(AllowSpecificOrigins,
builder =>
{
builder.WithOrigins("https://localhost:44365", "https://localhost:44398")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
});
});
services.AddScoped<AccountAuthController>();
services.AddMvc().AddNewtonsoftJson();
services.AddResponseCompression(opts =>
{
opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
new[] { "application/octet-stream" });
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseResponseCompression();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBlazorDebugging();
}
app.UseCors(AllowSpecificOrigins);
app.UseStaticFiles();
app.UseClientSideBlazorFiles<Client.Startup>();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapFallbackToClientSideBlazor<Client.Startup>("index.html");
});
}
}
}
controller
namespace EssentialShopCoreBlazor.Server.Controllers
{
[Route("essential/users/")]
public class AccountAuthController : ControllerBase
{
private static UserModel LoggedOutUser = new UserModel { IsAuthenticated = false };
private readonly UserManager<IdentityUsersModel> userManager;
private readonly IConfiguration configuration;
private readonly SignInManager<IdentityUsersModel> signInManager;
public AccountAuthController(UserManager<IdentityUsersModel> _userManager, SignInManager<IdentityUsersModel> _signInManager, IConfiguration _configuration)
{
userManager = _userManager;
signInManager = _signInManager;
configuration = _configuration;
}
[HttpPost]
[Route("create")]
public async Task<ActionResult<ApplicationUserModel>> CreateUser([FromBody] ApplicationUserModel model)
{
var NewUser = new IdentityUsersModel
{
UserName = model.UserName,
BusinessName = model.BusinessName,
Email = model.Email,
PhoneNumber = model.PhoneNumber
};
var result = await userManager.CreateAsync(NewUser, model.Password);
if (!result.Succeeded)
{
var errors = result.Errors.Select(x => x.Description);
return BadRequest(new RegistrationResult { Successful = false, Errors = errors });
}
return Ok(new RegistrationResult { Successful = true });
}
}}
client service
public class AuthService : IAuthService
{
private readonly HttpClient _httpClient;
private readonly AuthenticationStateProvider _authenticationStateProvider;
private readonly ILocalStorageService _localStorage;
string BaseUrl = "https://localhost:44398/essential/users/";
public AuthService(HttpClient httpClient,
AuthenticationStateProvider authenticationStateProvider,
ILocalStorageService localStorage)
{
_httpClient = httpClient;
_authenticationStateProvider = authenticationStateProvider;
_localStorage = localStorage;
}
public async Task<RegistrationResult> Register(ApplicationUserModel registerModel)
{
var result = await _httpClient.PostJsonAsync<RegistrationResult>(BaseUrl + "create", registerModel);
return result;
}
}