> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# ユーザー移行：パスワードとMFAのインポート

> Didit Authenticationでは、パスワード、確認済みメールアドレス、ソーシャル連携、認証アプリ、組織の役割を含むユーザーを、CSV・JSONファイルまたはバッチでインポートできます。

> [!WARNING]
>
> Authenticationは新しい機能です。すべてのインポートは、まずサンドボックスのアプリケーションで実行し、その後で本番環境のアプリケーションにインポートしてください。

インポートは、別のIDプラットフォームからDidit Authenticationへユーザーを移行する機能です。パスワード、確認済みのメールアドレス、ソーシャルサインインのリンク、認証アプリ、組織での役割も一緒に移行されます。アプリケーションがDiditでユーザーをサインインさせるまで、ユーザー側には何も変化はありません。

インポートには、`write:authentication`(一覧の取得には`read:authentication`)を持つコンソールセッションで、アプリケーションのAuthentication管理APIを使用します。エンドユーザーのトークンや検証用のAPIキーは使用しません。例では、`MANAGEMENT_BASE`は次のとおりです。

```text theme={null}
https://apx.didit.me/auth/v2/organizations/{org_id}/applications/{application_id}/authentication/
```

## 2つのインポート方法

| | ファイルジョブ | バッチリクエスト |
| - | - | - |
| エンドポイント | `POST import-jobs/`(multipart) | `POST imports/`(JSON) |
| サイズ | CSVまたはJSONファイル1つ、最大25MB・100,000人 | 1リクエストあたり`rows`を1〜1,000件 |
| ドライラン | 常に最初にバックグラウンドで実行 | デフォルトは`dry_run: true` |
| コミット | `POST import-jobs/{id}/start/` | `dry_run: false`を指定した同じリクエスト |
| 部分的な失敗 | 1,000人ごとのバッチが個別にコミットされ、中断されたインポートは再開される | すべて成功するか、すべて失敗する |

## ファイルのインポート

### 1. アップロード

```bash theme={null}
curl --fail-with-body "${MANAGEMENT_BASE}import-jobs/" \
  -H "Authorization: Bearer $CONSOLE_ACCESS_TOKEN" \
  -F file=@users.csv \
  -F passwords=import \
  -F keep_social=true \
  -F keep_verified=true \
  -F keep_mfa=true \
  -F keep_ids=true
```

| フィールド | 意味 |
| - | - |
| `file` | CSVまたはJSON、UTF-8(バイトオーダーマークも可)、最大25MB。 |
| `passwords` | `import`(デフォルト)はパスワードハッシュを保持します。`reset`はハッシュを無視するため、全員が新しいパスワードを設定します。 |
| `keep_social` | ソーシャルサインインのリンクを保持します。 |
| `keep_verified` | `email_verified`を保持します。`false`の場合、すべてのメールアドレスが未確認になります。 |
| `keep_mfa` | 認証アプリのシークレットを保持します。 |
| `keep_ids` | `external_id`を保持します。`false`の場合、`vendor_data`はDiditのアイデンティティIDになります。 |
| `firebase_signer_key`、`firebase_salt_separator`、`firebase_rounds`、`firebase_mem_cost` | Firebaseプロジェクトのハッシュパラメーター。4つすべてを送信するか、いずれも送信しないでください。 |

各`keep_*`スイッチは、`false`を送信しない限り`true`です。この呼び出しは`202`とジョブを返します。1つのアプリケーションが同時に保持できるアップロード済みファイルは最大5件です。

### 2. ドライランを確認する

アップロードするたびにドライランが開始されます。ドライランはすべてのユーザーをチェックし、何も書き込みません。`GET import-jobs/{id}/`をポーリングしてください。

