0.2.3 • Published 1 year ago

@notiz/nest-auth v0.2.3

Weekly downloads
-
License
MIT
Repository
-
Last release
1 year ago

Nest Auth

npm version

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

  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
}

DB Diagram

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.

GuardAPIRequiresDescription
JwtGuardRest, GraphQL-valid JWT access token
RolesGuardRest, GraphQL@Roles(roles)JwtGuard + the user must have a specific role
AuthGuardRest-Combined decorator with @Roles(role), RolesGuard, @ApiBearerAuth, @ApiUnauthorizedResponse and @ApiForbiddenResponse
GqlAuthGuardGraphQL-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.

  1. 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;
}
  1. 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
0.3.0-dev.3

1 year ago

0.3.0-dev.0

1 year ago

0.3.0-dev.2

1 year ago

0.3.0-dev.1

1 year ago

0.2.3

2 years ago

0.2.2-dev.0

2 years ago

0.2.1

2 years ago

0.2.0

2 years ago

0.2.2

2 years ago

0.0.13

2 years ago

0.0.14

2 years ago

0.1.0

2 years ago

0.1.1

2 years ago

0.0.15

2 years ago

0.0.16

2 years ago

0.0.12

2 years ago

0.0.10

2 years ago

0.0.11

2 years ago

0.0.9

3 years ago

0.0.8

3 years ago

0.0.7

3 years ago

0.0.6

3 years ago

0.0.5

3 years ago

0.0.4

3 years ago

0.0.3

3 years ago

0.0.2

3 years ago

0.0.1

3 years ago