Fetch access token from authorization header without bearer prefix - c#

I'm using the Microsoft.AspNetCore.Authentication.JwtBearer and System.IdentityModel.Tokens.Jwt packages for my .NET Core project.
There are some controller endpoints protected by the [Authorize] annotation that have to fetch the access token from the request. Currently I'm fetching the access token in my controller method this way:
string accessTokenWithBearerPrefix = Request.Headers[HeaderNames.Authorization];
string accessTokenWithoutBearerPrefix = accessTokenWithBearerPrefix.Substring("Bearer ".Length);
and I would like to know if there is a better "ready to use" solution for this because using the code above might still lead to errors while taking the substring from the bearer token.

Here is a clever way to get the header without having to go in to the headers dictionary. This will also let the framework parse the token, which is what I believe you are looking for:
[HttpGet, Route("someEndpoint")]
public IActionResult SomeEndpoint([FromHeader] string authorization)
{
if(AuthenticationHeaderValue.TryParse(authorization, out var headerValue))
{
// we have a valid AuthenticationHeaderValue that has the following details:
var scheme = headerValue.Scheme;
var parameter = headerValue.Parameter;
// scheme will be "Bearer"
// parmameter will be the token itself.
}
return Ok();
}
You can also grab the header the old-school way:
[HttpGet, Route("someEndpoint")]
public IActionResult SomeEndpoint()
{
var authorization = Request.Headers[HeaderNames.Authorization];
if (AuthenticationHeaderValue.TryParse(authorization, out var headerValue))
{
// we have a valid AuthenticationHeaderValue that has the following details:
var scheme = headerValue.Scheme;
var parameter = headerValue.Parameter;
// scheme will be "Bearer"
// parmameter will be the token itself.
}
return Ok();
}
What's nice is AuthenticationHeaderValue.TryParse will cover oddball cases like if there is more than once space between the scheme and the token, or if there are spaces before the scheme, or spaces after the token... and trim it up for you.
Now, those cases should never happen, but... they may, and the execution of accessTokenWithBearerPrefix.Substring("Bearer ".Length); would fail. Which is why I believe you wanted a more concrete way of parsing the token.

You can set SaveToken in Startup.cs to true.
services.AddAuthentication()
.AddJwtBearer(options =>
{
// your other config
options.SaveToken = true;
});
and get access token from HttpContext with GetTokenAsync method.
using Microsoft.AspNetCore.Authentication;
public class SampleController : Controller
{
public void Index()
{
var accessToken = HttpContext.GetTokenAsync("access_token");
}
}

You can use the following code to get security token.
var stream ="[encoded jwt]";
var handler = new JwtSecurityTokenHandler();
var jsonToken = handler.ReadToken(stream);
var tokenS = handler.ReadToken(stream) as JwtSecurityToken;
Also, if you want to Ignore JWT Bearer token signature, you can refer to the code as below:
public TokenValidationParameters CreateTokenValidationParameters()
{
var result = new TokenValidationParameters
{
ValidateIssuer = false,
ValidIssuer = ValidIssuer,
ValidateAudience = false,
ValidAudience = ValidAudience,
ValidateIssuerSigningKey = false,
//IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(SecretKey)),
//comment this and add this line to fool the validation logic
SignatureValidator = delegate(string token, TokenValidationParameters parameters)
{
var jwt = new JwtSecurityToken(token);
return jwt;
},
RequireExpirationTime = true,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero,
};
result.RequireSignedTokens = false;
return result;
}

Related

How does the Jwt tokens work and Errorcode IDX12709

I implemented a Github project and don't understand how the user and the key system for Jwt work.
I now have a secret-key which is located in the AppSettings and when the user logs in, the following function is executed:
private string GenerateJwtToken(string username)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_appSettings.token);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[] { new Claim("username", username) }), //<-
Expires = DateTime.UtcNow.AddMinutes(30),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
So if I get it right then I generate here the token for the logged in user but what does the line with the username mean?
After that I store the username and the token inside the sessionStorage and if I trigger some other Controller where the [Authorize] attribute is defined, I add followed Header with the fetch:
headers: {
'Content-type': 'application/json',
'Authorization': `Bearer ${sessionStorage.getItem("token")}`,
},
Then it first run into this functions:
public async Task Invoke(HttpContext context, IAuthService authService)
{
var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
if (token != null)
attachUserToContext(context, authService, token);
await _next(context);
}
private void attachUserToContext(HttpContext context, IAuthService authService, string token)
{
try
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
tokenHandler.ValidateToken(token, new TokenValidationParameters //<- Error
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false,
ClockSkew = TimeSpan.Zero
}, out SecurityToken validatedToken);
var jwtToken = (JwtSecurityToken)validatedToken;
var userId = int.Parse(jwtToken.Claims.First(x => x.Type == "username").Value);
context.Items["User"] = "user";
}
catch
{
// do nothing if jwt validation fails
// user is not attached to context so request won't have access to secure routes
}
}
So here it validates that the token from the Header is not null and then it tries to do what!? Also why is again the username used here?
When it run the ValidateToken function it return a error: IDX12709: CanReadToken() returned false. JWT is not well formed: '[PII of type 'System.String' is hidden
https://github.com/cornflourblue/dotnet-5-jwt-authentication-api/tree/279c8058669bbfa59902a4473f62e5371167340c
JWT is split into three parts.
The first one is a header with information like encrypting algorithm.
The second part is the payload, where you actually can find your claims, in your case, it's gonna be a username, exp date,
you can add here whatever you need.
Unique username or id is added here for the server to know to who this token belongs and who is calling the server.
In other case how would you figure out who is calling the server, you would have to save this token in some database or somewhere with the assigned user.
The last part is the signature, this part is verified with your secret key to knowing that it's not some fake token, but actually, a token created by you.
Parts are split by dots.
Hard to say why your token validation fails, at first I would try checking your created token on https://jwt.io and see what's inside.
Maybe you validate token with bearer prefix, as it's written that token is formed badly, so it can be the issue.
One more thing instead of using custom middleware auth, you can use the JWT default authentication scheme
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = false,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII
.GetBytes(Configuration.GetSection("AppSettings:Token").Value)),
//...
};
});

C# generate JWT token, where verify signature should be placed [duplicate]

