loading...

Developing token authentication using ASP.NET Core

samueleresca profile image Samuele Resca ・5 min read

Originally posted on https://samueleresca.net

Developing token authentication using ASP.NET Core

Introduction

The following article shows how to developing token authentication using ASP.NET Core.

I have already written about ASP.NET Core here:

Token based authentication overview

Nowadays, Token based authentication is very common on the web and any major API or web applications use tokens.

Token authentication is stateless, secure and designed to be scalable. In fact, it is quickly becoming a de facto standard for modern single-page applications and mobile apps.

Developing token authentication using ASP.NET Core

The problems with server based authentication

Authentication is the process by which an application confirms user identity. Applications have traditionally persisted identity through session cookies, relying on session IDs stored server-side. A few major problems caused by this technique:

  • Scalability: if sessions are stored in memory, this provides problems with scalability;
  • CORS: as we want to expand our application to let our data be used across multiple mobile devices, we have to worry about cross-origin resource sharing (CORS);
  • CSRF: we will also have protection against cross-site request forgery(CSRF);
  • Sessions: Every time a user is authenticated, the server will need to create a record on our server;

How token based authentication works

Token based authentication is stateless. It don't store any information about our user on the server or in a session.

Here's the common steps of the token based authentication:

  1. user requests access by using username / password;
  2. application provides a signed token to the client;
  3. client stores that token and sends it along with every request;
  4. server verifies token and responds with data;

Every single request will require the token. The token should be sent in the HTTP header to keep the idea of stateless HTTP requests.

Implementing Token based authentication using ASP.Net Core

This example shows how to developing token authentication using ASP.NET Core, the following  UML schema shows the architecture of project:

Developing token authentication using ASP.NET Core

Setup the project

First of all, is necessary create new ASP.NET Core project. I suggest to use ASP.NET Yeoman Generator to generate project using Web application template and Visual Studio Code to edit the code.

Once the project is successfully created, add the following configurations to your appsettings.json:

https://gist.github.com/978e16a046424d00e933eca778702d93

The TokenAuthentication section configures some common information about token generation, for example the SectionKey used by token.

Tokens transmission / validation

There are two ways to transmit the authorization tokens:

  1. using  HTTP Authorization headers (aka  Bearer authentication);
  2. using browser cookies to save the authentication token;

Bearer token validation

The  Microsoft.AspNetCore.Authentication.JwtBearer package enables you to protect routes by using a JWT Token.

To enable Bearer token authentication, import the following Nuget package Microsoft.AspNetCore.Authentication.JwtBearer in the project.json:

https://gist.github.com/samueleresca/c4dd98829e7b8d3e830255cf958c6333

To initialize the Bearer authentication you need to split your Startup.cs file and use another partial class, for example Startup.Auth.cs:

https://gist.github.com/samueleresca/66256d739cb20c0a848f8c43240f12b4

The Startup.Auth.cs file initialize the Bearer Authentication using  configurations defined in the appsettings.json file. The tokenValidationParamaters object will be used also by Cookie validation.

Cookies validation

Cookies validation enables the Token transport over browser cookies, to enable the Cookie token authentication you need to add the following package inside the project.json:

"Microsoft.AspNetCore.Authentication.Cookies": "1.0.0"

and  create a custom validator for the input token.

To create the new validator add the following CustomJwtDataFormat.cs file:

https://gist.github.com/samueleresca/ec310b096a8543eb170d2fdebcbef8b7

Unprotect method decript and validate information provided by the input token. Call the following method in the Startup.Auth.cs file, to use the Cookie authentication:

https://gist.github.com/samueleresca/424b2062a9cee7daa58672e5fdec0d1d

Token generation

There isn't native support to Token generation in ASP.NET Core, but it is possible write a custom token generator middleware from scratch.

Firstly, you need to create a class which implement token options :

https://gist.github.com/samueleresca/c475abd4a6831a23981a55d76223757b

The middleware class will use TokenProviderOptions.cs to generate tokens:

https://gist.github.com/samueleresca/e4e7f1f06c6af12fb1b126a0479e0054

