🌐

[NestJS/GraphQL] Federation構成時の認証ヘッダの取り扱い

2022/03/14に公開

概要

NestJSにてApolloFederation構成を構築している場合
クライアントからのリクエストヘッダに入っている情報がゲートウェイから
各サービスにデータを取りに行くリクエストには反映されない問題(?)がある。
(Federation構成やMicroserviceについての詳細は省略)

今回、認証はFirebase Authを用いており、ゲートウェイ配下のサービスにて
Firebaseから発行されたBearerトークンを用いてユーザ認証をしたかったが
前述の通りゲートウェイとサービス間ではヘッダーは再構築されてしまうため
認証情報が抜け落ちてしまう。

@Injectable()
export class UsersGuard implements CanActivate {
  constructor(private readonly usersService: UsersService) {}
    return new Promise((res) => {
      const ctx = GqlExecutionContext.create(context);
      const { req } = ctx.getContext();
      const authToken = req.headers.authorization as string; <- ここが取れない

本記事では、クライアントのリクエストヘッダに含まれる情報を
ゲートウェイから配下のサービスへ橋渡しできるようにすることを目的とする。

前提

関連パッケージのバージョンだけ抜粋

"@apollo/gateway": "^0.48.1",
"@nestjs/apollo": "^10.0.5",
"@nestjs/common": "^8.0.0",
"@nestjs/core": "^8.0.0",
"@nestjs/graphql": "^10.0.5",

@nestjs/graphqlは10系を利用する。9系とはかなり書き方が異なるので注意。
(バージョンあげたい方は9->10系へのマイグレーションガイドを参照願います)
https://docs.nestjs.com/graphql/federation

結論

GatewayのGraphQLModule.forRootに渡すコンフィグの中でヘッダーを再構築すれば良い

import { IntrospectAndCompose, RemoteGraphQLDataSource } from '@apollo/gateway';
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloGatewayDriver, ApolloGatewayDriverConfig } from '@nestjs/apollo';

@Module({
  imports: [
    GraphQLModule.forRoot<ApolloGatewayDriverConfig>({
      driver: ApolloGatewayDriver,
      server: {
        cors: true,
      },
      gateway: {
        supergraphSdl: new IntrospectAndCompose({
          subgraphs: [
            { name: 'users', url: `${process.env.USERS_URL}/graphql` },
          ],
        }),
	// ここから -----------
        buildService: ({ name, url }) => {
          return new RemoteGraphQLDataSource({
            url,
            willSendRequest({ request, context }) {
              if (Object.keys(context).length > 0) {
                request.http.headers.set(
                  'Authorization',
                  context['req']['headers']['authorization'],
                );
              }
            },
          });
	 // ----------- ここまで
        },
      },
    }),
  ],
})
export class AppModule {}

buildServiceに渡している関数の引数でnameとurlがあるが
今回のケースだと
name: "users", url: ${process.env.USERS_URL}/graphqlになる。
データを取得しに行くサービスによって処理を変えたい場合はこれらの情報を使って分岐させられる。

まとめ

ApolloFederationを実現したいがゲートウェイで認証をせず
配下のサービスそれぞれで認証をしたい場合に
リクエストヘッダをサービスへ渡す方法を紹介した。

ApolloFederationをNestJS+GraphQLという技術スタックで
開発を進めている方々のヒントになればHappy。

情報を端折って書いたので質問とか指摘事項あればウェルカム
以上

参考リンク

apollo/gatewayのドキュメント

NestJSじゃないけど参考にした記事

Discussion