| フィールド | 意味 |
| - | - |
| `kind` | `dry_run`。インポートを開始すると`import`になります。 |
| `status` | `queued`、`running`の後、ドライランでは`passed`または`failed`、インポートでは`done`または`error`。 |
| `processed`、`total` | 進捗(人数)。 |
| `result` | ファイルの内容([結果](#結果)を参照)。 |
| `startable` | ドライランに合格し、ファイルの有効期限が切れていない場合は`true`。 |
| `expires_at` | インポートされなかったドライランから24時間後に、ファイルは削除されます。 |

失敗したドライランにはエラーが一覧表示されます。ファイルを修正して、もう一度アップロードしてください。

### 3. インポートを開始する

`POST import-jobs/{id}/start/`は、ドライランでチェックした内容をインポートし、`202`を返します。

* ユーザーは1,000人ずつのバッチでインポートされます。各バッチは個別にコミットされます。
* 1つのアプリケーションで同時に実行できるインポートは1件のみです(それ以外の場合は`import_running`)。
* 中断されたインポートは、最後にコミットされたバッチの後から再開されます。
* すでにインポートされたユーザーはスキップされるため、同じファイルを再度実行できます。
* インポートを開始した人には、インポートが完了または停止したときにメールが届きます。

アップロードされたファイルは暗号化して保存されます。ファイルは、インポートの終了時、ドライランの失敗時、またはインポートされなかったドライランから24時間後に削除されます。

## バッチでのインポート

最大100文字の`idempotency_key`を付けて、1リクエストあたり最大1,000人を送信します。

```json theme={null}
{
  "rows": [
    {
      "email": "user@example.com",
      "external_id": "user-1234",
      "email_verified": true,
      "display_name": "Example User",
      "password_hash": "$2b$12$...",
      "social": [{ "provider": "google", "subject": "1234567890" }],
      "organizations": [{ "slug": "acme", "name": "Acme", "role": "admin" }]
    }
  ],
  "dry_run": true,
  "idempotency_key": "migration-batch-0001"
}
```

* ドライランは結果とともに`200`を返し、何も書き込みません。
* `dry_run: false`の場合、インポートに成功すると`201`を返します。エラーを1件でも含むバッチは`400`を返し、誰もインポートされません。
* コミット済みのリクエストを同じキーで繰り返すと、記録済みの結果が返されます。同じキーで内容が異なる場合は`400 {"idempotency_key": "different_payload"}`が返されます。
* Firebaseのパラメーターは`firebase`オブジェクト`{signer_key, salt_separator, rounds, mem_cost}`に指定します。
* `GET imports/`はコミット済みのバッチを一覧表示します。

## ファイル形式

### JSON

ユーザーの配列、または`users`にその配列を持つオブジェクトです。

| フィールド | ルール |
| - | - |
| `email` | 必須。 |
| `external_id` | 最大255文字。そのユーザーの[`vendor_data`](https://docs.didit.me/ja/entities/vendor-data-linking)になります。 |
| `email_verified` | `true`または`false`(`1`/`0`、`yes`/`no`も可)。 |
| `display_name` | 最大200文字。 |
| `password_hash`、`password_salt`、`password_algorithm` | [パスワード](#パスワード)を参照。 |
| `totp_secret` | Base32形式の認証アプリのシークレット。 |
| `created_at`、`last_signed_in_at` | ISO 8601形式。タイムゾーンのない時刻はUTCとして扱われます。 |
| `social` | 最大8件の`{provider, subject, tenant_id}`。`provider`は`google`、`apple`、`github`、`microsoft`のいずれかです。`tenant_id`はMicrosoftの場合のみ必須です。 |
| `organizations` | 最大20件の`{slug, name, role}`。`role`は`admin`、`member`(デフォルト)、`billing`のいずれかです。 |

### CSV

JSONと同じ単一値の列に加えて、`google_id`、`apple_id`、`github_id`、`microsoft_id`、`microsoft_tenant_id`、`organization_slug`、`organization_name`、`organization_role`を使用します。複数の組織に所属するユーザーは、組織ごとに1行ずつ記述します。

```csv theme={null}
email,external_id,email_verified,password_hash,google_id,organization_slug,organization_role
user@example.com,user-1234,true,$2b$12$...,1234567890,acme,admin
```

不明なフィールドや列はエラーになり、無視されることはありません。

### 重複と既存のアカウント

* 同じメールアドレスの行は1人のユーザーとしてまとめられます。まったく同じ行は重複としてカウントされます。リンク、組織、認証アプリが追加されている行はマージされます。
* 1人のユーザーに対して2つの異なるパスワードハッシュ、認証アプリのシークレット、または組織での役割が存在する場合はエラーになります。同じ`external_id`またはプロバイダーのsubjectが2人のユーザーに紐付けられている場合もエラーです。
* そのメールアドレスのアカウントがすでに存在し、それが以前のインポートで作成されたものである場合、そのユーザーはスキップされます(`already_imported`)。ユーザーが自分でサインアップしていた場合は、`identity_conflict`エラーになります。インポートによって既存のアカウントが変更されることはありません。

## パスワード

| 形式 | 受け付ける内容 |
| - | - |
| bcrypt | `$2a$`、`$2b$`または`$2y$`、コスト4から13。 |
| Argon2id | `$argon2id$v=19$...`。メモリは最大64 MiB、メモリとパス数の積は最大256 MiB、レーンは最大8。 |
| Django PBKDF2-SHA256 | `pbkdf2_sha256$...`、反復回数10,000から2,000,000。 |
| Django scrypt | `scrypt$...`、ワークファクターは最大32,768、ブロックサイズは1から8、並列度は1から5。 |
| Firebase scrypt | `password_algorithm: "firebase_scrypt"`、ユーザーごとにbase64形式の`password_hash`と`password_salt`。加えて、インポートごとに1回、プロジェクトの`signer_key`、`salt_separator`、`rounds`（1から8）、`mem_cost`（1から14）を指定します。 |

Firebaseのパラメーターは暗号化して保存され、そのアプリケーション専用に使われます。これ以外の形式、またはこれらの上限を超えるコストの場合、そのユーザーには新しいパスワードが必要です。ユーザーが初めてサインインに成功すると、Diditはそのパスワードを独自の形式で保存し直します。

### 使用可能なパスワードがないユーザー

ハッシュがない、またはサポートされていないハッシュを持つユーザーがパスワードでサインインしようとすると、通常のパスワード誤りと同じ応答が返ります。さらにDiditは、新しいパスワードを設定するためのリンクをそのユーザーにメールで送信します（有効期間30分、1日あたり最大1回）。これは、メールアドレスが確認済みで、アカウントがブロックされておらず、パスワードによるサインインが有効な場合にのみ行われます。

結果の`reset_required`は、使用可能なパスワードがないユーザーの数です。`reset_emails`はそのうちメールアドレスが確認済みのユーザーの数で、この方法で連絡できるのはこれらのユーザーだけです。メールアドレスが未確認のユーザーは、パスワードでサインインできません。メールコードによるサインイン方法を有効にすれば、引き続きメールコードでサインインできます。

## ソーシャル連携・認証アプリ・組織

* **ソーシャル連携**は、アプリケーションですでに設定済みのプロバイダーについて、現在のクライアントIDのもとで保持されます。これにより、再訪時のサインインではメールによる確認が不要になります。GoogleとGitHubのsubjectはグローバルです。Appleのsubjectは開発者チームごと、Microsoftのsubjectはアプリ登録ごとに異なるため、これらの連携は以前のプラットフォームが同じチームまたは同じアプリ登録を使用していた場合にのみ一致します。設定していないプロバイダーへの連携は`social_unavailable`としてカウントされ、インポートされません。
* **認証アプリ**（TOTPシークレット: SHA-1、6桁、30秒）は暗号化して保存されます。インポートされたユーザーは、これを第2要素として使用する必要があります。リカバリーコードは付与されません。
* まだ存在しない**組織**は、そのslugをもとに作成されます。
* **パスキー**はインポートできません。パスキーは以前のサインインドメインに紐づいているため、ユーザーはDiditでサインインした後に新しいパスキーを追加します。

## 結果

すべてのドライラン、バッチ、ジョブは同じ`result`を返します。

| フィールド | 意味 |
| - | - |
| `rows`, `people_total` | 読み込んだ行数と、重複を除いたユーザー数。 |
| `ready`, `already_imported`, `duplicates`, `merged`, `skipped` | 各ユーザーがどのように処理されるか。 |
| `passwords` | 形式ごとの件数: `bcrypt`、`argon2id`、`firebase_scrypt`、`pbkdf2_sha256`、`scrypt`。 |
| `reset_required`, `reset_emails`, `unsupported_hashes` | 新しいパスワードが必要なユーザー。 |
| `verified_emails` | 確認済みのメールアドレスとともにインポートされたユーザー。 |
| `social_links`, `social_unavailable` | プロバイダーごとに保持された連携と、スキップされた連携。 |
| `authenticator_apps` | 認証アプリがインポートされたユーザー。 |
| `organizations` | `count`、`members`、および最大5件の`names`。 |
| `error_count`, `errors` | 最大100件のエラー。各エラーには、ゼロ始まりの`row`（CSVの行番号は`row + 2`）、`field`、`error`コードが含まれます。 |

レスポンスには、パスワードハッシュ、ソルト、認証アプリのシークレット、フィールドの値は一切含まれません。

| エラー | 意味 |
| - | - |
| `file_too_large`, `too_many_rows`, `empty_file` | 25 MBを超えている、100,000人を超えている、または空である。 |
| `invalid_encoding`, `invalid_json`, `invalid_csv` | ファイルを読み込めない。 |
| `unknown_column`, `missing_column`, `duplicate_column`, `unknown_field` | ファイルが形式と一致しない。 |
| `required`, `invalid_field`, `invalid_row` | 値が欠落しているか無効である。 |
| `identity_conflict` | そのメールアドレスのアカウントがすでに個別に登録されている。 |
| `social_link_conflict`, `password_conflict`, `authenticator_conflict`, `role_conflict` | 同一ユーザーの2つの行の内容が矛盾している。 |
| `too_many_uploads`, `import_running`, `not_startable`, `file_expired` | 現在はジョブを開始できない。 |

## 切り替える前に

サンドボックスのアプリケーションで、次の項目をテストします。

* インポートする各パスワード形式でのサインイン
* 使用可能なハッシュがないユーザーへの新しいパスワード設定メール
* インポートしたユーザーのソーシャルサインインと認証アプリのコード
* 同じファイルを2回実行した場合の動作

次に、各ユーザーの`vendor_data`が、そのユーザーの[認証と取引](https://docs.didit.me/ja/authentication/overview.md#製品をまたいで同じ人物として扱う)で使用している値と一致することを確認します。


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.