DEV Community

Sreeharsha
Sreeharsha

Posted on

Simplifying Authentication with JWT, TypeScript and Fastify

Authentication is the process of confirming the identity of a user, typically through credentials like a username and a password.
Authorization, determines if a user is permitted to perform an action, once their identity is authenticated.

JWT or JSON Web Token plays a crucial role in authentication and authorization process. It is an efficient and scalable solution for implementing secure authentication and authorization mechanisms in web applications. Learn more here: JWT.

This article demonstrates a simple way to implement authentication using JWT with TypeScript and fastify. For the database, I have Postgres and Prisma as the ORM(Object Relational Mapper) tool.

Getting Started

Follow the instructions provided in the github repository to setup the project: auth-demo.

Highlights

It's always import to store passwords by encrypting them before writing them to the database. I have done it using bcrypt and a simple salt value of 10 without any algorithm to hash the user password, during registration.

// src/modules/user/user.controller.ts

const newUser = await prismaClient.user.create({
            data: {
                username,
                password: await bcrypt.hash(password, 10),
            },
        });
Enter fullscreen mode Exit fullscreen mode

How to check the password during login? Since the salt value is private to the program, for any user request, the user can be authenticated by comparing the password with the encrypted password using bcrypt as well:

// src/modules/user/user.controller.ts

// Compare the password
const isValidPassword = await bcrypt.compare(password, user.password);
Enter fullscreen mode Exit fullscreen mode

It's surprisingly quite simple to do a minimal secure authentication with JWT using fastify-jwt plugin. This code in index.ts registers the fastify-jwt plugin with secret . This secret is used to sign and verify the JWTs. Ideally, for the most secure option you would generate a secret using an asymmetric algorithms like RS256, ES256 etc.

// src/index.ts

app.register(fastifyJwt, {
    secret: process.env.JWT_SECRET || "supersecret",
});
Enter fullscreen mode Exit fullscreen mode

To generate a JWT token, once a valid user request for login comes through, the JWT toke is generate using a payload. In this program the payload is the user id and the username. You don't want to put the password in the payload because JWTs can be decoded.

// Generate a token
const payload = { 
    id: user.id,
    username: user.username,
};
const token = request.server.jwt.sign(payload);
validTokens.add(token);  // Store the token in the in-memory store
Enter fullscreen mode Exit fullscreen mode

I setup an in-memory store for valid tokens using a HashSet for simplicity because it's a backend service. Ideally, would be managed on the client side. It enables the functionality of logging out, because of removing the appropriate token from the store. In user.controller.ts:

// src/modules/user/user.controller.ts

// In memory store to store the refresh tokens
const validTokens = new Set<string>();
.
.
.
// Remove the token from the in-memory store to logout user
validTokens.delete(token);

Enter fullscreen mode Exit fullscreen mode

To verify if the user is authenticated, it's a simple call from the FastifyRequest in the controller:

// src/modules/user/user.controller.ts
await request.jwtVerify();  // Verify the JWT token
Enter fullscreen mode Exit fullscreen mode

Additionally, I added further checks to see if the authorization header is present along with token when authenticating user as well as during logging out for more control.

Further Thoughts

Using JWT tokens provides a basic security to secure API endpoints, however with current trends in security, OAuth 2.0 and MFA is the standard.

Storing tokens in-memory for logout functionality is a simple workaround for the backend. In practice, it would be the up to the frontend client application to manage and store JWT tokens to maintain User session.

Conclusion

Authentication need not be complex for getting a proof of concept. With in-demand skills like Typescript and a simple framework like Fastify, you can streamline the process. This article provides a foundation for authentication in beginner applications.

Top comments (0)