introduce
This article provides a comprehensive step-by-step guide to implementing authentication and authorization in a NestJS backend application using Clerk.
What is a clerk?
Clerk is a comprehensive platform that provides an embeddable user interface, flexible APIs, and an intuitive and powerful dashboard for seamless user authentication and management. It covers everything from session management and multi-factor authentication to social login, magic links, email or SMS one-time passwords and more.
Why use clerks?
As data protection and privacy become increasingly important, authentication and security requirements, trends and best practices are always evolving. By offloading these responsibilities to a professional services provider, you can focus on building the core functionality of your application and deliver it faster.
Platforms like Clerk can take on these security tasks for you.
Prerequisites
- Basic knowledge of Typescript
- Familiar with NestJS basics
- Understand backend authentication concepts
- Running Node 18 or latest version
Project settings
This project requires a new or existing NestJS project, a Clerk account and application, and the Passport, Passport Strategy, and Clerk backend SDK libraries.
Create a NestJS project
You can use the Nest CLI to easily set up new NestJS projects. Using any package manager you prefer, execute the following command to create a new Nest app:
$ pnpm add -g @nestjs/cli
$ nest new clerk-auth
Checkout NestJS documentation Learn more.
Set up your Clerk account and application
If you don’t have a Clerk account yet, create a Clerk account and set up a new application in the Clerk dashboard. You can start Clerk Website.
Install required libraries
The libraries required for this project can be installed using the following command:
$ pnpm add @clerk/backend @nestjs/config @nestjs/passport passport passport-custom
Environment configuration
Create a .env
The file is located in the root directory of the project and is used to manage variables in different environments. production
, development
or staging
.
Add the following variables, replacing the placeholders with the actual keys obtained from the Clerk Account Dashboard.
# .env
CLERK_PUBLISHABLE_KEY=YOUR_PUBLISHABLE_KEY
CLERK_SECRET_KEY=YOUR_SECRET_KEY
Use the following commands to access environment variables throughout your application ConfigService
import ConfigModule
into the roots AppModule
.
// src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
],
})
export class AppModule {}
Integrating Clerk in NestJS
This section describes how to integrate and use the Clerk backend SDK in a NestJS project.
Create Clerk client provider
Registering the Clerk client as a provider allows it to be injected into a class using a decorator so that it can be used wherever needed throughout the code base, as shown in the next section.
// src/providers/clerk-client.provider.ts
import { createClerkClient } from '@clerk/backend';
import { ConfigService } from '@nestjs/config';
export const ClerkClientProvider = {
provide: 'ClerkClient',
useFactory: (configService: ConfigService) => {
return createClerkClient({
publishableKey: configService.get('CLERK_PUBLISHABLE_KEY'),
secretKey: configService.get('CLERK_SECRET_KEY'),
});
},
inject: [ConfigService],
};
Register ClerkClientProvider in AppModule
Next, you need to register the provider with Nest to enable dependency injection.
// src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { ClerkClientProvider } from 'src/providers/clerk-client.provider';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
],
providers: [ClerkClientProvider],
})
export class AppModule {}
Use a passport with a JWT issued by a clerk
Clerk issues a JWT token when a user registers or logs in through Clerk’s hosted page or front-end application. Then use that token as in bearer token Authorization
header The number of requests made to the NestJS backend application.
Develop clerical strategy
In NestJS, Passport
is the recommended way to implement authentication policies. You will create a custom Clerk policy for using Clerk client authentication tokens.
// src/auth/clerk.strategy.ts
import { User, verifyToken } from '@clerk/backend';
import { Injectable, Injectable, UnauthorizedException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy } from 'passport-custom';
import { UsersService } from 'src/users/users.service';
import { Request } from 'express';
import { ClerkClient } from '@clerk/backend';
@Injectable()
export class ClerkStrategy extends PassportStrategy(Strategy, 'clerk') {
constructor(
@Inject('ClerkClient')
private readonly clerkClient: ClerkClient,
private readonly configService: ConfigService,
) {
super();
}
async validate(req: Request): Promise<User> {
const token = req.headers.authorization?.split(' ').pop();
if (!token) {
throw new UnauthorizedException('No token provided');
}
try {
const tokenPayload = await verifyToken(token, {
secretKey: this.configService.get('CLERK_SECRET_KEY'),
});
const user = await this.clerkClient.users.getUser(tokenPayload.sub);
return user;
} catch (error) {
console.error(error);
throw new UnauthorizedException('Invalid token');
}
}
}
this validate()
The method returns the user data that NestJS automatically appends to request.user.
Create an authentication module
Create a AuthModule
Provide Clerk strategies and work with PassportModule
. Then, register AuthModule
exist AppModule
.
// src/auth/auth.module.ts
import { Module } from '@nestjs/common';
import { ClerkStrategy } from './clerk.strategy';
import { PassportModule } from '@nestjs/passport';
import { ClerkClientProvider } from 'src/providers/clerk-client.provider';
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [PassportModule, ConfigModule],
providers: [ClerkStrategy, ClerkClientProvider],
exports: [PassportModule],
})
export class AuthModule {}
// src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { ClerkClientProvider } from 'src/providers/clerk-client.provider';
import { AuthModule } from 'src/auth/auth.module';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
AuthModule,
],
providers: [ClerkClientProvider],
})
export class AppModule {}
Implement route protection
Protected routes are routes that require authenticated users to access.
Create Clerk Authentication Guard
Guards decide whether a route handler should handle a specific request based on certain runtime conditions.
If you want to protect all routes in your application by default, you need to follow these steps:
- Create a
Public
Decorator to mark routes accessible without authentication. - implement a
ClerkAuthGuard
Restrict access to protected routes, allowing only authenticated users to proceed.
// src/decorators/public.decorator.ts
import { SetMetadata } from '@nestjs/common';
export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
// src/auth/clerk-auth.guard.ts
import { type ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
import { IS_PUBLIC_KEY } from 'src/decorators/public.decorator';
@Injectable()
export class ClerkAuthGuard extends AuthGuard('clerk') {
constructor(private reflector: Reflector) {
super();
}
canActivate(context: ExecutionContext) {
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
]);
if (isPublic) {
return true;
}
return super.canActivate(context);
}
}
Enable authentication domain-wide
Since most endpoints are protected by default, you can configure authentication protection as domain-wide protection.
// src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { ClerkClientProvider } from '../providers/clerk-client.provider';
import { AuthModule } from '../auth/auth.module';
import { ClerkAuthGuard } from '../auth/clerk-auth.guard';
import { APP_GUARD } from '@nestjs/core';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
AuthModule,
],
providers: [
ClerkClientProvider,
{
provide: APP_GUARD,
useClass: ClerkAuthGuard,
},
],
})
export class AppModule {}
Define protected public routes
In these two controllers, Public
Decorators are used for AppController
Designate the route as public. In contrast, no decorators are required AuthController
Specify the route as protected because authentication protection is applied domain-wide by default.
// src/app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { Public } from 'src/decorators/public.decorator';
@Controller()
export class AppController {
@Public()
@Get()
async app() {
return 'Hello World';
}
}
// src/auth/auth.controller.ts
import { User } from '@clerk/backend';
import { Controller, Get } from '@nestjs/common';
import { CurrentUser } from 'src/decorators/current-user.decorator';
@Controller('auth')
export class AuthController {
@Get('me')
async getProfile(@CurrentUser() user: User) {
return user;
}
}
notes: Remember to register AppController
exist AppModule
and AuthController
exist AuthModule
.
in conclusion
Clerk serves as a platform that handles identity verification and security responsibilities, keeping up with the latest trends and best practices. This allows you to focus on building the core functionality of your application and accelerates your development process.
In this guide, we cover the steps to implement Clerk authentication, from setting up your project to securing your routes. These basic steps should help you start exploring the possibilities of an identity verification service platform.
A fully functional example of this project is included at the end of this article.
Using Clerk authentication and user management in NestJS backend applications
Using Clerk authentication and user management in NestJS backend applications
What’s inside?
The monorepo contains the following packages and applications:
Applications and packages
Every package and app is 100% typescript.
Utilities
This monorepo has some additional tools set up for you: