8

The Token Handler Pattern for better Authentication

 1 year ago
source link: https://levelup.gitconnected.com/secure-frontend-authorization-67ae11953723
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

The Token Handler Pattern for better Authentication

0*japwFH2G8DO01dYU

Photo by Sara Sperry on Unsplash

In this framework-agnostic article I will introduce you to a modern approach on building a secure authentication process for JavaScript applications. If you are already familiar with JWT tokens and OAuth 2.0, it is very likely that you will profit from reading this, because the article explains why storing tokens in the browser should not be done and will show you a modern approach on combining cookies and JWT tokens to create more secure applications.

OAuth 2.0

There are many good articles out there explaining the details of OAuth 2.0 perfectly, so I will instead focus on giving you a very simplified overview of OAuth 2.0. So, in principle OAuth 2.0 is the defacto industry standard for creating a secure authentication flow for any sort of application. Basically the client sends a login request to an authentication provider, which then sends a 302 Redirect, which triggers the browser to redirect to the login page provided by the authentication provider. After the login the authentication provider redirects back to the original route where the client triggered the login process and in addition sends an access token and a refresh token which are used for authentication when using bearer authentication (defacto standard).

1*BiD2wbB0A590rw1L9Jbqcw.png

strongly simplified process

Access tokens are appended in every HTTP request header as the authorization parameter such that the API can validate with the authentication provider, if the token is valid. Usually, an access token has a low expiration times, which sometimes is even in the matter of minutes, because this allows for better security such that if a malicious party could not use the access token long enough to make many malicious requests.

Refresh tokens are then used by the client to get a new access token from the authentication provider, when the current access token is expired.

Both of these tokens are considered to be a JWT (JSON Web Token) which basically is an encrypted json object containing standard data, but also application-specific data.

OAuth 2.0 is convenient and allows for easy third party identity provider integrations like Google Identity, Facebook, Microsoft, Azure Active Directory, etc. So, we trigger the login process and only have to store the access token and refresh token in the local storage and then append the token to the HTTP headers. Simple as that. But…

Cross Site Scripting

I wish there wasn’t this annoying thing called Cross Site Scripting, short: XSS. If we care about security and defending against XSS, we must not that simply just store the tokens in local storage.

XSS is a hacking technique used to inject custom JavaScript into the DOM at runtime. For example, a hacker could inject JavaScript code in an input field, by escaping the input with an “-sign and then executing custom JavaScript code, whereas every JavaScript API is accessible to the malicious attacker. Therefore the attacker can easily read the entries from local storage and get the tokens in his hands. From this point on you can only hope for the best meanwhile wishing you had put in more effort into securing the application in the first place. So what is the solution?

Token Handler Pattern

Let’s walk back a bit in time an talk about an older approach for authentication between frontend and backend applications. In the good old days, we did not have those fancy tokens, but we used simple cookies for verifying a user is logged in. Back then, the frontend sent a username and a password to the backend, which then created a cookie which resembles the authorization state and set it in the HTTP headers and the frontend would then always send the cookie in the HTTP headers in every request.

Let’s come back to our example, where the problem is that we store the tokens in the client. Badly, there is no way we can securely store the tokens in the client, since we do not want JavaScript to have access to them. The only way to bypass this problem is to create a dedicated backend that handles the login.

1*X6OTNQx8QiNvJB4iXi9aQg.png

simplified process

The frontend only communicates with a middleware which handles the authentication/authorization and acts as a proxy between the API and the frontend. The middleware triggers an HTTP 302 Redirect, which will automatically force the browser to redirect to the login page provided by the identity provider. After the login the identity provider redirects to a redirect_url, which must be specified in the original redirect request. The redirect_url points to a dedicated route on the frontend whereas the identity provider will add an authentication code to the query parameters. The frontend then will send the authentication code to the backend, which uses the authentication code to generate tokens. Those tokens will be stored in an http-only cookie, which is set in the HTTP headers in the response to the frontend. The frontend then stores this cookie in the browser and by adding the parameter credentials: ‘include’ to the initial request.

1*D8FRhWvHV5k-0Ee00ZgWqg.png

detailed login workflow

But what exactly does HttpOnly mean and why should this be more secure than storing a token on the client?

Http-Only Cookies

Why should it be more secure to store a token in a cookie than in local storage? Well, by default it is not, since the JavaScript API can also easily read all cookies by accessing document.cookie. So now XSS is a concern again.

But luckily, we can use so-called http-only cookies, which are not accessible by the JavaScript API. Even though they are stored exactly the same way as a normal cookie in the browser, they can only be appended to HTTP requests. For example, you could set the option credentials:’include’ from the fetch API to include all cookies, including the http-only cookie, in the request headers. That way, a XSS attacker has no possibility to get the http-only cookie and therefore cannot get the tokens into his hands.

First-Party vs Third-Party Cookies

There is one thing remaining we need to discuss — the difference between third-party and first-party cookies. A cookie created on another domain is always considered a third-party cookie, whereas a cookie created on the domain, the frontend is hosted on, is considered a first-party cookie.

1*Lcb7ku4Qx1sfSaPlr45wJg.png

It is not uncommon that the backend is hosted on a different domain, especially when the frontend is a Single Page Application (SPA). In that case, another XSS attack is still possible, because the attacker could inject code that executes a HTTP request to his own API and includes the cookies, such that his API will read the http-only cookie.

Luckily, there is again a way to prevent this, by making a first-party cookie and setting the header parameter SameSite=’strict’, such that the cookie is only sent to requests targeting the same domain. We would simply have to deploy the middleware on the same domain.

Serverless Token Handler

Although it is not necessary, it is the perfect scenario for using serverless computing, instead of creating a dedicated API for the middleware.

Serverless computing is a cloud service, that allows for developing and deploying simple stateless functions without having to think of infrastructure and servers. Although the name might be misleading, the functions will still run on a server hosted by a cloud computing provider, but we do not have to think about a servers. They can be scaled-out as needed and scaled-down as well, such that you only pay for your actual use.

1*XcH2YlJmZUeZB6l_h9rb5w.png

Azure Functions logo

We can create two simple functions that make a whole Web API only for the middleware needless. The first function would be the login function and the second one a proxy function that always redirects the request to the API and appending the access token in the headers.

Conclusion

Frankly, no system is ever completely secure. But this hypothesis should not stop us from creating applications that are more secure.

If you enjoyed this article and want to read unlimited on Medium, you can join Medium with my referral link:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK