Effective Security & Authentication for a modern full stack Web Application
Tags: webdev, security, cookies, authentication
Published: 2023-04-08 10:22:49

Over the past month, I have been researching ways to secure web applications without relying on third-party solutions, particularly for SAAS solutions that don't deal with highly sensitive information, such as banks. My goal has been to identify simple & straightforward methods to ensure the security of such web applications.

**EDIT**: My original strategy of not using refresh tokens had a flaw; hence the article has been updated by using refresh tokens inside http only secure cookies.

> **NOTE**: This article is for developers who do not want to use oauth2 or similar authentication solutions as a separate server is required for authentication in them. This is for baked in authentication that can be provided as part of REST or graphQL apis.

Below is the proposed solution.

There are two parts to any web application:

1. Front-end
2. Back-end

Most of my recent projects personal or work wise have a front-end framework essentially doing SSR or server side rendering.
For the back-end I use a GraphQL server.

Recently I started updating strategies for both options & I felt it was time I refreshed my approach to security.

## Secure your connections

The first rule of web security is serve everything over https. For that you are required to secure both your front & back end servers with SSL/TLS certificates.

## Front-end

For the SSR its all about configuring content security policy or CSP. Provide CSP header with a good policy to get relative security from XSS attacks.

_An example of a CSP policy:_

`Content-Security-Policy: default-src 'none'; script-src 'self' https://ajax.googleapis.com; style-src ‘self' ‘unsafe-inline’ fonts.googleapis.com; img-src ‘self' ‘data’; font-src ‘self' fonts.gstatic.com; connect-src 'self' https://api.example.com; frame-src 'self'; object-src 'none'; media-src 'self';`

In the above example we are disabling everything by default and then gradually adding each source explicitly. For example we have added google fonts support; & we are allowing to make calls to https://api.example.com. For more detailed explanation review the links below:

[MDN CSP Link](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy)

[Content Security Policy Website](https://content-security-policy.com/)

### Back-end

For our authentication strategy we will keep the following requirements in mind.

1. **Cookies required:**
 A front-end framework that does SSR would be easily able to render on the server if cookies were used for authentication.

2. **CSRF token for security:**
 cookie usage requires safety for CSRF attacks on post update & delete requests.

3. **JWT token generation:**
 JWT access tokens will be used for authentication.

4. **Short lived access tokens:**
 Access tokens need to be refreshed after short period of time.

5. **Refresh tokens:**
Refresh tokens will have to be used for short lived access tokens.

6. **Logout capability from the server:**
 For a reasonable SAAS offering there needs to be a way to logout users who’s access token is compromised, refresh tokens will serve that purpose.

> **NOTE**: JWT tokens are a topic in itself, if you are not familiar with them use the link below to get a better understanding. JWTs are tokens that have a signature portion that can be verified by the server to see that the token has not been tampered with. The claims that are part of the JWT are not secret if the token is compromised and can be read by anyone; hence data that needs to be kept secret should not be saved in them.

Reference below links for more details:

[CSRF Prevention](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#synchronizer-token-pattern )

[What is JWT](https://jwt.io/introduction/)

### The strategy:

1. The client requests authentication via username password to the server.

2. The server validates that username password against a database table with hashed passwords & generates a signed JWT.

> **NOTE**: Visible user passwords should never be stored without hashing them & providing a salt value for the hash via a library such as bcrypt. This is a separate topic & beyond the scope of this article.

4. The server then generates three cookies;

- 
An http only cookie that contains a signed JWT with claims such as expiry date & the user details required by the client. As this is a secure cookie javascript will not be able to access it at all & it will be transported on every request back to the server.

- 
It creates a refresh token possibly a guid value that expires potentially in 3 months time & stores it against the user record in the user table. It will be sent to the client in another http only cookie. The client won’t have to do anything.
- 
A non http only cookie readable by the client that contains a x-csrf-token. This token will be returned by the client in a custom header. This is for protection against csrf attacks.

### Scenario:

![Authentication Scenario](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h8dqp6x9lmhmspmrrzec.jpeg)

Lets look at a scenario how this strategy plays out.

- 
The client logs in with username password. On success the server creates a GUID as a refresh token and stores it in the database table against the user record as a list as the user can login from multiple devices. 

- 
The server sends the access token containing the JWT & the refresh token as the GUID in two http only secure cookies that will automatically be transported back on each request.

- 
The client on subsequent post, delete or update requests sends back the x-csrf-token value by reading it from the readable cookie sent by the server. As a third party is not able to add custom headers to a request this tells the server that the request is not compromised.

- 
On every request the server verify’s the access token/JWT; checks the x-csrf-token value is the same one as in the sent cookie; and verifies that a refresh token value exists.

- When the access token expires lets say every 15 minutes; the refresh token is verified & another access token/JWT is generated by the server & sent back in the http only cookie that will overwrite the previous one on the client. To increase security the refresh token can be regenerated again.

- 
If a certain users access token is stolen or compromised and we need to log them out we invalidate the refresh token by looking up that value in the list stored against that user in the database table.

- The next request sent with that access token will fail and the user will have to re-login.
This way the user will also be logged out of all the device where the refresh token is from. To be safe the whole list of refresh tokens can be removed to log that user out of all devices.

###Conclusion:

Securing a web application requires a comprehensive approach that covers both front-end and back-end security. Implementing security measures, such as HTTPS, CSP, access token & refresh token management, CSRF protection & short lived access tokens can help protect against attacks and keep your users' data safe.

I hope I have been able to describe how we can mitigate all these issues with a reasonable & simple authentication strategy.

Photo by [Micah Williams](https://unsplash.com/@mr_williams_photography?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)