I'm trying to support JWT bearer token (JSON Web Token) in my web API application and I'm getting lost.
I see support for .NET Core and for OWIN applications.
I'm currently hosting my application in IIS.
How can I achieve this authentication module in my application? Is there any way I can use the <authentication> configuration similar to the way I use forms/Windows authentication?
I answered this question: How to secure an ASP.NET Web API 4 years ago using HMAC.
Now, lots of things changed in security, especially that JWT is getting popular. In this answer, I will try to explain how to use JWT in the simplest and basic way that I can, so we won't get lost from jungle of OWIN, Oauth2, ASP.NET Identity, etc..
If you don't know about JWT tokens, you need to take a look at:
https://www.rfc-editor.org/rfc/rfc7519
Basically, a JWT token looks like this:
<base64-encoded header>.<base64-encoded claims>.<base64-encoded signature>
Example:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmJmIjoxNDc3NTY1NzI0LCJleHAiOjE0Nzc1NjY5MjQsImlhdCI6MTQ3NzU2NTcyNH0.6MzD1VwA5AcOcajkFyKhLYybr3h13iZjDyHm9zysDFQ
A JWT token has three sections:
Header: JSON format which is encoded in Base64
Claims: JSON format which is encoded in Base64.
Signature: Created and signed based on Header and Claims which is encoded in Base64.
If you use the website jwt.io with the token above, you can decode the token and see it like below:
Technically, JWT uses a signature which is signed from headers and claims with security algorithm specified in the headers (example: HMACSHA256). Therefore, JWT must be transferred over HTTPs if you store any sensitive information in its claims.
Now, in order to use JWT authentication, you don't really need an OWIN middleware if you have a legacy Web Api system. The simple concept is how to provide JWT token and how to validate the token when the request comes. That's it.
In the demo I've created (github), to keep the JWT token lightweight, I only store username and expiration time. But this way, you have to re-build new local identity (principal) to add more information like roles, if you want to do role authorization, etc. But, if you want to add more information into JWT, it's up to you: it's very flexible.
Instead of using OWIN middleware, you can simply provide a JWT token endpoint by using a controller action:
public class TokenController : ApiController
{
// This is naive endpoint for demo, it should use Basic authentication
// to provide token or POST request
[AllowAnonymous]
public string Get(string username, string password)
{
if (CheckUser(username, password))
{
return JwtManager.GenerateToken(username);
}
throw new HttpResponseException(HttpStatusCode.Unauthorized);
}
public bool CheckUser(string username, string password)
{
// should check in the database
return true;
}
}
This is a naive action; in production you should use a POST request or a Basic Authentication endpoint to provide the JWT token.
How to generate the token based on username?
You can use the NuGet package called System.IdentityModel.Tokens.Jwt from Microsoft to generate the token, or even another package if you like. In the demo, I use HMACSHA256 with SymmetricKey:
/// <summary>
/// Use the below code to generate symmetric Secret Key
/// var hmac = new HMACSHA256();
/// var key = Convert.ToBase64String(hmac.Key);
/// </summary>
private const string Secret = "db3OIsj+BXE9NZDy0t8W3TcNekrF+2d/1sFnWG4HnV8TZY30iTOdtVWJG8abWvB1GlOgJuQZdcF2Luqm/hccMw==";
public static string GenerateToken(string username, int expireMinutes = 20)
{
var symmetricKey = Convert.FromBase64String(Secret);
var tokenHandler = new JwtSecurityTokenHandler();
var now = DateTime.UtcNow;
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, username)
}),
Expires = now.AddMinutes(Convert.ToInt32(expireMinutes)),
SigningCredentials = new SigningCredentials(
new SymmetricSecurityKey(symmetricKey),
SecurityAlgorithms.HmacSha256Signature)
};
var stoken = tokenHandler.CreateToken(tokenDescriptor);
var token = tokenHandler.WriteToken(stoken);
return token;
}
The endpoint to provide the JWT token is done.
How to validate the JWT when the request comes?
In the demo, I have built
JwtAuthenticationAttribute which inherits from IAuthenticationFilter (more detail about authentication filter in here).
With this attribute, you can authenticate any action: you just have to put this attribute on that action.
public class ValueController : ApiController
{
[JwtAuthentication]
public string Get()
{
return "value";
}
}
You can also use OWIN middleware or DelegateHander if you want to validate all incoming requests for your WebAPI (not specific to Controller or action)
Below is the core method from authentication filter:
private static bool ValidateToken(string token, out string username)
{
username = null;
var simplePrinciple = JwtManager.GetPrincipal(token);
var identity = simplePrinciple.Identity as ClaimsIdentity;
if (identity == null || !identity.IsAuthenticated)
return false;
var usernameClaim = identity.FindFirst(ClaimTypes.Name);
username = usernameClaim?.Value;
if (string.IsNullOrEmpty(username))
return false;
// More validate to check whether username exists in system
return true;
}
protected Task<IPrincipal> AuthenticateJwtToken(string token)
{
string username;
if (ValidateToken(token, out username))
{
// based on username to get more information from database
// in order to build local identity
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, username)
// Add more claims if needed: Roles, ...
};
var identity = new ClaimsIdentity(claims, "Jwt");
IPrincipal user = new ClaimsPrincipal(identity);
return Task.FromResult(user);
}
return Task.FromResult<IPrincipal>(null);
}
The workflow is to use the JWT library (NuGet package above) to validate the JWT token and then return back ClaimsPrincipal. You can perform more validation, like check whether user exists on your system, and add other custom validations if you want.
The code to validate JWT token and get principal back:
public static ClaimsPrincipal GetPrincipal(string token)
{
try
{
var tokenHandler = new JwtSecurityTokenHandler();
var jwtToken = tokenHandler.ReadToken(token) as JwtSecurityToken;
if (jwtToken == null)
return null;
var symmetricKey = Convert.FromBase64String(Secret);
var validationParameters = new TokenValidationParameters()
{
RequireExpirationTime = true,
ValidateIssuer = false,
ValidateAudience = false,
IssuerSigningKey = new SymmetricSecurityKey(symmetricKey)
};
SecurityToken securityToken;
var principal = tokenHandler.ValidateToken(token, validationParameters, out securityToken);
return principal;
}
catch (Exception)
{
//should write log
return null;
}
}
If the JWT token is validated and the principal is returned, you should build a new local identity and put more information into it to check role authorization.
Remember to add config.Filters.Add(new AuthorizeAttribute()); (default authorization) at global scope in order to prevent any anonymous request to your resources.
You can use Postman to test the demo:
Request token (naive as I mentioned above, just for demo):
GET http://localhost:{port}/api/token?username=cuong&password=1
Put JWT token in the header for authorized request, example:
GET http://localhost:{port}/api/value
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmJmIjoxNDc3NTY1MjU4LCJleHAiOjE0Nzc1NjY0NTgsImlhdCI6MTQ3NzU2NTI1OH0.dSwwufd4-gztkLpttZsZ1255oEzpWCJkayR_4yvNL1s
The demo can be found here: https://github.com/cuongle/WebApi.Jwt
I've managed to achieve it with minimal effort (just as simple as with ASP.NET Core).
For that I use OWIN Startup.cs file and Microsoft.Owin.Security.Jwt library.
In order for the app to hit Startup.cs we need to amend Web.config:
<configuration>
<appSettings>
<add key="owin:AutomaticAppStartup" value="true" />
...
Here's how Startup.cs should look:
using MyApp.Helpers;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Jwt;
using Owin;
[assembly: OwinStartup(typeof(MyApp.App_Start.Startup))]
namespace MyApp.App_Start
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
TokenValidationParameters = new TokenValidationParameters()
{
ValidAudience = ConfigHelper.GetAudience(),
ValidIssuer = ConfigHelper.GetIssuer(),
IssuerSigningKey = ConfigHelper.GetSymmetricSecurityKey(),
ValidateLifetime = true,
ValidateIssuerSigningKey = true
}
});
}
}
}
Many of you guys use ASP.NET Core nowadays, so as you can see it doesn't differ a lot from what we have there.
It really got me perplexed first, I was trying to implement custom providers, etc. But I didn't expect it to be so simple. OWIN just rocks!
Just one thing to mention - after I enabled OWIN Startup NSWag library stopped working for me (e.g. some of you might want to auto-generate typescript HTTP proxies for Angular app).
The solution was also very simple - I replaced NSWag with Swashbuckle and didn't have any further issues.
Ok, now sharing ConfigHelper code:
public class ConfigHelper
{
public static string GetIssuer()
{
string result = System.Configuration.ConfigurationManager.AppSettings["Issuer"];
return result;
}
public static string GetAudience()
{
string result = System.Configuration.ConfigurationManager.AppSettings["Audience"];
return result;
}
public static SigningCredentials GetSigningCredentials()
{
var result = new SigningCredentials(GetSymmetricSecurityKey(), SecurityAlgorithms.HmacSha256);
return result;
}
public static string GetSecurityKey()
{
string result = System.Configuration.ConfigurationManager.AppSettings["SecurityKey"];
return result;
}
public static byte[] GetSymmetricSecurityKeyAsBytes()
{
var issuerSigningKey = GetSecurityKey();
byte[] data = Encoding.UTF8.GetBytes(issuerSigningKey);
return data;
}
public static SymmetricSecurityKey GetSymmetricSecurityKey()
{
byte[] data = GetSymmetricSecurityKeyAsBytes();
var result = new SymmetricSecurityKey(data);
return result;
}
public static string GetCorsOrigins()
{
string result = System.Configuration.ConfigurationManager.AppSettings["CorsOrigins"];
return result;
}
}
Another important aspect - I sent JWT Token via Authorization header, so typescript code looks for me as follows:
(the code below is generated by NSWag)
#Injectable()
export class TeamsServiceProxy {
private http: HttpClient;
private baseUrl: string;
protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;
constructor(#Inject(HttpClient) http: HttpClient, #Optional() #Inject(API_BASE_URL) baseUrl?: string) {
this.http = http;
this.baseUrl = baseUrl ? baseUrl : "https://localhost:44384";
}
add(input: TeamDto | null): Observable<boolean> {
let url_ = this.baseUrl + "/api/Teams/Add";
url_ = url_.replace(/[?&]$/, "");
const content_ = JSON.stringify(input);
let options_ : any = {
body: content_,
observe: "response",
responseType: "blob",
headers: new HttpHeaders({
"Content-Type": "application/json",
"Accept": "application/json",
"Authorization": "Bearer " + localStorage.getItem('token')
})
};
See headers part - "Authorization": "Bearer " + localStorage.getItem('token')
Here's a very minimal and secure implementation of a Claims based Authentication using JWT token in an ASP.NET Core Web API.
first of all, you need to expose an endpoint that returns a JWT token with claims assigned to a user:
/// <summary>
/// Login provides API to verify user and returns authentication token.
/// API Path: api/account/login
/// </summary>
/// <param name="paramUser">Username and Password</param>
/// <returns>{Token: [Token] }</returns>
[HttpPost("login")]
[AllowAnonymous]
public async Task<IActionResult> Login([FromBody] UserRequestVM paramUser, CancellationToken ct)
{
var result = await UserApplication.PasswordSignInAsync(paramUser.Email, paramUser.Password, false, lockoutOnFailure: false);
if (result.Succeeded)
{
UserRequestVM request = new UserRequestVM();
request.Email = paramUser.Email;
ApplicationUser UserDetails = await this.GetUserByEmail(request);
List<ApplicationClaim> UserClaims = await this.ClaimApplication.GetListByUser(UserDetails);
var Claims = new ClaimsIdentity(new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, paramUser.Email.ToString()),
new Claim(UserId, UserDetails.UserId.ToString())
});
//Adding UserClaims to JWT claims
foreach (var item in UserClaims)
{
Claims.AddClaim(new Claim(item.ClaimCode, string.Empty));
}
var tokenHandler = new JwtSecurityTokenHandler();
// this information will be retrived from you Configuration
//I have injected Configuration provider service into my controller
var encryptionkey = Configuration["Jwt:Encryptionkey"];
var key = Encoding.ASCII.GetBytes(encryptionkey);
var tokenDescriptor = new SecurityTokenDescriptor
{
Issuer = Configuration["Jwt:Issuer"],
Subject = Claims,
// this information will be retrived from you Configuration
//I have injected Configuration provider service into my controller
Expires = DateTime.UtcNow.AddMinutes(Convert.ToDouble(Configuration["Jwt:ExpiryTimeInMinutes"])),
//algorithm to sign the token
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var tokenString = tokenHandler.WriteToken(token);
return Ok(new
{
token = tokenString
});
}
return BadRequest("Wrong Username or password");
}
now you need to Add Authentication to your services in your ConfigureServices inside your startup.cs to add JWT authentication as your default authentication service like this:
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(cfg =>
{
cfg.RequireHttpsMetadata = false;
cfg.SaveToken = true;
cfg.TokenValidationParameters = new TokenValidationParameters()
{
//ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["JWT:Encryptionkey"])),
ValidateAudience = false,
ValidateLifetime = true,
ValidIssuer = configuration["Jwt:Issuer"],
//ValidAudience = Configuration["Jwt:Audience"],
//IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JWT:Key"])),
};
});
now you can add policies to your authorization services like this:
services.AddAuthorization(options =>
{
options.AddPolicy("YourPolicyNameHere",
policy => policy.RequireClaim("YourClaimNameHere"));
});
ALTERNATIVELY, You can also (not necessary) populate all of your claims from your database as this will only run once on your application startup and add them to policies like this:
services.AddAuthorization(async options =>
{
var ClaimList = await claimApplication.GetList(applicationClaim);
foreach (var item in ClaimList)
{
options.AddPolicy(item.ClaimCode, policy => policy.RequireClaim(item.ClaimCode));
}
});
now you can put the Policy filter on any of the methods that you want to be authorized like this:
[HttpPost("update")]
[Authorize(Policy = "ACC_UP")]
public async Task<IActionResult> Update([FromBody] UserRequestVM requestVm, CancellationToken ct)
{
//your logic goes here
}
Hope this helps
In my case the JWT is created by a separate API so ASP.NET need only decode and validate it. In contrast to the accepted answer we're using RSA which is a non-symmetric algorithm, so the SymmetricSecurityKey class mentioned above won't work.
Here's the result.
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Threading;
using System.Threading.Tasks;
public static async Task<JwtSecurityToken> VerifyAndDecodeJwt(string accessToken)
{
try
{
var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>($"{securityApiOrigin}/.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever());
var openIdConfig = await configurationManager.GetConfigurationAsync(CancellationToken.None);
var validationParameters = new TokenValidationParameters()
{
ValidateLifetime = true,
ValidateAudience = false,
ValidateIssuer = false,
RequireSignedTokens = true,
IssuerSigningKeys = openIdConfig.SigningKeys,
};
new JwtSecurityTokenHandler().ValidateToken(accessToken, validationParameters, out var validToken);
// threw on invalid, so...
return validToken as JwtSecurityToken;
}
catch (Exception ex)
{
logger.Info(ex.Message);
return null;
}
}
I think you should use some 3d party server to support the JWT token and there is no out of the box JWT support in WEB API 2.
However there is an OWIN project for supporting some format of signed token (not JWT). It works as a reduced OAuth protocol to provide just a simple form of authentication for a web site.
You can read more about it e.g. here.
It's rather long, but most parts are details with controllers and ASP.NET Identity that you might not need at all. Most important are
Step 9: Add support for OAuth Bearer Tokens Generation
Step 12: Testing the Back-end API
There you can read how to set up endpoint (e.g. "/token") that you can access from frontend (and details on the format of the request).
Other steps provide details on how to connect that endpoint to the database, etc. and you can chose the parts that you require.
You no need to work with weird JwtSecurityTokenHandler API
Use JwtUtils Nuget package with simple API
var claims = new Dictionary<string, object>
{
{ "exp", 1639942616 },
{ "uname", "i.a.ivanov" },
{ "claim1", "claim1_value" },
{ "claims_array", new [] {"claim_item1", "claim_item2"}}
};
string token = JWT.HS256.Create(claims, "{TOKEN_SECRET}");
You can follow this code for token controller or for more details you can visit here : How to Secure API using JWT Tokens. Building CRUD API using JWT Tokens with ASP.NET Core and Entity Framework Core and Swagger
From here you can learn to use JWT Token in a very easy way
using JWTToken.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
namespace JWTToken.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class TokenController : ControllerBase
{
public IConfiguration _configuration;
private readonly InventoryContext _context;
public TokenController(IConfiguration config, InventoryContext context)
{
_configuration = config;
_context = context;
}
[HttpPost]
public async Task<IActionResult> Post(UserInfo _userData)
{
if (_userData != null && _userData.Email != null && _userData.Password != null)
{
var user = await GetUser(_userData.Email, _userData.Password);
if (user != null)
{
//create claims details based on the user information
var claims = new[] {
new Claim(JwtRegisteredClaimNames.Sub, _configuration["Jwt:Subject"]),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Iat, DateTime.UtcNow.ToString()),
new Claim("Id", user.UserId.ToString()),
new Claim("FirstName", user.FirstName),
new Claim("LastName", user.LastName),
new Claim("UserName", user.UserName),
new Claim("Email", user.Email)
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]));
var signIn = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(_configuration["Jwt:Issuer"], _configuration["Jwt:Audience"], claims, expires: DateTime.UtcNow.AddDays(1), signingCredentials: signIn);
return Ok(new JwtSecurityTokenHandler().WriteToken(token));
}
else
{
return BadRequest("Invalid credentials");
}
}
else
{
return BadRequest();
}
}
private async Task<UserInfo> GetUser(string email, string password)
{
return await _context.UserInfos.FirstOrDefaultAsync(u => u.Email == email && u.Password == password);
}
}
}

