I have an ASP.NET Core MVC application that uses JWT for validation
I add the authentication in the startup class, using our token secret in our appsettings file to validate the token.
services.Configure<ApplicationSettings>(Configuration.GetSection("AppSettings"));
var key = System.Text.Encoding.UTF8
.GetBytes(Configuration.GetSection("AppSettings:Token").Value);
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 TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false,
ClockSkew = TimeSpan.Zero
};
});
And add the authorization middleware
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors("MyPolicy");
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseAuthentication();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
Now when a user tries to login the following controller method is run, using the same token secret to generate the token.
[HttpPost("login")]
public async Task<IActionResult> Login([FromBody] UserForLoginDto userForLoginDto)
{
var user = await _userManager.FindByNameAsync(userForLoginDto.Username);
var result = await _signInManager
.CheckPasswordSignInAsync(user, userForLoginDto.Password, false);
if (result.Succeeded)
{
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim("UserID",user.Id.ToString())
}),
Expires = DateTime.UtcNow.AddDays(1),
SigningCredentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8
.GetBytes(appSettings.Token)), SecurityAlgorithms.HmacSha256Signature)
};
var tokenHandler = new JwtSecurityTokenHandler();
var securityToken = tokenHandler.CreateToken(tokenDescriptor);
var token = tokenHandler.WriteToken(securityToken);
return Ok(new { token });
}
return Unauthorized();
}
So when the user logs in, a token is generated and send back to the client.
At this point I would expect that I could just add [Authorize] attribute to a controller method, and then the MVC framework will look for a valid token in the http headers. So I create a test controller method
[HttpGet]
[Authorize]
public IActionResult Get()
{
return Ok("Test");
}
And send a request that corresponds to the test controller method with the Authorization header set to Bearer <Token> yet I still get a 401 unauthorized.
Can anyone explain why this might happen? Please tell me if you need additional information.
I think it's the matter of using your middleware:
app.UseRouting();
app.UseAuthorization();
app.UseAuthentication();
Could try it in the following way:
app.UseAuthentication();
app.UseRouting();
app.UseAuthorization();
So first, we use authenticate the user - where the middleware reads the token and inject the identity to http context
Related
I have a .NET Core Web API that accepts a JWT bearer token from a Google sign-in button for authentication. It appears that authentication is succeeding as I am able to get the ClaimsPrincipal from the HttpContext.User in my controller methods. However, when I add the [Authorize] attribute to my controller, I get back a 403 for failed authorization. I believe I am using the default authorization which should allow any authenticated user. What am I missing?
Here is the SecurityTokenValidator that I am using:
public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
{
var payload = GoogleJsonWebSignature.ValidateAsync(securityToken, _validationSettings).Result;
validatedToken = _jwtHandler.ReadJwtToken(securityToken);
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, payload.Name),
new Claim(ClaimTypes.Name, payload.Name),
new Claim(JwtRegisteredClaimNames.FamilyName, payload.FamilyName),
new Claim(JwtRegisteredClaimNames.GivenName, payload.GivenName),
new Claim(JwtRegisteredClaimNames.Email, payload.Email),
new Claim(JwtRegisteredClaimNames.Sub, payload.Subject),
new Claim(JwtRegisteredClaimNames.Iss, payload.Issuer),
};
var principal = new ClaimsPrincipal();
principal.AddIdentity(new ClaimsIdentity(claims));
return principal;
}
Here is where I wire that up in Startup.cs:
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
var googleClientId = Configuration[ConfigurationKeys.GoogleClientId];
if(googleClientId == null)
{
throw new Exception($"Configuration property not found: {ConfigurationKeys.GoogleClientId}");
}
var validationSettings = new ValidationSettings();
validationSettings.Audience = new List<string> { googleClientId };
o.SecurityTokenValidators.Clear();
o.SecurityTokenValidators.Add(new GoogleSecurityTokenValidator(validationSettings));
});
Here is where I configure the app:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors(x => x
.AllowAnyMethod()
.AllowAnyHeader()
.SetIsOriginAllowed(origin => true)
.AllowCredentials()
);
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
I have finally found the problem. When creating a ClaimsIdentity object, the AuthenticationType needs to be specified. For my solution, I changed set it to JwtBearerDefaults.AuthenticationScheme although I'm not sure that is correct.
principal.AddIdentity(new ClaimsIdentity(claims, JwtBearerDefaults.AuthenticationScheme));
I found the answer here: https://stackoverflow.com/a/20254797/3984834
Trying to setup JWT with Ast.Net Core app and somehow when I use the [Authorize] attribute on the method it shows Bearer error="invalid_token"
Not sure what I am missing here.
AppSettings:
"Jwt": {
"Key": "ThisisaKeyforAPIAccess",
"Issuer": "TestSite.com"
}
Method to generate Access Token:
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(issuer: _config["Jwt:Issuer"],
audience: _config["Jwt:Issuer"],
expires: DateTime.Now.AddMinutes(10),
signingCredentials: credentials);
return new
{
token = new JwtSecurityTokenHandler().WriteToken(token),
expiration = token.ValidTo
};
Auth.cs (To Check Token)
public static IServiceCollection AddAuthentication(this IServiceCollection services, IConfiguration configuration)
{
var issuerID = configuration.GetSection("Jwt").GetValue<string>("Issuer");
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 = issuerID,
ValidIssuer = issuerID,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["Jwt:Key"]))
};
});
return services;
}
and finally in Startup.cs
services.AddAuthentication(_configuration);
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme).RequireAuthenticatedUser().Build();
});
and in Configure method for Startup class
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApiVersionDescriptionProvider provider)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseAuthentication();
app.UseMvc();
}
If I use the [Authorize] attribute I am getting invalid token on the method. Not sure what I am missing here.
I followed this tutorial to configure JWT authorization in my Web API app. The token generation and handout works fine, but when I send a request back to the server with the token, it doesn't populate the identity, so it fails if authorization is required.
I've tested both with a reactjs frontend and Postman. Both end up returning nothing (without Authorize decorator - User.Identity.isAuthorized is false), or 404 with the decorator. I have confirmed that the token is being sent properly.
I'm also using Identity, if that matters.
ConfigureServices method
public void ConfigureServices(IServiceCollection services)
{
...
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
};
});
}
Configure method
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider serviceProvider)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAuthentication();
app.UseCors("SiteCorsPolicy");
app.UseMvc();
...
}
Function to build the token
private string BuildToken(AuthViewModel user)
{
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.Username),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken
(
_config["Jwt:Issuer"],
_config["Jwt:Audience"],
//claims,
expires: DateTime.Now.AddMinutes(30),
signingCredentials: creds
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
Excerpt from appsettings.json
"Jwt": {
"Key": "<secret stuff>",
"Issuer": "http://localhost:53530/",
"Audience": "http://localhost:8080/"
}
Test function I'm trying to call but is failing
[HttpGet("currentuser"), Authorize]
public async Task<ApplicationUser> GetCurrentUser()
{
var username = User.Identity.Name;
return await _context.ApplicationUsers.SingleOrDefaultAsync(u => u.UserName == username);
}
I figured it out. I had to add a new Authorization Policy.
services.AddAuthorization(auth =>
{
auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser().Build());
});
Then I decorated the controller with
[Authorize("Bearer"]
I've been messing with this for a couple days, trying different tutorials, so I know this was working at one point without the policy. Dunno why I needed it this time or why it wasn't part of the tutorial.
If someone figures out what I screwed up in the first place, I'm all ears.
I ran into the same issue (.net core 2.1) and was really happy to make it work using your answer #atfergs.
After fiddling with the whole setup I found out that no new Authorization Policy is required.
It is sufficient to decorate the controller with
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
considering the following setup
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{...}
Now
User?.Identity?.IsAuthenticated
is true :)
Cheers!
I started doing angular2 + asp.net core application, started implementing Auth0. I created client application and a user.
Here is client application setup, provided url for Api:
User login works fine:
Now I have an api with this controller:
[Route("api")]
public class PingController : Controller
{
[Authorize]
[HttpGet]
[Route("ping/secure")]
public string PingSecured()
{
return "All good. You only get this message if you are authenticated.";
}
}
And in startup.cs I tried implementing like this:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
var options = new JwtBearerOptions
{
Audience = "uUdThU122xYPugR8gLoNTr3HdJ6sWvQV",
Authority = "https://dntquitpls.eu.auth0.com/",
};
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
};
app.UseJwtBearerAuthentication(options);
app.UseCors(builder =>
builder.WithOrigins("http://localhost:61290/").AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod()
);
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapWebApiRoute("defaultApi",
"api/{controller}/{id?}");
});
}
And it does not work getting this:
Api part is done by Auth0 Api tutorial, for example if I create a Api and there is a test Bearer token it works with that in api, also i configure Startup.cs file by that Api, but unfortunately with my Bearer token from response does not work.
Please any ideas why it does not work and I am not getting authorized?
Found a solution, now it works, the problem was in Startup.cs file in options HS256 Encoding, which is used for UseJwtBearerAuthentication, solution:
var keyAsBytes = Encoding.ASCII.GetBytes("CLIENT_SECRET");
var options = new JwtBearerOptions
{
TokenValidationParameters =
{
ValidIssuer = "https://dntquitpls.eu.auth0.com/",
ValidAudience = "uUdThU122xYPugR8gLoNTr3HdJ6sWvQV",
IssuerSigningKey = new SymmetricSecurityKey(keyAsBytes)
}
};
app.UseJwtBearerAuthentication(options);
source:
http://www.jerriepelser.com/blog/using-roles-with-the-jwt-middleware/
if you want to work with RS256 encoding use this:
var certificationData = Configuration["auth0:certificate"];
var certificate = new X509Certificate2(Convert.FromBase64String(certificationData));
var options = new JwtBearerOptions()
{
Audience = Configuration["auth0:clientId"],
Authority = Configuration["auth0:authority"],
AutomaticChallenge = true,
AutomaticAuthenticate = true,
TokenValidationParameters = {
ValidIssuer = Configuration["auth0:authority"],
ValidAudience = Configuration["auth0:clientId"],
IssuerSigningKey = new X509SecurityKey(certificate)
}
};
app.UseJwtBearerAuthentication(options);
I am working on a sample SPA application to get my hands on ASP.NET 5. I am using Visual Studio Community 2015 RC.
I am stuck on Bearer token generation. I need to generate a token for AngularJS app so that I can call and authenticate APIs.
Have a look at this similar question Token Based Authentication in ASP.NET Core
Matt DeKrey's answer may solve your problem.
You can implement claim based authentication like below;
Add a method in Startup.cs
public void ConfigureAuthentication(IServiceCollection services)
{
var key = Encoding.ASCII.GetBytes("very-secret-much-complex-secret");
var tokenValidationParameters = new TokenValidationParameters
{
// The signing key must match
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
// Validate the JWT issuer (Iss) claim
ValidateIssuer = false,
//ValidIssuers = validIssuerList,
// Validate the JWT audience (Aud) claim
ValidateAudience = false,
//ValidAudiences = validAudienceList,
// Validate token expiration
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(o =>
{
o.TokenValidationParameters = tokenValidationParameters;
});
}
And call this method in ConfigureServices method on Startup.cs
public void ConfigureServices(IServiceCollection services)
{
//DI Injections
services.AddScoped<IAuthService, AuthService>();
services.AddScoped<IAudienceService, AudienceService>();
ConfigureAuthentication(services);
services.AddMvc(
options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
}
Then, UseAuthentication in the Configure method
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// 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.UseAuthentication();
app.UseHttpsRedirection();
app.UseMvc();
}
Above we configured our API to use JWT authentication as authorization layer. Lets see how we generate a valid token below;
public async Task<string> Authenticate(string apiKey, string sharedSecret)
{
//get audience by apikey and password from database
//create token from createdobject
var audience = await audienceService.GetByCredentials(apiKey, sharedSecret);
// return null if auudience not found
if (audience == null)
return null;
// authentication successful so generate jwt token
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes("very-secret-much-complex-secret");
var signingCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature);
//arange claims from permissions
var claims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.Sub, audience.Name),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
};
claims.AddRange(audience.Permissions.Where(p => p.Value).Select(p => new Claim(ClaimsIdentity.DefaultRoleClaimType, p.Key.GetHashCode().ToString())));
var token = new JwtSecurityToken(
audience.Name,
audience.Name,
claims,
expires: DateTime.UtcNow.AddDays(7),
signingCredentials: signingCredentials
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
You can find the whole project in my GitHub repo:https://github.com/ilkerkaran/simple-claim-based-auth