やりたいこと
Nuxt3バックエンド
APIバックエンドをNuxt3で作る
認証はfirebaseでBearer tokenがクライアント側からくる
認証が必要がルートのみ、Server側でtokenをチェックする
なるべくスマートに実装したい
クライアント側
クライアント側は別に何でも良い。
以下はNuxt2でfirebaseを作ったtwitter認証の例
async signin() {
const provider = new this.$fireModule.default.auth.TwitterAuthProvider()
await this.$fire.auth
.signInWithPopup(provider)
.then(async (result) => {
const token = await result.user.getIdToken(true)
this.$axios.setToken(token, 'Bearer')
this.$axios.$get('/api/authed/me')
})
.catch((error) => {
// Handle Errors here.
})
},
Nuxt3 バックエンド側
ポイントだけ押さえたディレクトリ構成
・server/api/authed/** が認証が必要なAPI
・server/api/middleware/auth.tsでtokenを検証
・server/utils/auth.tsでfirebaseに接続(これはどこに置いてもよい)
.
├── server
│ ├── api
│ │ ├── authed
│ │ │ └── me.get.ts
│ ├── middleware
│ │ └── auth.ts
│ └── utils
│ └── firebase.ts
firebaseに接続
firebase-adminが入ってて、firebaseへの接続設定はうまくいっているものとしますよ。これはNuxt3とは関係ないので省略します。
import { initializeApp, applicationDefault } from 'firebase-admin/app'
import { getAuth } from 'firebase-admin/auth'
initializeApp({
credential: applicationDefault()
});
export default function verifyIdToken(token: string) {
return getAuth().verifyIdToken(token)
}
firebaseのverifyIdToken()がどこからでも使えるようにexportしました。
ミドルウェアを設定する
import verifyIdToken from '~/server/utils/firebase'
export default eventHandler((event) => {
if (event.req.url.startsWith('/api/authed')) {
const token = getRequestHeader(event, 'Authorization').split('Bearer ')[1]
return verifyIdToken(token).then((decodedToken) => {
event.context.auth = decodedToken
}).catch((error) => {
throw createError({ statusCode: 403, statusMessage: "Forbbiden, Not authenticated."});
});
}
});
ここが最大のポイント。Nuxt3のserverMiddlewareで、すべてのAPIリクエストに対して実行する定義を設定します。
ただし、/api/authed以下のみ認証をチェックします。
Authorizationヘッダを取得して、utils/firebase.tsで定義したverifyIdTokenを実行。エラーになった場合は403を返します。
認証が通った場合は、以後の処理側でuserdataを参照できるように、contextへ入れておきます。
認証後ルート
export default defineEventHandler((event) => {
console.log('[Auth?] ' + event.context.auth)
return event.context.auth
})
認証を通したAPIroute以下では、event.context.authに認証済みユーザーのデータが入っているので、後は煮るなり焼くなり好きにします。
参考
なんかこの辺の実装は自分でやれって感じのスタンスなんですかね、Nuxt3。
↓こんなふうにできたらいいんですけど(現時点ではできません)
export default defineNuxtConfig({
nitro: {
routeRules: {
'/api/authed/***': {
middleware: AuthedRoutes
},
}
}
});
APIをauthed/以下に分けたくない場合
defineAuthenticatedEventHandlerを作ってラップする方法もあるけど、間違って認証が必要なところでdefineEventHandlerを使ってしまうと認証なしでAPIを通しそうなので怖いのでこちらは採用しませんでした。