Guide til å sikre Next.js/Nest.js-applikasjoner med Clerk Express SDK

Guide to securing Next.js/Nest.js application with Clerk Express SDK
In this Guide you can read about our implementation of Clerk as we transitioned from Auth0 to Clerk. In this guide you will learn how to secure your Next.js application using the App Router and with an external Nest.js api backend.
What is Clerk?
Clerk is a cutting-edge identity provider renowned for its advanced security features. By leveraging encryption, multi-factor authentication, and continuous updates, Clerk ensures robust protection for digital identities. Embrace Clerk to fortify applications with top-tier security measures, prioritizing data privacy and integrity in a dynamic digital landscape.
Frontend
Implementing Frontend Security Measures
To configure the app router effectively within Next.js , ensure the establishment of dedicated folders within your project structure, specifically the 'src' and 'app' directories. Start the process by integrating the Clerk Next.js SDK into your project seamlessly.
Within the 'src' folder (Note: Avoid placing middleware in the 'app' folder), introduce a 'middleware.ts' file. Optimal security measures can be achieved by restricting access to all routes except those designated for public use. Given the limited public-facing routes in the Medal Social application, this approach ensures a swift and efficient implementation. The following code snippet exemplifies the configuration within the middleware.ts file:
import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";
const isPublicRoute = createRouteMatcher(["/logout(.*)", "/end(.*)"]);
export default clerkMiddleware(async (auth, request) => {
if (!isPublicRoute(request)) {
await auth.protect();
}
});
export const config = {
matcher: [
// Skip Next.js internals and all static files, unless found in search params
"/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)",
// Always run for API routes
"/(api|trpc)(.*)",
],
};
When communicating with our external API in Nest.js through server action, it is crucial to include the access token with the request. This token serves as a unique identifier, allowing our backend to accurately identify the user making the request and retrieve the appropriate data. This vital step ensures precise data fetching and enhances the overall security and integrity of the application.
"use server"
import { auth } from "@clerk/nextjs/server";
export async function makeBackendRequest(request: RequestDto): Promise<any> {
const { getToken } = await auth();
const token = await getToken();
if (!token) {
throw new Error("No access token");
}
const res = await fetch(`externalapi.example.com/channel`, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(request),
});
if (!res.ok) {
throw new Error("Failed to create request");
}
const data = await res.json();
return data;
} catch (error) {
console.error("Error creating request:", error);
return null;
}
Ensuring Your Frontend Security
By implementing Clerk for frontend security, you have fortified your application to prevent unauthorized access. Clerk's robust identity management system ensures that only authenticated users can interact with your app, upholding a secure environment. With this setup, authenticated users can confidently make queries to the backend, maintaining the integrity and privacy of data transactions within your application.
Backend
Enhancing Backend Security: Installing Clerk Express SDK.
To enhance security for our Nest.js backend, we adopt a straightforward approach. Previously, the most convenient method was utilizing the Clerk Node.js SDK. However, starting January 8, 2025, this SDK will no longer be accessible. Given that Clerk lacks a dedicated Nest.js package, we now turn to the Clerk Express SDK. To integrate this, install @clerk/express into your project. In your main.ts file, within the bootstrap function, include the command: app.use(clerkMiddleware());
This integration enables us to leverage middleware functions for extracting specific user data during authentication processes.
Enhancing Security Measures: Setting up Guards
In this section, we will focus on setting up Guards to further strengthen the security measures in your Nest.js backend. Guards play a crucial role in controlling access to certain endpoints and ensuring that only authenticated users can interact with specific parts of the application. Let's delve into the implementation of Guards to enhance the overall security of your backend system.
Now, let's proceed with the detailed guide on how to set up Guards effectively for your Nest.js backend.
import { Injectable, CanActivate, ExecutionContext, UnauthorizedException, } from '@nestjs/common';
import { authenticateRequest, getAuth, createClerkClient } from '@clerk/express';
import { Request, Response } from 'express';
@Injectable() export class ClerkAuthGuard implements CanActivate {
private clerkClient: ReturnType<typeof createClerkClient>;
constructor() {
this.clerkClient = createClerkClient({
secretKey: process.env.CLERK_SECRET_KEY
});
}
async canActivate(context: ExecutionContext): Promise<boolean> {
const http = context.switchToHttp();
const request = http.getRequest<Request>();
try {
const authState = await authenticateRequest({
clerkClient: this.clerkClient,
request,
options: {
debug: process.env.NODE_ENV !== 'production',
enableHandshake: true
}
});
if (!authState.isSignedIn) {
throw new UnauthorizedException('User is not signed in')
}
const auth = getAuth(request);
if (!auth) {
throw new UnauthorizedException('No authentication information found')
}
if (!auth.userId) {
throw new UnauthorizedException( 'User ID not found in authentication information' );
} if (!auth.sessionId) {
throw new UnauthorizedException( 'Session ID not found in authentication information', );
}
return true;
} catch (error) {
if (error instanceof UnauthorizedException) {
throw error;
}
console.error('Authentication error:', error);
throw new UnauthorizedException('Authentication failed');}} }
Securing Your Controllers
Now you can use the @UseGuards decorator from Nest.js to secure your endpoints like this:
import { ClerkAuthGuard } from 'src/auth/auth.guard';
@UseGuards(ClerkAuthGuard)
@Controller('todo') export class TodoController {
constructor(private todoService: TodoService) {}
@Post()
@UsePipes(new ValidationPipe())
createTodo(@Body() createTodoDto: CreateTodoDto) {
return this.todoService.createTodo(createTodoDto);
}
}
Your backend is now secured. The external API, which has access to your Database, cannot be accessed by unauthenticated users. Additionally, as we extract user data, we have full control over the data accessible to the current user, ensuring they only access their own or their team's data.
Summary: Enhancing Backend Security
By implementing Clerk Express SDK in your Nest.jS backend, you have fortified your application's security measures effectively. From setting up middleware for user authentication to implementing guards for endpoint security and securing controllers to restrict unauthorized access, your backend now ensures data integrity and privacy. With these measures in place, your application provides a secure environment for user interactions and data handling.
Summary
- Frontend Implementation: The frontend setup with Clerk Next.js SDK ensures that only authenticated users can access the app, enhancing security.
- API Request Handling: The frontend securely interacts with the backend using access tokens for user identification, providing a secure link between the frontend and external API.
- Backend Security: The backend employs Clerk Express SDK in Nest.js, implementing middleware for user authentication, guards for endpoint security, and controller protection to restrict unauthenticated access, ensuring data integrity and privacy.
- Overall Security and Functionality: By implementing Clerk for both frontend and backend, the application is fortified with top-tier security measures, providing a secure environment for user interactions and data handling.