Nest.jsでGoogle認証(OAuth2.0)を使う

に公開

フロントエンドがNext.jsでバックエンドがNest.jsのアプリケーションにおいて、Google認証を入れたいときのバックエンド側の実装をまとめてみました。
※ フロントエンドの実装に関しては後日掲載します。

調べた中でいろんな実装方法があるようですが今回の実装方法としましては
①Next.js(フロントエンド)でAuth.jsを使用してGoogle認証をする
②認証後、id_tokenとrefresh_tokenが発行され
request header経由でバックエンドにid_tokenを渡す
※ id_tokenは有効期限が1時間なので、期限切れの場合はrefresh_tokenを使用しid_tokenを再取得する
③バックエンド側のmiddlewareでrequest header内のid_tokenが有効なものかチェックする

といった流れになります。

今回は、③のバックエンドのmiddlewareでのid_tokenのチェックを実際に見ていきます。

ミドルウェアを用意

src > middleware > auth.middleware.ts

import {
  Injectable,
  NestMiddleware,
  UnauthorizedException,
} from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

// 1) google-auth-libraryをインストールしておきます
import { OAuth2Client } from 'google-auth-library';


// 2) NestMiddlewareを継承したAuthMiddlewareを用意します。injectableも忘れずに!
@Injectable()
export class AuthMiddleware implements NestMiddleware {
  private readonly googleClient: OAuth2Client;
  // 3) Next.jsのGoogle認証の際に作成したclientIdをenvファイルから読み込みます
  constructor() {
    this.googleClient = new OAuth2Client(process.env.GOOGLE_CLIENT_ID);
  }
  async use(
    // 4) reqの中にuser情報を入れたかったので拡張しています
    req: Request & { user: { name: string; email: string } },
    res: Response,
    next: NextFunction,
  ) {
    // 5) Authorizationリクエストヘッダーの中身を所得
    const auth = req.headers['authorization'];


    if (!auth) {
      throw new UnauthorizedException('Unauthorized');
    }

    // 6) フロントエンド側で <type> <id_token> 形で用意しています。今回はid_tokenのみ取得
    const items = auth.split(' ');
    const idToken = items[1];

    if (!idToken) {
      throw new UnauthorizedException('Unauthorized');
    }

    try {
      // 7) id_tokenとclientIdをgoogleClientのverifyIdTokenに渡すことで有効どうかチェックしてくれます
      const ticket = await this.googleClient.verifyIdToken({
        idToken: idToken,
        audience: process.env.GOOGLE_CLIENT_ID,
      });


      const payload = ticket.getPayload();
      const user = {
        name: payload.name,
        email: payload.email,
      };
      req.user = user;

      next();
    } catch (error) {
      throw new UnauthorizedException('Unauthorized');
    }
  }
}


moduleで適用

AppModuleに以下のように書くことで
この作成したmiddlewareを全体で適用することができます。

src > app.module.ts

import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
// 省略
import { AuthMiddleware } from './middleware/auth.middleware';

@Module({
    // 省略
})

export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(AuthMiddleware).forRoutes('*');
  }
}


以上がNest.js側でのid_tokenの実装になります。