ASP.NET Core 3.1 - How to persist JWT Tokens once Authenticated

I have been trying to get JWT Authentication working and it is not entirely clear how this needs to be done, and what the best ways are to do this in ASP.NET Core 3.1.
I was using Cookie based authentication which I assume is tied to the session id, which is tied to the running server instance. If I want to use multiple servers with different IP addresses and ports, I assume that cookies would no longer work and therefor require something else that can be validated across systems.
I have been following various web examples but it is not clear what to do beyond the point where I have a JWT Token once the user has been "Authenticated" - Logged In. Once users are logged in they can access any part of the system via: html links (the menu).
How do I pass the tokens around with all subsequent requests?
Do I redirect the user to a Welcome page after the user has been authenticated and store the token in the browser sessionStore or localStorage or Cookie? What is the best way to deal with this.
options.success = function (obj) {
sessionStorage.setItem("token", obj.token);
sessionStorage.setItem("userName",$("#userName").val());
}
HTTP HEADERS
Would the Authorization HTTP Header variable work and would this be
sent around in all subsequent requests by the browser, acting as the
HTTP client. How long does this HTTP header last, is it lost once the TCP socket is closed? How do I set this HTTP Header Variable in ASP.NET Core
3.1?
Would the server then use this Header to validate the token, and also
pass it on again for use in subsequent requests?
Currently I have this, which returns the token in the body once the user is authenticated:
var claims = await GetClaims(user);
var token = GenerateSecurityToken(claims);
return Ok(new { Token = token })
AJAX CALLS
I have several forms and several AJAX calls, how do implement this as a manual approach seems rather tedious.
Is there a way to get the JWT token from a hidden form variable similar to the AntiForgery token #Html.AntiForgeryToken()
as used in all my Ajax calls?
jQuery using the hidden form variable:
request = $.ajax({
async: true,
url: url,
type: "POST",
contentType: "application/json; charset=utf-8",
dataType: "json",
headers: {
RequestVerificationToken:
$('input:hidden[name="__RequestVerificationToken"]').val()
},
WHAT DO I ADD FOR JWT ?
data: JSON.stringify(data)
}).done(function() {
completion();
}).fail(function() {
// fail
});
HTML FORMS
I have Razor Pages and have some forms which then POST back to the controllers. How do I include the token?
CONTROLLERS
Is there anything else that needs to be performed when using JWT
besides what I have in my Startup.cs? I know I need to deal with Token refreshes but I will leave for a seperate question.
LINKS FROM THE MENU - HTTP GET
I could manipulated the menu / links presented to the user, by adding the token to the end of the URL, but how should this be done?
After quite a bit of reading I found some answers along with a working solution.
HTTP HEADERS
Once you have the token, the token needs to be persisted in order to get access to the system. Using HTTP headers to store the token is not going to persist as the HTTP protocols 1.0 and 1.1 and 1.2 will close the TCP socket at some point along with the state it had, the token. Not ideal for WebClients where you do not control Http connections, but could be used for Mobile development, Android or IOS were you can control the HttpHeaders.
LOCAL STORAGE
You could use the browsers localStorage or sessionStorage, but these have some security risks where JavaScript can read the values - XSS attack.
COOKIES
Another option is to store the token within the Cookie; The cookie will be passed along with every http request, and nothing special on the client side needs to happen regarding this. This method is not prone to XSS attacks. But is prone to CSRF. But again CORS can help with this.
It is also best to set the Cookie to be HttpOnly, this way the cookie will only be delivered over HTTPS. Read more here
Here is my implementation based on an article I found
here
Startup.cs ConfigureServices...
// openssl rand -hex 16 => 32 bytes when read
var jwt_key = Configuration.GetSection("JwtOption:IssuerSigningKey").Value;
var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(jwt_key));
var tokenValidationParameters = new TokenValidationParameters
{
// The signing key must match!
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey,
// Validate the JWT Issuer (iss) claim
ValidateIssuer = true,
ValidIssuer = "some uri",
// Validate the JWT Audience (aud) claim
ValidateAudience = true,
ValidAudience = "the web",
// Validate the token expiry
ValidateLifetime = true,
// If you want to allow a certain amount of clock drift, set that here:
ClockSkew = TimeSpan.Zero
};
services.AddSingleton(tokenValidationParameters);
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
{
int minute = 60;
int hour = minute * 60;
int day = hour * 24;
int week = day * 7;
int year = 365 * day;
options.LoginPath = "/auth/login";
options.AccessDeniedPath = "/auth/accessdenied";
options.Cookie.IsEssential = true;
options.SlidingExpiration = true;
options.ExpireTimeSpan = TimeSpan.FromSeconds(day/2);
options.Cookie.Name = "access_token";
options.TicketDataFormat = new CustomJwtDataFormat(
SecurityAlgorithms.HmacSha256,
tokenValidationParameters);
});
CustomJwtDataFormat This will be validating our tokens.
public class CustomJwtDataFormat :ISecureDataFormat<AuthenticationTicket>
{
private readonly string algorithm;
private readonly TokenValidationParameters validationParameters;
public CustomJwtDataFormat(string algorithm, TokenValidationParameters validationParameters)
{
this.algorithm = algorithm;
this.validationParameters = validationParameters;
}
public AuthenticationTicket Unprotect(string protectedText)
=> Unprotect(protectedText, null);
public AuthenticationTicket Unprotect(string protectedText, string purpose)
{
var handler = new JwtSecurityTokenHandler();
ClaimsPrincipal principal = null;
SecurityToken validToken = null;
try
{
principal = handler.ValidateToken(protectedText, this.validationParameters, out validToken);
var validJwt = validToken as JwtSecurityToken;
if (validJwt == null)
{
throw new ArgumentException("Invalid JWT");
}
if (!validJwt.Header.Alg.Equals(algorithm, StringComparison.Ordinal))
{
throw new ArgumentException($"Algorithm must be '{algorithm}'");
}
// Additional custom validation of JWT claims here (if any)
}
catch (SecurityTokenValidationException e)
{
System.Console.WriteLine(e);
return null;
}
catch (ArgumentException e)
{
System.Console.WriteLine(e);
return null;
}
// Validation passed. Return a valid AuthenticationTicket:
return new AuthenticationTicket(principal, new AuthenticationProperties(), "Cookie");
}
// This ISecureDataFormat implementation is decode-only
public string Protect(AuthenticationTicket data)
{
throw new NotImplementedException();
}
public string Protect(AuthenticationTicket data, string purpose)
{
throw new NotImplementedException();
}
}
LoginController After the username and password is validated, call SignInUser
private string GenerateSecurityToken(List<Claim> claims)
{
var tokenHandler = new JwtSecurityTokenHandler();
var expire = System.DateTime.UtcNow.AddMinutes(userService.GetJwtExpireDate());
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Expires = expire,
SigningCredentials = new SigningCredentials(tokenValidationParameters.IssuerSigningKey, SecurityAlgorithms.HmacSha256Signature),
Audience = tokenValidationParameters.ValidAudience,
Issuer = tokenValidationParameters.ValidIssuer
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
private async Task<List<Claim>> GetClaims(UserModel user) {
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.Email),
new Claim(ClaimTypes.Email, user.Email),
};
// add roles
var roleList = await userService.UserRoles(user.Email);
foreach (var role in roleList)
{
var claim = new Claim(ClaimTypes.Role, role.Role);
claims.Add(claim);
}
return claims;
}
private async Task<IActionResult> SignInUser(UserModel user, bool rememberMe)
{
var claims = await GetClaims(user);
var token = GenerateSecurityToken(claims);
// return Ok(new { Token = token });
// HttpContext.Request.Headers.Add("Authorization", $"Bearer {token}");
// HttpContext.Response.Cookies.Append(
HttpContext.Response.Cookies.Append("access_token", token, new CookieOptions { HttpOnly = true, Secure = true });
return RedirectToAction("Index", "Home", new { area = "" });
}

