Next.js 14 のServer Actions を利用した、Supabaseの匿名認証の最低限な実装

[匿名認証 1 Supabase 匿名認証メモ #認証 - Qiita](https://qiita.com/masakinihirota/items/1eb3c2029d5f0c9049e5)
[匿名認証 2 Next.js 14 のServer Actions を利用した、Supabaseの匿名認証の最低限な実装 #ServerActions - Qiita](https://qiita.com/masakinihirota/items/090b8f699049e3d13ad9)

----------------------------------------

# 匿名認証の参考URL

Anonymous Sign-Ins | Supabase Docs
https://supabase.com/docs/guides/auth/auth-anonymous

Supabaseの匿名認証は、ユーザーに対してメールアドレスもパスワードも、GoogleやGitHub、Apple等のOAuth認証も必要とせず、何のユーザー情報も必要とせずにユーザーをログインさせて、そのウェブサイトの機能を試してもらうための認証方式です。

※30日間で自動削除されます。(公式：予定)
(現在はまだ手動で削除する必要があります。)

通常のユーザーと同じようにログイン出来ます。
同じような操作も可能になりますが、RLSやフラグ(is_anonymous)を使用することで差別化できます。

匿名認証でログインした後、ログアウトしてしまうとアカウント情報が引き継がれません。



# 前提

Next.js 14 のインストール済み
Supabaseのローカル開発環境を構築済み

Dockerを使うとローカルでSupabaseを使えます。
※Supabase CLIをインストールしておく必要があります。

## Next.jsとSupabaseの基本的な使い方の参考

Use Supabase with Next.js | Supabase Docs
https://supabase.com/docs/guides/getting-started/quickstarts/nextjs

👆 Next.jsでのSupabaseの基礎的な使用方法です。



## 例

```terminal
npx create-next-app -e with-supabase [アプリ名]

supabase init
supabase start
supabase status
scoop update supabase ＜＜Supabase CLI更新コマンド

npm run dev

```

止める時

```terminal
supabase stop

```

## 環境変数の設定

```.env
NEXT_PUBLIC_SUPABASE_URL=your-project-url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key

```

👆 Supabaseの環境変数を入力します。

.gitignoreファイルの変更

```.gitignore (一部)
# local env files
.env

```

## RLSとは (参考)

データベース内のどの行へのアクセスを許可するかのポリシー(=ルール)です。

public.usersテーブルがあるとして、今ログインしているのは「ユーザーA」だとします。

そのテーブルには

```
ユーザーA 個人データ メールアドレス等
ユーザーB 個人データ メールアドレス等
ユーザーC 個人データ メールアドレス等

```

の3人が登録されているとして
RLSで現在ログインしているユーザーを許可と設定すると

ユーザーAへアクセスできますが、
ユーザーB、ユーザーCにはアクセスできません。

だから RLS(row 行 level 水平 security 安心、安全)
行単位のセキュリティと名付けられています。



----------------------------------------

# 匿名認証

## ローカル環境での匿名認証の許可

開発環境がローカル環境の場合

```supabase\config.toml
# Allow/disallow anonymous sign-ins to your project.
enable_anonymous_sign_ins = true

```

この設定をtrueにする必要があります。

この設定ファイルを編集した場合、
DockerのローカルSupabaseを再起動させます。

```terminal
supabase stop
supabase start

```

## サーバー環境での匿名認証の許可

開発環境がサーバー環境の場合

サーバーのSupabaseダッシュボードから
左サイドメニューの Project Settingsを選択します。

Settingsメニューが表示されます。
そのCONFIGURATION＞＞Authenticationを選択します。

User Signupsの

```
Allow anonymous sign-ins
Enable anonymous sign-ins for your project.

```

を有効化します。

[Supabaseダッシュボード 匿名認証の場所](https://supabase.com/dashboard/project/_/settings/auth)



----------------------------------------

ここまでが下準備、ここから実際の匿名認証の超基礎的なコード。

# 匿名認証のコード

DBに匿名ユーザーを登録するまでの最低限です。

```app\page.tsx
import Link from 'next/link';

export default async function Index() {
  return (
    <div className="">
      {/* SignInページへのリンク */}
      <Link href="/signin">SignInページへのリンク</Link>
    </div>
  );
}

```

```app\signin\page.tsx
import { anonymousSignIn } from './actions';

export default async function SignIn() {
  return (
    <form action={anonymousSignIn}>
      <button type="submit">SignIn</button>
    </form>
  );
}

```

※React19 Next.js14のサーバーアクションで匿名認証の関数を実行しています。
サーバー関数は👇 app\signin\actions.tsに書かれています。

```app\signin\actions.ts
'use server';

import { createClient } from '@/utils/supabase/server';

// ページのキャッシュを再検証するための関数です。これにより、ページのデータが最新の状態に保たれます
import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';

export async function anonymousSignIn() {
  const supabase = createClient();
  const { data, error } = await supabase.auth.signInAnonymously();
  console.log('data:', data);

  if (error) {
    // エラー時の処理
  }

  // サインイン後にページのデータを最新の状態に更新するために使用されます。
  revalidatePath('/', 'layout');
  redirect('/');
}

```



## 匿名認証部分のコード

👇一部

```app\signin\actions.ts
  const supabase = createClient();
  const { data, error } = await supabase.auth.signInAnonymously();
  console.log('data:', data);

```

Supabaseのクライアントを作り、 匿名認証用の関数を実行して、
console.logでデータを出力させています。

👇出力結果

```terminal
 GET /favicon.ico 200 in 4737ms
data: {
  user: {
    id: 'c4c21c05-b510-4503-9f8d-430ec67f7f32',
    aud: 'authenticated',
    role: 'authenticated',
    email: '',
    phone: '',
    last_sign_in_at: '2024-07-06T19:41:29.122365579Z',
    app_metadata: {},
    user_metadata: {},
    identities: [],
    created_at: '2024-07-06T19:41:29.11744Z',
    updated_at: '2024-07-06T19:41:29.12594Z',
    is_anonymous: true
  },
  session: {
    access_token: 'ey*****10',
    token_type: 'bearer',
    expires_in: 3600,
    expires_at: 1720298489,
    refresh_token: 'fr*****Gg',
    user: {
      id: 'c4c21c05-b510-4503-9f8d-430ec67f7f32',
      aud: 'authenticated',
      role: 'authenticated',
      email: '',
      phone: '',
      last_sign_in_at: '2024-07-06T19:41:29.122365579Z',
      app_metadata: {},
      user_metadata: {},
      identities: [],
      created_at: '2024-07-06T19:41:29.11744Z',
      updated_at: '2024-07-06T19:41:29.12594Z',
      is_anonymous: true
    }
  }
}
 POST /signin 303 in 146ms

```

ローカルのSupabase ダッシュボードから
左サイドメニューのAuthentication
もしくは
左サイドメニューのTable Editor でauthスキーマのusersテーブルを確認すると
匿名のユーザーが一人追加されています。

![匿名ユーザーを一人追加.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44761/7ac3b9f9-6ffc-b269-43bc-654d8eb8f6c3.png)



is_anonymous: が 「TRUE」で匿名認証であることが分かります。

revalidatePath('/', 'layout');
を実行することで、最新の状態に更新されます。

## revalidatePath関数

`revalidatePath()`
👆この関数は状態管理の負担を減らしてくれます。

例えば、いいねボタンを押して、別の遠いい場所のコンポーネントでいいねボタン総カウント数を保存している場所がある場合、これまでは状態管理をする必要がありましたが、
revalidatePathを利用することで、状態管理をせずとも最新の状態になります。

※例えば、孫＞＞子＞＞親＞＞子＞＞孫のコンポーネント間でpropsを渡してバケツリレーをしたり、Reduxなどのライブラリを使いストアをグローバル変数のように使って離れたコンポーネント間で状態の同期を取れるようにしたりします。

なので、サインインをした後に、レイアウト(ログイン情報を表示しているはずの、別の遠いコンポーネント)コンポーネントではまだログインしていない状態のはずですが、revalidatePathを利用することで、指定した場所で最新の情報を読み取りログインした状態になっています。

状態管理は、うまく使えばいいですが、バグの温床にもなる所なので、revalidatePathを使うことで負担が減ります。


このコードでは最終的に

```terminal
POST /signin 303 in 146ms

```

が出力され、ステータスコード303に対応する必要があります。（対応方法は未勉強）

# その他

RLSは有効にしていません。

匿名認証を使用する場合、
hCaptcha
や
Turnstile by Cloudflare
を利用することを公式では強く推奨しています。

ユーザーのセッション(ログイン中)確認は、
getUserメソッドを使います。

```TypeScript
const { data: { user } } = await supabase.auth.getUser()

```

サーバ上でユーザの認可をチェックする場合は、常にこのgetUserメソッドを使用する必要があります。クライアントでは、getSession().session.user を使用することで、より高速な結果を得ることができます。

https://supabase.com/docs/reference/javascript/auth-getuser

機械的にユーザーを作成するようなBOTなどを弾くセキュリティ機能です。

応用として、メール認証やOAuth認証でも認証した場合、匿名認証の時の入力データを引き継がせるような設定も可能です。
※自身でコードを書く必要があります。



# 参考

Supabase 匿名認証メモ #認証 - Qiita

https://qiita.com/masakinihirota/items/1eb3c2029d5f0c9049e5

Supabase公式の匿名認証

https://github.com/supabase/supabase/blob/master/apps/docs/content/guides/auth/auth-anonymous.mdx

【Supabase】Flutter + Supabase Authentication のAnonymous Sign-insを使って匿名サインインのサンプルを作ってみた - 狐好きぷろぐらまー

https://pregum-fox.hatenablog.jp/entry/2024/05/07/043423

👆設定の説明が画像付き解説






※ 以下、未調査

# 匿名認証したユーザーがOAuth認証(GoogleやGitHub、Facebook、Slack等)に切り替える方法

Anonymous Sign-Ins | Supabase Docs
https://supabase.com/docs/guides/auth/auth-anonymous#sign-in-anonymously

## 自動

複数のOAuth認証をした場合、メールが同じだと自動でLinkされる。
匿名認証の場合は不明。

## 手動

手動の設定をONにします。

```typescript
const { data, error } = await supabase.auth.linkIdentity({ provider: 'google' })
```

このコードを実行すると匿名認証していても、Google認証画面が立ち上がり、認証の手続きが開始されるようだ。