[キャッチアップ] supabase Auth

supabase の DB に普段お世話になってるけど、認証認可周りでも使ってみたいのでざっとドキュメントに目を通すスクラップ。
今回の目的はフロントエンドにメールアドレス/パスワードでのユーザー登録、ログインを提供することなので、主に Authentication、特にパスワード認証を中心に読み進める。

公式ドキュメントの Overview ざっと見る
Auth には2種類ある
- Authentication(認証): クライアントは誰なのか
- Authorization(認可): クライアントに何をさせて良いのか
supabase Auth は他の supabase システムと組み合わせても良いし、スタンドアロンで動かしてもよい。基本的には Postgresql と統合した機能を提供する。
認証
認証方法は多数提供されてる
- メールアドレス+パスワード
- マジックリング
- 電話番号
- ソーシャル連携
- GitHub
- 他多数
認可
Postgresql の行レベルセキュリティ(RLS) による認可の設定が可能。
ポリシー機能によって SQL での柔軟な制約を定義できる。
例えば users
テーブルに対して、 auth.uid() = user_id
というポリシーを定義していれば、以下のようにクライアントから明示せずとも自身のユーザー情報しか取得できなくなるなど。
let { data, error } = await supabase.from('users').select('user_id, name')
// console.log(data)
// Still => { id: 'd0714948', name: 'Jane' }
この仕組みは以下の流れで実現している。
- ユーザー登録が行われると、
auth.users
テーブルにユーザー情報が登録される - supabase はユーザーの UUID を含んだ JWT をクライアントに渡す
- クライアントはすべてのリクエストに JWT を含むようにする
- Postgresql は JWT を認識し、どのユーザーからのリクエストかを特定する
- Postgresql は
auth.uid()
という特殊な関数を通じて、SQLレベルで制限することができる
ユーザー管理
supabase は認証のためのエンドポイントを提供する
- ユーザー登録
- ログイン
- パスワード
- パスワードレス・ワンタイムパスワード
- ソーシャル
- ログアウト
ユーザーが登録されると、supabase はユーザーにユニークIDを付与する。この ID はデータベース内のどこからでも参照可能で、他のテーブルと結合して使用できる。

メールアドレス認証についてざっと確認
セットアップ
プロジェクトでメール認証を有効化する (デフォルトで有効化されてる?)
メールアドレスの確認プロセスと、アドレス変更時に新旧両アドレスでの確認プロセスを有効化する。
ユーザー登録
const { data, error } = await supabase.auth.signUp({
email: 'example@email.com',
password: 'example-password',
})
ログイン
async function signInWithEmail() {
const { data, error } = await supabase.auth.signInWithPassword({
email: 'example@email.com',
password: 'example-password',
})
}
ログアウト
async function signOut() {
const { error } = await supabase.auth.signOut()
}

軽く実装してみる
今まではデータベースしか使ってなくて、それも API サーバーからの利用だけだったので、JavaScript SDK を入れるとこから始まる。
インストール
$ yarn add @supabase/supabase-js
クライアントセットアップ
クライアントモジュールを作成して、とりあえずクライアント生成まで実装しちゃう。
import { createClient } from '@supabase/supabase-js'
const client = createClient(process.env['SUPABASE_URL'] || '', process.env['SUPABASE_KEY'] || '', {})
とりあえず supabase の情報は env からとって、オプションは指定せずデフォルトで進める。
ユーザー登録
適当にユーザー登録用関数を提供して試してみる。
export async function signUpByEmail(email: string, password: string) {
const { data, error } = await client.auth.signUp({ email, password })
console.log({ data, error })
return { data, error }
}
これを正当なメールアドレスで呼び出してみると、確認用メールが飛んでくる。この文面は管理画面から変えられるみたい。
supabase のユーザー一覧画面で見ると、UUIDは既に発行されるけど、ステータスが Waiting for verification..
状態になってる。
これをメール内の確認用URLを開くことで、有効化される。有効化されたあとは発行元URLのトップページにリダイレクトされるみたい。
ユーザーログイン
同様にログイン用の関数を用意して
export async function signInByEmail(email: string, password: string) {
const { data, error } = await client.auth.signInWithPassword({ email, password })
console.log({ data, error })
}
先程登録したユーザーを使ってこの関数を呼び出すと、 data
に以下のような内容が含まれてる。
- session
- access_token
- refresh_token
- expires_at
- expires_in
- user
- id

認証済みだとCookieに sb-access-token
が入ってるけど、これは自サイトドメインに保存されてるから、Cookieのドメインが supabase になってても自サイトのAPIに対して送信できるんだっけか?
fetch の credentials オプションあたりの話な気もする。

あー、 getSession()
使えば supabase に対して supabase のCookieを使ってセッション取得できるから、そのレスポンスのトークンを保持してAPIサーバーに投げればいいのかな。

export async function fetchSession() {
const { data, error } = await client.auth.getSession()
console.log(data)
}
これでCookieが保持してるアクセストークンからセッションを取得できるようになった。
オブジェクトでセッション情報が手に入ればあとは API サーバーに投げるだけで大丈夫そう。