This post is the first part of our series about jwt authentication in asp.net core, in this series I talk about how we can use a unified Authentication Server with jwt authentication mechanism as an Identity Provider (IDP) for issuing token, authenticating and authorizing users with using a jwt token as an access token for their permissions to access the resources and identifying user identity in .net core applications. we can use this approach for both monolithic and microservices architectures as well. in the first part of this series I talk more bit more about theory and in the future parts I implement a sample in .net core for this article.
of my posts about using Xunit in .net core and its features for testing code, these posts series cover all parts of xunit in detail.
Authentication and authorization are critical aspects of a web application. for this purpose, we can use two approaches
Cookie-Based mechanism and
Token-Based Mechanism. For many years applications used a Cookie-based mechanism for authentication and authorization but these days they use very often. today’s APIs must be supported with different devices such as mobile, browsers, desktop applications and enforcing all of them to use a cookie-based approach is impossible so the preferred approach is using a signed token to authentication and authorization for support all platform and devices, by the way, it’s up to you to choose correct approach in specific scenarios. the best solution for use token-based authentication and authorization is OAuth 2.0 and the JSON Web Token (JWT).
What is JSON Web Token?
JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.
Let’s explain some concepts of this definition:
- Compact: Because of its size, it can be sent through a URL, POST parameter, or inside an HTTP header. Additionally, due to its size, its transmission is fast.
- Self-contained: The payload contains all the required information about the user, to avoid querying the database more than once.
JWT tokens specifically provide a very convenient way to package up common properties about a user in the form of claims. JWTs can be encrypted also to provide secrecy between parties; we will focus on signed tokens. Signed tokens can verify the integrity of the claims contained within it, while encrypted tokens hide those claims from other parties.
Using Token vs Cookie Based Authentication
On of the most use and traditional approach for signing users and keep user’s identities in browsers is using of Cookie In web applications.
In this approach after users enter their credentials into the browser and after validating this credential at server side for ensuring valid credential, the server sends an encrypted cookie to users browser with a
Then for a subsequent request from the user’s browser to a requested domain, the browser automatically will attach the cookie to the request and on the server-side, this cookie will be processed and user information and identity will be retrieved and the user will sign in to the application. this mechanism can be used for users that use a browser perfectly but it will not work in non-browser based apps like a mobile app or windows apps.
for these situations, we can use a Token-based approach like
JSON Web Token (JWT) that will work on all platforms even without a browser. In this approach after the clients enters their credentials to the application, the application checks the credential and if credential is valid, a Jwt token will issue and send to the client, now the client has to store token ourselves somewhere like local storage in browser or memory in the mobile app, and then manually send it with every request with an
Authorization Header to the application for authenticating into application.
Advantages of Token-Based Authentication
Stateless, easier to scale: The token contains all the information to identify the user, eliminating the need for the session state. If we use a load balancer, we can pass the user to any server, instead of being bound to the same server we logged in on.
Reusability: We can have many separate servers, running on multiple platforms and domains, reusing the same token for authenticating the user. It is easy to build an application that shares permissions with another application.
JWT Security: Since we are not using cookies, we don’t have to protect against cross-site request forgery (CSRF) attacks. We should still encrypt our tokens using JWE if we have to put any sensitive information in them, and transmit our tokens over HTTPS to prevent man-in-the-middle attacks.
Performance: There is no server-side lookup to find and deserialize the session on each request. The only thing we have to do is calculate the HMAC SHA-256 to validate the token and parse its content.
The Token-Based Authentication Workflow
Authentication is the process by which an application confirms user identity,
Token Authentication is a way to authenticate users into an application. The authentication token is created by Identity Server (IDP) and contain embedded information about the Identity of the user and their permissions, so the website can receive the token and discover who the user is and what permissions they have without necessarily needing to talk to a central database. Because the token can be a self-contained entity that conveys all the required information for authenticating the request, it is often referred to as
stateless authentication. In this case, the server-side does not need to maintain the state of a user such as the old way with the session store on the server. The token itself is cryptographically signed to prevent tampering.
Steps in token authentication flow with Jwt:
- A user enters the name and password into the client (client means the browser or mobile devices etc).
- The client then sends these credentials (i.e. username and password) to the Identity Provider (IDP).
- Then the IDP authenticates the client credentials (i.e. username and password) and then it generates and returns a
Jwt Access Tokento the client. This Access Token contains useful information about the logged-in user such as username, user subject and user’s permissions such as user roles and permissions, and some other useful information in the case of claims.
- The client application then includes the Jwt Access Token in the Authorization header of the HTTP request for subsequent requests to access the restricted resources from the Resource Server until the token is expired.
In authentication, when the user successfully logs in using his credentials, a JSON Web Token will be returned and must be saved locally (typically in local storage, but cookies can be also used), instead of the traditional approach of creating a session in the server and returning a cookie.
Whenever the user wants to access a protected route, it should send the JWT, typically in the
Authorization header using the
Bearer schema. Therefore, the content of the header should look like the following:
Authorization: Bearer <token>
This is a
stateless authentication mechanism because the user state is never saved in the server memory. The server’s protected routes will check for a valid JWT in the Authorization header, and if it is there, the user will be allowed. As JWTs are self-contained, all the necessary information is there, reducing the need to go back and forth to the database.
Do note that with
signed tokens, all the information contained within the token is exposed to users or other parties, even though they are unable to change it. This means you should not put secret information within the token.
JSON Web Token (JWT) structure
JWT consist of three parts:
a JWT typically looks like the following structure
Base64-Encoded-Header . Base64-Encoded-Payload . Signature
this is an example of a Jwt token with the above structure
We can check and validate our token in this site and check the expiration time of our token. in this site you can past
expproperty that is expiration time our token and check when token will be expired.
iatare in seconds since Unix epoch format.
The header consists of two parts: the type of the token, which is JWT, and the signing algorithm being used, such as HMAC SHA256 or RSA.
An example of a header is
This header is a JSON object that will be encoded to base64 string and place in the first part of our JWT.
You can use of jwt.io to see how our JSON header object will be encoded like this
The second part of the token is the payload, which contains the claims. Claims are statements about an entity (typically, the user) and additional data. The payload usually contains some claims about the logged-in user and their permissions and roles.
There are three types of claims: registered, public, and private claims.
Register claims or Reserved claims name are a set of predefined claims which are not mandatory but recommended such as iss (issuer): Time token created, exp (expiration time), sub (subject), aud (audience), iat (Issued At)and others. Register claims are registered in IANA JSON Web Token Registry.
Public Claims are Custom claim names that are required to be collision-resistant. But to avoid collisions they should be defined in the IANA JSON Web Token Registry or be defined as a URI that contains a collision resistant namespace. An example of a public claim name could be:
Private Claims are Custom claim names that are not required to be collision-resistant. they use in places where JWTs are only exchanged in a closed environment(private) between known systems, such as inside an enterprise. These are claims that we can define ourselves, like user IDs, user roles, or any other information.
Using private claims might have conflicting semantic meanings outside of a closed or private system so use them with caution.
This is a sample of a payload for a token
This payload is a JSON object that will be encoded to base64 string and place in the second part of our JWT.
be careful don’t put sensitive data inner payload because the information contained in the JWT is not encrypted and we can easily decode it from base64 is and readable by anyone.
when our payload encrypt to base64 it look like this
To create the signature part you have to take the encoded header, the encoded payload, a secret key that held on the server, the algorithm specified in the header, and signing it using an encryption algorithm, such as HMAC SHA-256.
the signature produce with this instruction and will look like this
the server uses the secret key on the server to sign new token and
The signature is used to verify the message wasn’t changed. it guarantees that if someone tries to replace the payload, the token becomes invalid. if someone tries to changes the payload they have to regenerate the signature for this new payload and for this purpose they need a secret key to generate the signature but this secret key only available in the server os they can’t do this. the secret key in The signature is held by the server and it can use to verify existing tokens and sign a new token. on the server-side, for checking data integrity we compare requested token signature with a new computed signature with our secret key on the payload data that comes from the client, now if they are equal so we ensure our data is not changed by a malicious user.
suppose our secret key on the server is
secret, now our final third part of our token that produces with above instruction will look like this
Approaches for Securing Application in .Net Core
There are two main approaches for securing applications in .net core
- Using Cookie-Based Authentication: these days we frequently use token-based authentication instead of cookie-based authentication for some reasons that we mentioned earlier to this article. if you are interested in the implementation of cookie-based authentication in .net you can use these links: link1, link2 for more information and samples.
- Using Token-Based Authentication: In this article, we implement token-based authentication with jwt token and use of .net core JwtMiddleware for simplicity but we can create our custom middleware for handling token-based authentication.
Token-Based Authentication in .Net Core
There are two ways of implementing token-based authentication:
- Using the Authorization Header for transmitting and validating tokens.
- Using Cookie to transmitting the token
1. Token-Based Authentication: Authorization Header
In order to access a protected resource we need to attach a token like access token to our header, for example, we can attach our token to
request header and then send our request to the resource server to access the protected resource and after our request arrives in the resource server, it validates our token. .Net core does this action with thanks to a builtin
JwtBearerAuthentication authentication middleware. The
JwtBearer middleware looks for tokens (JWT) in the
HTTP Authorization Header of incoming requests. If a valid token is found the request is authorized and the request can access protected resources successfully, else access denied. we can also validate our token manually in some cases ourselves, I explain this later in this post.
Validating Tokens With Using .Net Core Authentication Middleware
To authenticate our token we need to config builtin .net core authentication middleware and configure it to accept JWT tokens to protect some of our API for direct access, So we add
AddAuthentication with its config to our Service Collection to configure the Authentication Middleware that we will add in next step in the Configure method our startup. then for configuring Authentication Middleware with Jwt, we use
AddAuthentication and inner this AddJwtBearer we can set some options on
JwtBearerOptions to control how our tokens are validated.
- Issuer, is the principal that has issued JWT. If token has different issuer than expected, the validation will fail and caller will receive 401 unauthorized.
- Audience, is the recipient that JWT is intended for. If token contains different audience than expected, the validation will fail and caller will receive 401 unauthorized.
- Signing Key, is the key you use for signing the token. with
JwtBearerOptionswe need to provide the key(s) our tokens will be signed with, that it can be different depending on whether we’re using a
symmetric key(such as HMAC-SHA256) or
asymmetric key(such as RSA-RS256).
- ClockSkew, has been set to TimeSpan.Zero. The this represents the time duration after expiry of token, for which the token should be considered valid. I wanted to immediately invalidate the token after expiry time, so I set it to TimeSpan.Zero.
Next step is to setup our Authentication Middleware, so we need to add
app.UseAuthentication() to Configure method of the startup before
AddMvc, also we add
app.UseCors("CorsPolicy") to control Cors in the application. we want our API to access from several API resources so to enable CORS and enable our API to be accessed from any Origin with any Method type and any Header we set some config in our ConfigureServices method.
When using endpoint routing, the call to
UseAuthentication must go:
UseRouting, so that route information is available for authentication decisions.
UseEndpoints, so that users are authenticated before accessing the endpoints.
Validating Tokens Without .Net Core Authentication Middleware
In some cases, we need to authenticate without.net core authentication middleware but if possible try to use this middleware because it matches perfectly with .Net core Authorization, for this purpose we use
JwtSecurityTokenHandler class in System.IdentityModel.Tokens.Jwt NuGet package.
ValidateToken method of
JwtSecurityTokenHandler getting a
TokenValidationParameters like that we used in authentication middleware below code is an example of manually validate our token.
SaveToken = true on
JwtBearer Middleware use to subsequent access this token in the application using
GetTokenAsync extension method on
HttpContext class. setting this property will be save token on
AuthenticationProperties of HttpContext. this method returns null if
SaveToken = false. you can read more in this link.
2. Token-Based Authentication: Using Cookie
we can use token-based authentication with a cookie but be careful with this purpose you need a cookie supported platform like browsers. in this approach token will be transferred for validating token within a request with the use of browser cookie mechanism. for using a cookie for transmitting token for authentication we need this
Microsoft.AspNetCore.Authentication.Cookies NuGet package.
.Net Core Cookie Authentication Middleware doesn’t support validating token that comes from cookie so for implement this with a custom validator and for this purpose, we class with the name of
CookieJwtFormat that implements
ISecureDataFormat interface to validating our token that comes from the cookie.
Unprotect method used for validating the information provided by the input token from the cookie. we don’t implement
Protect method because it uses for issuing a token and we just need a validating token in this place.
The AccessTokenFormat property references an object implementing ISecureDataFormat<AuthenticationTicket>, which in this case is our
We’ll be using ASP.NET Core’s authentication middleware and config it to use the cookie Authentication. we use
AddAuthentication to configure authentication middleware in ConfigureServices method of the startup. and we use
app.UseAuthentication() in Configure method our startup for setup authentication middleware.
The authentication scheme specified in the
AddAuthentication is just a string (in this case it resolves to “Cookie”). You can specify any string value as Authentication Scheme to distinguish it from other schemes in case there are more than one schemes.
AuthenticationScheme is useful when there are multiple instances of cookie authentication and you want to authorize with a specific scheme.
If an incoming cookie named
access_token contains a valid JWT, your protected MVC or Web API routes will be authorized. we can apply any rules to validate our token in our custom token validator (CookieJwtFormat).
Symmetric and Asymmetric key for Signing Tokens
Tokens that generate by our authorization server will be signed with a symmetric key (HMAC-SHA256) or an asymmetric key (RSA-RS256). we can configure our middleware for use of each of them for signing and validating a token.
Symmetric Key (HMAC-SHA256)
the symmetric key, or shared secret, is a secret value (like a password) that is kept on the API Resources for validating and authorize access from token and the authorization server that’s issuing tokens.
The authorization server signs the token payload with the secret key and the API Resources validate that incoming tokens that signed using the same key.
we can use .Net Authentication Middleware for authentication with using a secret key for validating our token for this purpose we use
AddJwtBearer to setup our secret key.
be careful about storing secret keys, In the above code, we store our secret in code but this is a sample and is not a real-world scenario. prevent hard-code your secret in your code because others can read your secret such as when you push your code on the GitHub and that is not a good choice. the best choice is writing our secret on environment variable on the server and read them in application with using .Net core capability for reading environment variables or use of Microsoft Secret Manager. this link is best practices and some approach for storing secret by Microsoft.
For issuing a token with using a
Secret Key or a
Symmetric HMAC Key we can use this code
Asymmetric Keys (RSA-RS256)
With the asymmetric signing, you don’t need to keep a secret key on your server for both Resource Server and Authorization Server. Instead, we using a public/private key, the authorization server signs tokens with a secret private key and publishes a public key that anyone can use to validate tokens like Resource servers.
a bad thing about the symmetric key is that both authorization and resource server share the same key for signing and validating the token. it is bad because the resource server can issue token with a secret key and this is not acceptable and the resource server only should validate the tokens, not more. for this reason use of an asymmetric key like RSA is a better choice against HMAC as a symmetric key.
As a solution for this, there is an option to use asymmetric keys like RSA-RS256 instead of symmetric HMAC-SHA256.
We run these commands in our command-line tools.
after executing these commands OpenSSL generate two
public-key-pem in the current working directory in your command line, the content this file for private and the public key is following:
Public Key & Private Key
.NET has a
RSA class for working with RSA keys in
System.Security.Cryptography namespace. this class has a method
FromXmlString that needs an XML file instead of a pem file so we need to convert our pem files for private and public keys to XML files. for this purpose, you can search in google and find an appropriate converter but I use this site. after converting our pem keys to XML files the output will be like this for private and public keys. after converting to XML we can format our XML output using an XML formatting tool like this and save each of them in an XML file.
If you are not using .Net Core 3 and upper you may get this error
'Operation is not supported on this platform', here is a GitHub issue for this problem and for lower than .Net Core 3 you have to create this converting yourself like this gist, but for .Net Core 3 and upper we don’t need that and we can use builtin
After preparing our public/private key we can use these keys on the authorization server and the resource server, on resource server we should validate our token so we only need to have a public key for this purpose, so we create a folder with name of Keys on resource server and put our public key in this folder. for handling validate our token with .Net Core Authentication middleware is very similar to symmetric key approach, only instead
SymmetricSecurityKey we use a
RsaSecurityKey. bellow is the code we need to config our authentication for validating token with RSA public key.
also for issuing a token on the authorization server, we need a private key that this private key only placed in authorization server for issue a token and other resource servers don’t have this key. For issuing a token with using an
Asymmetric RSA Key we can use this code