From 2765751da3be3a0ebabd87577856fa3bef484bf6 Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Thu, 5 Dec 2024 23:21:46 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=E2=9C=A8=20add=20AuthGuard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/auth/interface-adapters/src/index.ts | 2 + .../src/lib/guard/auth.guard.ts | 44 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 libs/auth/interface-adapters/src/lib/guard/auth.guard.ts diff --git a/libs/auth/interface-adapters/src/index.ts b/libs/auth/interface-adapters/src/index.ts index 53b15e2..679303a 100644 --- a/libs/auth/interface-adapters/src/index.ts +++ b/libs/auth/interface-adapters/src/index.ts @@ -1 +1,3 @@ +export * from './lib/guard/auth.guard'; + export * from './lib/auth.module'; diff --git a/libs/auth/interface-adapters/src/lib/guard/auth.guard.ts b/libs/auth/interface-adapters/src/lib/guard/auth.guard.ts new file mode 100644 index 0000000..0bed37b --- /dev/null +++ b/libs/auth/interface-adapters/src/lib/guard/auth.guard.ts @@ -0,0 +1,44 @@ +import { + CanActivate, + ExecutionContext, + Injectable, + UnauthorizedException, +} from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { JwtService } from '@nestjs/jwt'; +import { Request } from 'express'; + +@Injectable() +export class AuthGuard implements CanActivate { + constructor( + private jwtService: JwtService, + private configService: ConfigService, + ) {} + + async canActivate(context: ExecutionContext): Promise { + const authConfig = this.configService.get('auth'); + const request = context.switchToHttp().getRequest(); + const token = this.extractTokenFromHeader(request); + + if (!token) { + throw new UnauthorizedException(); + } + + try { + const payload = await this.jwtService.verifyAsync(token, { + secret: authConfig.secret, + }); + // 💡 We're assigning the payload to the request object here + // so that we can access it in our route handlers + request['user'] = payload; + } catch { + throw new UnauthorizedException(); + } + return true; + } + + private extractTokenFromHeader(request: Request): string | undefined { + const [type, token] = request.headers.authorization?.split(' ') ?? []; + return type === 'Bearer' ? token : undefined; + } +} From f29f6ce841b427cd9abdc788f4af23d707aaffee Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Thu, 5 Dec 2024 23:22:17 +0900 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20=E2=9C=A8=20UseGuards=20in=20UsersR?= =?UTF-8?q?esolver?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/lib/resolver/users.resolver.ts | 9 +++++++-- libs/users/interface-adapters/src/lib/users.module.ts | 2 ++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/libs/users/interface-adapters/src/lib/resolver/users.resolver.ts b/libs/users/interface-adapters/src/lib/resolver/users.resolver.ts index 5e665db..4362151 100644 --- a/libs/users/interface-adapters/src/lib/resolver/users.resolver.ts +++ b/libs/users/interface-adapters/src/lib/resolver/users.resolver.ts @@ -1,3 +1,5 @@ +import { AuthGuard } from '@auth/interface-adapters'; +import { UseGuards } from '@nestjs/common'; import { Args, ID, Query, Resolver } from '@nestjs/graphql'; import { UsersService } from '@users/application'; import { User } from '@users/domain'; @@ -8,9 +10,12 @@ import { UserDto } from '../dto/user.dto'; export class UsersResolver { constructor(private usersService: UsersService) {} + @UseGuards(AuthGuard) @Query(() => UserDto, { nullable: true }) - async getUser(@Args({ name: 'id', type: () => ID }) id: string): Promise { - const user: User| null = await this.usersService.findById(id); + async getUser( + @Args({ name: 'id', type: () => ID }) id: string, + ): Promise { + const user: User | null = await this.usersService.findById(id); if (!user) { return null; diff --git a/libs/users/interface-adapters/src/lib/users.module.ts b/libs/users/interface-adapters/src/lib/users.module.ts index 6bb1454..c1484b0 100644 --- a/libs/users/interface-adapters/src/lib/users.module.ts +++ b/libs/users/interface-adapters/src/lib/users.module.ts @@ -1,4 +1,5 @@ import { Module } from '@nestjs/common'; +import { JwtService } from '@nestjs/jwt'; import { DatabaseModule } from '@shared/infrastructure-mongoose'; import { UsersService, GetUserByIdUseCase, GetUserByEmailUseCase } from '@users/application'; import { @@ -15,6 +16,7 @@ import { UsersResolver } from './resolver/users.resolver'; providers: [ UsersResolver, UsersService, + JwtService, GetUserByIdUseCase, GetUserByEmailUseCase, { From ae9a859e1692c8ed07e3f8f64083ea48fe6468a7 Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Thu, 5 Dec 2024 23:22:36 +0900 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=E2=9C=A8=20pass=20request=20header?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/users/src/app/app.module.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/users/src/app/app.module.ts b/apps/users/src/app/app.module.ts index 4dc918e..76ec9ed 100644 --- a/apps/users/src/app/app.module.ts +++ b/apps/users/src/app/app.module.ts @@ -37,6 +37,7 @@ import { AppService } from './app.service'; playground: process.env['NODE_ENV'] !== 'production', sortSchema: true, plugins: [ApolloServerPluginInlineTrace()], + context: ({ req }: { req: Request }) => ({ req }), // Make sure request is passed to the context }), UsersModule, AuthModule, From 5c837c58c9bd0a91ea7145a514e8354aef63216b Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Sat, 7 Dec 2024 09:44:48 +0900 Subject: [PATCH 4/4] =?UTF-8?q?feat:=20=E2=9C=A8=20rebuild=20header=20in?= =?UTF-8?q?=20gateway=20app?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/gateway/src/app/app.module.ts | 16 +++++++++++++++- apps/users/src/app/app.module.ts | 4 +++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/apps/gateway/src/app/app.module.ts b/apps/gateway/src/app/app.module.ts index 5e3f967..4eb32f7 100644 --- a/apps/gateway/src/app/app.module.ts +++ b/apps/gateway/src/app/app.module.ts @@ -1,4 +1,4 @@ -import { IntrospectAndCompose } from '@apollo/gateway'; +import { IntrospectAndCompose, RemoteGraphQLDataSource } from '@apollo/gateway'; import { gatewayConfig, userAppConfig } from '@shared/config'; import { ApolloGatewayDriver, ApolloGatewayDriverConfig } from '@nestjs/apollo'; import { Module } from '@nestjs/common'; @@ -31,6 +31,20 @@ import { AppService } from './app.service'; }, ], }), + buildService: ({ url = '' }) => { + return new RemoteGraphQLDataSource({ + url, + willSendRequest({ request, context }) { + // rebuild header + if (Object.keys(context).length > 0 && request.http) { + request.http.headers.set( + 'Authorization', + context['req']['headers']['authorization'], + ); + } + }, + }); + }, }, }; }, diff --git a/apps/users/src/app/app.module.ts b/apps/users/src/app/app.module.ts index 76ec9ed..fde11ff 100644 --- a/apps/users/src/app/app.module.ts +++ b/apps/users/src/app/app.module.ts @@ -37,7 +37,9 @@ import { AppService } from './app.service'; playground: process.env['NODE_ENV'] !== 'production', sortSchema: true, plugins: [ApolloServerPluginInlineTrace()], - context: ({ req }: { req: Request }) => ({ req }), // Make sure request is passed to the context + context: ({ req }: { req: Request }) => { + return { headers: req?.headers }; + }, }), UsersModule, AuthModule,