@mia-burton/nestjs-oauth2-server
v0.3.1
Published
NestJS oauth2 server by MiaBurton inspired by SWITCH IT CONSULTING
Downloads
45
Readme
OAuth2 Server Module
This module turns your NestJS Application into an OAuth2 Server. It is based on
- TypeORM: For entities and database model
- NestJS CQRS Module: For commands and queries
- Passport: For securing routes
- It works on node>=18
Installation
npm install --save @mia-burton/nestjs-oauth2-server # or yarn install @mia-burton/nestjs-oauth2-server
Usage
Adding the entities to your TypeORM configuration
The module comes with entities you have to add the configuration node_modules/@mia-burton/**/*.entity.js
to let typeorm scan your entities or add them to the entitie
configuration variable in TypeORM.
Implement the UserValidatorInterface
The UserValidatorInterface
lets you validate a user using the PasswordGrantStrategy
. It is queried in your application
to validate that the given username
and password
matches with a user in your application's DB.
IMPORTANT: When the user is not found your have to throw an InvalidUserException
.
import {Injectable} from "@nestjs/common";
import {UserValidatorInterface, UserInterface, InvalidUserException} from "@mia-burton/nestjs-oauth2-server";
@Injectable()
export class UserValidator implements UserValidatorInterface {
async validate(username, password): Promise<UserInterface> {
// check if the user exists with the given username and password
// ...
// or
throw InvalidUserException.withUsernameAndPassword(username, password);
}
}
The validate method should return an instance of the UserInterface
:
export interface UserInterface {
id: string;
username: string;
email: string;
}
The user interface is then accessible in the payload once connected ot the AccessToken
is used.
Implement the UserLoaderInterface
The UserLoaderInterface
lets you load a user from the database when the AccessToken
is user specific.
The user is then accessible in the request context of your application.
IMPORTANT: When the user is not found your have to throw an InvalidUserException
.
import {Injectable} from "@nestjs/common";
import {UserLoaderInterface, UserInterface, InvalidUserException} from "@mia-burton/nestjs-oauth2-server";
@Injectable()
export class UserLoader implements UserLoaderInterface {
async load(userId: string): Promise<UserInterface> {
// Load the user from the database
// ...
// or throw and
throw InvalidUserException.withId(userId);
}
}
Implement the module in your application
Import the OAuth2Module into the root AppModule.
//app.module.ts
import { Module } from '@nestjs/common';
import { OAuth2Module } from '@mia-burton/nestjs-oauth2-server';
@Module({
imports: [
OAuth2Module.forRoot({
userLoader: new UserLoader(),
userValidator: new UserValidator(),
}),
],
})
export class AppModule {}
Of course you can use an async configuration:
//app.module.ts
import { Module } from '@nestjs/common';
import { OAuth2Module } from '@mia-burton/nestjs-oauth2-server';
@Module({
imports: [
OAuth2Module.forRootAsync({
useFactory: () => ({
userLoader: new UserLoader(),
userValidator: new UserValidator(),
})
}),
],
})
export class AppModule {}
The OAuth2 Controller
The modules is shipped with a specific controller that lets you expose the oauth2/token
endpoint in your application
and use the different implemented strategies accordingly to the request sent.
Client Credentials
Used for server-to-server communications.
curl -X POST http://localhost:3000/oauth2/token -d '{"grant_type":"client_credentials", "client_id":"6ab1cfab-0b3d-418b-8ca2-94d98663fb6f", "client_secret": "6nV9GGm1pu8OY0HDZ3Y7QsVnxtkb60wu", "scopes": ["app-1"]}'
Refresh Token
Used to refresh an existing token
curl -X POST http://localhost:3000/oauth2/token -d '{"grant_type":"refresh_token", "client_id":"6ab1cfab-0b3d-418b-8ca2-94d98663fb6f", "client_secret": "6nV9GGm1pu8OY0HDZ3Y7QsVnxtkb60wu", "refresh_token": "6nV9GGm1pu8OY0HDZ3Y7QsVnxtkb6fgqstyudhjqskdqsd"}'
Password
Used to refresh an existing token
curl -X POST http://localhost:3000/oauth2/token -d '{"grant_type":"password", "client_id":"6ab1cfab-0b3d-418b-8ca2-94d98663fb6f", "client_secret": "secret", username": "[email protected]", "password": "changeme", "scopes": ["app-1"]}'
Securing your routes using the AccessTokenStrategy
The module comes with a PassportJS
strategy of type http-bearer
. You can secure your routes using the passport
AuthGuard
with the access-token
strategy name:
@Controller('oauth2-secured')
export class TestSecuredController {
@Get('me')
@UseGuards(AuthGuard('access-token'))
async auth(): Promise<any> {
return {message: 'hello'};
}
}
Using the global validation pipes
IMPORTANT: In addition, you should enable the global validation pipe in your NestJS application. In your main.ts
you should use the useGlobaPipes
with the ValidationPipe
and the transform
options set to true
:
async function bootstrap() {
const app = await NestFactory.create(AppModule);
useContainer(app.select(AppModule), {fallbackOnErrors: true});
app.useGlobalPipes(new ValidationPipe({
transform: true,
}));
await app.listen(3000);
}
bootstrap();