How to Secure APIs with OAuth and JWT in a NestJS Application
In today's digital landscape, securing your APIs is more crucial than ever. With the rise of microservices and mobile applications, ensuring that your APIs are protected from unauthorized access is a top priority for developers. This article will guide you through securing APIs using OAuth and JWT (JSON Web Tokens) in a NestJS application. By the end of this article, you'll have a clear understanding of how to implement these security measures effectively.
Understanding OAuth and JWT
What is OAuth?
OAuth is an open standard for access delegation commonly used for token-based authentication. It allows third-party services to exchange user data without exposing user credentials. OAuth is widely used in scenarios where users need to authorize applications to access their data on other platforms, such as Google or Facebook.
What is JWT?
JSON Web Tokens (JWT) are compact, URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is used as the payload of a JSON Web Signature (JWS) structure or as the plaintext of a JSON Web Encryption (JWE) structure. JWTs are often used for authentication and information exchange, providing a stateless mechanism to verify the user's identity and permissions.
Use Cases for OAuth and JWT
Implementing OAuth and JWT in your NestJS application can be beneficial in various scenarios, such as:
- User Authentication: Securely verify user identity and authenticate them for API access.
- Third-Party Integrations: Allow users to log in with their existing accounts from services like Google, Facebook, or GitHub.
- Microservices Architecture: Secure communication between different services within your application.
Setting Up Your NestJS Application
Prerequisites
Before we dive into the implementation, ensure you have the following set up:
- Node.js installed on your machine.
-
NestJS CLI installed globally. If you haven’t installed it yet, run:
bash npm install -g @nestjs/cli
-
A new NestJS project created:
bash nest new api-security-demo
-
Navigate to the project directory:
bash cd api-security-demo
Installing Required Packages
You'll need several packages to implement OAuth and JWT in your NestJS application. Run the following command to install them:
npm install @nestjs/jwt @nestjs/passport passport passport-jwt bcryptjs
Setting Up JWT Authentication
- Create a Users Module: Create a new module for handling user authentication.
bash
nest generate module users
nest generate service users
nest generate controller users
- User Entity and Service: Define a simple user entity and a service to interact with it. For simplicity, you can hardcode a user.
typescript
// users/user.entity.ts
export class User {
id: number;
username: string;
password: string;
}
```typescript // users/users.service.ts import { Injectable } from '@nestjs/common'; import { User } from './user.entity';
@Injectable() export class UsersService { private readonly users: User[] = [{ id: 1, username: 'test', password: 'test' }];
async findOne(username: string): Promise<User | undefined> {
return this.users.find(user => user.username === username);
}
} ```
- Creating JWT Strategy: Implement the JWT strategy to validate incoming tokens.
```typescript // auth/jwt.strategy.ts import { Injectable } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { ExtractJwt, Strategy } from 'passport-jwt'; import { UsersService } from '../users/users.service';
@Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { constructor(private readonly usersService: UsersService) { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), secretOrKey: 'your_jwt_secret', }); }
async validate(payload: any) {
return this.usersService.findOne(payload.username);
}
} ```
- Implementing Authentication Controller: Create an authentication controller to handle login and token generation.
```typescript // auth/auth.controller.ts import { Controller, Post, Body } from '@nestjs/common'; import { AuthService } from './auth.service';
@Controller('auth') export class AuthController { constructor(private readonly authService: AuthService) {}
@Post('login')
async login(@Body() body: { username: string; password: string }) {
return this.authService.login(body.username, body.password);
}
} ```
- AuthService for Token Generation: Implement the service to handle user login and JWT generation.
```typescript // auth/auth.service.ts import { Injectable } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { UsersService } from '../users/users.service'; import * as bcrypt from 'bcryptjs';
@Injectable() export class AuthService { constructor(private readonly usersService: UsersService, private jwtService: JwtService) {}
async login(username: string, password: string) {
const user = await this.usersService.findOne(username);
if (user && bcrypt.compareSync(password, user.password)) {
const payload = { username: user.username };
return {
access_token: this.jwtService.sign(payload),
};
}
throw new Error('Invalid credentials');
}
} ```
Protecting Routes with Guards
To secure your routes, you’ll need to implement guards.
// auth/jwt-auth.guard.ts
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}
Applying Guards to Routes
Now, you can protect your routes by applying the JwtAuthGuard
.
// app.controller.ts
import { Controller, Get, UseGuards } from '@nestjs/common';
import { JwtAuthGuard } from './auth/jwt-auth.guard';
@Controller('protected')
export class AppController {
@UseGuards(JwtAuthGuard)
@Get()
getProtectedResource() {
return { message: 'This is a protected resource' };
}
}
Conclusion
In this article, we explored how to secure APIs using OAuth and JWT in a NestJS application. We covered the necessary components, such as user authentication, JWT handling, and route protection. By implementing these security measures, you can ensure that your APIs remain secure and accessible only to authorized users.
Key Takeaways
- OAuth allows for secure delegation of access without exposing user credentials.
- JWTs provide a compact way to represent claims and are ideal for stateless authentication.
- NestJS, with its modular architecture, makes it easy to implement these security protocols.
By following these steps, you can safeguard your NestJS application and enhance the overall security of your APIs. Happy coding!