Next Auth v5 + App Router の情報を記載します。
v4以前だと違う方法で取得可能だったようなのでバージョンに応じて適宜確認下さい
NextAuth.js は認証を行う際に値を保存し取り出すことが出来ます。
import NextAuth, { NextAuthConfig } from 'next-auth'
export const authConfig = {
providers: [
// メール・パスワード認証の場合
Credentials({
async authorize(credentials) {
// ・・中略・・
// ログインに成功した場合はユーザー情報を返す(他ファイルからも参照出来るように内部的に保存している)
return isLoginSuccess ? { name: 'hoge', email: 'fuga@test.com' } : null
},
}),
],
// ・・中略・・
} satisfies NextAuthConfig
// auth を使うことで他ファイルでも保存した認証情報を参照可能
export const { auth } = NextAuth(authConfig)
import { auth } from '@/auth'
export async function GET() {
// 保存した値を読み込む
const session = await auth()
// {
// "session": {
// "user": {
// "name": "hoge",
// "email": "fuga@test.com"
// },
// "expires": "2024-08-07T07:23:11.988Z"
// }
// }
}
NextAuth.jsを使い始める上で
- 保存した値を簡単に確認する方法はないだろうか?
- そもそも どこに・どのような方法で値が保存されている んだろうか?
という疑問が湧きました。
調べて分かったことを記載します!
先にざっくりの結論としては以下4点になります
- 保存されている値の確認は Route Handler の確認が手軽
- 認証時に保存した値はcookie にJWTで保存
- ↑からdecodeすることは難しい
- ただし、encode・decodeを自分で定義することでcookieの値からもdecode可能
検証した環境
1 | next-auth | 5.0.0-beta.17 |
2 | next | 14.1.3 |
認証時に保存した値を確認する
開発初期は専用のRoute Handlerを用意することで、保存した認証情報の確認が楽に感じました!
import { NextResponse } from 'next/server'
import { auth } from '@/auth'
// NextAuth.jsで保存した認証情報を確認するためのエンドポイント
export async function GET() {
const session = await auth()
return NextResponse.json({ session })
}
未認証
認証済み
このデータはブラウザを1度落とし、しばらくしてからアクセスしても確認出来ます。
どこに、どのような方法で保存されているのでしょうか?
認証データの保存される仕組み
認証データは authjs.session-token
という名前でcookieにJWTで保存されています。
メール/パスワード認証の場合を例に、保存されるタイミングを見ていきます。
NextAuth.js の設定を定義
import NextAuth, { NextAuthConfig } from 'next-auth'
import Credentials from 'next-auth/providers/credentials'
export const authConfig = {
providers: [
Credentials({
async authorize(credentials) {
// apiアクセスを再現するため2秒間待機
await new Promise((resolve) => setTimeout(resolve, 2000))
// 特定の値だった場合にログイン出来たことにする
return credentials.email === 'test@test.com' && credentials.password === 'password'
? { name: 'hoge', email: 'fuga@test.com' }
: null
},
}),
],
} satisfies NextAuthConfig
export const { signIn, auth } = NextAuth(authConfig)
ログインページとログイン用のServer Actionsを用意
import { login } from '@/actions/login'
export default function LoginPage() {
return (
<div>
<h1>ログイン</h1>
<form action={login}>
<label>
メールアドレス
<input type="email" name="email" required />
</label>
<label>
パスワード
<input type="password" name="password" required />
</label>
<button type="submit">ログイン</button>
</form>
</div>
)
}
'use server'
import { signIn } from '@/auth'
export async function login(formData: FormData) {
// NextAuthのログイン用関数を実行
await signIn('credentials', formData)
}
最初cookieに値がなかった状態から
ログイン後cookieに認証情報が保存されます!
先ほどのエンドポイントにアクセスすると意図した値が保存出来ていることが分かります。
ここからはcookieに保存された authjs.session-token
のJWTについて見ていきます
cookieの認証情報について
シンプルなJWTで保存されています!
{ヘッダ}.{ペイロード}.{署名}
つまり、ペイロード部分に今回保存した値も入っているわけですね。
「じゃあdecode方法が分かればdecodeして確認出来る?」
とピンと来た方もいらっしゃるかもしれません。
const cookieToken = cookies().get('authjs.session-token')
// cookieのJWTからdecode可能?
const decoded = await decode(cookieToken!.value)
その通り!…なもののここからが少し複雑です
認証情報保存の仕組み
NextAuthの認証情報、保存・取得の仕組みは内部的に以下のようになっています
保存
- ログイン時などに 認証情報をreturn する
- 1でreturnした値・secret・salt を使いNextAuthが用意している
encode
関数を使ってJWTに変換 - 2のJWTを
authjs.session-token
という名前でcookieに保存
取得
- cookieのJWTを取得
- 1のJWT・secret・salt を使いNextAuthが用意している
decode
関数でdecode、保存した値を使えるようにする
細かく書いたものの肝は
- 保存・取得共に
2.
でNextAuth.jsのencode・decodeがsaltを利用していること - その
salt
の値が分からないこと
です。
つまり↓の部分がNextAuth.jsの内部で隠蔽されて不明なためdecodeが出来ません
import { cookies } from 'next/headers'
import { decode } from 'next-auth/jwt'
export async function GET() {
const cookieToken = cookies().get('authjs.session-token')
const decoded = await decode({
token: cookieToken!.value,
secret: process.env.AUTH_SECRET,
// saltの値が分からない・・
salt: '',
})
// ・・・
}
cookieからdecode出来るようにする
ここからは本来の開発では実装の必要がなく、
NextAuth.jsと更に仲良くなるための実験的な内容です 😃
「なんとかcookieの値から自分でdecodeして確認する方法はないだろうか?」
と思い調べてみました。
結論としてはNextAuthの設定でencode・decodeを自分で定義することで確認が出来るようになります!
encode・decodeを定義
NextAuth.jsの設定でencode・decodeを自分で定義します。
NextAuthの encode
・decode
を使っているものの
saltの値は自分で定義している、というのが重要ポイント!
import NextAuth, { NextAuthConfig } from 'next-auth'
import Credentials from 'next-auth/providers/credentials'
import { decode, encode } from 'next-auth/jwt'
export const authConfig = {
providers: [
Credentials({
async authorize(credentials) {
await new Promise((resolve) => setTimeout(resolve, 2000))
return credentials.email === 'test@test.com' && credentials.password === 'password'
? { name: 'hoge', email: 'fuga@test.com' }
: null
},
}),
],
jwt: {
encode: async ({ token, secret }) => {
// カスタムのencode方法
return encode({ token, secret, salt: 'your-custom-salt' })
},
decode: async ({ token, secret }) => {
// カスタムのdecode方法
return decode({ token, secret, salt: 'your-custom-salt' })
},
},
} satisfies NextAuthConfig
export const { signIn, auth } = NextAuth(authConfig)
cookieからdecodeする
あとはRoute Handlerでcookieの値からdecodeする際にも同じsaltを利用します
import { NextResponse } from 'next/server'
import { auth } from '@/auth'
import { cookies } from 'next/headers'
import { decode } from 'next-auth/jwt'
export async function GET() {
const cookieToken = cookies().get('authjs.session-token')
const decoded = await decode({
token: cookieToken!.value,
secret: process.env.AUTH_SECRET,
salt: 'your-custom-salt',
})
console.log('decoded:', decoded)
const session = await auth()
return NextResponse.json({ session })
}
decoded は以下のように出力されます
decoded: {
name: 'hoge',
email: 'fuga@test.com',
sub: '3793d34a-2f0e-4060-a2a0-4bf50733503c',
iat: 1720433662,
exp: 1723025662,
jti: 'f06c7e97-dd81-4d6f-bf24-2c474fe2b1fa'
}
cookieのJWTからも認証情報を取得出来るようになりました!🙌