The TokenProviderMiddleware class implement the Invoke method to generate tokens by using the TokenProviderOptions. In order to initialize the middleware, it is necessary modify the Startup.Auth.cs file and add in the ConfigureAuth method:

https://gist.github.com/samueleresca/4c8a8844a944005a4086fa20b0ebbfad

The tokenProviderOptions defines the options of the token generator. The IdentityResolver is the Task method which will check the identity of users. For demo purposes, the IdentityResolver is implemented by a simple method called GetIdentity.

Final steps

Now is possible call the ConfigureAuth method inside the Startup.cs file:

https://gist.github.com/samueleresca/765a08c9681f8478074014f2c910a0f3

Getting token

You can obtain the JWT token by calling the following route /api/token/ using POST and passing the username and password data:

POST api/token
Content-Type: application/x-www-form-urlencoded
username=TEST&password=TEST123

Authorize controllers

All controllers decorated by the attribute [Authorize] are protected by the JWT authentication.
In each http call you need to pass the access_token parmeter:

http://hostname/controller/route?access_token=MY_TOKEN

 

The demo code is available on Github.

Cheers :)

Posted on Jan 14 '17 by:

samueleresca profile

Samuele Resca

@samueleresca

Samuele Resca is an Microsoft MVP Visual Studio and Development Technologies, Software Engineer, specializing mainly on ASP.NET MVC and in general about everything that revolves around the web. Samuele was born in 1994, and works as a software developer @ YOOX NET-A-PORTER Group He loves the MVC frameworks, ASP.NET MVC, Javascript, Node.js and Typescript.

Discussion

markdown guide
 

Very Nice. Thank you!!

The implementation since ok however when trying to access the api using the acquired token I get:

Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET localhost:2709/api/values

Microsoft.AspNetCore.Authorization.DefaultAuthorizationService:Information: Authorization failed for user: (null).
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Warning: Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
Microsoft.AspNetCore.Mvc.ChallengeResult:Information: Executing ChallengeResult with authentication schemes ().
Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware:Information: AuthenticationScheme: Cookie was challenged.

Seems that some part of the puzzle is missing.

Anyone have an idea why?

 

I am not able to install any packages for dotnet core, including the ones here.
Package Microsoft.AspNetCore.Identity 2.0.0 is not compatible with netcoreapp1.1 (.NETCoreApp,Version=v1.1). Package Microsoft.AspNetCore.Identity 2.0.0 supports: netstandard2.0 (.NETStandard,Version=v2.0)
One or more packages are incompatible with .NETCoreApp,Version=v1.1.
Package restore failed. Rolling back package changes for

this is the error I get when installing Microsoft.AspNetCore.Identity. Same for Microsoft.AspNetCore.Authentication.JwtBearer or any AspNetCore package. I have made sure that the project type is correct.

 

When I try to implement with a .Net Core 2.0 project I get the following error in the Startup.Auth.cs file:
"JwtBearBearerAppBuilderExtensions.UseJwtBearerAuthentication(IapplicationBuilder,JwtBearerOptions) is obsolete"

Any idea how to resolve?

 
 

Thanks for a great article! my question is how to refresh the token when it expires? or what's the mechanism I should adapt in order not to prompt my user for credentials when they're in the middle of doing something considering my token lifetime would only be 10 minutes.

 

