# ログインを追加する

## メールアドレスとパスワード

```
POST /auth/v1/signup          {"email":"...","password":"..."}
POST /auth/v1/token?grant_type=password   {"email":"...","password":"..."}
GET  /auth/v1/user            Authorization: Bearer <access_token>
POST /auth/v1/logout
```

パスワードは10文字以上が必要で、過去に漏えいしたことのあるパスワードは拒否されます。

## ソーシャルログイン

11のプロバイダーに対応しています: Google、Kakao、Naver、Apple、GitHub、GitLab、Microsoft、Facebook、LINE、Discord、X。
`list_auth_providers` が、今何が設定されているかと、まだ設定していないものまですべて返します。

設定はツールだけで完了します。コンソールに入る必要はありません。

```
list_auth_providers   現在の状態とコールバックアドレス
set_auth_provider     クライアント ID とシークレットを保存して有効にする
set_auth_provider_enabled   認証情報は残したまま無効化・有効化
remove_auth_provider  認証情報ごと削除
list_redirect_urls    ログイン後に戻れるアドレス
add_redirect_url      戻り先アドレスを許可
remove_redirect_url   戻り先アドレスを削除
```

順序が重要です。**プロバイダーのコンソールに、先にコールバックアドレスを登録してください。**
`list_auth_providers` の `callback_url` がそのアドレスです。1文字でも違うとプロバイダーがログイン全体を
拒否しますが、そのとき表示されるエラーには、このプラットフォームのことも何が間違っていたのかも書かれていません。

そして、**戻り先アドレスを登録しないと、ログインはできてもどこにも移動しません。**
`add_redirect_url` で追加します。スキーム、ホスト、ポートがすべて一致する必要があり、パスは見ないため、
`https://myapp/done` を追加すると `https://myapp` が保存され、その下のどのパスにも戻れます。
先頭部分だけを比較すると `https://myapp.evil.test` が通ってしまうため、オリジン全体を比較します。

ログインはできるのにどこにも移動しない場合は、まず `list_redirect_urls` を確認してください。たいていはそこにないアドレスです。
開発用に追加した `http://localhost` などは、リリース前に `remove_redirect_url` で外しておくほうがよいでしょう。

```
GET /auth/v1/authorize?provider=kakao&redirect_to=https://myapp/done
```

完了すると `redirect_to` に戻り、トークンは**アドレスの # の後ろ**に付いて届きます（クエリ文字列ではありません）。
クエリ文字列はプロキシのログや Referer ヘッダーに残りますが、# の後ろはサーバーに送信されないからです。

```js
const token = new URLSearchParams(location.hash.slice(1)).get('access_token')
```

## 自分で登録するプロバイダー（custom:）

上の 11 か所のほかに、OAuth 2.0 / OpenID Connect プロバイダーを `custom:<名前>` として登録できます（Supabase と同じ名前）。同じプロジェクトの組み込みの Kakao 枠をほかのアプリが使っているとき、`custom:kakao-xts` のように 2 つ目の Kakao アプリを置くのにも使います。

```
set_auth_provider  provider: "custom:kakao-xts", client_id, client_secret,
                   authorize_url, token_url, userinfo_url（または issuer だけ）,
                   scopes, claim_map: {"id":"id","email":"kakao_account.email",
                   "email_verified":"kakao_account.is_email_verified","phone":"kakao_account.phone_number"}
```

- アドレスは公開された https だけです。`claim_map`（最初の名前 `claims` も受け付けます）を省くと OpenID の標準クレーム（sub、email、email_verified、name、picture、phone_number）を読みます。
- `provider_type: "oidc"` なら、id_token の署名・発行者・対象（client_id）・有効期限・nonce を issuer が公開する鍵（`jwks_uri`、省くと issuer の設定ドキュメントから探します）で確認してから読みます。アプリが受け取った id_token で `signInWithIdToken({ provider: 'custom:<名前>', token })` もできます。省くと `oauth2`（既定）で、`claim_map` で読みます。
- `token_auth` はシークレットを本文（`body`、既定）で送るか HTTP Basic（`basic`）で送るか、`pkce` は既定でオンです。`email_optional: true` ならメールアドレスのないユーザーも受け付けます。
- コンソールのログイン / プロバイダー画面のカスタムプロバイダー、`list_custom_providers`・`create_custom_provider`（Supabase の形）、`set_auth_provider`、CLI はすべて同じプロバイダーを見て変更します。プランごとに置ける数が決まっています（Free 3、Pro 10）。
- アプリではいつもどおり `supabase.auth.signInWithOAuth({ provider: 'custom:kakao-xts', options: { scopes: 'phone_number' } })` です。`scopes` は登録した scope に追加されます。
- セッションにはプロバイダーのトークンが `provider_token`（と `provider_refresh_token`）として一度だけ載ります。それでプロバイダーの API（たとえば Kakao の電話番号）を呼びます。このプラットフォームは PKCE の交換が終わるまで（最大 10 分）暗号化して保持し、その後削除します。
- 既存のアカウントにつなげるのは、**プロバイダーがメールアドレスを確認したと言うときだけ**です（`email_verified` の対応）。
- Supabase と同じく `/auth/v1/settings` には表示されません。CLI: `oneend auth providers set custom:<名前> ...`。

## メールアドレスのないアカウント

Kakao、Naver、GitHub、GitLab、Facebook、LINE、X は、メールアドレスなしで戻ってくることがあります。
メールアドレスを必須として扱うコードは、韓国ではほとんどのログインを拒否してしまいます。

同じ人の既存アカウントへのひも付けも、**プロバイダーがメールアドレスを確認済みだと言うときだけ**行います。
未確認のメールアドレスでひも付けると、他人のメールアドレスを入力した人がそのアカウントを乗っ取れてしまいます。

## 匿名ログイン

`set_anonymous_sign_in` で有効にすると、メールアドレスなしですぐにセッションを受け取れます。現在の状態は
`list_auth_providers` の `anonymous_sign_in` です。匿名ユーザーも `<ref>_authenticated` ロールを持つため、
アクセスルールで区別するには、トークンの `is_anonymous` を見てください。

## SMS認証コードでのログイン

電話番号で登録・ログインするにはSMSアカウントが必要です。`get_sms_settings` で今の設定を確認し、
`set_sms_settings` で設定します。自分で契約したSolapi(CoolSMS)、NHN Cloud、Aligo、Twilioのアカウントを使うか、
有料プランならプラットフォームのアカウント(`platform`、1通 ₩100、韓国のみ)を選びます。削除は `remove_sms_settings` です。

```js
await supabase.auth.signInWithOtp({ phone: '010-1234-5678' })
await supabase.auth.verifyOtp({ phone: '010-1234-5678', token: '123456', type: 'sms' })
```

番号はGoTrueと同じく国番号からの数字だけ(`821012345678`)で保存し、`010` で始まる番号は韓国の番号として読みます。
1つの番号には60秒に1回、1時間に5回、1つのIPアドレスからは1時間に20回まで送ります。誤った認証コードを5回入力すると、
そのコードは使えなくなります。頻繁すぎる要求は `over_sms_send_rate_limit`(429)で拒否されます。
`allowed_countries` は既定で `82` のみです。海外の有料番号にSMSを送らせる詐欺がよくあるため、使わない国は入れないでください。

## 認証メール: 送信アドレス（SMTP）とテンプレート

登録確認・パスワード再設定・ログインリンク・メールアドレス変更・招待のメールは、既定ではプラットフォームの送信アドレスから、
リクエストの言語（韓国語・英語・日本語）の既定の文面で送られます。自分のアドレスから送るには `set_smtp_settings`（Supabaseの
SMTP Settingsと同じ項目: sender_email、sender_name、host、port、username、password、minimum_interval_seconds）で設定し、
`test_smtp_settings` でテストメールを送ります。`get_smtp_settings` はパスワードを表示せず、末尾4文字だけを表示します。
そのサーバーが拒否した場合はプラットフォームのアドレスから代わりに送り、理由を `last_error` に残します。削除は
`remove_smtp_settings` です。1時間の上限（`rate_limit_per_hour`）や同じアドレスへの間隔を超えると
`over_email_send_rate_limit`（429）で拒否されます。

文面は `get_email_templates` で確認し、`set_email_template` で種類ごと（confirmation、invite、magic_link、recovery、
email_change、reauthentication）に件名とHTML本文を設定します。Supabaseで使っていたテンプレートをそのまま貼り付けられます:
`{{ .ConfirmationURL }}`、`{{ .Token }}`、`{{ .TokenHash }}`、`{{ .SiteURL }}`、`{{ .Email }}`、`{{ .NewEmail }}`、
`{{ .RedirectTo }}`、`{{ .Data.<key> }}`。保存前に `preview_email_template` で確認し、既定の文面に戻すには
`remove_email_template` を使います。`{{ .SiteURL }}` は最初のリダイレクトアドレスのオリジンです。reauthenticationはまだ送信しません。

```js
await supabase.auth.signUp({ email: 'user@example.com', password: '…' })
await supabaseAdmin.auth.admin.inviteUserByEmail('new@example.com', { redirectTo: 'https://shop.example/welcome' })
```

## SAML SSO

会社のIdP(Okta、Microsoft Entra ID、Google Workspaceなど)でログインできるようにします。`list_sso_providers` が
IdPに入力する `acs_url` と `entity_id` を返します。`add_sso_provider` にIdPのメタデータ(URLかXML)と
メールドメインを渡すと登録され、`update_sso_provider` で変更し、`remove_sso_provider` で削除します。
新しいIdPはエンタープライズプランでのみ追加できます(`list_sso_providers` の `new_providers_allowed`)。
登録済みのIdPはプランに関係なくそのまま動作し、変更もできます。

```js
const { data } = await supabase.auth.signInWithSSO({ domain: 'example.com' })
location.href = data.url
```

