---
title: "認証"
description: "利用者（API の名前では players = プレイヤー）は、登録する前にアプリやゲームを使い始められます。`Kumo.init()`は初回の訪問でゲストを作り、以後の訪問ではそのゲストを復元します。"
url: "/ja/docs/guides/auth/"
lang: ja
index: "/ja/llms.txt"
---
# 認証

利用者（API の名前では players = プレイヤー）は、登録する**前に**アプリやゲームを使い始められます。`Kumo.init()`は初回の訪問でゲストを作り、以後の訪問ではそのゲストを復元します。データを残したくなった利用者は、同じアカウントにメール・Google・Discord・Apple・Xを連携し、すべてをそのまま引き継げます。

## ゲスト（自動）

```js
const kumo = await Kumo.init({ projectKey: 'pk_live_…' });
kumo.auth.player;        // { id, displayName, isGuest: true, createdAt, identities: [{ provider: 'guest' }] }
await kumo.auth.setDisplayName('Ace');   // ルームで表示される名前
```

先に自分のログイン画面を見せたい場合は、`init`に`autoGuest: false`を渡します。セッションは公開鍵ごとに`localStorage`へ保存されるので、devとprodが混ざることはありません。ストレージが使えない場合（プライベートモードや一部のiframe）、SDKはメモリで代用し、利用者は再読み込みのたびに新しいゲストになります。

## メールとパスワード

```js
// 今のゲストを永続アカウントにする — データはそのまま
await kumo.auth.linkEmail('ace@example.com', 'correct horse battery');

// 別の端末で
await kumo.auth.signInWithEmail('ace@example.com', 'correct horse battery');

// ゲストを経ずに新しいアカウントを作る
await kumo.auth.signUpWithEmail('ace@example.com', 'correct horse battery', 'Ace');

await kumo.auth.signOut();
kumo.auth.onChange((player) => renderAccount(player));   // ログアウト時は null
```

パスワードは8文字以上が必要です。ログインのエラーから、そのメールが登録済みかどうかが漏れることはありません。

## 登録画面の「KUMODeckからのお知らせ」の欄（既定で出ます）

メールアドレスの登録画面には、KUMODeckが描く欄がもう1つ出ます（「KUMODeckからのお知らせを受け取る」＝新しいゲームや機能のご案内・月2回まで）。既定で出る欄なので、**自分で登録画面を作る場合も画面に入れてください。** アプリごとに外すこともできます（下）。

```js
const news = await kumo.news.mountNewsOptIn(document.querySelector('#news-box'));  // 欄を描く（出さない場合は何も描かない）
await kumo.auth.signUpWithEmail(email, password);
await news.submit(email);   // 利用者がチェックしていなければ何もしない
```

あなたにも利用者にも困らない理由:

- **利用者が選べます。** 別のチェックボックスで、**最初はチェックなし**です。チェックしなくても登録は同じようにできます。
- **確認メールで決まります。** KUMODeckの確認メールのリンクを押すまで、何も送られません。
- **あなたに費用はかかりません。** この名簿の費用はKUMODeckが持ち、あなたの請求には載りません。
- 大人にだけ出ます。子ども向けのアプリやゲーム（`audience: "kids"`）では出ません。欄の文言（KUMODeckの名前と「このアプリを動かしているサービス」）は正しさを保つためにKUMODeckが描くので、自分で書かないでください。

この欄を自分のアプリから外すには、ダッシュボード（概要 →「KUMODeckからのお知らせの欄」）でOFFにするか、`kumo.config.json`で外して反映します:

```sh
kumodeck features off kumoNews && kumodeck config push --env production   # "kumoNews": { "optIn": false } を書きます
```

コードはそのままで大丈夫です。外すと`mountNewsOptIn`は何も描かず、`submit`も何もしません。すでに登録した利用者には、本人が止めるまでKUMODeckのお知らせが届きます。もう一度出すには`kumodeck features on kumoNews`。

## セッション

| トークン | 有効期間 | 保存先 |
|---|---|---|
| アクセストークン（JWT） | 15分 | メモリ + `localStorage` |
| リフレッシュトークン | 90日、**使うたびに回転** | `localStorage` |

