README
Nest Auth
Installation
npm i @notiz/nest-auth
nest add nestjs-prisma
# auth
npm i @nestjs/config @nestjs/jwt @nestjs/passport passport passport-jwt
# auth types
npm i -D @types/passport-jwt
# oauth
npm i openid-client @nestjs/axios
# utils
npm i bcrypt dayjs cuid
Rest with Swagger
npm install --save @nestjs/swagger swagger-ui-express class-transformer class-validator
GraphQL
npm i @nestjs/graphql graphql@^15 apollo-server-express graphql-scalars
Usage
Prisma Schema
Nest Auth requires following models and enums in your schema.prisma
. You can add relations and more properties to the models as you like.
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
name String?
email String @unique
emailVerified DateTime?
password String?
role Role @default(USER)
image String?
providerAccountId String?
authProvider AuthProvider @default(EMAIL)
accessToken String?
refreshToken String?
verificationRequest VerificationRequest[]
// TODO add relations and more properties
@@unique(fields: [authProvider, providerAccountId], name: "socialAuth")
}
enum Role {
ADMIN
USER
}
enum AuthProvider {
EMAIL
GOOGLE
GITHUB
}
model VerificationRequest {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
type VerificationType
token String
expires DateTime
user User? @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade)
userId String?
@@unique(fields: [type, token], name: "verification")
}
enum VerificationType {
ACCOUNT
PASSWORDLESS
RESET_PASSWORD
}
Authentication
Rest
import {
LoggerMailService,
AuthModule,
PasswordlessModule,
} from '@notiz/nest-auth';
import { PrismaModule } from 'nestjs-prisma';
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { UsersModule } from './users/users.module';
import { GraphQLModule } from '@nestjs/graphql';
import { join } from 'path';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
PrismaModule.forRoot({ isGlobal: true }),
AuthModule.forRoot(LoggerMailService),
PasswordlessModule.forRoot(LoggerMailService),
],
controllers: [],
providers: [LoggerMailService],
})
export class AppModule {}
GraphQL
import {
LoggerMailService,
GraphqlAuthModule,
GraphqlPasswordlessModule,
} from '@notiz/nest-auth';
import { PrismaModule } from 'nestjs-prisma';
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { UsersModule } from './users/users.module';
import { GraphQLModule } from '@nestjs/graphql';
import { join } from 'path';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
PrismaModule.forRoot({ isGlobal: true }),
// GraphQL
GraphQLModule.forRoot({
playground: true,
autoSchemaFile: join(process.cwd(), 'demo/schema.gql'),
sortSchema: true,
}),
GraphqlAuthModule.forRoot(LoggerMailService),
GraphqlPasswordlessModule.forRoot(LoggerMailService),
],
controllers: [],
providers: [LoggerMailService],
})
export class AppModule {}
OAuth
Install the following packages to support OAuth
npm i openid-client @nestjs/axios
Import SocialLoginModule
for social login endpoints.
import { LoggerMailService, SocialLoginModule, Google, GitHub } from '@notiz/nest-auth';
import { PrismaModule } from 'nestjs-prisma';
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { UsersModule } from './users/users.module';
import { GraphQLModule } from '@nestjs/graphql';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
PrismaModule.forRoot({ isGlobal: true }),
SocialLoginModule.forRoot(LoggerMailService,
providers: [
// enable your oauth providers
GitHub({
clientId: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
}),
Google({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
]
),
UsersModule,
],
controllers: [],
providers: [LoggerMailService],
})
export class AppModule {}
Note: Checkout supported OAuth Providers
See .env variables necessary for OAuth providers.
Securing endpoints
Guards
To secure your own endpoints use one of the following guards.
Guard | API | Requires | Description |
---|---|---|---|
JwtGuard |
Rest, GraphQL | - | valid JWT access token |
RolesGuard |
Rest, GraphQL | @Roles(roles) |
JwtGuard + the user must have a specific role |
AuthGuard |
Rest | - | Combined decorator with @Roles(role) , RolesGuard , @ApiBearerAuth , @ApiUnauthorizedResponse and @ApiForbiddenResponse |
GqlAuthGuard |
GraphQL | - | Combined decorator with @Roles(role) and RolesGuard |
Guards can be used as controller/resolver-scoped or method-scoped.
Controller/Resolver scoped
Controller
@Controller('products')
@ApiTags('products')
@UseGuards(JwtGuard)
export class ProductsController {
@Get()
...
@Post()
...
}
@Controller('admin')
@ApiTags('admin')
@AuthGuard('ADMIN')
export class AdminController {
@Get()
...
@Post()
...
}
@Controller('products')
@ApiTags('products')
@AuthGuard('USER')
export class ProductsController {
@Get()
...
@Post()
...
}
Resolver
@Resolver(() => Product)
@UseGuards(JwtGuard)
export class ProductsResolver {
@Mutation(() => Product)
...
@Query(() => [Product], { name: 'products' })
...
}
@Resolver(() => Admin)
@GqlAuthGuard('ADMIN')
export class AdminResolver {
@Mutation(() => Product)
...
@Query(() => [Product], { name: 'products' })
...
}
@Resolver(() => Product)
@GqlAuthGuard('USER')
export class ProductsResolver {
@Mutation(() => Product)
...
@Query(() => [Product], { name: 'products' })
...
}
Method scoped
Controller
@Controller('users')
@ApiTags('users')
export class UsersController {
@Get('me')
@UseGuards(JwtGuard)
@ApiResponse({ status: 200, type: UserEntity })
me(@CurrentUser() user: User): UserEntity {
return new UserEntity(user);
}
@Get('me')
@AuthGuard('ADMIN')
@ApiResponse({ status: 200, type: [UserEntity] })
findAllUsers(): UserEntity[] {
return [...];
}
}
Resolver
@Resolver(() => User)
export class UsersResolver {
@Query(() => User)
@UseGuards(JwtGuard)
async me(@CurrentUser() user: User): Promise<User> {
return user;
}
@Query(() => User)
@GqlAuthGuard('ADMIN')
async findAllUsers(): Promise<User[]> {
return [...];
}
}
Current User
To access the current user in your endpoint use @CurrentUser()
decorator. This works with Rest and GraphQL.
- Receive full user object
import { CurrentUser, AuthGuard, GqlAuthGuard } from '@notiz/nest-auth';
// REST
@Get('me')
@AuthGuard()
@ApiResponse({ status: 200, type: UserEntity })
me(@CurrentUser() user: User): UserEntity {
return new UserEntity(user);
}
// GraphQL
@Query(() => User)
@UseGuards(GqlAuthGuard)
me(@CurrentUser() user: User): Promise<User> {
return user;
}
- Access user properties
import { CurrentUser, AuthGuard, GqlAuthGuard } from '@notiz/nest-auth';
// REST
@Get('updateProfile')
@AuthGuard()
@ApiResponse({ status: 204 })
updateProfile(@CurrentUser('id') id: string) {
console.log(id);
}
// GraphQL
@Query(() => Void)
@UseGuards(GqlAuthGuard)
updateProfile(@CurrentUser('email') email: string) {
console.log(email);
}
.env
# JWT
JWT_ACCESS_TOKEN_SECRET=accessTokenSecret
JWT_ACCESS_TOKEN_EXPIRES_IN=7d
JWT_REFRESH_TOKEN_SECRET=refreshTokenSecret
JWT_REFRESH_TOKEN_EXPIRES_IN=30d
# oauth
BASE_URL=http://localhost:3000
AUTH_SUCCESS_URL=/auth/login
AUTH_ERROR_URL=/auth/login
# Bycrpt
BCRYPT_ROUNDS=10
# Google
GOOGLE_CLIENT_ID=google_id
GOOGLE_CLIENT_SECRET=google_secret
# GitHub
GITHUB_CLIENT_ID=github_id
GITHUB_CLIENT_SECRET=github_secret