feat: add guards and decorators
This commit is contained in:
parent
cce2d83101
commit
59f57ded54
@ -30,6 +30,7 @@
|
||||
"@nestjs/swagger": "^11.2.5",
|
||||
"@teacinema/common": "^1.0.0",
|
||||
"@teacinema/contracts": "^1.0.0",
|
||||
"@teacinema/passport": "^1.0.0",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.3",
|
||||
"cookie-parser": "^1.4.7",
|
||||
|
||||
@ -1,13 +1,22 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { ConfigModule } from '@nestjs/config'
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config'
|
||||
import { PassportModule } from '@teacinema/passport'
|
||||
|
||||
import { AuthModule } from '../modules/auth/auth.module'
|
||||
|
||||
import { AppController } from './app.controller'
|
||||
import { AppService } from './app.service'
|
||||
import { getPassportConfig } from './config'
|
||||
|
||||
@Module({
|
||||
imports: [ConfigModule.forRoot({ isGlobal: true }), AuthModule],
|
||||
imports: [
|
||||
ConfigModule.forRoot({ isGlobal: true }),
|
||||
AuthModule,
|
||||
PassportModule.registerAsync({
|
||||
useFactory: getPassportConfig,
|
||||
inject: [ConfigService]
|
||||
})
|
||||
],
|
||||
controllers: [AppController],
|
||||
providers: [AppService]
|
||||
})
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
export * from './cors.config'
|
||||
export * from './swagger.config'
|
||||
export * from './validation-pipe.config'
|
||||
export * from './passport.config'
|
||||
|
||||
10
src/core/config/passport.config.ts
Normal file
10
src/core/config/passport.config.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { ConfigService } from '@nestjs/config'
|
||||
import { PassportOptions } from '@teacinema/passport'
|
||||
|
||||
export function getPassportConfig(
|
||||
configService: ConfigService
|
||||
): PassportOptions {
|
||||
return {
|
||||
secretKey: configService.getOrThrow<string>('PASSPORT_SECRET_KEY')
|
||||
}
|
||||
}
|
||||
7
src/global.d.ts
vendored
Normal file
7
src/global.d.ts
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
declare namespace Express {
|
||||
export interface Request {
|
||||
user?: {
|
||||
id: string
|
||||
}
|
||||
}
|
||||
}
|
||||
7
src/shared/decorators/current-user.decorator.ts
Normal file
7
src/shared/decorators/current-user.decorator.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { createParamDecorator } from '@nestjs/common'
|
||||
import type { Request } from 'express'
|
||||
|
||||
export const CurrentUser = createParamDecorator((_, ctx) => {
|
||||
const req = ctx.switchToHttp().getRequest<Request>()
|
||||
return req.user?.id ?? null
|
||||
})
|
||||
2
src/shared/decorators/index.ts
Normal file
2
src/shared/decorators/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './protected.decorator'
|
||||
export * from './current-user.decorator'
|
||||
5
src/shared/decorators/protected.decorator.ts
Normal file
5
src/shared/decorators/protected.decorator.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { applyDecorators, UseGuards } from '@nestjs/common'
|
||||
|
||||
import { AuthGuard } from '../guards'
|
||||
|
||||
export const Protected = () => applyDecorators(UseGuards(AuthGuard))
|
||||
38
src/shared/guards/auth.guard.ts
Normal file
38
src/shared/guards/auth.guard.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import {
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
UnauthorizedException
|
||||
} from '@nestjs/common'
|
||||
import { PassportService } from '@teacinema/passport'
|
||||
import { Request } from 'express'
|
||||
|
||||
@Injectable()
|
||||
export class AuthGuard implements CanActivate {
|
||||
public constructor(private readonly passportService: PassportService) {}
|
||||
public canActivate(context: ExecutionContext): boolean {
|
||||
const request = context.switchToHttp().getRequest<Request>()
|
||||
const token = this.extractToken(request)
|
||||
const result = this.passportService.verify(token)
|
||||
|
||||
if (!result.valid) {
|
||||
throw new UnauthorizedException(result.reason)
|
||||
}
|
||||
|
||||
request.user = {
|
||||
id: result.userId
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private extractToken(request: Request) {
|
||||
const [type, token] = request.headers.authorization?.split(' ') ?? []
|
||||
|
||||
if (!token || type !== 'Bearer') {
|
||||
throw new UnauthorizedException('Authorization header is missing')
|
||||
}
|
||||
|
||||
return token
|
||||
}
|
||||
}
|
||||
1
src/shared/guards/index.ts
Normal file
1
src/shared/guards/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './auth.guard'
|
||||
33
yarn.lock
33
yarn.lock
@ -1011,6 +1011,17 @@
|
||||
load-esm "1.0.3"
|
||||
tslib "2.8.1"
|
||||
|
||||
"@nestjs/common@^11.1.12":
|
||||
version "11.1.13"
|
||||
resolved "https://registry.yarnpkg.com/@nestjs/common/-/common-11.1.13.tgz#3d4bcc2a593236546bc62f7801b0d4450b26a9ac"
|
||||
integrity sha512-ieqWtipT+VlyDWLz5Rvz0f3E5rXcVAnaAi+D53DEHLjc1kmFxCgZ62qVfTX2vwkywwqNkTNXvBgGR72hYqV//Q==
|
||||
dependencies:
|
||||
uid "2.0.2"
|
||||
file-type "21.3.0"
|
||||
iterare "1.2.1"
|
||||
load-esm "1.0.3"
|
||||
tslib "2.8.1"
|
||||
|
||||
"@nestjs/config@^4.0.2":
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@nestjs/config/-/config-4.0.2.tgz#a2777a1fd2d0d594bab3953f50fbca95c14cce52"
|
||||
@ -1032,6 +1043,18 @@
|
||||
path-to-regexp "8.3.0"
|
||||
tslib "2.8.1"
|
||||
|
||||
"@nestjs/core@^11.1.12":
|
||||
version "11.1.13"
|
||||
resolved "https://registry.yarnpkg.com/@nestjs/core/-/core-11.1.13.tgz#4444e124d255759d545a631f63e9d84fe1a1e3e5"
|
||||
integrity sha512-Tq9EIKiC30EBL8hLK93tNqaToy0hzbuVGYt29V8NhkVJUsDzlmiVf6c3hSPtzx2krIUVbTgQ2KFeaxr72rEyzQ==
|
||||
dependencies:
|
||||
uid "2.0.2"
|
||||
"@nuxt/opencollective" "0.4.1"
|
||||
fast-safe-stringify "2.1.1"
|
||||
iterare "1.2.1"
|
||||
path-to-regexp "8.3.0"
|
||||
tslib "2.8.1"
|
||||
|
||||
"@nestjs/mapped-types@2.1.0":
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@nestjs/mapped-types/-/mapped-types-2.1.0.tgz#b9b536b7c3571567aa1d0223db8baa1a51505a19"
|
||||
@ -1214,6 +1237,16 @@
|
||||
dependencies:
|
||||
"@trivago/prettier-plugin-sort-imports" "^5.2.2"
|
||||
|
||||
"@teacinema/passport@^1.0.0":
|
||||
version "1.0.1"
|
||||
resolved "https://git.ksv741.keenetic.pro/api/packages/teacinema/npm/%40teacinema%2Fpassport/-/1.0.1/passport-1.0.1.tgz#83a8a605534b23929d3b1090d28023626fd5cc9f"
|
||||
integrity sha512-hgFHXtWgXOemAf9InBLuhHbtF8wS01HKmot0nSaUqGlQCtu74ArMMCG8mup1SR50UGz5DEpHPEL8G5CzVnTTSQ==
|
||||
dependencies:
|
||||
"@nestjs/common" "^11.1.12"
|
||||
"@nestjs/core" "^11.1.12"
|
||||
reflect-metadata "^0.2.2"
|
||||
rxjs "^7.8.2"
|
||||
|
||||
"@tokenizer/inflate@^0.4.1":
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@tokenizer/inflate/-/inflate-0.4.1.tgz#fa6cdb8366151b3cc8426bf9755c1ea03a2fba08"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user