Multiple JWT bearer authentication in .net core 2.1 - Claims issue - authentication

Project: .net core 2.1 APIs
In my project I have a requirement to include 2 JWT bearer authentication.
a) We create token JWT internally and use it for authentication
b) We get JWT token from external third party and need to get this authenticated as well.
I tried following code in start up:
services.AddAuthentication( )
.AddJwtBearer("InteralBearer", options =>
{
SymmetricSecurityKey key = TokenGenerator.GenerateKey();
options.Audience = "***************";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = "***************",
ValidateAudience = true,
ValidAudience = "***************",
ValidateIssuerSigningKey = true,
IssuerSigningKey = key,
ValidateLifetime = true
};
})
.AddJwtBearer("ExternalBearer", options =>
{
options.Audience = "***************";
options.Authority = "***************";
});
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes("ExternalBearer", "InteralBearer")
.Build();
options.AddPolicy("Applicant", new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes("ExternalBearer", "InteralBearer")
.RequireClaim("role", "Applicant")
.Build());
});
In my controller I have:
[ApiController]
[Authorize(Policy = "Applicant")]
public class ApplicantController : ApplicantAbstract
{
}
I also have custom autorization filter:
public class SelfAuthorizationFilter : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
ClaimsPrincipal principal = context.HttpContext.User;
........
}
}
When I above set up, issue is, context.HttpContext.User does not return any claims as part of "Identity" object in the request. I am expecting "Claims" object to have different claims which is already configured.
Every thing works fine if I have either "InternalBearer" or "ExternalBearer", but not both.
What am I doing wrong here?

Related

Check if user exists in ASP.NET Core WebAPI JWT Authentication

I have succesfully setup JWT authentication/authorization in my WebAPI, but there's one problem: I can create a new user account, generate it's JWT token, then delete the account while the token is still valid.
How and where should I check if the user associated with the token actually exists before authorizing?
Here's my code to setup JWT (Startup.cs):
var secretKey = Configuration.GetValue<string>("SecretKey");
var symmetricKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "localhost",
ValidAudience = "localhost",
IssuerSigningKey = symmetricKey
};
});
I'm using the [Authorize] attribute on my controllers and the user ID is in the JWT token.
Thanks in advance!
You can also validate the user in AddJwtBearer events :
options.Events = new JwtBearerEvents()
{
OnTokenValidated = context =>
{
//get userid if type is "userid"
var userid = context.Principal.Claims.Where(x => x.Type == "userid").FirstOrDefault().Value;
if (true )
{
context.Fail("invaild token");
}
return Task.CompletedTask;
},
};
If you want to check database in that event , you can use dependency inject to get db context like :
var dbcontext = context.HttpContext.RequestServices.GetRequiredService<ApplicationDbContext>();

Cookie authentication not working properly with JWT authentication ASP.NET CORE

I am practicing writing web applications using ASP.NET CORE and I came across a problem with Identity. I tried searching online to see if someone else has had such a problem to no avail.
I have built a simple web api that uses JWT for authentication and everything works perfectly. But I needed to also enable users to login using forms i.e cookie authentication. The following is my configure services method.
private static void ConfigureJwt(IConfiguration configuration, IServiceCollection services)
{
services.AddSingleton<JwtSettings>();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddCookie( conf =>
{
conf.SlidingExpiration = true;
conf.LoginPath = "/account/login";
conf.LogoutPath = "/account/logout";
})
.AddJwtBearer(options =>
{
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey =
new SymmetricSecurityKey(Encoding.ASCII.GetBytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")),
ValidateAudience = false,
ValidateIssuer = false,
RequireExpirationTime = false,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero,
ValidateActor = true
};
});
}
So, since the DefaultChallengeScheme is set to "JwtBearerDefaults.AuthenticationScheme" I assumed that, if I wanted to authorize a user who has logged in using cookie authentication, I should just specify the cookie authentication scheme in that particular controller method like below.
[Route("[controller]/[action]")]
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
public class HomeController : Controller
{
// GET
[HttpGet]
public IActionResult Index()
{
return View();
}
}
But I am always redirected to the login page.
The only way I got this to work is when I remove the default authentication settings
private static void ConfigureJwt(IConfiguration configuration, IServiceCollection services)
{
var jwtSettings = new JwtSettings();
configuration.Bind(nameof(JwtSettings), jwtSettings);
services.AddSingleton<JwtSettings>();
services.AddAuthentication()
.AddCookie( conf =>
{
conf.SlidingExpiration = true;
conf.LoginPath = "/account/login";
conf.LogoutPath = "/account/logout";
})
.AddJwtBearer("jwt", options =>
{
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey =
new SymmetricSecurityKey(Encoding.ASCII.GetBytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")),
ValidateAudience = false,
ValidateIssuer = false,
RequireExpirationTime = false,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero,
ValidateActor = true
};
});
}
Then use the normal [Authorize] attribute on cookie related routes
[Route("[controller]/[action]")]
[Authorize]
public class HomeController : Controller
{
// GET
[HttpGet]
public IActionResult Index()
{
return View();
}
}
Then in all my API routes, I specified the JWT's authentication scheme
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[HttpGet(ApiRoutes.Posts.GetAll)]
public async Task<IActionResult> GetAll()
{
var posts = await _postService.GetAllAsync();
return Ok(posts);
}
So my question is, why didn't the initial configuration work? And since my app mainly JWT uses authentication, I would like for it to be the default authentication scheme and only specify cookie authentication scheme in few controller methods since it's rarely used. Is this possible? If yes, how can I achieve this?
After more digging, I stumbled on this thread, which answered my question.
I had misunderstood how authentication works in an asp.net core app that uses identity.
When you use Identity to authenticate and login a user, the default authentication scheme used is called "Identity.Applicaiton" and not "Cookies".
var result = await _signInManager.PasswordSignInAsync(loginModel.Email, loginModel.Password, true, false);
if (result.Succeeded)
{
return LocalRedirect("/home/index");
}
But if you want to use the "Cookies" authentication scheme, you have to authenticate and login a user using HttpContext.SignInAsync as seen below and explicitly select "Cookies" as your authentication scheme.
var claims = new[]
{
new Claim("email", user.Email),
};
var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(identity));