How to validate JWT during websocket request. .net core

I am working on a small .net core app that uses JWT authentication and websockets.
I have succesfully implemented generating and validating tokens for standard web api controllers. However I also want to validate the token for a WebSocket request which of course won't work with the [Authorize] attribute.
I have setup my middleware pipeline like this:
app.UseWebSockets();
app.Use(async (http, next) => {
if (http.WebSockets.IsWebSocketRequest == false) {
await next();
return;
}
/// Handle websocket request here. How to check if token is valid?
});
// secretKey contains a secret passphrase only your server knows
var secretKey = .....;
var signKey = new SigningCredentials (
new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey)),
SecurityAlgorithms.HmacSha256
);
var tokenValidationParameters = new TokenValidationParameters {
ValidateIssuer = false,
ValidateAudience = false,
// The signing key must match!
ValidateIssuerSigningKey = true,
IssuerSigningKey = signKey.Key,
// Validate the token expiry
ValidateLifetime = true,
// If you want to allow a certain amount of clock drift, set that here:
ClockSkew = TimeSpan.FromMinutes(1),
};
app.UseJwtBearerAuthentication(new JwtBearerOptions {
AutomaticAuthenticate = true,
AutomaticChallenge = true,
TokenValidationParameters = tokenValidationParameters
});
I hope this can help someone, even though the post is kind of old.
I found out the answer, not after Googling, but Binging ! I inspired myself from this official code.
You can write your own class that handles authorization very simply, using the magic of JwtBearerOptions. This class (hopefully) contains everything you need to validate the JWTs by yourself.
So, you have to inject it as a Service, as well as using it to configure your Authentication. Something like that in your Startup.ConfigureServices:
this.JwtOptions = new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
TokenValidationParameters = yourTokenValidationParameters
};
services.AddSingleton<JwtBearerOptions>(this.JwtOptions);
Then, you have to create a class that is gonna Validate your Token (This is where my code was inspired). Let's call it a Backer, because it's got your back !:
public class JwtBearerBacker
{
public JwtBearerOptions Options { get; private set; }
public JwtBearerBacker(JwtBearerOptions options)
{
this.Options = options;
}
public bool IsJwtValid(string token)
{
List<Exception> validationFailures = null;
SecurityToken validatedToken;
foreach (var validator in Options.SecurityTokenValidators)
{
if (validator.CanReadToken(token))
{
ClaimsPrincipal principal;
try
{
principal = validator.ValidateToken(token, Options.TokenValidationParameters, out validatedToken);
}
catch (Exception ex)
{
// Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the event.
if (Options.RefreshOnIssuerKeyNotFound && Options.ConfigurationManager != null
&& ex is SecurityTokenSignatureKeyNotFoundException)
{
Options.ConfigurationManager.RequestRefresh();
}
if (validationFailures == null)
validationFailures = new List<Exception>(1);
validationFailures.Add(ex);
continue;
}
return true;
}
}
return false;
}
}
Then, in your Middleware, just access the request header, the JwtOptions dependency and call the Backer:
protected string ObtainAppTokenFromHeader(string authHeader)
{
if (string.IsNullOrWhiteSpace(authHeader) || !authHeader.Contains(" "))
return null;
string[] authSchemeAndJwt = authHeader.Split(' ');
string authScheme = authSchemeAndJwt[0];
if (authScheme != "Bearer")
return null;
string jwt = authSchemeAndJwt[1];
return jwt;
}
protected async Task<bool> AuthorizeUserFromHttpContext(HttpContext context)
{
var jwtBearerOptions = context.RequestServices.GetRequiredService<JwtBearerOptions>() as JwtBearerOptions;
string jwt = this.ObtainAppTokenFromHeader(context.Request.Headers["Authorization"]);
if (jwt == null)
return false;
var jwtBacker = new JwtBearerBacker(jwtBearerOptions);
return jwtBacker.IsJwtValid(jwt);
}
public async Task Invoke(HttpContext context)
{
if (!context.WebSockets.IsWebSocketRequest)
return;
if (!await this.AuthorizeUserFromHttpContext(context))
{
context.Response.StatusCode = 401;
await context.Response.WriteAsync("The door is locked, dude. You're not authorized !");
return;
}
//... Whatever else you're doing in your middleware
}
Also, the AuthenticationTicket and any other information about the auth has been already handled by the framework's JwtBearerMiddleware, and will be returned anyway.
Finally, the client side. I advise you using a client library that actually supports additional HTTP Headers. For example, as far as I know, the W3C Javascript client does not provide this functionality.
Here you are! Thanks to Microsoft for its Open Source codebase.
I worked this out slightly different way as I was depending on WebSocket() on the client side.
So on the client side first I authenticate user to get token and attach it to the header as subprotocol:
socket = new WebSocket(connectionPath, ["client",token]);
Token is sent within request header under sec-websocket-protocol key.
So before authentication kicks in I extract token and append it to the context.
.AddJwtBearer(x =>
{
// ....
x.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
if (context.Request.Headers.ContainsKey("sec-websocket-protocol") && context.HttpContext.WebSockets.IsWebSocketRequest)
{
var token = context.Request.Headers["sec-websocket-protocol"].ToString();
// token arrives as string = "client, xxxxxxxxxxxxxxxxxxxxx"
context.Token = token.Substring(token.IndexOf(',') + 1).Trim();
context.Request.Headers["sec-websocket-protocol"] = "client";
}
return Task.CompletedTask;
}
};
Then on my WebSocket controller I simply stick [Authorize] attribute:
[Authorize]
[Route("api/[controller]")]
public class WSController : Controller
{
[HttpGet]
public async Task Get()
{
var context = ControllerContext.HttpContext;
WebSocket currentSocket = await context.WebSockets.AcceptWebSocketAsync("client"); // it's important to make sure the response returns the same subprotocol
// ...
}

JWT authentication for ASP.NET Web API

I'm trying to support JWT bearer token (JSON Web Token) in my web API application and I'm getting lost.
I see support for .NET Core and for OWIN applications.
I'm currently hosting my application in IIS.
How can I achieve this authentication module in my application? Is there any way I can use the <authentication> configuration similar to the way I use forms/Windows authentication?
I answered this question: How to secure an ASP.NET Web API 4 years ago using HMAC.
Now, lots of things changed in security, especially that JWT is getting popular. In this answer, I will try to explain how to use JWT in the simplest and basic way that I can, so we won't get lost from jungle of OWIN, Oauth2, ASP.NET Identity, etc..
If you don't know about JWT tokens, you need to take a look at:
https://www.rfc-editor.org/rfc/rfc7519
Basically, a JWT token looks like this:
<base64-encoded header>.<base64-encoded claims>.<base64-encoded signature>
Example:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmJmIjoxNDc3NTY1NzI0LCJleHAiOjE0Nzc1NjY5MjQsImlhdCI6MTQ3NzU2NTcyNH0.6MzD1VwA5AcOcajkFyKhLYybr3h13iZjDyHm9zysDFQ
A JWT token has three sections:
Header: JSON format which is encoded in Base64
Claims: JSON format which is encoded in Base64.
Signature: Created and signed based on Header and Claims which is encoded in Base64.
If you use the website jwt.io with the token above, you can decode the token and see it like below:
Technically, JWT uses a signature which is signed from headers and claims with security algorithm specified in the headers (example: HMACSHA256). Therefore, JWT must be transferred over HTTPs if you store any sensitive information in its claims.
Now, in order to use JWT authentication, you don't really need an OWIN middleware if you have a legacy Web Api system. The simple concept is how to provide JWT token and how to validate the token when the request comes. That's it.
In the demo I've created (github), to keep the JWT token lightweight, I only store username and expiration time. But this way, you have to re-build new local identity (principal) to add more information like roles, if you want to do role authorization, etc. But, if you want to add more information into JWT, it's up to you: it's very flexible.
Instead of using OWIN middleware, you can simply provide a JWT token endpoint by using a controller action:
public class TokenController : ApiController
{
// This is naive endpoint for demo, it should use Basic authentication
// to provide token or POST request
[AllowAnonymous]
public string Get(string username, string password)
{
if (CheckUser(username, password))
{
return JwtManager.GenerateToken(username);
}
throw new HttpResponseException(HttpStatusCode.Unauthorized);
}
public bool CheckUser(string username, string password)
{
// should check in the database
return true;
}
}
This is a naive action; in production you should use a POST request or a Basic Authentication endpoint to provide the JWT token.
How to generate the token based on username?
You can use the NuGet package called System.IdentityModel.Tokens.Jwt from Microsoft to generate the token, or even another package if you like. In the demo, I use HMACSHA256 with SymmetricKey:
/// <summary>
/// Use the below code to generate symmetric Secret Key
/// var hmac = new HMACSHA256();
/// var key = Convert.ToBase64String(hmac.Key);
/// </summary>
private const string Secret = "db3OIsj+BXE9NZDy0t8W3TcNekrF+2d/1sFnWG4HnV8TZY30iTOdtVWJG8abWvB1GlOgJuQZdcF2Luqm/hccMw==";
public static string GenerateToken(string username, int expireMinutes = 20)
{
var symmetricKey = Convert.FromBase64String(Secret);
var tokenHandler = new JwtSecurityTokenHandler();
var now = DateTime.UtcNow;
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, username)
}),
Expires = now.AddMinutes(Convert.ToInt32(expireMinutes)),
SigningCredentials = new SigningCredentials(
new SymmetricSecurityKey(symmetricKey),
SecurityAlgorithms.HmacSha256Signature)
};
var stoken = tokenHandler.CreateToken(tokenDescriptor);
var token = tokenHandler.WriteToken(stoken);
return token;
}
The endpoint to provide the JWT token is done.
How to validate the JWT when the request comes?
In the demo, I have built
JwtAuthenticationAttribute which inherits from IAuthenticationFilter (more detail about authentication filter in here).
With this attribute, you can authenticate any action: you just have to put this attribute on that action.
public class ValueController : ApiController
{
[JwtAuthentication]
public string Get()
{
return "value";
}
}
You can also use OWIN middleware or DelegateHander if you want to validate all incoming requests for your WebAPI (not specific to Controller or action)
Below is the core method from authentication filter:
private static bool ValidateToken(string token, out string username)
{
username = null;
var simplePrinciple = JwtManager.GetPrincipal(token);
var identity = simplePrinciple.Identity as ClaimsIdentity;
if (identity == null || !identity.IsAuthenticated)
return false;
var usernameClaim = identity.FindFirst(ClaimTypes.Name);
username = usernameClaim?.Value;
if (string.IsNullOrEmpty(username))
return false;
// More validate to check whether username exists in system
return true;
}
protected Task<IPrincipal> AuthenticateJwtToken(string token)
{
string username;
if (ValidateToken(token, out username))
{
// based on username to get more information from database
// in order to build local identity
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, username)
// Add more claims if needed: Roles, ...
};
var identity = new ClaimsIdentity(claims, "Jwt");
IPrincipal user = new ClaimsPrincipal(identity);
return Task.FromResult(user);
}
return Task.FromResult<IPrincipal>(null);
}
The workflow is to use the JWT library (NuGet package above) to validate the JWT token and then return back ClaimsPrincipal. You can perform more validation, like check whether user exists on your system, and add other custom validations if you want.
The code to validate JWT token and get principal back:
public static ClaimsPrincipal GetPrincipal(string token)
{
try
{
var tokenHandler = new JwtSecurityTokenHandler();
var jwtToken = tokenHandler.ReadToken(token) as JwtSecurityToken;
if (jwtToken == null)
return null;
var symmetricKey = Convert.FromBase64String(Secret);
var validationParameters = new TokenValidationParameters()
{
RequireExpirationTime = true,
ValidateIssuer = false,
ValidateAudience = false,
IssuerSigningKey = new SymmetricSecurityKey(symmetricKey)
};
SecurityToken securityToken;
var principal = tokenHandler.ValidateToken(token, validationParameters, out securityToken);
return principal;
}
catch (Exception)
{
//should write log
return null;
}
}
If the JWT token is validated and the principal is returned, you should build a new local identity and put more information into it to check role authorization.
Remember to add config.Filters.Add(new AuthorizeAttribute()); (default authorization) at global scope in order to prevent any anonymous request to your resources.
You can use Postman to test the demo:
Request token (naive as I mentioned above, just for demo):
GET http://localhost:{port}/api/token?username=cuong&password=1
Put JWT token in the header for authorized request, example:
GET http://localhost:{port}/api/value
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmJmIjoxNDc3NTY1MjU4LCJleHAiOjE0Nzc1NjY0NTgsImlhdCI6MTQ3NzU2NTI1OH0.dSwwufd4-gztkLpttZsZ1255oEzpWCJkayR_4yvNL1s
The demo can be found here: https://github.com/cuongle/WebApi.Jwt
I've managed to achieve it with minimal effort (just as simple as with ASP.NET Core).
For that I use OWIN Startup.cs file and Microsoft.Owin.Security.Jwt library.
In order for the app to hit Startup.cs we need to amend Web.config:
<configuration>
<appSettings>
<add key="owin:AutomaticAppStartup" value="true" />
...
Here's how Startup.cs should look:
using MyApp.Helpers;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Jwt;
using Owin;
[assembly: OwinStartup(typeof(MyApp.App_Start.Startup))]
namespace MyApp.App_Start
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
TokenValidationParameters = new TokenValidationParameters()
{
ValidAudience = ConfigHelper.GetAudience(),
ValidIssuer = ConfigHelper.GetIssuer(),
IssuerSigningKey = ConfigHelper.GetSymmetricSecurityKey(),
ValidateLifetime = true,
ValidateIssuerSigningKey = true
}
});
}
}
}
Many of you guys use ASP.NET Core nowadays, so as you can see it doesn't differ a lot from what we have there.
It really got me perplexed first, I was trying to implement custom providers, etc. But I didn't expect it to be so simple. OWIN just rocks!
Just one thing to mention - after I enabled OWIN Startup NSWag library stopped working for me (e.g. some of you might want to auto-generate typescript HTTP proxies for Angular app).
The solution was also very simple - I replaced NSWag with Swashbuckle and didn't have any further issues.
Ok, now sharing ConfigHelper code:
public class ConfigHelper
{
public static string GetIssuer()
{
string result = System.Configuration.ConfigurationManager.AppSettings["Issuer"];
return result;
}
public static string GetAudience()
{
string result = System.Configuration.ConfigurationManager.AppSettings["Audience"];
return result;
}
public static SigningCredentials GetSigningCredentials()
{
var result = new SigningCredentials(GetSymmetricSecurityKey(), SecurityAlgorithms.HmacSha256);
return result;
}
public static string GetSecurityKey()
{
string result = System.Configuration.ConfigurationManager.AppSettings["SecurityKey"];
return result;
}
public static byte[] GetSymmetricSecurityKeyAsBytes()
{
var issuerSigningKey = GetSecurityKey();
byte[] data = Encoding.UTF8.GetBytes(issuerSigningKey);
return data;
}
public static SymmetricSecurityKey GetSymmetricSecurityKey()
{
byte[] data = GetSymmetricSecurityKeyAsBytes();
var result = new SymmetricSecurityKey(data);
return result;
}
public static string GetCorsOrigins()
{
string result = System.Configuration.ConfigurationManager.AppSettings["CorsOrigins"];
return result;
}
}
Another important aspect - I sent JWT Token via Authorization header, so typescript code looks for me as follows:
(the code below is generated by NSWag)
#Injectable()
export class TeamsServiceProxy {
private http: HttpClient;
private baseUrl: string;
protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;
constructor(#Inject(HttpClient) http: HttpClient, #Optional() #Inject(API_BASE_URL) baseUrl?: string) {
this.http = http;
this.baseUrl = baseUrl ? baseUrl : "https://localhost:44384";
}
add(input: TeamDto | null): Observable<boolean> {
let url_ = this.baseUrl + "/api/Teams/Add";
url_ = url_.replace(/[?&]$/, "");
const content_ = JSON.stringify(input);
let options_ : any = {
body: content_,
observe: "response",
responseType: "blob",
headers: new HttpHeaders({
"Content-Type": "application/json",
"Accept": "application/json",
"Authorization": "Bearer " + localStorage.getItem('token')
})
};
See headers part - "Authorization": "Bearer " + localStorage.getItem('token')
Here's a very minimal and secure implementation of a Claims based Authentication using JWT token in an ASP.NET Core Web API.
first of all, you need to expose an endpoint that returns a JWT token with claims assigned to a user:
/// <summary>
/// Login provides API to verify user and returns authentication token.
/// API Path: api/account/login
/// </summary>
/// <param name="paramUser">Username and Password</param>
/// <returns>{Token: [Token] }</returns>
[HttpPost("login")]
[AllowAnonymous]
public async Task<IActionResult> Login([FromBody] UserRequestVM paramUser, CancellationToken ct)
{
var result = await UserApplication.PasswordSignInAsync(paramUser.Email, paramUser.Password, false, lockoutOnFailure: false);
if (result.Succeeded)
{
UserRequestVM request = new UserRequestVM();
request.Email = paramUser.Email;
ApplicationUser UserDetails = await this.GetUserByEmail(request);
List<ApplicationClaim> UserClaims = await this.ClaimApplication.GetListByUser(UserDetails);
var Claims = new ClaimsIdentity(new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, paramUser.Email.ToString()),
new Claim(UserId, UserDetails.UserId.ToString())
});
//Adding UserClaims to JWT claims
foreach (var item in UserClaims)
{
Claims.AddClaim(new Claim(item.ClaimCode, string.Empty));
}
var tokenHandler = new JwtSecurityTokenHandler();
// this information will be retrived from you Configuration
//I have injected Configuration provider service into my controller
var encryptionkey = Configuration["Jwt:Encryptionkey"];
var key = Encoding.ASCII.GetBytes(encryptionkey);
var tokenDescriptor = new SecurityTokenDescriptor
{
Issuer = Configuration["Jwt:Issuer"],
Subject = Claims,
// this information will be retrived from you Configuration
//I have injected Configuration provider service into my controller
Expires = DateTime.UtcNow.AddMinutes(Convert.ToDouble(Configuration["Jwt:ExpiryTimeInMinutes"])),
//algorithm to sign the token
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var tokenString = tokenHandler.WriteToken(token);
return Ok(new
{
token = tokenString
});
}
return BadRequest("Wrong Username or password");
}
now you need to Add Authentication to your services in your ConfigureServices inside your startup.cs to add JWT authentication as your default authentication service like this:
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(cfg =>
{
cfg.RequireHttpsMetadata = false;
cfg.SaveToken = true;
cfg.TokenValidationParameters = new TokenValidationParameters()
{
//ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["JWT:Encryptionkey"])),
ValidateAudience = false,
ValidateLifetime = true,
ValidIssuer = configuration["Jwt:Issuer"],
//ValidAudience = Configuration["Jwt:Audience"],
//IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JWT:Key"])),
};
});
now you can add policies to your authorization services like this:
services.AddAuthorization(options =>
{
options.AddPolicy("YourPolicyNameHere",
policy => policy.RequireClaim("YourClaimNameHere"));
});
ALTERNATIVELY, You can also (not necessary) populate all of your claims from your database as this will only run once on your application startup and add them to policies like this:
services.AddAuthorization(async options =>
{
var ClaimList = await claimApplication.GetList(applicationClaim);
foreach (var item in ClaimList)
{
options.AddPolicy(item.ClaimCode, policy => policy.RequireClaim(item.ClaimCode));
}
});
now you can put the Policy filter on any of the methods that you want to be authorized like this:
[HttpPost("update")]
[Authorize(Policy = "ACC_UP")]
public async Task<IActionResult> Update([FromBody] UserRequestVM requestVm, CancellationToken ct)
{
//your logic goes here
}
Hope this helps
In my case the JWT is created by a separate API so ASP.NET need only decode and validate it. In contrast to the accepted answer we're using RSA which is a non-symmetric algorithm, so the SymmetricSecurityKey class mentioned above won't work.
Here's the result.
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Threading;
using System.Threading.Tasks;
public static async Task<JwtSecurityToken> VerifyAndDecodeJwt(string accessToken)
{
try
{
var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>($"{securityApiOrigin}/.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever());
var openIdConfig = await configurationManager.GetConfigurationAsync(CancellationToken.None);
var validationParameters = new TokenValidationParameters()
{
ValidateLifetime = true,
ValidateAudience = false,
ValidateIssuer = false,
RequireSignedTokens = true,
IssuerSigningKeys = openIdConfig.SigningKeys,
};
new JwtSecurityTokenHandler().ValidateToken(accessToken, validationParameters, out var validToken);
// threw on invalid, so...
return validToken as JwtSecurityToken;
}
catch (Exception ex)
{
logger.Info(ex.Message);
return null;
}
}
I think you should use some 3d party server to support the JWT token and there is no out of the box JWT support in WEB API 2.
However there is an OWIN project for supporting some format of signed token (not JWT). It works as a reduced OAuth protocol to provide just a simple form of authentication for a web site.
You can read more about it e.g. here.
It's rather long, but most parts are details with controllers and ASP.NET Identity that you might not need at all. Most important are
Step 9: Add support for OAuth Bearer Tokens Generation
Step 12: Testing the Back-end API
There you can read how to set up endpoint (e.g. "/token") that you can access from frontend (and details on the format of the request).
Other steps provide details on how to connect that endpoint to the database, etc. and you can chose the parts that you require.
You no need to work with weird JwtSecurityTokenHandler API
Use JwtUtils Nuget package with simple API
var claims = new Dictionary<string, object>
{
{ "exp", 1639942616 },
{ "uname", "i.a.ivanov" },
{ "claim1", "claim1_value" },
{ "claims_array", new [] {"claim_item1", "claim_item2"}}
};
string token = JWT.HS256.Create(claims, "{TOKEN_SECRET}");
You can follow this code for token controller or for more details you can visit here : How to Secure API using JWT Tokens. Building CRUD API using JWT Tokens with ASP.NET Core and Entity Framework Core and Swagger
From here you can learn to use JWT Token in a very easy way
using JWTToken.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
namespace JWTToken.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class TokenController : ControllerBase
{
public IConfiguration _configuration;
private readonly InventoryContext _context;
public TokenController(IConfiguration config, InventoryContext context)
{
_configuration = config;
_context = context;
}
[HttpPost]
public async Task<IActionResult> Post(UserInfo _userData)
{
if (_userData != null && _userData.Email != null && _userData.Password != null)
{
var user = await GetUser(_userData.Email, _userData.Password);
if (user != null)
{
//create claims details based on the user information
var claims = new[] {
new Claim(JwtRegisteredClaimNames.Sub, _configuration["Jwt:Subject"]),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Iat, DateTime.UtcNow.ToString()),
new Claim("Id", user.UserId.ToString()),
new Claim("FirstName", user.FirstName),
new Claim("LastName", user.LastName),
new Claim("UserName", user.UserName),
new Claim("Email", user.Email)
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]));
var signIn = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(_configuration["Jwt:Issuer"], _configuration["Jwt:Audience"], claims, expires: DateTime.UtcNow.AddDays(1), signingCredentials: signIn);
return Ok(new JwtSecurityTokenHandler().WriteToken(token));
}
else
{
return BadRequest("Invalid credentials");
}
}
else
{
return BadRequest();
}
}
private async Task<UserInfo> GetUser(string email, string password)
{
return await _context.UserInfos.FirstOrDefaultAsync(u => u.Email == email && u.Password == password);
}
}
}

Categories

Resources