In ASP.NET Core 8, Microsoft added a new authentication handler named BearerToken. In this blog post, I will explain how it works and its purpose. This handler is part of a bigger push by Microsoft to simplify authentication in ASP.NET Core, but in this blog post, we will focus on this handler.

BearerToken: The new Authentication handler in .NET 8

In ASP.NET Core 8, Microsoft added a new authentication handler named BearerToken. In this blog post, I will explain how it works and its purpose. This handler is part of a bigger push by Microsoft to simplify authentication in ASP.NET Core, but in this blog post, we will focus on this handler.

Head over to the blog post Improvements to auth and identity in ASP.NET Core 8 to read more about their effort to improve ASP.NET Core authentication.

Background

In the past, after you authenticated as a user, your application typically issues a session cookie. This cookie contains your user identity in the form of a ClaimsPrincipal. The content of the cookie is protected and encrypted using the built-in Data Protection API.

The issuer of this cookie is the Cookie authentication handler. This handler has two main purposes:

  •  When a user has been authenticated, the handler issues a new session cookie based on the provided user details, as shown in the picture below:
  •  Authenticating all incoming requests by looking for a session cookie. If a valid cookie is found, it will create a ClaimsPrincipal user object based on the information inside the cookie. This user object is then passed along the request pipeline in ASP.NET Core.

Why do we need a new handler?

Today, systems typically rely on the session cookie to authenticate user requests. This means that you are usually forced to use the user interface as provided by the backend server, with few options for customization. This often results in an inconsistent experience for users when they transition from a browser-based app experience to a server-rendered one.
Some of the goals by Microsoft are:

  •  Improve Cookie-Based Authentication: Introduce more customization and consistent user experience between single-page and server-rendered apps.
  •  Introduce Token-Based Authentication: Shift from cookies to the more flexible and widely-used, token-based authentication system.

The BearerToken handler is a core component of this effort.

What does the BearerToken handler do?

The new BearerToken handler can be seen as an alternative to the cookie handler, but with a twist..
Instead of issuing cookies, it will issue an access and refresh token. These tokens are not JWT tokens (for accessing external APIs). Instead, they are meant to be used between the client and web applications.

The BearerToken handler is not meant to be a stand-alone component. Instead, it is meant to be used with the ASP.NET Core Identity or a similar library.

Sample Application

I created a tiny sample application that shows the BearerToken handler in action. You can find the source code on GitHub
Running the Sample Application Do the following steps to use the application:
  • Start a web client, like Postman.
  • Start the application and click on the Login link.
  • As a result, you will be presented with the access and refresh token in JSON.
  • In Postman:
    • Make a GET request to the site homepage at https://localhost:5001
    • Choose Auth -> Bearer Token
    • Copy/paste the access token from the page.
  • Press Send to send the request to the site

As a result, you should get an HTML page back. Click on the Preview tab to view the page in Postman, as shown below. On the page, you will see the claims extracted from the access token you sent to the application.

Why can’t I run this directly in the browser?

The BearerToken handler is meant for clients who prefer to do authentication programmatically instead of going through the usual UI pages. That is why you must use tools like Postman so that you can get the token to be included in the authorization header.
Feel free to replace the BearerToken handler with the cookie handler in the source code. If you do, it will work as a normal web application using a session cookie.

What’s inside the tokens?

We saw that the result of a sign-in is this JSON document:
				
					{
  "token_type": "Bearer",
  "access_token": "CfDJ8F2Y2KDGq...",
  "expires_in": 3600,
  "refresh_token": "CfDJ8F2Y2KDG..."
}

				
			

What’s inside the access token? Can we look inside it?

By default, it is encrypted using the Data Protection API, but with a little code, we can make a transparent data protector that will not perform any encryption. This allows us to peek inside the tokens.
To do this, we can implement a custom transparent IDataProtector:

				
					public class MyDataProtector : IDataProtector
{
    public IDataProtector CreateProtector(string purpose)
    {
        return new MyDataProtector();
    }

    public byte[] Protect(byte[] plaintext)
    {
        return plaintext;
    }

    public byte[] Unprotect(byte[] protectedData)
    {
        return protectedData;
    }
}

				
			

Then, we configure the BearerToken handler to use this custom protector instead:

				
					.AddBearerToken(o =>
{
    o.BearerTokenProtector = new TicketDataFormat(
                                    new MyDataProtector()
                                        .CreateProtector(""));
    o.RefreshTokenProtector = new TicketDataFormat(
                                    new MyDataProtector()
                                        .CreateProtector(""));
});

				
			