First of all, I thank you for this tutorial.. very useful..
I have a couple of questions (I'm new to JWT).

Let's say that I have a login page.
From this page I get the token.

I wonder to know how to pass it to the next controller (I use a redirect action when the credentials are correct) and globally how to add the famous header (Authorization:Bearer myToken) to all the controllers I use

My second question is how to manage the token refresh?

Thanks once again

 

Also ran the api/token with the username and password all worked well I got the toekn in return which I appened to a HTTP get call to the server but the authorization fails

 

Errata corrige:

You need to pass the token in the HEADER of the request:

Authorization:Bearer <TOKEN>
 

When i pass the Authorization Bearer in header, it gives me this error:
The header name format is invalid.

This is how i'm passing it:

client.DefaultRequestHeaders.Add("Authorization:Bearer", json.accessToken);

where client is a HttpClient variable and json.accessToken is the value of the token.

Another approach..
If you download the tool Postman you can easily check how this works.
After you revieced your token, you just have to put into Header:
Key: Authorization
Value: Bearer YOUR_TOKEN
including the whitespace between Baerer and your token!

I get nothing but 401s using this very approach in Postman.

Try This

client.DefaultRequestHeaders.Add("Authorization",string.Concat("Bearer ", json.accessToken));

 

"In each http call you need to pass the access_token parmeter:
hostname/controller/route?access_t..."
Can i pass the token in the headers section (like, a cookie, for example)? or i need to pass the token in the url?

Edit: Nevermind, didn't saw your "Errata corrige:"

Great article! \o

 

Thank you for the article, it helped me a lot understanding Token generation/validation as I am new to all this asp.net/asp.net core universe.
I have a question though related with the middleware necessity. As you provided a route for the token generation (/api/token), I ask myself if another approach wouldn't be the implementation of the TokenProviderOption class as a Configuration/Option object as explained in this Microsoft doc (docs.microsoft.com/en-us/aspnet/co...) letting the TokenController manage token generation instead of creating a Middleware, apparently, just to hold the customized options for the token generation process.
I would be grateful for any insights on this matter!

 

First of all let me say this is an excellent post.

I have one question though.

I have implemented this with an WEB API project. If the user is not authorized (e.g. token expired) a 404 Not found is returned.

How can you return an unauthorized status instead of "404 Not found"

With cookies I had implemented it as such

 services.AddIdentity<ApplicationUser, IdentityRole>(identityOptions =>
            {
                identityOptions.Cookies.ApplicationCookie.Events =
                    new CookieAuthenticationEvents
                    {
                        OnRedirectToLogin = context =>
                        {
                            if (context.Request.Path.StartsWithSegments("/api") && context.Response.StatusCode == (int)HttpStatusCode.OK)
                                context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                            else
                                context.Response.Redirect(context.RedirectUri);

                            return Task.CompletedTask;
                        }
                    };                


            })
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders(); 

Thanks
Shaheem

 

I get the following error when trying to build: 'TokenProviderOptions' does not contain a definition for 'NonceGenerator' and no extension method 'NonceGenerator' accepting a first argument of type 'TokenProviderOptions' could be found (are you missing a using directive or an assembly reference?)

I see in your TokenProviderOptions you do not have a property for NonceGenerator()?

 

Couldn't find it either, so I googled Blog.TokenAuthGettingStarted and navigated to the same file, for some reason I could find it in there.


/// <summary>
/// Generates a random value (nonce) for each generated token.
/// </summary>
/// <remarks>The default nonce is a random GUID.</remarks>
public Func<Task<string>> NonceGenerator { get; set; }
= () => Task.FromResult(Guid.NewGuid().ToString());

 

When a request arrives with a valid token in the header, how should the end point determine which user the token is associated with?

 

The token contains all the necessary informations. For example, you can see token infos here: (jwt.io/)[https://jwt.io/]

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJURVNUIiwianRpIjoiZTI2NGQ3NzktZTg2Ni00NGVlLThjOTUtZmI5YTA3NjNhM2ViIiwiaWF0IjoxNTI0NTIzNjY2LCJuYmYiOjE1MjQ1MjM2NjYsImV4cCI6MTUyNDUyMzk2NiwiaXNzIjoiRGVtb0lzc3VlciIsImF1ZCI6IkRlbW9BdWRpZW5jZSJ9.SORiDZO7kohrJ-Ew_hsTtJ5r9Zqn1RN9sjSTJni7MDo

 

Thanks a lot for the Net.Core v2.0 UPDATE!!!

 

Hey Samuele Is it possible to use token based authentication in simple asp.net project ,mean (without MVC ,wihtout Asp.net core) please tell me ,If yes then How ?

 

Hi. The excelent example worked for me.

But , how to bring back the username on a Controller ?

 

Nice article. Do you know how to validate/refresh token after database changed, eg: user change password... Thank you.

 

Token based authentication is great... until you need to revoke a token.

 

Is it possible to use this set up and point the Token IdentityResolver to a controller? If so, how?

 

Amazing article, helped me a lot since I'm a beginner in token based auth. thank you!