応答かアサーションの少なくとも一方がIdPの証明書で署名されている必要があり、このプロジェクトが送った要求への応答だけを受け付けます。
IdPから始めるログインは受け付けません。SSOで初めてログインした人には新しいアカウントが作られ、同じメールアドレスの既存のアカウントとは統合されません。

## ログイン設定をまとめて(サインイン / プロバイダー)

コンソールのサインイン / プロバイダー画面と同じ設定を、Supabase の auth config のキーで扱います。`get_auth_config` がすべてを小文字のキーで返し
(`external_google_client_id`、`sms_provider`、`saml_enabled` など)、`update_auth_config` が指定したキーだけを変えます。
有効にしたプロバイダーに必要な値がないか形式が誤っていれば何も保存せず、キーごとに理由を返します。シークレットは `••••abcd` のように
末尾4文字だけが見え、そのまま返せば変わりません。自分で定義した OAuth・OIDC プロバイダーは `list_custom_providers`、
`create_custom_provider`、`update_custom_provider`、`set_custom_provider_enabled`、`remove_custom_provider` で、
Firebase・Clerk・WorkOS・Auth0・Cognito のトークンは `list_third_party_auth`、`add_third_party_auth`、`remove_third_party_auth` で扱います。

```json
{ "external_kakao_enabled": true, "external_kakao_client_id": "REST_API_KEY", "external_kakao_secret": "CLIENT_SECRET",
  "external_kakao_scopes": "profile_nickname,account_email,gender", "external_kakao_email_optional": true }
```

カカオの同意項目(`external_kakao_scopes`)はそのまま scope として送り、受け取った値はユーザーの `user_metadata` に保存します。
NAVER は読める情報を NAVER Developers で決めるので、`external_naver_scopes` は保存する項目を選びます。

## 登録ユーザーの削除

`remove_user { ref, id, confirm }` が登録ユーザーを1人、完全に削除します。id は `list_users` が返した UUID で、confirm にはそのユーザーのメールアドレス（または id）をそのまま入れます。
元に戻せないため、ユーザーがはっきり求めたときだけ使ってください。削除の記録は監査ログ（access）に残ります。

## 登録ユーザーの一括インポート

`import_users { ref, users: [{email, id?, encrypted_password?, email_confirmed?, user_metadata?, app_metadata?}], dry_run? }` が、別のサービス（例: Supabase の `auth.users` エクスポート）の登録ユーザーを一度に500人まで取り込みます。
`id` と bcrypt ハッシュ（`$2a$`、`$2b$`）はそのまま保たれるため、パスワードを決め直す必要はありません。平文のパスワードは受け付けません。先にリスト全体を検査し、誤りのある行は番号で示して何も取り込みません（`dry_run: true` は検査のみ）。
すでにあるアカウント（同じ id またはメール）はスキップして `skipped` と数えるので、同じ呼び出しを繰り返しても安全です。REST は `POST /v1/projects/<ref>/users/import {users, dry_run}` です。

## 登録ユーザーの変更とログインリンク

`update_user { ref, id, ... }` が登録ユーザー1人のメールアドレス、パスワード（`password`）、メールの確認（`email_confirm`）、利用停止（`ban_duration`。例: `24h`、解除は `none`）、メタデータを変更します。
パスワードを変えるか停止すると、その人のログインはすべて切れます。パスワードのルールは登録時と同じです。
`generate_link` は招待（`invite`）、パスワードなしのログイン（`magiclink`）、パスワードの再設定（`recovery`）、登録の確認（`signup`）のリンクをメールを送らずに作り、`action_link` として返します。
開いた人はそのユーザーとしてログインするので、求めた人にだけ渡してください。`redirect_to` は `add_redirect_url` で登録したアドレスだけです。
REST では `PATCH /v1/projects/<ref>/users/<id>` と `POST /v1/projects/<ref>/users/<メール>/links {type, password, data, redirect_to}` です。

## テーブルから見たログイン中のユーザー

`auth_<ref>.uid()` がユーザーの識別子を返します。アクセスルールの中で使います。

## パスキー、2段階認証、OAuth アプリ

```
get_passkey_settings     ユーザーのパスキーでのサインインの設定
set_passkey_settings     オン・オフ、RP 名、RP ID、許可するオリジン
list_user_factors        ユーザー1人の2段階認証の手段とパスキー
remove_user_factors      2段階認証をすべて削除 （confirm にメールアドレス）
remove_user_passkey      パスキーを1つ削除
list_oauth_apps          このプロジェクトでサインインする他のアプリとアドレス
create_oauth_app         アプリを登録 （client_secret は一度だけ表示）
reset_oauth_app_secret   シークレットを作り直す
remove_oauth_app         アプリの登録を削除 （confirm にアプリ名）
```

OAuth サーバーのオン・オフ、登録の許可、利用停止は `update_auth_config`（`OAUTH_SERVER_ENABLED`、`DISABLE_SIGNUP`）と `update_user`（`ban_duration`）で行います。パスワード再設定のメールの代わりに、`generate_link` の recovery リンクを直接渡してください。

---

https://end.blackall.ai/docs/auth?lang=ja
