Closed11

Supabaseに入門する

keitaknkeitakn

概要

認証基盤としてSupabaseがどの程度使いやすいか?を調査する。

サインアップ、サインイン、GoogleなどのOpenID プロバイダーによる認証をどのように実装していくか調査する。

keitaknkeitakn

アカウント作成

https://supabase.com/dashboard/sign-up

GitHubアカウントで登録が出来るので登録する。

プロジェクト作成

next-supabase-examples という検証用プロジェクトを作ったので、とりあえずこれと同じ名前でプロジェクトを作成する。

必要事項を入力、特に難しくはない。

Planはとりあえずフリーを選択。

create-project

keitaknkeitakn

公式の Supabase Auth with the Next.js App Router を参照しながら進める

以下のガイドに従って進めていく。

https://supabase.com/docs/guides/auth/auth-helpers/nextjs

npx create-next-app -e with-supabase とするとプロジェクトの初期構成を作ってくれるようだ。

ただしこの生成方法で生成されたテンプレートにはいくつか問題があったので通常の create-next-app で生成されたファイルからマージしている。

  • ESLintの設定がなかったので追加(本家の create-next-app の結果にはある)
  • パッケージのバージョンが常に latest になっていたりするので、こちらはバージョン固定をした
  • tsconfig.jsonpaths の設定が消えてしまったいたので create-next-app の結果と同じ設定に変更

具体的な変更内容は以下のPRにまとめている。

https://github.com/keitakn/next-supabase-examples/pull/3

keitaknkeitakn

Supabase のアクセストークンをAPI認証として利用出来るか?

ServerComponents内で以下のようにすると、JWT形式のアクセストークンを取得出来た。

  const supabase = createServerComponentClient({ cookies })

  const token = await supabase.auth.getSession();

  console.log(token);

ちなみにpayloadの形は以下のような形。

{
  "aud": "authenticated",
  "exp": 1689956112,
  "iat": 1689952512,
  "iss": "https://https://xxxxxxx.supabase.co/auth/v1",
  "sub": "UUIDv4形式",
  "email": "keita@exmple.com",
  "phone": "",
  "app_metadata": {
    "provider": "email",
    "providers": [
      "email"
    ]
  },
  "user_metadata": {},
  "role": "authenticated",
  "aal": "aal1",
  "amr": [
    {
      "method": "password",
      "timestamp": 1689952512
    }
  ],
  "session_id": "UUIDv4形式"
}

このJWTを検証すれば、自作で作ったAPIサーバー等でもこの値を認証・認可に利用出来そう。

keitaknkeitakn

Supabaseのアクセストークンを検証

以下は jose を使って検証を実施している例。(実行しているのはAppRouterのServerComponents内)

import { cookies } from 'next/headers';
import * as jose from 'jose';

const supabase = createServerComponentClient({ cookies });

const token = await supabase.auth.getSession();

const secretKey = new TextEncoder().encode(
  'https://supabase.com/dashboard/project/{自分のプロジェクトID}/settings/api 内からコピーしたJWT Secretを設定',
);

const jwtVerifyResult = await jose.jwtVerify(String(token.data.session?.access_token), secretKey, {issuer: 'https://https://{自分のプロジェクトID}.supabase.co/auth/v1'});
console.log(jwtVerifyResult);

もちろん自作したAPIサーバー内でも標準的な機能を備えたJWT用ライブラリを使って検証を実施出来る。

例えばPython + LangChain + FastAPIで作成した自作API内でもSupabaseのアクセストークンを利用してアクセス制御を行う事が可能。

keitaknkeitakn

Googleログインの実装

以下を見ながら実装していく。

https://supabase.com/docs/guides/auth/social-login/auth-google

Supabase の以下のURLからGoogleによる認証を有効化する。

project-id には実際のプロジェクトIDが入る。

https://supabase.com/dashboard/project/{project-id}/auth/providers

