no authentication handler is configured to authenticate for the scheme: "bearer" .net core 2.0 - .net-core

I am new to .net Core, I am trying to upgrade a project from .net Core 1.0 to 2.0,
when I am trying to access the API I am getting this error.
"no authentication handler is configured to authenticate for the scheme: "bearer" .net core 2.0".
As UseJwtBearerAuthentication doesnt work in .net core 2.0 I replacing it with AddAuthentication.
Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime appLifetime)
{
app.UseAuthentication();
app.UseCors("AllowAll");
app.UseMvc();
}
public void ConfigureServices(IServiceCollection services)
{
var tvp = new TokenValidationParameters
{
// The signing key must match!
ValidateIssuerSigningKey = true,
IssuerSigningKey = _signingKey,
// Validate the JWT Issuer (iss) claim
ValidateIssuer = true,
ValidIssuer = "ABC",
// Validate the JWT Audience (aud) claim
ValidateAudience = true,
ValidAudience = "User",
// Validate the token expiry
ValidateLifetime = true,
// If you want to allow a certain amount of clock drift, set that here:
ClockSkew = TimeSpan.FromMinutes(5)
};
services.AddSingleton(s => tvp);
ConfigureAuth(services, tvp);
}
private void ConfigureAuth(IServiceCollection services, TokenValidationParameters tvp)
{
//TODO: Change events to log something helpful somewhere
var jwtEvents = new JwtBearerEvents();
jwtEvents.OnAuthenticationFailed = context =>
{
Debug.WriteLine("JWT Authentication failed.");
return Task.WhenAll();
};
jwtEvents.OnChallenge = context =>
{
Debug.WriteLine("JWT Authentication challenged.");
return Task.WhenAll();
};
jwtEvents.OnMessageReceived = context =>
{
Debug.WriteLine("JWT Message received.");
return Task.WhenAll();
};
jwtEvents.OnTokenValidated = context =>
{
Debug.WriteLine("JWT Message Token validated.");
return Task.WhenAll();
};
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(o =>
{
o.TokenValidationParameters = tvp;
o.Events = jwtEvents; });
}
Under Configure method I have:
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseAuthentication();
app.UseCors("AllowAll");
app.UseRequestResponseLogging();
app.UseNoCacheCacheControl();
app.UseMvc();
AuthController.cs
[HttpPost]
[EnableCors("AllowAll")]
[AllowAnonymous]
[Authorize(AuthenticationSchemes =
JwtBearerDefaults.AuthenticationScheme)]
public IActionResult Authenticate([FromBody] UserContract model)
{
}
AuthenticationMiddleware:
public class AuthenticationMiddleware
{
private readonly RequestDelegate _next;
public AuthenticationMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context, IAuthUser authUser)
{
if (context.User?.Identity != null)
{
if (context.User?.Identity?.IsAuthenticated == true)
{
authUser.Username = context.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value;
}
using (LogContext.PushProperty("Username", authUser.Username))
{
await _next.Invoke(context);
}
}
}

You can use AddJwtBearer method , please refer to below article for how to use extension :
https://developer.okta.com/blog/2018/03/23/token-authentication-aspnetcore-complete-guide
Code sample below for AddJwtBearer with options and events is for your reference :
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer("Bearer",options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "Issuer",
ValidAudience = "Audience",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("Yourkey"))
};
options.Events = new Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
{
var loggerFactory = context.HttpContext.RequestServices
.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger("Startup");
logger.LogInformation("Token-Expired");
context.Response.Headers.Add("Token-Expired", "true");
}
return System.Threading.Tasks.Task.CompletedTask;
},
OnMessageReceived = (context) =>
{
return Task.FromResult(0);
}
};
});
And use on controller/action like :
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
Don't forget to enable authentication in Configure method :
app.UseAuthentication();

Related

.NET 6.0 JWT token is not recognized from localhost, but it IS recognized through SwaggerUI-- what's up?