ASP.Net Core - JWT Authentication with WebAPI and MVC Frontend not working

The Project consists of two Parts:
ASP.Net Core API
ASP.Net Core MVC Frontend
Basically, what I want to do is authentication via JWT. So the API issues JWT and the MVC Frontend uses Identity with the claims and roles declared in the JWT.
Startup.cs in the API:
private const string SecretKey = "my_Secret_Key";
private readonly SymmetricSecurityKey _signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(SecretKey));
#region JWT Auth
// jwt wire up
// Get options from app settings
var jwtAppSettingOptions = Configuration.GetSection(nameof(JwtIssuerOptions));
// Configure JwtIssuerOptions
services.Configure<JwtIssuerOptions>(options =>
{
options.Issuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
options.Audience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)];
options.SigningCredentials = new SigningCredentials(_signingKey, SecurityAlgorithms.HmacSha256);
});
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)],
ValidateAudience = true,
ValidAudience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)],
ValidateIssuerSigningKey = true,
IssuerSigningKey = _signingKey,
RequireExpirationTime = false,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(configureOptions =>
{
configureOptions.ClaimsIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
configureOptions.TokenValidationParameters = tokenValidationParameters;
configureOptions.SaveToken = true;
});
// api user claim policy
services.AddAuthorization(options =>
{
options.AddPolicy(Constants.Policies.ApiAccess, policy => policy.RequireClaim(Constants.JwtClaimIdentifiers.Rol, Constants.JwtClaims.ApiAccess));
});
#endregion
JWT Generation:
public async Task<string> GenerateEncodedToken(string userName)
{
User user = _userManager.GetUserByUserName(userName);
List<string> userRoles = _userManager.GetRoles(user.Guid);
var claimsToEncode = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, userName),
new Claim("web", user.WebId),
new Claim(JwtRegisteredClaimNames.Jti, await _jwtOptions.JtiGenerator()),
new Claim(JwtRegisteredClaimNames.Iat, ToUnixEpochDate(_jwtOptions.IssuedAt).ToString(), ClaimValueTypes.Integer64),
new Claim(Constants.JwtClaimIdentifiers.Rol,Constants.JwtClaims.ApiAccess),
};
// Create the JWT security token and encode it.
var jwt = new JwtSecurityToken(
issuer: _jwtOptions.Issuer,
audience: _jwtOptions.Audience,
claims: claimsToEncode,
notBefore: _jwtOptions.NotBefore,
expires: _jwtOptions.Expiration,
signingCredentials: _jwtOptions.SigningCredentials);
jwt.Payload.Add("roles", userRoles.ToArray());
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
return encodedJwt;
}
Authorization works like a charm with this in the API.
Now I want to do the following:
Implement the same in the Frontend, so that:
MVC Frontend receives Credentials, send them to the API, get Token, and Authorize with the Claims and Roles in the Token.
I tried several things, but none of them worked so far.
What do I have to insert in the Startup.cs in the Frontend so that Identity checks not against the secret key (which the Frontend is not allowed to have) but against a public key? Or do I have to implement a Endpoint in the API which validates the JWT remotely?
When you get the token in web client, you can store it in a session object and send that whenever you are requesting something from the webapi

.NET Core 2 Web API JWT token not recognized

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!

How can I implement Bearer Token in MVC 6 API vNext?

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