用户导入 API¶
用户导入 API 支持将用户从其他系统批量导入到 Authany 项目。该 API 不属于 Admin API GraphQL 的一部分。不过,访问该 API 端点仍需要 Admin API JWT 令牌。
以下是关于用户导入 API 的其他重要说明:
- 实际的用户导入过程是异步的,即执行在后台完成。API 提供了一个端点,供开发者查询导入的状态。
- 导入任务成功发起后,API 会返回该任务的 ID。查询导入状态时需要用到这个 ID。
- 发送给该 API 的 HTTP 请求正文大小限制为 500KB。
- 使用用户导入 API 不会触发
user.pre_create和user.createdHook。 - 该 API 支持 Bcrypt 密码格式。要以此格式导入密码,需要在作为用户
password字段值的对象中指定type和password_hash。例如:
"password": {
"type": "bcrypt",
"password_hash": "$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy"
},
端点 ¶
导入用户 API 提供两个端点,一个用于发起用户导入任务,另一个用于查询任务状态。这些端点仅支持安全的 HTTPS 请求,并要求通过 Bearer 授权请求头(Authorization: Bearer <Admin API JWT Token>)提供有效的 Admin API JWT 令牌。
以下是这些端点及其预期输入的更多详情。
发起导入¶
POST /_api/admin/users/import
使用此端点创建新的用户导入任务。
请求头
| 名称 | 值 |
|---|---|
| Content-Type | application/json |
| Authorization | Bearer <Admin API JWT Token> |
| Host | <Your Authgear Project Domain> |
请求体
发起导入端点通过 HTTP(S) 请求体接受 JSON 输入。以下是输入示例:
{
"identifier": "email",
"records": [
{
"email": "user@example.com",
"email_verified": true,
"password": {
"type": "bcrypt",
"password_hash": "$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy"
}
}
]
}
输入格式 ¶
用于发起导入的端点通过 HTTP 请求体接受 JSON 输入。以下示例 JSON 文档展示了输入的预期结构和字段:
{
"upsert": true,
"identifier": "email",
"records": [
{
"preferred_username": "jdoe",
"email": "johndoe@example.com",
"phone_number": "+85123456789",
"email_verified": true,
"phone_number_verified": true,
"name": "John Doe",
"given_name": "John",
"family_name": "Doe",
"middle_name": "",
"nickname": "JD",
"profile": "https://example.com",
"picture": "https://example.com",
"website": "https://example.com",
"gender": "male",
"birthdate": "1990-01-01",
"zoneinfo": "Asia/Hong_Kong",
"locale": "zh-Hant-HK",
"address": {
"formatted": "1 Unnamed Road, Central, Hong Kong Island, HK",
"street_address": "1 Unnamed Road",
"locality": "Central",
"region": "Hong Kong",
"postal_code": "N/A",
"country": "HK"
},
"custom_attributes": {
"member_id": "123456789"
},
"roles": ["role_a", "role_b"],
"groups": ["group_a"],
"disabled": false,
"password": {
"type": "bcrypt",
"password_hash": "$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy"
},
"mfa": {
"email": "johndoe@example.com",
"phone_number": "+85123456789",
"password": {
"type": "bcrypt",
"password_hash": "$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy"
},
"totp": {
"secret": "secret"
}
}
}
]
}
为了更好地理解输入内容,我们来仔细看看上述 JSON 文档根节点上直接包含的三个字段(upsert、identifier、records)。
upsert:这是一个可选的布尔值,默认值为false。当该字段的值设为true,且已存在具有相同身份标识的用户时,系统会根据每个属性的更新行为更新该用户的数据。identifier:该字段为必填项。它告诉 Authany 使用哪个属性来识别已存在的用户。可接受的值为以下字符串:preferred_username、email和phone_number。records:开发者可以在此以数组形式提供希望导入的所有用户数据。数组中的每个直接对象代表一个用户。在对象内部,您可以使用如上例所示的各个字段来定义该用户的标准属性。您也可以像上例中那样,在嵌套于custom_attributes字段内的对象中定义自定义属性。
响应
id:这是所创建导入任务的唯一 ID。查询任务状态时需要用到该 ID。
status:导入任务创建后,status 的值为 pending;用户导入任务完成后,值为 completed。
查询状态¶
GET /_api/admin/users/import/{ID}
使用此端点查询现有用户导入任务的状态。请将 {ID} 替换为有效的用户导入任务 ID。
请求头
| 名称 | 值 |
|---|---|
| Authorization | Bearer <Admin API JWT Token> |
| Host | <Your Authgear Project Domain> |
响应
{
"id": "task_4WZ0V7EPT4GZ2ABVN03QXYZ122W835C1",
"created_at": "2024-04-04T06:56:36.02508096Z",
"status": "completed",
"summary": {
"total": 2,
"inserted": 2,
"updated": 0,
"skipped": 0,
"failed": 0
},
"details": [
{
"index": 0,
"record": {
"email": "user1@example.com",
"email_verified": true,
"password": {
"password_hash": "REDACTED",
"type": "bcrypt"
}
},
"outcome": "inserted",
"user_id": "0f0f65ee-4c7d-45a0-a740-bcbbfd3fcf06"
},
{
"index": 1,
"record": {
"email": "user2@example.com",
"email_verified": false,
"family_name": "Doe",
"given_name": "John",
"name": "John Doe",
"password": {
"password_hash": "REDACTED",
"type": "bcrypt"
}
},
"outcome": "inserted",
"user_id": "9c71fc29-6db6-4a18-aa73-774139fed16d",
"warnings": [
{
"message": "email_verified = false has no effect in insert."
}
]
}
]
}
更新行为 ¶
属性的更新行为决定了当已有用户与指定 identifier 类型的值相同时,Authany 会如何处理该属性。例如,如果 identifier 为 "email",那么当已有用户的邮箱地址与您正在尝试导入的当前用户相同时,每个属性的更新行为就决定了 Authany 会如何处理该属性。
每个属性都可以具有以下三种更新行为之一:
- UPDATED_IF_PRESENT_AND_REMOVED_IF_NULL:具有此更新行为的属性,如果新值不为 null,会将用户的该属性更新为新值。如果新值显式为 null,该属性会被从用户身上删除。如果该属性缺失,则不执行任何操作。
- UPDATED_IF_PRESENT:当属性的更新行为为此项时,若该属性存在则会被更新。如果属性不存在,则不执行任何操作。
- IGNORED:如果用户已存在,该属性的新值会被忽略。如果属性缺失,也不执行任何操作。
各字段的更新行为¶
下表列出了所有属性及其更新行为,供参考:
| 项目 | 更新行为 | 说明 |
|---|---|---|
preferred_username、email、phone_number |
UPDATED_IF_PRESENT_AND_REMOVED_IF_NULL | 如果它不是 identifier,则应用该更新行为。相应的登录 ID 会按需被创建、更新或删除。 |
email_verified、phone_number_verified |
UPDATED_IF_PRESENT | 例如,在首次导入时,如果 email_verified 缺失,则邮箱会被标记为未验证。 |
| 所有其他标准属性 | UPDATED_IF_PRESENT_AND_REMOVED_IF_NULL | 需要特别说明的是,address 不会与现有值合并,而是替换现有的 address 值。 |
custom_attributes.* |
UPDATED_IF_PRESENT_AND_REMOVED_IF_NULL | custom_attributes 中的每个属性,其更新行为分别独立应用。因此在 upsert 中缺失的自定义属性不会改变现有值。 |
roles、groups |
UPDATED_IF_PRESENT | 如果存在,用户的角色和用户组会与该值保持一致。例如,假设用户原本拥有 ["role_a", "role_b"],而 roles 为 ["role_a", "role_c"],则 role_b 会被移除,role_c 会被添加。 |
disabled |
UPDATED_IF_PRESENT | 重新导入记录时若未指定 disabled,不会意外更改此前通过其他方式设置的禁用状态。 |
password |
IGNORED | 如果在记录首次导入时未提供该字段,后续导入无法再补充添加。 |
mfa.email |
UPDATED_IF_PRESENT_AND_REMOVED_IF_NULL | 如果提供,用户可以使用邮箱 OTP 进行双重验证。 |
mfa.phone_number |
UPDATED_IF_PRESENT_AND_REMOVED_IF_NULL | 如果提供,用户可以使用手机 OTP 进行双重验证。 |
mfa.password |
IGNORED | 如果在记录首次导入时未提供该字段,后续导入无法再补充添加。 |
mfa.totp |
IGNORED | 如果在记录首次导入时未提供该字段,后续导入无法再补充添加。 |
使用示例¶
以下是一份详细指南,包含使用用户导入 API 批量导入用户以及查询任务状态的示例。