Problem
I have a Target Framework: .NET 6.0 API backend with an exposed API with Identity Framework implemented in it.
I can successfully obtain data 0 issue on unauthorized API Endpoints with this above them on both SwaggerUI AND my localhost frontend. [AllowAnonymous]
When it comes to authorized API Endpoints however it is a totally different story.
SwaggerUI has NO PROBLEM AT ALL when I put my generated "JWT" into the "Authorize" padlock on the side of the screen.
Localhost JWT are simply not accepted or seen or understood or something I totally don't understand.
Request seems fine
Screenshot of the API request I am sending over has the authorization with the token, I also took the token to JWT io and it can parse out the name without problem.
I've also tried removing or adding "bearer" to the authorization header sent over and that makes no difference.
Screencap of request:
https://i.imgur.com/CY7F32o.png
My entire Startup.cs
(I know showing anything less would make it harder for you all to help me so >_< )
namespace API
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DbContext>(opt =>
{
opt.UseLazyLoadingProxies();
opt.UseMySql(Configuration.GetConnectionString("DefaultConnection"), ServerVersion.AutoDetect(Configuration.GetConnectionString("DefaultConnection")), opt => opt.EnableRetryOnFailure());
});
services.AddIdentity<UserModel, IdentityRole>(options =>
{
//options.Password.RequiredLength = 5;
//options.Password.RequireLowercase
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(15);
options.User.RequireUniqueEmail = true;
})
.AddEntityFrameworkStores<DbContext>()
.AddDefaultTokenProviders();
services.Configure<CookiePolicyOptions>(options =>
{
options.MinimumSameSitePolicy = SameSiteMode.None;
options.Secure = CookieSecurePolicy.Always;
});
services.ConfigureApplicationCookie(options =>
{
options.Cookie.Name = "auth_cookie";
options.Cookie.SameSite = SameSiteMode.None;
options.LoginPath = new PathString("/api/contests");
options.AccessDeniedPath = new PathString("/api/contests");
options.Events.OnRedirectToLogin = context =>
{
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
return Task.CompletedTask;
};
});
services.Configure<SmtpSettings>(Configuration.GetSection("SMTP"));
services.AddSingleton<IEmailManager, EmailManager>();
services.AddSingleton<IAuthorizationPolicyProvider, PermissionPolicyProvider>();
services.AddScoped<IAuthorizationHandler, PermissionAuthorizationHandler>();
services.AddControllers(opt =>
{
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
opt.Filters.Add(new AuthorizeFilter(policy));
});
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new() { Title = "API", Version = "v1" });
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
In = ParameterLocation.Header,
Description = "Please insert JWT with Bearer into field",
Name = "Authorization",
Type = SecuritySchemeType.ApiKey
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement {
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] { }
}
});
});
//Allow localhost to actually contact the server.
services.AddCors(opt =>
{
opt.AddPolicy("CorsPolicy", policy =>
{
policy.AllowAnyHeader()
.AllowAnyMethod()
.WithExposedHeaders("WWW-Authenticate")
.WithOrigins("http://localhost:3000", "http://localhost:5000", "https://localhost:5000")
.AllowCredentials();
});
});
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["TokenKey"]));
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(opt =>
{
//"Normal" API Auth.
opt.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = key,
ValidateAudience = false,
ValidateIssuer = false,
ValidateLifetime = true, //~5 minute leeway?
ClockSkew = TimeSpan.Zero //force time.
};
//SignalR auth.
opt.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/chat"))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
}).AddDiscord(options =>
{
options.CorrelationCookie.SameSite = SameSiteMode.Lax;
options.ClientId = Configuration["Discord:ClientId"];
options.ClientSecret = Configuration["Discord:ClientSecret"];
options.Scope.Add("email");
//options.CallbackPath = "/";
});
services.AddScoped<IJwtGenerator, JWTGenerator>();
services.AddScoped<IUserAccessor, UserAccessor>();
services.AddScoped<IImageAccessor, ImageAccessor>();
services.Configure<Infrastructure.Images.CloudinarySettings>(Configuration.GetSection("Cloudinary"));
services.AddRazorPages().AddRazorRuntimeCompilation();
services.AddServiceLayer();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseDeveloperExceptionPage();
app.UseMiddleware<ErrorHandlingMiddleware>();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "API v1");
c.DocExpansion(Swashbuckle.AspNetCore.SwaggerUI.DocExpansion.None);
});
app.UseCors(c => c.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseCors("CorsPolicy");
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapFallbackToController("Index", "Fallback");
});
}
}
}

asp.net authorize by default (without [Authorize])

I have an asp.netcore .net 5 application.
I've got authorization working with the [Authorize] attribute above the controller endpoint.
How can I make it use authorization without having to use the [Authorize] attribute? Here is how I set up my authorization:
I have this in ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{...
FirebaseApp.Create(new AppOptions()
{
Credential = GoogleCredential.FromFile("firebase_admin_sdk.json"),
});
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://securetoken.google.com/vepo-xxx";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = "https://securetoken.google.com/vepo-xxx",
ValidateAudience = true,
ValidAudience = "vepo-xxx",
ValidateLifetime = true
};
});
...}
And in I Configure have:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, VepoContext context, ISearchIndexService searchIndexService)
{...
app.UseAuthentication();
app.UseAuthorization();
...}
See the docs for Require authenticated users. You can add a RequireAuthenticatedUser() to all controllers:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddControllers(config =>
{
// using Microsoft.AspNetCore.Mvc.Authorization;
// using Microsoft.AspNetCore.Authorization;
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
});

