Firebase Authenticationを使ったNestJSでの認証機能の作成方法
この記事では、Firebase Authenticationを使ったNestJSでの認証機能を作成する方法を解説します。
要約
- Passportを利用する
- Firebase Admin SDKをインストールする
- Firebaseの認証ロジックを実装したPassportの実装を行う
- コントローラーにPassportによるGuardを設定する
内容
まずは、PassportというNode.jsの認証ライブラリを使用します。NestJSでは、@nestjs/passportモジュールを使用してこのライブラリを利用することができます。以下のようにnpmモジュールをインストールします。
$ npm install --save @nestjs/passport passport passport-http-bearer $ npm install --save-dev @types/passport-http-bearer
また、Firebase Authenticationを使用するためには、firebase-adminをセットアップする必要があります。以下のコマンドを使用して、firebase-adminをインストールします。
$ npm install firebase-admin --save
firebase-adminを利用できるようにするためには、main.tsファイルに以下のようなコードを追加します。
main.ts
import admin from 'firebase-admin'; import * as serviceAccount from './firebase.json'; ... async function bootstrap() { ... admin.initializeApp({ credential: admin.credential.cert(serviceAccount as admin.ServiceAccount), }); await app.listen(3000); } bootstrap();
次に、Firebaseの認証ロジックを実装したPassportの実装を行います。以下のコードがその例です。
firebase-auth.strategy.ts
import { Strategy } from 'passport-http-bearer'; import { PassportStrategy } from '@nestjs/passport'; import { Injectable, UnauthorizedException } from '@nestjs/common'; import admin from 'firebase-admin'; @Injectable() export class FirebaseAuthStrategy extends PassportStrategy(Strategy) { constructor() { super(); } async validate(token: string): Promise<string> { try { const decodedToken = await admin.auth().verifyIdToken(token); return decodedToken.uid; } catch (err) { throw new UnauthorizedException(); } } }
validate関数のtokenの引数はAPIにアクセスした際のベアラートークンが格納されます。こちらをFirebase Authで取得したIDトークンが格納されるようにし、verifyIdTokenでトークンを検証します。
例えば、以下のようなcurlコマンドからのアクセスになると思います。
curl http://localhost:3000/posts -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2Vybm..."
ベアラートークンの部分はFirebase AuthのIDトークンになるイメージです。
NestJSのモジュールでFirebaseAuthStrategyを利用できるようにするには、以下のコードをauth.module.tsファイルに追加します。そして、このモジュールを大元のapp.module.tsで利用します。
auth.module.ts
import { Module } from '@nestjs/common'; import { FirebaseAuthStrategy } from './firebase-auth.strategy'; import { PassportModule } from '@nestjs/passport'; @Module({ imports: [PassportModule], providers: [FirebaseAuthStrategy], }) export class AuthModule {}
最後に、コントローラーにPassportによるGuardを設定します。これにより、PostsControllerで作成されたWebAPIのエンドポイントはFirebase AuthのIDトークンによる認証に守られることになります。
posts.controller.ts
import { Body, Controller, Delete, Get, Param, ParseIntPipe, Post, Put, UseGuards, } from '@nestjs/common'; import { PostsService } from './posts.service'; import { AuthGuard } from '@nestjs/passport'; @Controller({ path: 'posts', version: '1', }) @UseGuards(AuthGuard('bearer')) export class PostsController { ... }
全体のコードは以下にあります。
まとめ
本記事では、Firebase Authenticationを用いてNestJSにおけるWebAPIの認証機能の作成方法を解説しました。Passportを使用して認証機能を実装するということがわかれば、そのほかの認証方法についてもロジック部分を変更すればできると思います。Firebase Authenticationを使ったNestJSの認証機能の実装を行いたい場合に、本記事が参考になればと思います。
NestJSでSentryを使う方法
最近はNestJSでWeb APIを作ってます。NestJSでは、大体以下の公式サイトにWeb APIを作成する際に必要となる機能の実装が書かれています。DBへの問い合わせやロギング処理など
Documentation | NestJS - A progressive Node.js framework
ただSentryの使い方の記述はなかったため、調べて実装してみました。エラートラッキングする際にSentryを使用すると便利なので基本的にはSentryを入れるようにしています。
やり方
探すといくつかNestJS向けのSentryのモジュールが見つかるのですが、以下が良さそうだったのでこちらで実装してみました。
npmモジュールのインストールは以下でできます。
npm install --save @ntegral/nestjs-sentry @sentry/node
そして、以下のようにNestJSのモジュールで利用します。ここではルートのモジュールであるapp.module.ts
で適用します。
src/app.module.ts
import { Module } from '@nestjs/common'; import { APP_INTERCEPTOR } from '@nestjs/core'; import { SentryInterceptor, SentryModule } from '@ntegral/nestjs-sentry'; @Module({ imports: [ SentryModule.forRoot({ dsn: process.env.SENTRY_DSN, debug: false, logLevels: ['error'], }), ], providers: [ { provide: APP_INTERCEPTOR, useFactory: () => new SentryInterceptor(), }, ], }) export class AppModule {}
SentryModule.forRoot
で利用できます。dsn,debug,logLevelsといったSentry向けのパラメータを設定します。NestJSをWeb APIとして利用する場合、グローバルのインターセプターを利用することで全てのAPIの実行時の例外をキャッチすることができます。その部分をSentryのインターセプターにすることで例外が発生した場合にSentryにエラーを送信することができます。その部分がnew SentryInterceptor()
の部分になります。
ちなみにWeb APIではなくて、スケジュール実行するプログラムの場合ではインターセプターがないので、以下のようにします。
例外をキャッチする実装とともにclient.instance().captureException
メソッドを利用するとSentryにエラーを送信することができます。
src/app.service.ts
... @Injectable() export class AppService { public constructor(@InjectSentry() private readonly client: SentryService) {} @Interval(10000) handleInterval() { try { ... } catch (e) { this.client.instance().captureException(e); throw e; } } }
本番環境でSentryにエラーを送信するようにし、開発環境では送信しないといった条件分岐をしたいといったことが考えられます。
以下のdsnのパラメータを渡す際に環境変数化して、本番環境ではdsnの値を与える、開発環境では環境変数を設定しない(値をなし)とすると本番環境のみでSentryにエラーを送信することが可能です。
src/app.module.ts
... SentryModule.forRoot({ dsn: process.env.SENTRY_DSN, debug: false, logLevels: ['error'], }), ...
まとめ
このようにSentryは簡単に導入できてエラーをトラッキングするのに優れているので導入することをオススメします。
『エンジニアリングマネージャーのしごと』を読んだ
ITエンジニア本大賞2023のベスト10にも選ばれた『エンジニアリングマネージャーのしごと』を読んだ感想です。
ITエンジニア本大賞2023の結果を見て、全く知らなかった書籍もあれば、自分が観測しているソフトウェアエンジニア界隈で話題になった書籍もあり、ITエンジニアでも全く知らない界隈があるんだなぁとなかなか興味深い結果だと思いました。
以下、『エンジニアリングマネージャーのしごと』の感想です。
本全体の内容としては、ピープルマネジメントがメインの内容です。その中でもコミュニケーションや委譲、教育、評価面談、採用といったテーマが扱われています。
このようなテーマをマネージャーとしてどのように具体的に実践するかが書かれています。いずれかのテーマに関心がある方には役立つものになっていると思います。
特に自分が参考になったのは教育観点での発達の最近接領域という考え方です。これは個人の成長を促すためにどのようなレベルのタスクを与えていくかの考え方です。
学習者が支援ありでできるタスクを与えていくのが発達の最近接領域ということになります。
完成するために支援が必要なタスクをタスクの完成を支援する豊富な支援者とともに取り組むことで、より高い難易度のタスクに取り組めるようになり、継続的にスキルを向上させていくことができるという考えです。
この考え方はよい発見でした。どのように人を教育していくかということの根本的な考え方を知ることができたと思います。
また、組織において学習の機会を作るという観点で、問題を学習の機会に変えるというところが参考になりました。具体的にはマネジメントバグという手法です。
これは部門や会社全体などの組織にまつわる問題をメンバーが自由に投稿し、組織に関わる全員がその問題を確認し、解決のためのアクションを促すといった手法です。
このようにオープンに問題解決ができる仕組みを構築することで、組織力の強化につながりそうだと感じました。
ソフトウェア開発において問題になりがちなドキュメントをどのように扱うかの問題に関しても言及されています。コードがなぜこのようになっているかといったことは時が経つと忘れがちです。そんな時にアーキテクチャー決定記録(ADR:Architecture Decision Record)が役立ちます。
そのようなコードを書く判断に至った背景をドキュメントすることによってアーキテクチャの品質を保つことができるはずです。
自分の現在の環境が組織の新陳代謝が激しい環境であるため、こういった決定の背景を残していくことは持続的なソフトウェア開発にとって重要だと感じました。
このように『エンジニアリングマネージャーのしごと』にはソフトウェアエンジニア組織のマネージメントについて役立つノウハウが具体的に書かれています。
このようなノウハウを一つでも現場に取り入れてみるとマネージメントや組織力の向上につながるのではないかと思います。