トークンを自分で扱う必要はありません。SDKは`token_expired`で更新し、1回だけ再試行します。同じアプリのタブ同士はWeb Locks APIで調整するので、2つのタブが同じリフレッシュトークンを取り合うことはありません。使用済みのリフレッシュトークンを再提示すると、セッションの系列ごと失効します（盗まれたトークンは、どちらかが再び使った時点で無効になります）。

サインアウトで止まるのはリフレッシュトークンです。すでに渡したアクセストークンは期限まで、**最大15分**使えます（署名だけで確かめ、DBを引かないため）。SDKはサインアウトで捨てるので、トークンが別の所に写されたときだけ関係します。BANは利用者の**書き込みをすぐに**止めます。読むのは次のリフレッシュまで（最大15分）使え、そのリフレッシュが失敗してサインアウトになります。

## Google・Discord・Apple・Xでログイン

どのプロバイダーも、`kumo.config.json`で**ONにするまでは無効**です。

```json kumo.config.json
{
  "auth": {
    "redirectUrls": ["https://mygame.example.com/*"],
    "providers": {
      "google": { "enabled": true },
      "x": { "enabled": true }
    }
  }
}
```

| プロバイダー | 用意するもの |
|---|---|
| X | 自分のXアプリを登録し、そのOAuth 2.0のClient IDとClient Secretをダッシュボードの**サインイン方法**→ Xに入力します。手順は[自分のXアプリ](#自分のxアプリ)を参照してください |
| Google・Discord・Apple | プロバイダー側でOAuthクライアントを作り、client idとsecret（Appleの場合はServices ID、Team ID、Key ID、秘密鍵）をダッシュボードの**サインイン方法**に入力します。登録するリダイレクトURIはそこに表示されます（`…/v1/auth/oauth/<provider>/callback`）。認証情報は暗号化して保存され、設定ファイルに入ることはありません |

```js
// 既定はポップアップ — クリックのハンドラから呼ぶ
await kumo.auth.signInWithProvider('google');
await kumo.auth.signInWithProvider('discord', { mode: 'redirect' });   // アプリ内ブラウザ向け
await kumo.auth.linkProvider('apple');                                 // 今のゲストに連携し、進捗を残す

// リダイレクトの後、結果は Kumo.init() が受け取る。読むには:
const result = await kumo.auth.completeRedirectSignIn();   // { player, provider, linked } または null
```

- **戻り先URLは`auth.redirectUrls`で許可したものだけ**です（完全一致のURL、またはパスの前方一致を表す末尾の`*`）。development中のlocalhostと、KUMODeckがホスティングするURLは自動で許可されます。これにより、攻撃者が自分のサイトでログイン用のコードを受け取ることを防ぎます。
- OAuthはPKCEを使います。URLのフラグメントで戻ってくるコードは、利用者のブラウザにしか存在しないverifierがなければ役に立ちません。
- アカウントがメールアドレスで統合されることはありません。すでに別の利用者に属しているIDを連携しようとすると、`identity_already_linked`で失敗します。
- Xのアプリ内ブラウザでは、ポップアップが安定しないため、既定のモードは`redirect`です。
- `kumo.auth.unlinkIdentity(provider)`はログイン手段を外します（身に覚えのない連携を消すときなど）。この端末以外はすべてログアウトされます。**最後のログイン手段は外せません**: 外した後にほかのプロバイダーもパスワード付きのメールも残らない場合は、409 `last_login_method`で失敗し、何も変わりません。外せてしまうと、この端末がログアウトした時点でアカウント（セーブも）に二度と入れなくなるためです。利用者には、先に別の手段を付けてから（`linkEmail` / `linkProvider`）古いほうを外すよう案内してください。

  ```js
  try {
    await kumo.auth.unlinkIdentity('google');
  } catch (e) {
    if (e instanceof KumoError && e.code === 'last_login_method') showHint('先に別のログイン手段を追加してください');
    else throw e;
  }
  ```

### 自分のXアプリ

Xでログインは、ほかのプロバイダーと同じく、自分で登録したXアプリで動きます。登録して保存するまでは、Xでログインを始めると409 `provider_not_configured`で失敗します。作業は10〜20分で、アプリ（またはスタジオ）ごとに1回です。

1. [console.x.com](https://console.x.com)に自分のXアカウントでサインインし、Developer AgreementとPolicyに同意して、用途を入力します（例:「自分のWebアプリの利用者がXのアカウントでログインするため。公開プロフィールを読むだけで投稿はしない」）。
2. 従量課金（Pay Per Use）の**本番（Production）環境**でアプリを作ります。旧Free / 開発環境のアプリはXに拒否されます（403 `client-not-enrolled`）。
3. 名前・説明・アイコンはアプリの名前でかまいません（Xの同意画面にこの名前が出ます）。どれにも「X」「Twitter」やXのロゴを入れないでください。
4. **User authentication settings**でOAuth 2.0を有効にし、種別は**Web App**（confidential client）にします。PKCEはKUMODeckが付けるので、設定はいりません。
5. スコープは`users.read`と`tweet.read`だけにします。
6. コールバックURLには、ダッシュボードの**サインイン方法**→ Xに表示される`…/v1/auth/oauth/x/callback`のURLを、表示どおり完全一致で登録します。
7. Webサイト・利用規約・プライバシーポリシーのURLを入力します。プライバシーポリシーには、Xから受け取る情報（@handle・名前・アイコン・XのユーザーID）を書いてください。
8. OAuth 2.0の**Client IDとClient Secret**を生成し（Secretは一度しか表示されません）、ダッシュボードの**サインイン方法**→ Xに貼り付けて保存したら、`auth.providers.x.enabled`をONにします。

XのAPIの利用分は、Xから自分のXアプリに直接請求されます。KUMODeckは請求しません。Xによると、ログインのために利用者本人のプロフィール（`/2/users/me`）を読むのは無料です。

Xのログインの規則は、アプリの画面にもかかります。Xでログインのボタンをほかのログイン方法と少なくとも同じくらい目立たせ、ログイン後に本人のXの@handle・アイコン・Xのロゴを見せ（`kumo.x.profile()`）、ログインの前にプライバシーポリシーへのリンクを見せてください。

KUMODeckの共用Xアプリが使える環境では、ダッシュボードに自分のアプリの代わりに**設定なしで使う**の選択肢も出ます。[Xで共有する](/ja/docs/guides/sharing/index.md#xでログイン)を参照してください。

## メールの確認とパスワードの再設定

メールのリンクはアプリのページに戻ってきます（そのページは`auth.redirectUrls`に入っている必要があります）。`redirectUrl`を渡さない場合、リンクはアプリのドメインにある小さなブランドなしの組み込みページ（`/.auth/verify`、`/.auth/reset`）を開き、そこで処理を終えます。アプリのURLを渡せば、自分の画面で処理できます。

```js
await kumo.auth.resendVerification();                     // ログイン中のメールアカウント向け
await kumo.auth.sendPasswordReset('ace@example.com');     // 常に成功する（メールの有無を明かさない）

// 再設定メールからページが開かれたとき:
const pending = kumo.auth.pendingAction();                // { type: 'reset_password', token } または null
if (pending) await kumo.auth.resetPassword(pending.token, newPassword);
```

確認リンクは24時間、再設定リンクは30分有効で、それぞれ一度だけ使えます。パスワードを再設定すると、利用者はすべての端末でログアウトされます。メールが確認済みかどうかは`kumo.auth.player.emailVerified`でわかります。

## データのエクスポートとアカウントの削除

アプリストアはアプリ内でのアカウント削除を求め、プライバシー関連の法律は利用者に自分のデータの写しを受け取る権利を与えています。両方を設定画面に置いてください。

```js
const data = await kumo.account.exportData();       // このプレイヤーについて保存しているすべて（JSON。パスワードのハッシュは含まない）
await kumo.account.delete({ password });            // 取り消せない。ゲストはパスワードの代わりにセッションで確認する
```

削除すると利用者のデータは消えます。`auth.exportData()`と`auth.deleteAccount()`は同じ関数です。自分のサーバー（`/v1/admin/players/:playerId/export`、`/v1/admin/players/:playerId/delete`）やダッシュボードからも、利用者のデータのエクスポートや削除ができます。

## プレイヤーのBAN

ずるをしたり、ほかの利用者に嫌がらせをしたりする利用者をBANできます。BANすると、その利用者はすべての端末でログアウトされ、ルームの接続もすぐに切れ、ログインと書き込みを断られます。BANはいつでも使えます。オンにする機能はありません。

| どこから | 方法 |
|---|---|
| ダッシュボード | **プレイヤー** → 対象の利用者 → **プレイヤーをBAN**／**BANを解除** |
| AIエージェント（MCP） | `player_ban`／`player_unban`（毎回あなたに確認します） |
| 自分のサーバーやFunctions（秘密鍵） | `POST /v1/admin/players/:playerId/ban` `{ reason?, durationHours? }` · `POST /v1/admin/players/:playerId/unban` `{}` |

自分のサーバーやFunctionsからは、`durationHours`（0より大きく8784＝366日まで）を付けると、その時間でBANが自動的に解けます。付けなければ、解除するまで続きます。`reason`（500文字まで）はあなたのためのメモで、ダッシュボードと監査ログに出ます。応答は`{ player: { id, banned, bannedAt, banReason, revokedSessions, banExpiresAt } }`です。存在しない利用者や別の環境の利用者は404 `player_not_found`になります。公開鍵ではBANできません。

BANは利用者ごとに1つだけです。どこで掛けたBANも、ほかのどこからでも解除できます。ずるを見つけたその場で、自分のコードからBANできます。Functionsのスターターでは、`src/kumo.ts`の`players.ban()`／`players.unban()`がこの呼び出しをします（[Functions](/ja/docs/guides/functions/index.md)）。

### BANへの異議申し立て

BANした利用者は、BANの見直しを1回だけ求められます（BAN 1件につき1回）。BANへの異議申し立てはBANの一部なので、いつでも使えます。アプリの中では、BANを読んで異議を送ります。

```js
const { sanctions } = await kumo.sanctions.mine();
const ban = sanctions.find((s) => s.kind === 'ban' && s.active);
if (ban && !ban.appeal) await kumo.sanctions.appeal(ban.id, message); // BAN 1件につき1回・2000文字まで
```

BANされた利用者が自分のBANを`kumo.sanctions.mine()`（`GET /v1/players/me/sanctions`）で読めるのは、セッションが残っている間（BANから最大15分）です。異議の画面はそのときに出してください。次に訪れたときでも、SDKは異議を送れます。BANで失効したリフレッシュトークンを残しておき、本人の証明に使います（`POST /v1/auth/appeal`）。BANのIDが分からないときは`kumo.sanctions.appeal(null, message)`で送れます。そのトークンを失効させたBANが対象になります。

BANへの異議には、ダッシュボード（**プレイヤー** → **BANの異議申し立て**: 受け入れてBANを解く／退ける・短い返事も付けられます）、AIエージェント（`appeals_list`／`appeal_resolve`・毎回あなたに確認します）、自分のサーバー（`GET /v1/admin/appeals?status=open` · `POST /v1/admin/appeals/:appealId/resolve` `{ accept, response? }`）から答えます。答えたあとは、BANの異議が`status: 'accepted'`（BANが解けた）か`'rejected'`になり、あなたの返事が`response`に入ります。

## サーバー側での確認

自分のサーバー（やFunctions）で利用者が誰かを知る必要がある場合は、ページからアクセストークンを送ってもらい（`Authorization: Bearer <token>`。`await kumo.auth.getAccessToken()`で取れます）、サーバーから環境の**シークレットキー**でKUMODeckに確かめます。

```http
POST /v1/admin/players/verify-token
Authorization: Bearer sk_…
{ "token": "<利用者のアクセストークン>" }
→ { "player": { "id", "displayName", "banned" } }
```

別の環境のトークンや期限切れのトークンは401 `token_expired`です（ページがリフレッシュして再試行します）。`banned`は自分で確かめてください。BANされた利用者でも確認は通るので、何をさせるかは自分のコードが決めます。Functionsのスターターでは`src/kumo.ts`の`players.verify()`（30秒のキャッシュ付き）がこの呼び出しで、`functions-d1` Skillの`requireUser()`は401 / 403まで返します（[Functions](/ja/docs/guides/functions/index.md)）。シークレットキーはサーバーだけに置き、ページには送らないでください。[REST API](/ja/docs/reference/rest-api/index.md)を参照してください。