AuthenticateResult is returing false after successful SAML response

I am using SAML2 core middleware with ASP.Net for authentication. I am able to get a successful SAML response with statuscode of 200 that I could see in the browser but my code to authenticateResult is returning false. Once the authentication is success, I am trying to get the claims and pass the user information to the application. Could someone please help?
Here is my code to AccountController.cs :
public async Task<IActionResult> Callback(string returnUrl)
{
var authenticateResult1 = await HttpContext.AuthenticateAsync();
if (authenticateResult1.Succeeded)
{
var claimsIdentity = (ClaimsIdentity)authenticateResult1.Principal.Identity;
if (!claimsIdentity.HasClaim(c => c.Type == "ClaimTypes.Email"))
{
claimsIdentity.AddClaim(new Claim(" ClaimTypes.Email", "Test Test"));
await HttpContext.SignInAsync(authenticateResult1.Principal, authenticateResult1.Properties);
}
}
Here is what I have in startup.cs :
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddSamlCore(options =>
{
options.SignOutPath = "/signedout";
options.ServiceProvider.EntityId = "XXXXXXXX";
options.MetadataAddress = "https://xxxxx.xxxxxxx.com/simplesaml/saml2/idp/metadata.php";
options.ForceAuthn = true;
options.WantAssertionsSigned = true;
options.RequireMessageSigned = false;
options.UseTokenLifetime = false;
// Service Provider Properties (optional) - These set the appropriate tags in the metadata.xml file
options.ServiceProvider.ServiceName = "XXXXX";
options.ServiceProvider.Language = "en-US";
options.ServiceProvider.OrganizationDisplayName = "XXXXXXX";
options.ServiceProvider.OrganizationName = "xxxxxxxxx";
options.ServiceProvider.OrganizationURL = "https://xxxxxxx.com";
options.ServiceProvider.ContactPerson = new ContactType()
{
Company = "xxxxxxxxxxx",
GivenName = "Guy Fawks",
EmailAddress = new[] { "someemailhere#xxxxxxxx.com" },
// contactType = ContactTypeType.technical,
TelephoneNumber = new[] { "+1 234 5678" }
};
//Events - Modify events below if you want to log errors, add custom claims, etc.
options.Events.OnRemoteFailure = context =>
{
return Task.FromResult(0);
};
options.Events.OnTicketReceived = context =>
{
var identity = context.Principal.Identity as ClaimsIdentity;
var claims = context.Principal.Claims;
if (claims.Any(c => c.Type == ClaimTypes.Email))
{
var userId = claims.FirstOrDefault(c => c.Type == ClaimTypes.Email).Value;
var name = claims.FirstOrDefault(c => c.Type == ClaimTypes.Name);
identity.TryRemoveClaim(name);
identity.AddClaim(new Claim(ClaimTypes.Name, userId));
}
return Task.FromResult(0);
};
})
.AddCookie();
services.AddMvc();
}
// 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();
IdentityModelEventSource.ShowPII = true; //To show detail of error and see the problem
}
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.UseAuthentication(); // AT modified on 4/17
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
```

Cannot access Authorized content in .NET Core using JWT authentication

I developed a sample app where I use JWT authentication to access the private content.
After getting a token in Postman, I am able to pass it to the private controller action also via Postman and it works all well.
But when I try to use SignInManager to access, it won't work. User.Identity.Name and User.Identity.IsAuthenticated are always nulls
Here's my startup code
public class Startup
{
private AppModule appModule;
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)
{
appModule = new AppModule();
AppModule.Configuration = Configuration; //setting up of static variable
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
string connectionSring = null;
if (true) //(env.IsDevelopment())
connectionSring = appModule.GetConnectionString(EuConstants.LOCAL_CONNECTION_NAME);
else
connectionSring = appModule.GetConnectionString(EuConstants.DEFAULT_CONNECTION_NAME);
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer(connectionSring);
});
/* tell the framework to use our cusom user and role classes */
services.AddIdentity<Gn_User, Gn_Role>(options =>
{
options.Stores.MaxLengthForKeys = 128;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.ConfigureApplicationCookie(o => {
o.LoginPath = "/portal/login";
});
services.AddAuthorization();
services.AddAuthentication(option => {
option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
option.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options => {
options.SaveToken = true;
options.RequireHttpsMetadata = true;
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = Configuration["Jwt:Site"],
ValidIssuer = Configuration["Jwt:Site"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:SigningKey"]))
};
});
services.AddScoped<AppModule>(sp => appModule
);
services.AddMvc(
options =>
{
// make sure that all attributes by default required authentication
// options.Filters.Add(new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build()));
}
).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider provider)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseAuthentication();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
// tell the application to use the authentication
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Portal}/{action=Index}/{id?}");
});
}
}
and here's the authentication method to generate the token
public async Task<ActionResult> Login([FromBody] Gn_User model)
{
string password = "yaser#1234";
var user = await _userManager.FindByNameAsync(model.UserName);
if (user != null && await _userManager.CheckPasswordAsync(user, password))
{
var claim = new[] {
new Claim(JwtRegisteredClaimNames.Sub, user.UserName)
};
var signinKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(AppModule.Configuration["Jwt:SigningKey"]));
int expiryInMinutes = Convert.ToInt32(AppModule.Configuration["Jwt:ExpiryInMinutes"]);
var token = new JwtSecurityToken(
issuer: AppModule.Configuration["Jwt:Site"],
audience: AppModule.Configuration["Jwt:Site"],
expires: DateTime.UtcNow.AddMinutes(expiryInMinutes),
signingCredentials: new SigningCredentials(signinKey, SecurityAlgorithms.HmacSha256)
);
return Ok(
new
{
token = new JwtSecurityTokenHandler().WriteToken(token),
expiration = token.ValidTo
});
}
return Unauthorized();
}
You forget to add your claims to your JWT.
public async Task<ActionResult> Login([FromBody] Gn_User model)
{
string password = "yaser#1234";
var user = await _userManager.FindByNameAsync(model.UserName);
if (user != null && await _userManager.CheckPasswordAsync(user, password))
{
var claim = new[] {
new Claim(JwtRegisteredClaimNames.Sub, user.UserName)
};
var signinKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(AppModule.Configuration["Jwt:SigningKey"]));
int expiryInMinutes = Convert.ToInt32(AppModule.Configuration["Jwt:ExpiryInMinutes"]);
var token = new JwtSecurityToken(
issuer: AppModule.Configuration["Jwt:Site"],
audience: AppModule.Configuration["Jwt:Site"],
/////////////////////////////////
claims : claim,// Dont Forget To Add Claims To Your JWT
/////////////////////////////////
expires: DateTime.UtcNow.AddMinutes(expiryInMinutes),
signingCredentials: new SigningCredentials(signinKey, SecurityAlgorithms.HmacSha256)
);
return Ok(
new
{
token = new JwtSecurityTokenHandler().WriteToken(token),
expiration = token.ValidTo
});
}
return Unauthorized();
}
I worked for me by adding the following code to register the identity.
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
instead of passing the parameters as options
services.AddAuthentication(op => {
op.defaultSchema...... = JwtBearerDefaults.AuthenticationScheme
})

Core 2.0 API Auth with JWT returns unauthorized

I'm trying to add Token Authentication with JWT to my .Net Core 2.0 app. I have a simple controller that returns a list of users for testing.
[Authorize]
[Route("api/[controller]")]
public class UsersController : Controller
{
...
[HttpGet]
[Route("api/Users/GetUsers")]
public IEnumerable<ApplicationUser> GetUsers()
{
return _userManager.Users;
}
}
I have an API Controller for Token security. It has a login method which returns a Token string result.
[HttpPost(nameof(Login))]
public async Task<IActionResult> Login([FromBody] LoginResource resource)
{
if (resource == null)
return BadRequest("Login resource must be asssigned");
var user = await _userManager.FindByEmailAsync(resource.Email);
if (user == null || (!(await _signInManager.PasswordSignInAsync(user, resource.Password, false, false)).Succeeded))
return BadRequest("Invalid credentials");
string result = GenerateToken(user.UserName, resource.Email);
// Token is created, we can sign out
await _signInManager.SignOutAsync();
return Ok(result);
}
private string GenerateToken(string username, string email)
{
var claims = new Claim[]
{
new Claim(ClaimTypes.Name, username),
new Claim(ClaimTypes.Email, email),
new Claim(JwtRegisteredClaimNames.Nbf, new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds().ToString()),
new Claim(JwtRegisteredClaimNames.Exp, new DateTimeOffset(DateTime.Now.AddDays(1)).ToUnixTimeSeconds().ToString()),
};
var token = new JwtSecurityToken(
new JwtHeader(new SigningCredentials(
new SymmetricSecurityKey(Encoding.UTF8.GetBytes("the secret that needs to be at least 16 characeters long for HmacSha256")),
SecurityAlgorithms.HmacSha256)),
new JwtPayload(claims));
return new JwtSecurityTokenHandler().WriteToken(token);
}
I have a small console app just for testing the API. When I attempt to Get the Users using the jwt. I receive an immediate "unauthorized". If I remove the "[Authorize]" from the users Controller... success. It appears that my header Authorization is not recognized, but not sure why.
private static async Task<String> GetUsers(String jwt)
{
var url = "https://localhost:44300/";
var apiUrl = $"/api/Users/";
using (var client = new HttpClient() { BaseAddress = new Uri(url) })
{
client.BaseAddress = new Uri(url);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {jwt}");
using (var response = await client.GetAsync(apiUrl))
{
if (response.StatusCode == System.Net.HttpStatusCode.OK)
return await response.Content.ReadAsStringAsync();
else return null;
}
}
}
I'm basing my attempts on the article here ... some of which might be slightly out of date.
Update - Excerpt of Startup.cs
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = "Jwt";
options.DefaultChallengeScheme = "Jwt";
}).AddJwtBearer("Jwt", options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
//ValidAudience = "the audience you want to validate",
ValidateIssuer = false,
//ValidIssuer = "the isser you want to validate",
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("the secret that needs to be at least 16 characeters long for HmacSha256")),
ValidateLifetime = true, //validate the expiration and not before values in the token
ClockSkew = TimeSpan.FromMinutes(5) //5 minute tolerance for the expiration date
};
});
Configure...
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
//app.UseJwtBearerAuthentication(
// new Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions
// );
}
Solution:
This line was escaping the token therefore causing it to be invalid when passed in the next request:
var result = await response.Content.ReadAsStringAsync();
Replaced with:
var result = await response.Content.ReadAsAsync<string>();
Note: To use this ext method I had to "install-package Microsoft.AspNet.WebApi.Client"
I used JWT authentication in my one of project. I would like to show my implementation, maybe this will help you. But probably you forget to add UseAuthentication(); into configure method in startup class.
startup.cs
public void Configure(IApplicationBuilder app)
{
app.UseAuthentication();
app.UseMvc();
}
public void ConfigureServices(IServiceCollection services)
{
var appSettings = Configuration.GetSection("AppSettings");
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}
)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = true,
ValidAudience = appSettings["JwtAudience"],
ValidateIssuer = true,
ValidIssuer = appSettings["JwtIssuer"],
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(appSettings["JwtSigningKey"]))
};
});
}
generateToken method
private string GenerateToken(string email)
{
SecurityKey securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_appSettings.Value.JwtSigningKey));
var token = new JwtSecurityToken(
issuer: _appSettings.Value.JwtIssuer,
audience: _appSettings.Value.JwtAudience,
claims: new[]
{
new Claim(JwtRegisteredClaimNames.UniqueName, email),
new Claim(JwtRegisteredClaimNames.Email, email),
new Claim(JwtRegisteredClaimNames.NameId, Guid.NewGuid().ToString())
},
expires: DateTime.Now.AddMinutes(_appSettings.Value.JwtExpireMinute),
signingCredentials: new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256)
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
I had created a nuget package NetCore.Jwt to simplify this process recently. I didn't find it worth writing all the code each time you needed a Jwt, when you can handle cookies simply with the function SignInAsync. However, if you prefer the manual way, Celal's answer is a clear and straightforward guide for this process.
Alternatively, you can install NetCore.Jwt and use the following in your startup:
services.AddAuthentication(NetCoreJwtDefaults.SchemeName)
.AddNetCoreJwt(options =>
{
// configure your token options such as secret, expiry, and issuer here
});
In your Login function, you can use the extension function for HttpContext
string token = HttpContext.GenerateBearerToken( new Claim[]
{
new Claim(ClaimTypes.Name, username),
new Claim(ClaimTypes.Email, email),
new Claim(JwtRegisteredClaimNames.Nbf, new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds().ToString()),
new Claim(JwtRegisteredClaimNames.Exp, new DateTimeOffset(DateTime.Now.AddDays(1)).ToUnixTimeSeconds().ToString()),
});
In your program.cs dont forget to have this code (and in order) :
app.UseAuthentication();
app.UseAuthorization();

Resources