管理画面のメニューの Authentication → Providers から遷移する。(UIが変わる可能性はアリ)

OAuthの情報を入力する必要があるので、Google CloudのAPIとサービス → 認証情報から OAuth 2.0 クライアント ID を作成する。(Callback URLは後でGoogle Cloud側で使うのでコピーしておく)

https://console.cloud.google.com/apis/credentials

Client IDとClient Secretをコピーして Supabase 側で以下の情報を入力して事前準備完了。

  • Client ID (for OAuth)
  • Client Secret (for OAuth)
  • Authorized Client IDs これは空でOK。
keitaknkeitakn

以下は実装時のPR。

https://github.com/keitakn/next-supabase-examples/pull/6

最初はReactServerComponents内で supabase.auth.signInWithOAuth を使ってGoogleログインの実装を試みたが invalid request: both auth code and code verifier should be non-empty というエラーが発生してしまい上手くいかなかった。

これはissueが報告されており、どうやらNext.js側の問題らしい。

https://github.com/supabase/auth-helpers/issues/545

仕方がないのでGoogleログイン用のButtonComponentを作って onClick 時に実行する方法を取った。

ちなみに認証成功時のアクセストークンのpayloadは以下のような感じだ。

emailログインの時よりも情報量が多くpayloadからGoogleアカウントの情報が取得出来るようになっている。

{
  "aud": "authenticated",
  "exp": 1690123901,
  "iat": 1690120301,
  "iss": "https://https://xxxxxxx.supabase.co/auth/v1",
  "sub": "UUIDv4形式",
  "email": "Gmailのメールアドレス",
  "phone": "",
  "app_metadata": {
    "provider": "google",
    "providers": [
      "google"
    ]
  },
  "user_metadata": {
    "avatar_url": "アバター画像のURL",
    "email": "Gmailのメールアドレス",
    "email_verified": true,
    "full_name": "ユーザー名のフルネーム",
    "iss": "https://accounts.google.com",
    "name": "ユーザー名",
    "picture": "エンドユーザーの画像URL",
    "provider_id": "数値(型は文字列)",
    "sub": "数値(型は文字列)"
  },
  "role": "authenticated",
  "aal": "aal1",
  "amr": [
    {
      "method": "oauth",
      "timestamp": 1690120301
    }
  ],
  "session_id": "UUIDv4形式"
}
keitaknkeitakn

ちなみに先にメールアドレスとパスワードで登録して同じGmailのアカウントを使ってGoogleログインを実行すると同じユーザーと見なしてくれる。

逆に先にGoogleログインで登録して後でパスワードとemailで登録しようとすると、重複していると見なされてしまい、登録APIでエラーが発生してしまった。

同一のメールアドレスで登録している他のソーシャルアカウント(例えばFacebookとか)で登録を試みた場合はどうなるのかが気になる。

keitaknkeitakn

細かい問題

無料版だとGoogleログイン時の同意画面に自分の supabase のプロジェクトのDomainが表示されてしまう。

ここを独自Domainにしたい場合は有料プランを契約してCustomDomainsの設定をする必要がある。

keitaknkeitakn

Supabaseの良いと思った点

  • 安いのに高機能、認証基盤としての機能は十分で認証の履歴等も閲覧可能
  • パスワードレス認証(マジックリンクによるログインやSMS認証に対応)に対応している
  • ドキュメントが充実している
  • RDBが付いているのは嬉しい

Supabaseのちょっと残念なところ

  • 対応しているソーシャルログインが少ない + B to B向けの物が多い

ちなみに対応しているソーシャルログインは以下の通り。

今後実装される可能性もあるが現状だとカスタムAuthもないので、LINEログイン等は実装出来ない。

今回は要件としてLINEログインが必須だったので残念ながら不採用とした。

keitaknkeitakn

とは言えサービスのポテンシャルはかなり感じたので、今後別の機会にいつか使ったみたい。

このスクラップは2023/07/31にクローズされました