概述
用于跨域身份管理的系统 (SCIM) 是一种标准 API,用于自动化用户和群组预配/取消预配,并将用户和群组数据从客户的身份提供程序 (IdP) 更新到 Udemy Business 帐户中。许多身份提供程序(例如 Okta、Azure AD 和 OneLogin)都支持 SCIM。 您还可以将 Udemy Business SCIM API 用于其他 IdP 或本土工具。
如果您的组织使用以下 IdP 之一,请参阅下面的指南,了解如何配置 SCIM:
SCIM 使用标准化的 REST API,数据格式为 JSON。Udemy Business 支持 SCIM 标准的 2.0 版。该 API 适用于 Enterprise 方案中的所有客户。
Udemy Business SCIM API 支持以下功能:
- 预配用户
- 取消预配用户(停用)
- 更改电子邮件地址
- 更改用户详细信息
- 预配群组
- 在群组中添加/移除用户
SCIM 协议说明
SCIM 协议是一种应用级 REST 协议,用于在 Web 上预配和管理身份数据。该协议是客户端-服务器协议,其中客户端是身份提供程序 (IdP),服务器是 Udemy Business。
基本流程如下:
- 当客户在 IDP 中为用户授予 Udemy Business 访问权限时,IdP 会向我们发送请求,以检查特定用户是否存在于我们的数据库中。他们通过用户名或电子邮件等属性发出用户搜索请求。
- 如果用户不存在,则 IdP 会发送创建用户的请求。
- 如果用户存在,则 IdP 会发送该用户的更新请求。
- 如果撤销 Udemy Business 的访问权限,则 IdP 会向我们发送从我们的数据库中停用该用户的请求。
- IdP 还会发送更改用户详细信息的请求。
如何访问 API?
为了获取连接到 SCIM API 的授权凭据,必须在您的 Udemy Business 帐户中通过管理 -> 设置 -> 预配 (SCIM) 页面设置 SCIM 集成。请注意:仅管理员可以访问此页面。
单击开始设置。
在下一步中,选择选择提供程序,然后选择自定义。
单击生成令牌。
在此屏幕上,单击复制以将持有者令牌复制到剪贴板。
您需要在请求中包含带有持有者令牌的授权 HTTP 标头,例如:
GET /scim/v2/Users HTTP/1.1
Host: myorganization.udemy.com
Accept: application/scim+json
Authorization: Bearer <enter you Bearer token here>
Content-Type: application/scim+json
Udemy Business SCIM API 使用 HTTP 协议,并且只能通过安全的 HTTPS 连接使用。
该 API 的基本 URL 是: https://<yoursubdomain>.udemy.com/scim/v2/。
如果您正在开发一款应用来与 Udemy Business SCIM API 进行交互,建议参考本文档末尾包含的 SCIM RFC。Udemy Business SCIM API 实施符合标准。
速率限制
Udemy Business 根据标准 HTTP 速率限制协议对 SCIM API 应用速率限制。如果您的请求受到速率限制,您将收到 HTTP 429 响应,并且您应该等待 Retry-After 标头中指定的时间,然后重试。
SCIM API 端点
信息端点
这些是信息端点,用于配置客户端。他们不需要身份验证,因此您在访问这些端点时不需要包含授权标头。
GET /ServiceProviderConfig
返回有关 Udemy Business SCIM 实施的详细信息,包括支持的方法。
GET /Schemas
返回 SCIM 实施支持的架构的相关信息。支持的架构是用户和群组。
GET /Schemas/Users
返回支持用户资源的所有属性。
GET /Schemas/Groups
返回支持群组资源的所有属性。
用户端点
您可以使用这些端点列出用户、按属性筛选、添加新用户、更新用户信息或停用用户/将用户匿名。
如果 SCIM API 未返回所有用户,请联系 Udemy Business 支持。
支持的属性
SCIM 属性 | 是否必填? | 描述 |
电子邮件 [type=”work”]]['value’] |
是 | 用户的电子邮件。必须唯一 |
用户名 |
是 | IdP 的用户名。必须唯一。 |
活跃 |
是 | 标记停用/重新激活用户 |
外部 ID |
是 | IdP 用户的外部 ID。必须唯一。 |
urn:ietf:params:scim:schemas:extension: |
是 | 从 EnterpriseSchema 返回 employeeNumber 字段并将其存储为 external_id 字段 |
name.givenName |
否 | 用户的名字。虽然这不是必填属性,但我们建议始终指定这些属性,因为这样便于轻松识别用户。 |
name.familyName |
否 | 用户的姓氏。虽然这不是必填属性,但我们建议始终指定这些属性,因为这样便于轻松识别用户。 |
姓名,{ givenName, familyName } |
否 | 用户的名字和姓氏。虽然这不是必填属性,但我们建议始终指定这些属性,因为这样便于轻松识别用户。 |
title |
否 | 用户的职称,即“高级工程师” |
群组 |
否 | 用户所属的 SCIM 群组 |
请注意:如果您指定不在此列表中的任何其他属性,系统将忽略该属性。
GET/Users
返回用户的分页列表,默认情况下每页 12 个用户。您可以传入
count
and
startIndex
以对结果集进行分页。例如:
GET /scim/v2/Users?startIndex=1&count=100 HTTP/1.1
Host: myorganization.udemy.com
Accept: application/scim+json
Authorization: Bearer <enter you Bearer token here>
startIndex
- 是当前结果列表集中第一个结果从 1 开始的索引(偏移)
-
count
是列表响应页面中返回的资源数量(限制)。您可以在单个请求中检索至多 1000 个用户。如果省略此项目,则默认为 12。
示例请求
GET https://demo.udemy.com/scim/v2/Users
{
"schemas": [
"urn:ietf:params:scim:api:messages:2.0:ListResponse"
],
"totalResults": 18,
"startIndex": 1,
"Resources": [
{
"id": "KwLzN3",
"externalId": "00u3mlhj4x1E482sK5d7",
"userName": "firstName.lastName@domain.com",
"name": {
"givenName": "firstName",
"familyName": "lastName",
"formatted": "firstName lastName"
},
"emails": [
{
"value": "firstName.lastName@domain.com",
"type": "work",
"primary": true
}
],
"title": "",
"active": true,
"groups": [
{
"value": "NZOaw",
"display": "Group Test",
"$ref": "https://demo.udemy.com/scim/v2/Groups/NZOaw"
},
{
"value": "dn1K8",
"display": "NewGroup2",
"$ref": "https://demo.udemy.com/scim/v2/Groups/dn1K8"
}
],
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:User",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
],
"meta": {
"resourceType": "User",
"location": "https://demo.udemy.com/scim/v2/Users/KwLzN3",
"created": "2022-01-19T01:11:59Z",
"lastModified": "2024-11-22T21:58:48Z"
},
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
"employeeNumber": "64e63"
}
},
[...]
],
"itemsPerPage": 12
}
GET /Users?filter=
此端点用于按特定属性筛选用户。例如,可以根据用户名属性进行搜索:
GET /Users?filter=userName eq "example..name”
注意:在上述示例中,您需要对 URL 参数进行 URL 编码,因此 URL 将变为:
GET /Users?filter=userName%20eq%20%22example.name%22
此时将返回用户资源列表。如果没有任何结果,将返回一个空列表。
GET Users?filter=groups.value eq "{SCIM_Group_ID}"
这将返回属于此 SCIM 群组的所有用户
支持的筛选条件如下:
userName
externalID
emails[type eq=”work”]
groups
支持的运算符如下:
and
eq
响应:
- HTTP 状态代码 200 和成功的实体列表
- 如果提供了不受支持的筛选条件,则会返回 HTTP 状态代码 501
POST /Users
此端点用于在 Udemy Business 中创建(配置)新用户。
响应将包含一个
ID
属性,在所有后续请求中引用此用户时应当使用该属性。
请注意:
- 以这种方式创建的新用户在该用户首次登录之前不会 使用许可证。
- 如果此用户已有待处理的邀请,则此时将使用该邀请。
系统会将用户添加到群组,根据邀请中指定的条件分配适当的角色/课程作业。 - 尝试创建 Udemy Business 中已存在的用户将导致该用户变为由 SCIM 管理(在“管理用户”页面中显示一个小链接图标)。请注意,用户的状态和许可证使用情况不会更改。如果用户处于活跃状态,将保持活跃状态;如果用户已停用,将保持停用状态。
示例请求
POST https://demo.udemy.com/scim/v2/Users
{
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:User",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
],
"active": true,
"emails": [
{
"primary": true,
"type": "work",
"value": "demo.user@test.com"
}
],
"externalId" : "externalIdValue",
"meta": {
"resourceType": "User"
},
"userName": "DemoTest",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
"employeeNumber": "externalIdValue"
},
"name": {
"familyName": "Test",
"formatted": "formatted",
"givenName": "Demo"
}
}
示例响应
{
"id": "MPD698",
"name": {
"givenName": "Demo",
"familyName": "Test",
"formatted": "Demo Test"
},
"emails": [
{
"value": "demo.user@test.com",
"type": "work",
"primary": true
}
],
"title": "",
"active": true,
"groups": [],
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:User",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
],
"meta": {
"resourceType": "User",
"location": "https://demo.udemy.com/scim/v2/Users/MPD698",
"created": "2024-12-27T22:00:25Z",
"lastModified": "2024-12-27T22:00:26Z"
},
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
"employeeNumber": "externalIdValue"
},
"userName": "DemoTest",
"externalId": "externalIdValue"
}
响应:
- HTTP 状态代码 201 和成功的用户资源
- 如果组织中已存在具有相同用户名的成员,则会返回 HTTP 状态代码 409
- 如果请求未通过验证,则会返回 HTTP 状态代码 400,响应正文中会显示错误详细信息
GET /Users/<id>
此端点用于检索指定用户的用户详细信息。上述请求中的
ID
参数是在使用 SCIM 创建用户或列出所有现有用户时返回的唯一标识符。
响应:
- HTTP 状态代码 200 和成功的用户资源
- 如果未找到用户,则会返回 HTTP 状态代码 404
PUT /Users/<id>
此端点用于替换(覆盖)Udemy Business 中的用户详细信息。如果已指定,活跃属性可用于停用或重新激用户。
示例请求:
PUT https://demo.udemy.com/scim/v2/Users/MPD698
{
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:User",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
],
"id": "MPD698",
"userName": "demo.user@test.com",
"externalId": "NewExternalID",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
"employeeNumber": "NewExternalID"
},
"name": {
"givenName": "demo",
"familyName": "user"
},
"emails": [
{
"value": "demo.user@test.com",
"type": "work",
"primary": true
}
],
"active": true
}
示例响应
{
"id": "MPD698",
"name": {
"givenName": "demo",
"familyName": "user",
"formatted": "demo user"
},
"emails": [
{
"value": "demo.user@test.com",
"type": "work",
"primary": true
}
],
"title": "",
"active": true,
"groups": [],
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:User",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
],
"meta": {
"resourceType": "User",
"location": "https://demo.udemy.com/scim/v2/Users/MPD698",
"created": "2024-12-27T22:00:25Z",
"lastModified": "2024-12-27T22:17:52Z"
},
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
"employeeNumber": "NewExternalID"
},
"userName": "demo.user@test.com",
"externalId": "NewExternalID"
}
响应:
- HTTP 状态代码 200 和更新的用户资源
- 如果用户不存在,则会返回 HTTP 状态代码 404
- 如果尝试停用组织所有者,则会返回 HTTP 状态代码 400
PATCH /Users/<id>
此端点用于对我们系统中的用户详细信息进行部分更新,这意味着您只能使用此端点来更改用户的某些属性。与之相比,PUT 可替换整个用户。
它可以包含活跃属性,这将会导致停用或重新激活用户。
- 每个请求的正文必须包含 URI 值为“urn:ietf:params:scim:api:messages:2.0:PatchOp”的“架构”属性。
- HTTP PATCH 请求的正文必须包含“操作”属性,其值为一个或多个 PATCH 操作的数组。每个 PATCH 操作对象必须恰好有一个“op”成员,其值指示要执行的操作,并且可以是“添加”、“移除”或“替换”中的一个。
- “路径”属性可以为空,在这种情况下,“值”应当为采用 {“path”: “value”} 格式的字典。
示例请求
PATCH https://demo.udemy.com/scim/v2/Users/MPD698
{
"schemas": [
"urn:ietf:params:scim:api:messages:2.0:PatchOp"
],
"Operations": [
{
"op": "replace",
"path": "userName",
"value": "DemoUserName"
}
]
}
示例响应
{
"id": "MPD698",
"name": {
"givenName": "demo",
"familyName": "user",
"formatted": "demo user"
},
"emails": [
{
"value": "demo.user@test.com",
"type": "work",
"primary": true
}
],
"title": "",
"active": true,
"groups": [
{
"value": "5ypNz",
"display": "NewGroup",
"$ref": "https://demo.udemy.com/scim/v2/Groups/5ypNz"
}
],
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:User",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
],
"meta": {
"resourceType": "User",
"location": "https://demo.udemy.com/scim/v2/Users/MPD698",
"created": "2024-12-27T22:00:25Z",
"lastModified": "2024-12-27T22:17:52Z"
},
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
"employeeNumber": "NewExternalID"
},
"userName": "DemoUserName",
"externalId": "NewExternalID"
}
响应:
- HTTP 状态代码 200 和更新的成功用户资源
- 如果未找到用户,则会返回 HTTP 状态代码 404
- 如果尝试停用组织所有者或操作无效,则会返回 HTTP 状态代码 400
群组端点
支持的属性
SCIM 属性 |
是否必填? |
描述 |
显示名称 |
是 |
群组标题。在所有 Udemy Business 群组中必须唯一。 |
外部 ID |
否 |
身份提供程序中的群组外部 ID |
请注意:如果您指定不在此列表中的任何其他属性,系统将忽略该属性。
GET /Groups
此端点用于获取所有已预配群组的分页列表。包括对结果进行分页的 startIndex 和 count 查询字符串参数。
请记住,只会返回使用 SCIM 创建的群组。不会返回在 Udemy Business 中创建的群组。
示例请求
GET https://demo.udemy.com/scim/v2/scim/v2/Groups
"schemas": [
"urn:ietf:params:scim:api:messages:2.0:ListResponse"
],
"totalResults": 6,
"startIndex": 1,
"Resources": [
{
"id": "NZOaw",
"displayName": "Group Test",
"members": [
{
"value": "KwLzN3",
"display": "firstName lastName",
"type": "User",
"$ref": "https://demo.udemy.com/scim/v2/Users/KwLzN3"
},
{
"value": "eBmzpr",
"display": "user four",
"type": "User",
"$ref": "https://demo.udemy.com/scim/v2/Users/eBmzpr"
}
],
"externalId": null,
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:Group"
],
"meta": {
"resourceType": "Group",
"location": "https://demo.udemy.com/scim/v2/Groups/NZOaw",
"created": "2024-08-23T22:26:48Z",
"lastModified": "2024-08-23T22:26:48Z"
}
},
[...]
],
"itemsPerPage": 12
}
GET /Groups?filter=
此端点用于按特定属性筛选群组。例如,可以根据显示名称属性进行搜索:
GET /Groups?filter=displayName eq "Marketing”
此时将返回群组资源列表。如果没有任何结果,将返回一个空列表。
请注意:您需要对参数进行 URL 编码,因此请求会变为:
GET /Groups?filter=displayName%20eq%20%22Marketing%22
支持的筛选条件如下:
displayName
externalId
Id
member.value
支持的运算符如下:
and
eq
响应:
- HTTP 状态代码 200 和成功的实体列表
- 如果使用了不受支持的筛选条件,则会返回 HTTP 状态代码 501
POST /Groups
此端点用于在 Udemy Business 中创建(预配)新群组。
响应:
- HTTP 状态代码 409。如果组织中已存在相同名称的已预配群组,我们将返回 409(冲突)唯一的 scimType 错误代码。
- 成功创建群组后,我们将返回该群组的完整表示形式,HTTP 状态代码 201(已创建)以及包含创建群组资源 URL 的 Location 标头。
GET /Groups/<id>
此端点用于从 Udemy Business 中提取群组详细信息。
响应:
- HTTP 状态代码 200 和群组资源
- 如果未找到群组,则会返回 HTTP 状态代码 404
POST /Groups
此端点用于在 Udemy Business 中创建(预配)新群组。
警告:使用 POST 或 PUT /scim/v2/Groups 端点创建群组时,不要在请求中包含 members 属性。将会忽略任何指定的成员。要将用户添加到群组,请先创建群组,然后单独调用 PATCH /scim/v2/Groups/
示例请求
https://demo.udemy.com/scim/v2/Groups
{
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:Group"
],
"displayName": "Group1",
"externalId": "234523"
}
示例响应
{
"id": "vREOw",
"displayName": "Group1",
"members": [],
"externalId": "234523",
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:Group"
],
"meta": {
"resourceType": "Group",
"location": "https://demo.udemy.com/scim/v2/Groups/vREOw",
"created": "2025-01-15T22:24:54Z",
"lastModified": "2025-01-15T22:24:54Z"
}
}
PUT /Groups/<id>
此端点用于替换 Udemy Business 中的群组详细信息。
警告:使用 POST 或 PUT /scim/v2/Groups 端点创建群组时,不要在请求中包含 members 属性。将会忽略任何指定的成员。要将用户添加到群组,请先创建群组,然后单独调用 PATCH /scim/v2/Groups/
示例请求
PUT https://demo.udemy.com/scim/v2/Groups/vREOw
{
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:Group"
],
"displayName": "Group1",
"externalId": "MPD699"
}
示例响应
{
"id": "vREOw",
"displayName": "Group1",
"members": [],
"externalId": "MPD699",
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:Group"
],
"meta": {
"resourceType": "Group",
"location": "https://demo.udemy.com/scim/v2/Groups/vREOw",
"created": "2025-01-08T21:12:53Z",
"lastModified": "2025-01-15T22:35:55Z"
}
}
响应:
- HTTP 状态代码 200 和更新的群组资源
- 如果群组不存在,则会返回 HTTP 状态代码 404
PATCH /Groups/<id>
此端点用于对 Udemy Business 中的群组详细信息进行部分更新。
PATCH 端点比其他端点更难以处理,因为它支持不同类型的操作(及其组合):
示例请求
PATCH https://demo.udemy.com/scim/v2/Groups/5ypNz
{ "schemas":
["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations":[
{
"op":"add",
"path": "members",
"value":[{
"display": "demo user",
"$ref":"https://demo.udemy.com/scim/v2/Users/MPD698",
"value": "MPD698"
}
]
}
]
}
示例响应
204 No Content
- 替换操作可更改指定值。在我们的示例中为群组名称或成员。
- 移除操作可从群组中移除成员。
- 添加操作可向群组添加成员。
规则如下:
- 我们永远不会从群组中移除未预配的成员(例如,在“替换”成员操作时)。
- 无论操作次数多少,系统都将 PATCH 请求视为原子操作。
输入验证如下:
- 每个请求的正文必须包含 URI 值为“urn:ietf:params:scim:api:messages:2.0:PatchOp”的“架构”属性。
- HTTP PATCH 请求的正文必须包含“操作”属性,其值为一个或多个 PATCH 操作的数组。每个 PATCH 操作对象必须恰好有一个“op”成员,其值指示要执行的操作,并且可以是“添加”、“移除”或“替换”中的一个。
- “路径”属性可以为空,在这种情况下,“值”应当为采用 {“path”: “value”} 格式的字典。
- 对于“移除”操作,需要“成员”路径。
- 对于“添加”操作,应当存在“成员”或“外部 ID” “路径”。
- 对于“替换”操作,可能存在“成员”路径。如果不存在,则意味着我们正在替换群组详细信息(如群组名称)而不是成员。
注意:
- 向群组分配/取消分配用户是异步操作,因此不会在 Udemy Business 中立即反映这些更改。
- 我们不支持嵌套群组,因此在此请求期间将会忽略这些群组。
响应:
- 如果操作成功,则会返回 HTTP 状态代码 204
- 如果群组不存在,则会返回 HTTP 状态代码 404
- 如果尝试向非组织成员用户分配群组,则会返回 HTTP 状态代码 404 和错误详细信息
- 如果请求未通过验证,则会返回 HTTP 状态代码 400,响应正文中会显示错误详细信息
DELETE /Groups/<id>
此端点用于移除或取消配置 Udemy Business 中的群组。
规则如下:
- 如果群组中包含未预配的成员,则会从群组中移除已预配的用户,删除“OrganizationSCIMGroup”记录。
响应:
- 如果操作成功,则会返回 HTTP 状态代码 204
- 如果群组不存在,则会返回 HTTP 状态代码 404
延伸阅读
- SCIM 概述:http://www.simplecloud.info
- RFC 7642,SCIM - 定义、概述、概念和要求:https://tools.ietf.org/pdf/rfc7642.pdf
- RFC 7643,SCIM - 核心架构:https://tools.ietf.org/pdf/rfc7643.pdf
- RFC 7644,SCIM - 协议:https://tools.ietf.org/pdf/rfc7644.pdf