Adonis5をAPIサーバーとして扱う場合の認証はAPI tokensというOAT認証方式のトークンが採用されています。
Opaqueトークンは、JWTトークンのように有効期限を更新するためのリフレッシュトークンがない。そのため期限をすぎると再認証が必要になります。
しかし、AdonisにはOpaqueトークンを更新する機能が用意されていないので、トークンの期限を更新をどうしたかを紹介します。
Opaqueトークンの更新タイミング
まずOpaqueトークンの文字列、情報が含まれてないランダムなハッシュデータです。ですから頻繁にネットワーク上に流れてもセキュリティ上問題ありません。
(JWTなんかは30分とか非常に短い期限にするのが一般的です。)
なので、トークン自体の期限を2週間と長く設定して、APIにアクセスする度に期限をチェックして、10日以下になったときに、トークンの期限日を伸ばす仕様にする。
クライアントはトークンを更新する必要がない。なぜなら、DBに保存されているexpired_atを更新するだけでハッシュに変更はないからです。
実装方法
コードでは以下のようなことを行う。
if (await auth.use('api').check()) {
const expiresAt = auth.use('api').token?.expiresAt
const tokenHash = auth.use('api').token?.tokenHash
if (!!expiresAt && !!tokenHash) {
// トークンの期限が10日以下になった場合
if (DateTime.local().plus({day: 10}) > expiresAt) {
const dateTimeFormat = Database.query().client.dialect.dateTimeFormat
const newExpiresAt = DateTime.local().plus({day: 14}).toFormat(dateTimeFormat)
// DBのapi_tokens.expires_atを更新する
await Database.from('api_tokens').where('token', tokenHash).update({ 'expires_at': newExpiresAt })
}
}
return response.accepted(auth.use('api').user)
} else {
return response.unauthorized()
}
tokenをアップデートする関数が用意されていないので、DatabaseQueryBuilderで直接api_tokensテーブルを指定してupdateを行っている。
その際に、日付をクエリに入れるときFormatしないとエラーを吐く。
"update `api_tokens` set `expires_at` = 2022-10-19 13:20:32.841 +00:00 where `token` = 'a753bc6e3d3aee20afb19d53d56f87bb9b4d1e415cc70ab0ee009387b57f4dd9' - Unknown column '_zone' in 'field list'"
日時のフォーマットは、Adonisのauthパッケージを参考にdateTimeFormatを取得した。
https://github.com/adonisjs/auth/blob/master/src/TokenProviders/Database/index.ts#L110
const dateTimeFormat = Database.query().client.dialect.dateTimeFormat
これで同じトークンを使っていても定期的にサービスを利用していれば、期限切れを起こすことがなくなる認証ができた。
この他にも、AdonisJS v5へのアップグレードで行ったことをまとめました。
なにか役に立てたら幸いです。