
# ID 設定を更新

`PATCH /v1/identity-settings` · 権限 `identity-settings:write`

JSON マージパッチで環境のユーザー名設定を更新します (`application/merge-patch+json`、RFC 7396)。`If-Match` が必須です。存在しない場合は `428`、バージョンが古い場合は `412` になります。設定が一度も書き込まれていない場合、バージョンは `"0"` で、最初の書き込みで行が作成されます。何も変更しないパッチは何も書き込まず、バージョンを維持します。

## リクエスト

| フィールド | タイプ | 必須 | 説明 |
|---|---|---|---|
| `If-Match` | header | はい | 最後に読み取った設定の `ETag` (一度も書き込まれていない場合は `"0"`)。存在しない場合: `428`。無効な場合: `412`。 |
| `username_identifier` | string | いいえ | `required`、`optional`、`off` のいずれか。`null` にはできません。 |
| `username_min_length` | integer | いいえ | 3～64。`username_max_length` より大きくできません。`null` にはできません。 |
| `username_max_length` | integer | いいえ | 3～64。`username_min_length` より小さくできません。`null` にはできません。 |
| `reserved_usernames` | string[] \| null | いいえ | 最大 1000 件。リスト全体を置き換えます (RFC 7396 は配列をマージしません)。`null` または `[]` でクリアされます。各項目は正規化され、重複は除かれ、リストは並べ替えて保存されます。 |
| `verification_policy` | string | いいえ | `before_sign_in` (既定値) または `after_sign_in`。`after_sign_in` は未確認アカウントのクリーンアップを停止します。`null` にはできません。 |
| `unverified_account_ttl_days` | integer | いいえ | 1〜30 (既定値 7): `before_sign_in` 環境で未確認のサインアップを削除するまで保持する日数。`null` にはできません。 |

ユーザー名の設定は以降の書き込みにのみ適用され、既存のユーザーを変更することはありません。確認の設定は既存のサインアップにも作用します (下記参照)。

## 適用ルール

- `off` (既定): ユーザー名を書き込むリクエスト (`POST /v1/users`、`PATCH /v1/users/{id}`) は `422` とコード `identifier_type_disabled` (ポインター `/username`) で失敗します。既存のユーザー名のクリアは引き続き可能で、ユーザー名に触れないユーザーパッチは再検査されません。サインインはユーザー名でアカウントを解決しません。
- `optional`: ユーザー名を設定できます。`required`: 管理 API でユーザー名なしにユーザーを作成すると `422` (`required`) で失敗し、管理パッチでクリアすることもできません。サインアップフォームでユーザー名を求めるかどうかはホスト型ログインの役割で、ソーシャルおよびジャストインタイム (JIT) の作成ではユーザー名を求めません。
- 長さ: `username_min_length` より短いか `username_max_length` より長いユーザー名は `422` (`invalid`) で失敗します。
- 予約名: 正規化後の形が `reserved_usernames` にあるユーザー名は `422` とコード `username_reserved` で失敗します。無効なリスト項目は `422` とポインター `/reserved_usernames/<i>` で失敗します。
- 設定を変更しても既存のユーザーは変更されません。ルールは以降の書き込みにのみ適用されます。

## 未確認アカウントのクリーンアップ

`verification_policy` と `unverified_account_ttl_days` は、一度も確認されなかったサインアップの自動クリーンアップを制御します。 クリーンアップは、メールアドレス (または電話番号) が必須の識別子である場合にのみ実行されます。メールは既定で必須です。

- `before_sign_in` (既定値): サインアップで作成されたユーザーは、必須の識別子を確認するまでトークンを受け取れません。サインアップから `unverified_account_ttl_days` 日 (1〜30、既定値 7) 経っても確認済みのメールアドレスや電話番号がなく、一度もトークンを受け取っておらず、ソーシャルまたはエンタープライズ (SAML/OIDC) の ID を持たないサインアップユーザーは完全に削除されます。クリーンアップは定期的に (既定では 1 時間ごとに) 実行されます。
- `after_sign_in`: ユーザーは確認前にトークンを受け取ります (`email_verified: false`)。クリーンアップは実行されず、ユーザーは削除されません。
- 削除されないユーザー: 管理 API、インポート、SCIM、招待、またはジャストインタイムのフェデレーションで作成されたユーザー、ブロックされたユーザー、削除がすでに開始されたユーザー。環境でクリーンアップが開始された後に作成されたサインアップのみが対象です。
- 削除は取り消せません。ユーザーはハード削除され、理由が `unverified_expired` のトゥームストーンが保持され、`user.deleted` イベントが発行されます。
- ポリシーや期間の変更は次回の実行から有効になります。`unverified_account_ttl_days` を短くすると、より古い未確認のサインアップがすぐに対象になります。`after_sign_in` から `before_sign_in` に戻すと、期限切れの未確認サインアップは次回の実行で削除されます。

## レスポンス

`200 OK`、更新された設定と新しい `ETag`。

```json
{
  "username_identifier": "optional",
  "username_min_length": 3,
  "username_max_length": 32,
  "reserved_usernames": ["admin", "root", "support"],
  "verification_policy": "before_sign_in",
  "unverified_account_ttl_days": 7,
  "updated_at": "2026-10-05T09:30:00.123456Z"
}
```

## エラー

| HTTP | コード | 概要 |
|---|---|---|
| 400 | `unknown_field` | パッチが未知フィールドまたは読み取り専用フィールドを含んでいるか、JSON オブジェクトではありません (`malformed_request`)。 |
| 412 | `precondition_failed` | `If-Match` が現在のバージョンと一致しません。 |
| 415 | `unsupported_media_type` | コンテンツタイプが `application/merge-patch+json` ではありません。 |
| 422 | `validation_failed` | 値が無効、`min` が `max` を超えている、または `null` にできないフィールドが `null` に設定されました (`code: invalid`)。 |
| 428 | `precondition_required` | `If-Match` ヘッダーがありません。 |
| 401 | `unauthenticated` | 認証情報が見つからない、無効、または取り消されています。 |
| 403 | `insufficient_permissions` | 認証情報に必要な権限がありません。 |
| 429 | `rate_limited` | レート制限を超えました。`Retry-After` 期間後に再試行してください。 |
| 503 | `service_unavailable` | 依存関係が一時的に利用できません。`Retry-After` 期間後に再試行してください。 |

## 例

```bash
curl -X PATCH https://api.eu.keenauth.com/v1/identity-settings \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/merge-patch+json" \
  -H 'If-Match: "0"' \
  -d '{"username_identifier":"optional","username_max_length":32,"reserved_usernames":["admin","root","support"]}'
```
