Application authentication
.NET Core ASP.NET Core Identity C# JWT OAuth Visual Studio

Persisting JWT Tokens in a .NET Core Application

Welcome to today’s post.

In this post I will be showing how to persist and reuse JWT tokens within an ASP.Net Core web application.

The use of tokens is a common technique to use when securing a web application. In web client applications we can use either HTTP cookies or tokens in our browser session to persist the storage of the access credentials.

What are JWT Tokens and HTTP Cookies?

HTTP cookies are stored in the Cookies collection in the Request and Response properties as key/value pairs with additional properties that determine expiry dates and security.

JWT tokens are stored with claims within a payload in JSON format and can be encrypted. The tokens can be signed with a private secret key or private/public key.

The HTTP cookies when sent with the Secure attribute are secured over the HTTPS protocol. Secured cookies can only be sent over a HTTPS protocol.  

The claims within the JSON payload are used to determine the credentials needed for the user authentication and to authorization access to the protected areas of our application.

JWT Token and Cookie Storage Options

In most web clients with form-based authentication, the stored cookies and/or tokens are validated by using an identity server. In addition, these tokens have a defined duration, with an expiry date depending on the visibility of the application to the end-users. Publicly facing applications will have cookies and tokens with much earlier expiry dates and shorter token lifetimes.

JWT tokens are a form of access token that is used to store larger amounts of data on the browser session.  

With web applications that are server hosted and have server-rendered content (such as ASP.NET), the generation of JWT tokens and their validation can be done on the server-side (Blog 54 – Implementing JWT JSON Token Authentication in your .NET Core Applications – 19-11-2019).

In earlier posts I discuss JWT tokens in more detail where I showed the following:

  1. How to create a JWT identity server
  2. How to register and authenticate accounts using an existing JWT identity server.
  3. Decoding and validate JWT tokens to retrieve user claims.

Configuring JWT Token Authentication in ASP.NET Core Web Applications

Before the JWT token can be used within a web client, the authentication middleware for JWT bearer tokens has to be enabled within the startup ConfigureServices():

// jwt token authentication
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(
        options =>
       {
           options.Authority = "https://localhost/BookLoan.Identity.API/";
           options.Audience = "https://localhost/";
       }); 

This establishes the authority that provides our JWT tokens.

Storing JWT Authentication Tokens in a Cookie

To store JWT authentication tokens within a browser’s cookie collection, we will need to go through the following steps:

  1. Call the authentication endpoint from your identity server.
  2. Retrieve the token property from endpoint response.
  3. Retrieve the user principal from the token.
  4. Assign the principal to the context object user property.
  5. Create a new CookieOptions() options object and set the Expires property.
  6. Add the token and options object to the Response Cookies collection.

Following the above steps, the code below shows how to authenticate a user, retrieve the JWT token and store the token in a cookie:

public async Task<IActionResult> Authenticate([FromBody] UserViewModel userDto)
{
     var request = new HttpRequestMessage(HttpMethod.Post,                   "http://localhost/BookLoan.Identity.API/api/Users/Authenticate");

     var client = _clientFactory.CreateClient();

     var content = JsonConvert.SerializeObject(userDto);

     StringContent stringContent =
        new StringContent(content, System.Text.Encoding.UTF8,
           "application/json");

     var response = await client.PostAsync(request.RequestUri,
        stringContent);

     string token = "";

     if (response.IsSuccessStatusCode)
     {
        string respContent =  response.Content.ReadAsStringAsync().Result;

        AuthenticationResponseViewModel aitem = JsonConvert.DeserializeObject<AuthenticationResponseViewModel>(respContent);
        token = aitem.Token;

        ClaimsPrincipal principal =_tokenManager.GetPrincipal(token);

        _context.User = principal;

        var option = new CookieOptions();
        option.Expires = DateTime.Now.AddMinutes(30);
        if (_context.Request.Cookies["token"] == null)
           _context.Response.Cookies.Append("token", token, option);
                
        return new OkResult();
     }

     return new UnauthorizedResult();
}

The above cookie is appended to the HTTP response context cookie with a finite expiry duration. This protects the cookie from a potential browser hijacking if the browser is left unattended, not closed or the app is not logged out.

Retrieving claim details from a token is done as follows:

string name = "";
if (_context.Request.Cookies["token"] != null)
{
   string token = _context.Request.Cookies["token"]; 
   if (token.Length > 0)
   {
       ClaimsPrincipal claimsPrincipal =
          _tokenManager.GetPrincipal(token);
       foreach (Claim claim in claimsPrincipal.Claims)
       {
          if (claim.Type == ClaimTypes.Name)
          {
             name = claim.Value;
          }
       }
   }
}

If the claims contains roles, they can also be retrieved and used for authorization. This will be shown in a future post.

To use httpContext and httpClient in .NET Core applications the following are recommended (as I showed in previous posts):

  1. Inject IHttpContextAccessor into your service or controller that is using the web request http context. 
  2. Inject IHttpClientFactory into your service or controller that is using the http client for GET and POST requests over HTTP

Note: As we are bypassing the OWIN authentication that ASP.NET Identity uses for signing in, signing out and authentication, we have had to persist the authentication token and provide the remaining authentication functions in our own web API.

I will show in a future post how to combine authorization handlers and JWT declarative authorization of controller methods.

That’s all for today’s post.

I hope this post has been useful and informative.

Social media & sharing icons powered by UltimatelySocial