Supabaseのサーバーサイド認証
はじめに
このスクラップでは公式の以下ページの内容を詳しく見ていく。
Next.js Auth Helpersとは
- SessionをlocalStorageではなくCookieに保存するようにSupabase Authを設定する。
→ App Router全体(クライアント・サーバー)で認証機能を使用できるようになる。 - セッションはSupabaseへのリクエストと一緒に自動的に送信される
※上記はあくまでApp Routerで使える仕組み
Pages RouterでAuth Helpersを使いたいときはここから
プロジェクト作成(自動設定)
下記コマンドを使えば、cookieベースの認証が備わったNext.jsプロジェクトを作成できる。
npx create-next-app -e with-supabase
設定項目
- Cookie-based Auth
- Middleware to refresh user's session
- Code Exchange Route
- TypeScript
- Tailwind CSS
説明は下記に記載
プロジェクト作成(マニュアル)
以下は自身でセットアップする手順
すでにNext.jsのプロジェクトが出来上がっている前提
Next.js Auth Helpers のインストール
npm install @supabase/auth-helpers-nextjs @supabase/supabase-js
環境変数の設定
NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key
ミドルウェア上でセッション管理
プロジェクト直下にmiddleware.js
を作成
Supabaseクライアントをサーバー上で使うには、ユーザーの認証セッションがアクティブな状態を保証する必要がある。セッションはCookieで追跡されるため、これを読み取り必要に応じて更新する。
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'
export async function middleware(req) {
const res = NextResponse.next()
const supabase = createMiddlewareClient({ req, res })
// サーバーコンポーネントルートでは、必ずgetSessionを呼ぶ必要あり
await supabase.auth.getSession()
return res
}
Code Exchangeによるサインイン管理
app/auth/callback/route.js
を作成し、ルートハンドラを実装する
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
import { cookies } from 'next/headers'
import { NextResponse } from 'next/server'
export const dynamic = 'force-dynamic'
export async function GET(request) {
const requestUrl = new URL(request.url)
const code = requestUrl.searchParams.get('code')
if (code) {
const supabase = createRouteHandlerClient({ cookies })
await supabase.auth.exchangeCodeForSession(code)
}
// URL to redirect to after sign in process completes
return NextResponse.redirect(requestUrl.origin)
}
気になっていること
- そもそも認証プロセスの流れがよくわかっていない。クライアント側とサーバー側の両方を使用しているのか?それともサーバー側のみで完結しているか?
- SupabaseのAuth Helpersはサーバー上で認証を行っているという認識でよいのか?
- Route Handlersについて知識があいまい
レンダリング
ルートハンドラ
Next.jsのルートハンドラに寄り道します!
Supabaseの認証を知るためにも、Route Handlersを知る必要がありそう。。。
使い方
appディレクトリのroute.js|ts
にルートハンドラを定義する。
GET、POST、PUT、PATCH、DELETE、HEAD、OPTIONSの HTTPメソッドに対応
export async function GET(request: Request) {}
キャッシュについて
- Route Handlers は、ResponseオブジェクトでGETメソッドを使用するときデフォルトでキャッシュされる
import { NextResponse } from 'next/server'
export async function GET() {
const res = await fetch('https://data.mongodb-api.com/...', {
headers: {
'Content-Type': 'application/json',
'API-Key': process.env.DATA_API_KEY,
},
})
const data = await res.json()
return NextResponse.json({ data })
}
Dynamic Functions
ルートハンドラでは、cookies
, headers
といったNext.jsが提供する動的関数を使用できる。
おそらくSupabaseの認証機能についてもここが関係している気がする。。。🤔
長くなってきたので、次のスクラップにかく。
Dynamic Funcionsについて見ていく
Next.jsが提供する、動的関数を見ていく。
いくつかあるがCookiesとHeadesを見る
Cookies
-
next/headers
よりサーバー関数としてインポートする - cookiesを読み取ることができるようになる
- Route Handler 内で直接呼び出し、または他の関数の中にネストして呼び出しの可能
- インスタンスに新しくCookieを設定する際には
Set-Cookie
ヘッダを使用して、新しいResponse
を返す必要あり。
import { cookies } from 'next/headers'
export async function GET(request: Request) {
// 読み取り専用のcookieインスタンスを生成する
const cookieStore = cookies()
const token = cookieStore.get('token')
return new Response('Hello, Next.js!', {
status: 200,
headers: { 'Set-Cookie': `token=${token.value}` },
})
}
また、Web APIs 上で抽象化を使用して、Cookieを読み取ることも可能
import { type NextRequest } from 'next/server'
export async function GET(request: NextRequest) {
const token = request.cookies.get('token')
}
Headers
-
next/headers
よりサーバー関数としてインポートする - Route Handler 内で直接呼び出し、または他の関数の中にネストして呼び出し可能
- Header インスタンスは読み取り専用のため、headersをセットするには新しい
Response
を返す必要あり。
import { headers } from 'next/headers'
export async function GET(request: Request) {
const headersList = headers()
const referer = headersList.get('referer')
return new Response('Hello, Next.js!', {
status: 200,
headers: { referer: referer },
})
}
こちらもcookesと同様に、Web APIs上で抽象化を行うことで、Headersを読み取ることが可能。
import { type NextRequest } from 'next/server'
export async function GET(request: NextRequest) {
const requestHeaders = new Headers(request.headers)
}
Middlewareについて
Cookiesを使う
- リクエストでは、
Cookie
ヘッダーにCookiesが格納される - レスポンスでは、
Set-Cookie
ヘッダーにCookiesが格納される - Next.jsでは
NextRequest
とNextResponse
上にある、cookies
拡張を通してCookiesにアクセスしたり、操作したりできる。- Incoming Requestに対して、
cookies
はget, getAll, set, delete メソッドを持っている。また、Cookieの存在を確認するhas
メソッド、すべてのCookieを削除するclearメソッドがある - outgoing responseに対して、
cookies
はget, getAll, set, delete メソッドを持っている。
- Incoming Requestに対して、
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// "Cookie:nextjs=fast" header がリクエストに存在することを想定する
// `RequestCookies`APIを使ってリクエストからCookieを取得する
let cookie = request.cookies.get('nextjs')
console.log(cookie) // => { name: 'nextjs', value: 'fast', Path: '/' }
const allCookies = request.cookies.getAll()
console.log(allCookies) // => [{ name: 'nextjs', value: 'fast' }]
request.cookies.has('nextjs') // => true
request.cookies.delete('nextjs')
request.cookies.has('nextjs') // => false
// `ResponseCookies` APIを使ってcookiesを設定する
const response = NextResponse.next()
response.cookies.set('vercel', 'fast')
response.cookies.set({
name: 'vercel',
value: 'fast',
path: '/',
})
cookie = response.cookies.get('vercel')
console.log(cookie) // => { name: 'vercel', value: 'fast', Path: '/' }
// レスポンスは`Set-Cookie:vercel=fast;path=/test` headerを持っている
return response
}
Headersを設定する
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// Clone the request headers and set a new header `x-hello-from-middleware1`
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-hello-from-middleware1', 'hello')
// You can also set request headers in NextResponse.rewrite
const response = NextResponse.next({
request: {
// New request headers
headers: requestHeaders,
},
})
// Set a new response header `x-hello-from-middleware2`
response.headers.set('x-hello-from-middleware2', 'hello')
return response
}
Supabaseの認証に戻る
Supabaseのサーバーサイド認証の仕組み