Nuxt 3移行時の認証モジュールの選択とCognito strategyの実装

### はじめに

Amazon Cognitoを静的なNuxt3アプリケーションに統合するために、`@nuxtjs/auth`の Nuxt3版である `nuxt-alt/auth` の拡張機能 Cognito strategy を実装しました。
[`@toyopilgrim/nuxt-alt-auth`](https://www.npmjs.com/package/@toyopilgrim/nuxt-alt-auth)

今回はその実装に至る経緯、Nuxt３移行における認証モジュールの選択肢などについて投稿します。

最近、Nuxt 2からNuxt 3へのプロジェクト移行中に、主な課題の一つとして認証モジュールの適応がありました。というのは現在、@nuxtjs/authはNuxt3をサポートしていません。
※2023/10月時点では公式のNuxt3認証モジュールは開発中です。


### Authモジュールの選択

Nuxt 3への移行プロセスに関する洞察については、[Nuxt 3 Migration Cheatsheet](https://harlanzw.com/blog/nuxt-3-migration-cheatsheet) が非常に役立ちました。認証に関しては、このチートシートで以下のものが示されています：
- [sidebase/nuxt-auth](https://github.com/sidebase/nuxt-auth)
- [nuxt-alt/auth](https://github.com/nuxt-alt/auth)
- [@nuxtjs/supabase](https://supabase.nuxtjs.org)

一方、Nuxtコミュニティからの推奨事項としては：
- [sidebase/nuxt-auth](https://github.com/sidebase/nuxt-auth)
- [AuthJs Nuxt](https://authjs-nuxt.pages.dev)
- [Lucia](https://lucia-auth.com/guidebook/sign-in-with-username-and-password/nuxt/)や[Nuxt Auth Template](https://github.com/nuxt/examples/tree/main/auth/local)を使用して独自の認証を実装

これらのオプションの中で、[Sidebase Nuxt Auth](https://sidebase.io/nuxt-auth/getting-started) は特に成熟しており人気も高いようです。一方、後発の[AuthJs Nuxt](https://authjs-nuxt.pages.dev)も注目を集めており、10月18日に開催された Nuxt Nation 2023で作者のEmmanuel氏がデモを行いました。

はじめは公式のAuthモジュールがまだ利用できないので最も人気な代替手段である [Sidebase Nuxt Auth](https://sidebase.io/nuxt-auth/getting-started)を採用しようとしました。しかし、検討した結果、今回移行を任されている該当のアプリケーションの要件と合致しませんでした。[AuthJs Nuxt](https://authjs-nuxt.pages.dev)も同様で、これらの両方はサーバーサイドの設定に重きを置いています。言い換えるとSSR前提的な要素があります。今回求めていたのは、Nuxt2の非SSRな静的アプリで、それをNuxt3に移行させ、既に実装されているAmazon Cognitoを直接の認証プロバイダとして継続使用することでした。

Sidebase Nuxt v0.6からは、`local` providerを通じて静的アプリに対応していますが、特定のインターフェース(Auth.js)に一致するカスタマイズされたバックエンドが必要です。Amazon Cognitoのエンドポイント（例：`https://cognito-idp.ap-northeast-1.amazonaws.com/`）は、このインターフェースに準拠していません。

IFを合わせるべく認証APIラッパーを実装するという手段もありましたが、直近の目的はアプリケーションのNuxt3移行でしたので、なるべくライトな形で済ませたかったです。並行で Vuetify2 を Vuetify3に移行していてそっちの方がむしろ大変な感じでもあったのでサクッとAuth middlewareを動かしたかったのです。

そこで次なる候補が [nuxt-alt/auth](https://github.com/nuxt-alt/auth)。人気度やメンテ頻度は格段に劣るものの、ライブラリの使用感はNuxt2の`@nuxtjs/auth`とほぼ同じである点がとても有望でした。
実際に中身の実装をチェックしてみると、旧来の`@nuxtjs/auth`をそのままコピって Nuxt3として動くように改修されたようなものです。今回の私のゴールにはマッチしているわけですが、なぜなら上述のNextAuth.jsやAuth.jsベースのモジュールとは違い、静的アプリに対する制約が特にありません。

### Cognitoとの統合機能を実装

ただし、唯一不足していたのはCognitoと互換性のあるカスタムスキームでした。
この問題を解決するため、リポジトリをフォークし、内部でCognitoスキームを独自実装し、[`@toyopilgrim/nuxt-alt-auth`](https://www.npmjs.com/package/@toyopilgrim/nuxt-alt-auth)をpublishしました。
スキーム実装の中身は `@a1ter/nuxt-auth-aws-cognito-scheme`(もはやメンテナンスされていないものですが) を参考にしました。内部的には AWS Amplifyを使用しています。

設定方法を簡潔に記載します。

* cognito strategyを追加

```
export default defineNuxtConfig({
    modules: [
        '@toyopilgrim/nuxt-alt-auth',
        '@pinia/nuxt'
    ],
    auth: {
        globalMiddleware: true,
        /* cognito options */
        strategies: {
            cognito: {
                scheme: "cognito",
                credentials: {
                    userPoolId: process.env.COGNITO_USERPOOL_ID,
                    userPoolWebClientId: process.env.AUTH_CLIENT_ID,
                    region: process.env.COGNITO_REGION
                },
                endpoints: {
                    user: false
                }
            }
        }
    }
});
```

* SignIn componentなどで、以下のようにcallすればログインが行われます。 

```
await useAuth().loginWith("cognito", {
    data: loginData
  })
```

* おそらくすべてのNuxt３互換のAuthモジュールで `useAuth()` composableが使えると思います。移行中なので差分を減らしたく伝統的スタイルが好ましい場合は以下のように記述。

```
const context = useNuxtApp();
await context.$auth
  .loginWith("cognito", {
    data: loginData
  })
```


## まとめ

Nuxt 2からNuxt 3への移行は、特に認証モジュールに関しては比較的大変な部分かと思います。現在の`@nuxtjs/auth` はNuxt 3をサポートしていません。Nuxtコミュニティや移行のチートシートによってさまざまな代替モジュールが推奨されていますが、多くはサーバーサイドの設定に傾いています。Nuxt３では`server`が導入されたので自然な流れな気もします。
現時点で、Amazon Cognitoと直接リンクしている非SSRの静的アプリを運営している人々の選択肢の一つとして`@toyopilgrim/nuxt-alt-auth`を利用可能です。ただし、公式のNuxt Authモジュールがリリースされると、どのみちそちらに移行する方が良いと考えていますので、こちらの動きについて注目し続けます。


## 2024/01 追記
Nuxt3のマイグレーション作業が一通り落ち着いた段階で、結局 [sidebase/nuxt-auth](https://github.com/sidebase/nuxt-auth) を使えるように改修しました。バックエンドサーバー側でInterfaceを合わせたラッパーを実装して静的な Nuxt frontendから `local` provider設定にて実装したAuthラッパーAPIに接ぐという形です。これが現時点の王道だと思います。

したがって上記紹介した、Cognito strategy は既に利用していませんが、`nuxt-alt/auth`を使用するユーザーには一時的であっても利用価値があると思うので残しておきます。