We build your next generation software !
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
npm install --save @switchit/nestjs-oauth2-server # or yarn install @switchit/nestjs-oauth2-server
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 "@switchit/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.
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 "@switchit/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);
}
}
Import the OAuth2Module into the root AppModule.
//app.module.ts
import { Module } from '@nestjs/common';
import { OAuth2Module } from '@switchit/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 '@switchit/nestjs-oauth2-server';
@Module({
imports: [
OAuth2Module.forRootAsync({
useFactory: () => ({
userLoader: new UserLoader(),
userValidator: new UserValidator(),
})
}),
],
})
export class AppModule {}
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.
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"]}'
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"}'
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", "username": "j.doe@change.me", "password": "changeme", "scopes": ["app-1"]}'
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'};
}
}
IMPORTANT: The module comes with entities you have to add the configuration node_modules/@switchit/**/*.entity.js
to let typeorm scan your entities or add them to the entitie
configuration variable in TypeORM.
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();