見出し画像

NextAuth のアカウント連携機能を見る

社内で最近の Next.js 事情の話題になり、NextAuth にまで話が及んだ。その際、アカウント連携の話が上がったので、軽く挙動確認などを行った。


挙動と仕様 (ドキュメント)

まず、NextAuth は標準でアカウント連携機能を提供している。例えば OAuth で Google と LINE ログインに対応している場合を想定する。まず Google でログインした後にもう片方の LINE ログインすると、ログイン済み User に LINE アカウントが自動で紐付けられる。

試してはいないが、Credential 認証にも対応しているものと思われる。今日の時間が余れば NextAuth のコードまで追っかける。


NextAuth 指定のスキーマを、Prisma Schema で最小限に再現したファイルがこれとなる。モデルの説明や構成要素の解説などは Database Adapters Overview で述べられている。ここではアカウント連携が行われる条件が記載されている

A single User can have multiple Accounts, but each Account can only have one User.
Account creation in the database is automatic and happens when the user is logging in for the first time with a provider, or the Adapter.linkAccount method is invoked. (省略)
Linking Accounts to Users happen automatically, only when they have the same e-mail address, and the user is currently signed in. Check the FAQ for more information on why this is a requirement.

ひとつのユーザが複数のアカウントを持つことはできますが、 各アカウントが持つことができるユーザはひとつだけです。
データベースへのアカウントの作成は自動的に行われ、 ユーザがプロバイダから初めてログインしたときや Adapter.linkAccount メソッドが起動されたときに行われます。(省略)
アカウントとユーザの紐付けは自動的に行われ、両者が同じメールアドレスを持ち、ユーザが現在ログインしている場合にのみ行われます。これがなぜ必要なのか、詳しくは FAQ をご覧ください。

https://authjs.dev/reference/adapters#account


NextAuth で DB を使用するのはオプションだが、ユーザー情報を永続化したいなら必要となる。この辺はFAQ「Should I use a database?」で言及されている。

Using NextAuth.js without a database works well for internal tools - where you need to control who is able to sign in, but when you do not need to create user accounts for them in your application.
Using NextAuth.js with a database is usually a better approach for a consumer facing application where you need to persist accounts (e.g. for billing, to contact customers, etc).

NextAuth.jsをデータベースなしで使うのは、内部ツール(誰がサインインできるかを制御する必要があるが、アプリケーション内でユーザーアカウントを作成する必要がない場合)に適しています。
コンシューマ向けのアプリケーションで、アカウントを永続化する必要がある場合(課金や顧客との連絡など)には、NextAuth.jsとデータベースを併用するのがよいでしょう。

https://next-auth.js.org/faq#databases
翻訳は
 DeepL によるもの


アカウント連携のもう一つの方法として、ログイン時に同じメールアドレスが存在していた際に自動連携してほしいケースでは、 allowDangerousEmailAccountLinking が使える。一方で、名前から察することができるが、危険であるため使用する際には慎重な検討を重ねるようにという注意書きがされている


実装を追う

ということで仕様やドキュメントを見てきたが、ここからは私が時間切れになるまでコードを見ていく。


手始めに、先ほどヒントがあった Adapter.linkAccount を見る。一応注意書きではログイン済みかつメールアドレスが同一であった場合のみアカウント連携が行われると書かれているので、そこら辺を見たい感じ

Linking Accounts to Users happen automatically, only when they have the same e-mail address, and the user is currently signed in.

https://authjs.dev/reference/adapters#account


リファレンスによると @auth/core に属しているようだ。

あった

https://github.com/nextauthjs/next-auth/blob/7e8d49cc20b8554ba691c6d1e2b1dec0388dbe57/packages/core/src/adapters.ts#L231-L237

packages/adapter-* に定義されている様子。Prisma, DrizzleORM, TypeORM などのORMや、各DBのアダプタにも定義されていることを確認した。

Prisma の実装ではシンプルに書かれている。PostgreSQL の実装でもインサートしているだけの様子


では呼び出されている箇所はというと 4 箇所 (2 ファイル) だった

現在の NextAuth は、next-auth を安定版、 @auth/core を実験版と位置づけている。これらのファイルに残されている解説は以下。

この関数は、ユーザが現在ログインしているかどうか、すでにアカウントを持っているかどうか、使用している認証メカニズムに応じて、ユーザのサインイン、アカウントの作成、リンク(またはリンクしない)の複雑なフローを処理します。

ユーザーがサインインし、既存の有効なアカウントで認証されない限り、OAuthアカウントをリンクするような安全でない動作を防ぎます。

すべての検証 (OAuth フローやメールアドレス検証フローなど) は、このハンドラが呼ばれる前に行われ、このハンドラが複雑になるのを防ぎます。

ログイン済みで異なる OAuth アカウントからログインした場合にアカウント連携が起こるのはここ。この処理だとメールアドレスの合致を検証した形跡がなさそうに思えたが、ログイン検証処理で JWT なら JWT sub を元に検証しているのでここで判定するのか? 時間なくて検証できないが、ChatGPT (with Browsing) 曰く、JWT.sub をメールアドレスとするのは非推奨らしい。


ちなみに、

  • allowDangerousEmailAccountLinking が有効

  • OAuth でログインする

  • OAuth アカウントと紐付けされている User が存在しない

以上の条件が全て合致する場合、ここで、メールアドレスに合致する user でログインする処理が実行される。allowDangerousEmailAccountLinking が無効だと例外を返す。この後、ログイン済みユーザーとOAuthアカウントの連携が行われるようだ。



まとめ

さて、NextAuth のアカウント連携について、挙動および仕様と実装まで追ってきた。

NextAuth におけるアカウント連携の全体像は掴めたし、ログイン成功時のコールバック処理も棚ぼたで読むことができた。また、挙動を精査したいときに見る箇所は大体分かったので、必要になったらまた読もうと思う。



この記事が気に入ったらサポートをしてみませんか?