Now, when we sign-in again, we get the same JSON structure as before:

				
					{
  "token_type": "Bearer",
  "access_token": "BQAAABdCZWFyZXJUb2tlbjpBY2Nlc3NUb2tlbgEAAAALQmVhcmVyVG9rZW4BAAEABgAAAAEADlRvcmUgTmVzdGVuaXVzAQABAAEAAAAAAD1odHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9jb3VudHJ5BlN3ZWRlbgEAAQABAAAAAABCaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvZW1haWxhZGRyZXNzD3RvcmVAdG4tZGF0YS5zZQEAAQABAAAAAAAISm9iVGl0bGUWQ29uc3VsdGFudCBhbmQgdHJhaW5lcgEAAQABAAAAAAAISm9iTGV2ZWwGU2VuaW9yAQABAAEAAAAAAAd3ZWJwYWdlFmh0dHBzOi8vd3d3LnRuLWRhdGEuc2UBAAEAAQAAAAAAAAABAAAAAgAAAAsucGVyc2lzdGVudAAILmV4cGlyZXMdTW9uLCAyMSBBdWcgMjAyMyAxNDo1MTozOSBHTVQ",
  "expires_in": 3600,
  "refresh_token": "BQAAABhCZWFyZXJUb2tlbjpSZWZyZXNoVG9rZW4BAAAAC0JlYXJlclRva2VuAQABAAYAAAABAA5Ub3JlIE5lc3Rlbml1cwEAAQABAAAAAAA9aHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvY291bnRyeQZTd2VkZW4BAAEAAQAAAAAAQmh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2VtYWlsYWRkcmVzcw90b3JlQHRuLWRhdGEuc2UBAAEAAQAAAAAACEpvYlRpdGxlFkNvbnN1bHRhbnQgYW5kIHRyYWluZXIBAAEAAQAAAAAACEpvYkxldmVsBlNlbmlvcgEAAQABAAAAAAAHd2VicGFnZRZodHRwczovL3d3dy50bi1kYXRhLnNlAQABAAEAAAAAAAAAAQAAAAEAAAAILmV4cGlyZXMdTW9uLCAwNCBTZXAgMjAyMyAxMzo1MTozOSBHTVQ"
}

				
			

The tokens are still a bit scrambled, so this didn’t help us that much!
However, what is the first thing you check when you have a string of random characters? You check if it is base64 encoded, and there are plenty of online tools to do that. One is https://www.base64decode.org

If we do that, we can see the data stored inside the access token:

				
					.....BearerToken:AccessToken.....BearerToken...........Tore Nestenius..........=http://schemas.xmlsoap.org/ws/2005/05/identity/claims/country.Sweden..........Bhttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress.tore@tn-data.se...........JobTitle.Consultant and trainer...........JobLevel.Senior...........webpage.https://www.tn-data.se......................persistent...expires.Mon, 21 Aug 2023 14:51:39 GMT


				
			

(Replaced all non-ASCII characters with a .)

What is inside the refresh token?
If you base64 decode the refresh token, you will find:

				
					.....BearerToken:RefreshToken.....BearerToken...........Tore Nestenius..........=http://schemas.xmlsoap.org/ws/2005/05/identity/claims/country.Sweden..........Bhttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress.tore@tn-data.se...........JobTitle.Consultant and trainer...........JobLevel.Senior...........webpage.https://www.tn-data.se......................expires.Mon, 04 Sep 2023 13:51:39 GMT

				
			

Comparing the two tokens, you will see they contain the same user information except for the token type and expiration time (Highlighted in bold above). The expiration time can be customized in the BearerToken options.

What is the purpose of the refresh token?
As we see above, the refresh token contains the same user information as the access token. One observation when reviewing the source code for the BearerHandler is that it only issues refresh tokens, but it does not contain any code to handle them. Instead, this token is meant to be consumed by another library (like ASP.NET Core Identity). For more details, see this GitHub issue:
Add token refresh endpoints to identity

Another observation is that these tokens are not JWT-tokens, which are usually a common format to transfer user information between systems.

Comparing the BearerToken, Cookie, and JwtBearer Handlers

In ASP.NET Core, we now have three handlers that do almost the same thing. The picture below tries to summarize the differences:

All three aim to authenticate user requests, while two of them also handle user sign-in information.

What about security? From a security perspective, I have mixed feelings about issuing and storing tokens in the client and if this can be done securely. As we all know, public clients (like mobile and browser-based apps) can’t handle secrets. However, this is all new for us as .NET developers, so we must see what patterns will emerge. Personally, I would probably introduce some BFF-style proxy between the client and server, as excellently explained in these two videos:

Issues

Working on this blog post has also raised a few questions, including:
  • Token size: The tokens can get quite large when they are filled with user claims, and there is no option to add a SessionStore(ITicketStore) like we can when we use the Cookie handler.
  • No way to customize the content of the refresh token: I am a bit curious why the content of it is the same as in the access token. I would assume we need less information in the refresh token.

Conclusions

I hope you now better understand the purpose of the BearerToken handler, how it works, and its limitations. This handler is part of ASP.NET Identity, so it will be interesting to see how this all turns out. However, that is a subject for another blog post.

About the author

Hi, I’m Tore! I have been fascinated by computers since I unpacked my first Commodore VIC-20. Today, I enjoy providing freelance development and developer training services, focusing on ASP.NET Core, IdentityServer, OpenID Connect, Architecture, and Web Security. You can connect with me on LinkedIn and Twitter. You can also find out more about me and my services there, as well as my classes for developers, including my course Introduction to IdentityServer and OpenID-Connect.

Share This Story
Share on linkedin
Share on twitter
Share on facebook
Share on pinterest
Share on email

About me

My name is Tore Nestenius and I’m a trainer and senior software developer focusing on Architecture, Security and Identity, .NET, C#, Backend, and Cloud, among other things.

Do You Want Tore To Be Your Mentor?

Categories