From 31de4ab41e4a45cdcdafc29e716c096bb3daee0d Mon Sep 17 00:00:00 2001 From: YukiOnishi <58220747+YukiOnishi1129@users.noreply.github.com> Date: Wed, 4 Sep 2024 14:52:25 +0900 Subject: [PATCH] set bff auth guard --- bff/apollo-gateway/package-lock.json | 84 ++++++++++++++++++- bff/apollo-gateway/package.json | 2 + .../src/app/article/article.resolver.ts | 10 ++- .../src/app/article/article.service.ts | 8 +- bff/apollo-gateway/src/app/auth/auth.guard.ts | 40 +++++++++ .../src/graphql/context.interface.ts | 6 ++ bff/apollo-gateway/src/main.ts | 6 +- compose.yml | 3 + 8 files changed, 152 insertions(+), 7 deletions(-) create mode 100644 bff/apollo-gateway/src/app/auth/auth.guard.ts create mode 100644 bff/apollo-gateway/src/graphql/context.interface.ts diff --git a/bff/apollo-gateway/package-lock.json b/bff/apollo-gateway/package-lock.json index 83e483be..8e49867f 100644 --- a/bff/apollo-gateway/package-lock.json +++ b/bff/apollo-gateway/package-lock.json @@ -19,7 +19,9 @@ "@nestjs/mapped-types": "*", "@nestjs/microservices": "^10.4.1", "@nestjs/platform-express": "^10.0.0", + "@supabase/supabase-js": "^2.45.3", "apollo-server-express": "^3.13.0", + "dotenv": "^16.4.5", "google-protobuf": "^3.21.4", "graphql": "^16.9.0", "graphql-type-json": "^0.3.2", @@ -5538,6 +5540,80 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@supabase/auth-js": { + "version": "2.65.0", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.65.0.tgz", + "integrity": "sha512-+wboHfZufAE2Y612OsKeVP4rVOeGZzzMLD/Ac3HrTQkkY4qXNjI6Af9gtmxwccE5nFvTiF114FEbIQ1hRq5uUw==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.4.1.tgz", + "integrity": "sha512-8sZ2ibwHlf+WkHDUZJUXqqmPvWQ3UHN0W30behOJngVh/qHHekhJLCFbh0AjkE9/FqqXtf9eoVvmYgfCLk5tNA==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/node-fetch": { + "version": "2.6.15", + "resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz", + "integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/@supabase/postgrest-js": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.15.8.tgz", + "integrity": "sha512-YunjXpoQjQ0a0/7vGAvGZA2dlMABXFdVI/8TuVKtlePxyT71sl6ERl6ay1fmIeZcqxiuFQuZw/LXUuStUG9bbg==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/realtime-js": { + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.10.2.tgz", + "integrity": "sha512-qyCQaNg90HmJstsvr2aJNxK2zgoKh9ZZA8oqb7UT2LCh3mj9zpa3Iwu167AuyNxsxrUE8eEJ2yH6wLCij4EApA==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14", + "@types/phoenix": "^1.5.4", + "@types/ws": "^8.5.10", + "ws": "^8.14.2" + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.7.0.tgz", + "integrity": "sha512-iZenEdO6Mx9iTR6T7wC7sk6KKsoDPLq8rdu5VRy7+JiT1i8fnqfcOr6mfF2Eaqky9VQzhP8zZKQYjzozB65Rig==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.45.3", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.45.3.tgz", + "integrity": "sha512-4wAux6cuVMrdH/qUjKn6p3p3L9AtAO3Une6ojIrtpCj1RaXKVoyIATiacSRAI+pKff6XZBVCGC29v+z4Jo/uSw==", + "license": "MIT", + "dependencies": { + "@supabase/auth-js": "2.65.0", + "@supabase/functions-js": "2.4.1", + "@supabase/node-fetch": "2.6.15", + "@supabase/postgrest-js": "1.15.8", + "@supabase/realtime-js": "2.10.2", + "@supabase/storage-js": "2.7.0" + } + }, "node_modules/@ts-morph/common": { "version": "0.22.0", "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.22.0.tgz", @@ -5828,6 +5904,12 @@ "form-data": "^4.0.0" } }, + "node_modules/@types/phoenix": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.5.tgz", + "integrity": "sha512-xegpDuR+z0UqG9fwHqNoy3rI7JDlvaPh2TY47Fl80oq6g+hXT+c/LEuE43X48clZ6lOfANl5WrPur9fYO1RJ/w==", + "license": "MIT" + }, "node_modules/@types/qs": { "version": "6.9.15", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", @@ -5895,7 +5977,6 @@ "version": "8.5.12", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", - "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" @@ -8732,7 +8813,6 @@ "version": "16.4.5", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=12" diff --git a/bff/apollo-gateway/package.json b/bff/apollo-gateway/package.json index b6304c4e..04c241ac 100644 --- a/bff/apollo-gateway/package.json +++ b/bff/apollo-gateway/package.json @@ -33,7 +33,9 @@ "@nestjs/mapped-types": "*", "@nestjs/microservices": "^10.4.1", "@nestjs/platform-express": "^10.0.0", + "@supabase/supabase-js": "^2.45.3", "apollo-server-express": "^3.13.0", + "dotenv": "^16.4.5", "google-protobuf": "^3.21.4", "graphql": "^16.9.0", "graphql-type-json": "^0.3.2", diff --git a/bff/apollo-gateway/src/app/article/article.resolver.ts b/bff/apollo-gateway/src/app/article/article.resolver.ts index 2ad067c9..c9c74687 100644 --- a/bff/apollo-gateway/src/app/article/article.resolver.ts +++ b/bff/apollo-gateway/src/app/article/article.resolver.ts @@ -1,7 +1,10 @@ -import { Resolver, Args, Query } from '@nestjs/graphql'; +import { UseGuards } from '@nestjs/common'; +import { Resolver, Args, Query, Context } from '@nestjs/graphql'; import { ArticleService } from './article.service'; +import { GraphQLContext } from '../../graphql/context.interface'; import { ArticleConnection, ArticlesInput } from '../../graphql/types/graphql'; +import { SupabaseAuthGuard } from '../auth/auth.guard'; @Resolver() export class ArticleResolver { @@ -13,10 +16,13 @@ export class ArticleResolver { // } @Query(() => ArticleConnection) + @UseGuards(SupabaseAuthGuard) async articles( @Args('articlesInput') articlesInput: ArticlesInput, + @Context() context: GraphQLContext, ): Promise { - return await this.articleService.getArticles(articlesInput); + const user = context.req.user; + return await this.articleService.getArticles(user.id, articlesInput); } // @Query('article') diff --git a/bff/apollo-gateway/src/app/article/article.service.ts b/bff/apollo-gateway/src/app/article/article.service.ts index 791b99bd..907c93fa 100644 --- a/bff/apollo-gateway/src/app/article/article.service.ts +++ b/bff/apollo-gateway/src/app/article/article.service.ts @@ -29,7 +29,10 @@ export class ArticleService implements OnModuleInit { // return `This action adds a new article ${createArticleInput}`; // } - async getArticles(input: ArticlesInput): Promise { + async getArticles( + userId: string, + input: ArticlesInput, + ): Promise { const req = new GetArticlesRequest(); if (input?.first) req.setLimit(input.first); if (input?.after) req.setCursor(input.after); @@ -44,7 +47,8 @@ export class ArticleService implements OnModuleInit { if (input?.languageStatus) req.setLanguageStatus(new Int64Value().setValue(input.languageStatus)); if (input?.tag) req.setTag(new StringValue().setValue(input.tag)); - if (input?.userId) req.setUserId(new StringValue().setValue(input.userId)); + + req.setUserId(new StringValue().setValue(userId)); return new Promise((resolve, reject) => { this.articleService.getArticles(req, (err, res) => { diff --git a/bff/apollo-gateway/src/app/auth/auth.guard.ts b/bff/apollo-gateway/src/app/auth/auth.guard.ts new file mode 100644 index 00000000..92bcf698 --- /dev/null +++ b/bff/apollo-gateway/src/app/auth/auth.guard.ts @@ -0,0 +1,40 @@ +import { + Injectable, + CanActivate, + ExecutionContext, + UnauthorizedException, +} from '@nestjs/common'; +import { GqlExecutionContext } from '@nestjs/graphql'; +import { createClient } from '@supabase/supabase-js'; + +@Injectable() +export class SupabaseAuthGuard implements CanActivate { + private supabase = createClient( + process.env.SUPABASE_URL, + process.env.SUPABASE_ANON_KEY, + ); + + async canActivate(context: ExecutionContext): Promise { + const ctx = GqlExecutionContext.create(context).getContext(); + const req = ctx.req || context.switchToHttp().getRequest(); + + const authHeader = req.headers.authorization; + const token = authHeader?.split(' ')[1]; + + if (!token) { + throw new UnauthorizedException('Authorization token not found'); + } + + const { + data: { user }, + error, + } = await this.supabase.auth.getUser(token); + + if (error || !user) { + throw new UnauthorizedException('Invalid or expired token'); + } + + req.user = user; + return true; + } +} diff --git a/bff/apollo-gateway/src/graphql/context.interface.ts b/bff/apollo-gateway/src/graphql/context.interface.ts new file mode 100644 index 00000000..3e69ed61 --- /dev/null +++ b/bff/apollo-gateway/src/graphql/context.interface.ts @@ -0,0 +1,6 @@ +import { User } from '@supabase/supabase-js'; +import { Request } from 'express'; + +export interface GraphQLContext { + req: Request & { user?: User }; +} diff --git a/bff/apollo-gateway/src/main.ts b/bff/apollo-gateway/src/main.ts index bcc101e0..2b267126 100644 --- a/bff/apollo-gateway/src/main.ts +++ b/bff/apollo-gateway/src/main.ts @@ -1,9 +1,13 @@ import { NestFactory } from '@nestjs/core'; +import * as dotenv from 'dotenv'; import { AppModule } from './app/app.module'; +dotenv.config(); + async function bootstrap() { const app = await NestFactory.create(AppModule); - await app.listen(3000); + const port = Number(process.env.PORT) || 3000; + await app.listen(port); } bootstrap(); diff --git a/compose.yml b/compose.yml index a3b66a94..ff97716d 100644 --- a/compose.yml +++ b/compose.yml @@ -7,6 +7,9 @@ services: context: . dockerfile: ./bff/apollo-gateway/Dockerfile.dev environment: + - BFF_CONTAINER_PORT=${BFF_CONTAINER_PORT} + - SUPABASE_URL=${BFF_SUPABASE_URL} + - SUPABASE_ANON_KEY=${SUPABASE_ANON_KEY} - TZ=Asia/Tokyo volumes: - ./bff/apollo-gateway:/app