跳到主要内容

设备流:使用 Logto 认证 (Authentication)

备注:

本指南假设你已在 Logto 控制台创建了类型为“原生应用”,并将授权流设置为设备流的应用程序。

简介

OAuth 2.0 设备授权许可(设备流)专为输入能力有限的设备设计,例如智能电视、游戏主机、CLI 工具和 IoT 设备。它允许用户在设备上启动登录流程,但在带有浏览器的其他设备(如手机或笔记本电脑)上完成认证 (Authentication)。

由于设备本身无法处理基于浏览器的登录流程,设备会显示一个短码和一个 URL。用户在另一台设备上访问该 URL,输入代码并登录。与此同时,原始设备会轮询 Logto,直到授权完成。

获取应用凭据

在 Logto 控制台中,进入你的应用详情页以获取以下凭据:

  • App ID:你的应用的唯一标识符(也称为 client_id)。
  • Logto endpoint:你的 Logto 授权服务器端点。你可以在 Logto 控制台的“应用详情”中找到。

对于 Logto Cloud,端点为 https://{your-tenant-id}.logto.app

备注:

设备流应用属于公开客户端,因此不需要 App Secret。

请求设备码

通过向设备授权端点发送 POST 请求来启动设备流:

curl --request POST 'https://your.logto.endpoint/oidc/device/auth' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'scope=openid offline_access profile'

响应内容包括:

字段描述
device_code你的应用在轮询令牌端点时使用的唯一代码。
user_code显示给用户,在浏览器中输入的短码。
verification_uri用户输入 user_code 的 URL。
verification_uri_complete已预填 user_code 的 URL。用户可直接访问此 URL 跳过手动输入——你可以将其展示为二维码、可点击链接或其他方式。
expires_indevice_codeuser_code 的有效期(秒)。过期后应停止轮询。

向用户展示验证 URL

在你的设备屏幕上显示 user_codeverification_uri

或者,你也可以使用已预填代码的 verification_uri_complete,用户只需确认即可。你可以选择以二维码、可点击链接等方式展示。

轮询令牌

当用户在浏览器中完成认证 (Authentication) 时,你的设备应轮询令牌端点。你的应用每次轮询请求之间应至少等待 5 秒

curl --request POST 'https://your.logto.endpoint/oidc/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:device_code' \
--data-urlencode 'device_code=DEVICE_CODE'

DEVICE_CODE 替换为设备授权响应中的 device_code

停止轮询 的时机:

  • 收到成功的令牌响应。
  • 设备码响应中的 expires_in 时间已过。
  • 收到不可重试的错误,如 expired_tokenaccess_denied

令牌响应

用户批准后,响应内容包括:

字段描述
access_token访问令牌。默认是一个不透明令牌 (Opaque token);当请求了 resource 时,是一个 JWT,aud 为资源 URI。
id_token包含用户身份声明 (Claims) 的 ID 令牌。仅在请求了 openid 权限 (Scope) 时返回。
refresh_token用于在无需重新认证 (Authentication) 的情况下获取新令牌。仅在请求了 offline_access 权限 (Scope) 时返回。
token_type始终为 Bearer
expires_in令牌有效期(秒)。
scope授权服务器授予的权限 (Scopes)。

检查点:测试你的设备流

现在,测试你的设备流集成:

  1. 运行你的应用并触发设备流以获取 device_codeuser_code
  2. 在浏览器中打开 verification_uri 并输入 user_code,或直接使用 verification_uri_complete 跳过手动输入。
  3. 在浏览器中完成登录流程。
  4. 验证你的应用在轮询后是否收到令牌。

获取用户信息

解码 ID 令牌声明 (Claims)

令牌响应中的 id_token 是标准的 JSON Web Token (JWT)。你可以解码 Base64URL 编码的负载部分(JWT 的第二段,以 . 分隔)来访问基本的用户声明 (Claims),无需额外的网络请求。

解码后的负载包含如 sub(用户 ID)、nameemail 等声明 (Claims),具体取决于请求的权限 (Scopes)。

提示:

生产环境下,你应在信任声明 (Claims) 前验证 JWT 签名。使用你的 Logto 端点的 JWKS(https://your.logto.endpoint/oidc/jwks)来验证令牌。

从 userinfo 端点获取

ID 令牌包含基于请求权限 (Scopes) 的基本声明 (Claims)。部分扩展声明(如 custom_dataidentities)仅可通过 OIDC UserInfo 端点 获取:

curl --request GET 'https://your.logto.endpoint/oidc/me' \
--header 'Authorization: Bearer ACCESS_TOKEN'

ACCESS_TOKEN 替换为从令牌响应中获得的不透明访问令牌 (Opaque token)(不是 JWT 资源令牌)。响应为包含用户声明 (Claims) 的 JSON 对象,内容基于授予的权限 (Scopes)。

请求额外声明 (Claims)

你可能会发现 ID 令牌中缺少某些用户信息。这是因为 OAuth 2.0 和 OpenID Connect (OIDC) 遵循最小权限原则 (PoLP),而 Logto 构建于这些标准之上。

默认情况下,返回的声明(Claim)是有限的。如果你需要更多信息,可以请求额外的权限(Scope)以访问更多的声明(Claim)。

信息:

“声明(Claim)”是关于主体的断言;“权限(Scope)”是一组声明。在当前情况下,声明是关于用户的一条信息。

以下是权限(Scope)与声明(Claim)关系的非规范性示例:

提示:

“sub” 声明(Claim)表示“主体(Subject)”,即用户的唯一标识符(例如用户 ID)。

Logto SDK 将始终请求三个权限(Scope):openidprofileoffline_access

如需请求更多权限 (Scopes),可在设备授权请求的 scope 参数中添加。例如,若需请求用户的邮箱和手机号:

curl --request POST 'https://your.logto.endpoint/oidc/device/auth' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'scope=openid offline_access profile email phone'

权限 (Scopes) 与声明 (Claims)

以下是支持的权限 (Scopes) 及其对应的声明 (Claims) 列表:

标准 OIDC 权限 (Scopes)

openid(默认)

Claim nameTypeDescription
substring用户的唯一标识符

profile(默认)

Claim nameTypeDescription
namestring用户的全名
usernamestring用户名
picturestring终端用户头像的 URL。该 URL 必须指向一个图片文件(例如 PNG、JPEG 或 GIF 图片文件),而不是包含图片的网页。请注意,该 URL 应专门指向适合在描述终端用户时显示的头像,而不是终端用户拍摄的任意照片。
created_atnumber终端用户创建的时间。该时间以自 Unix 纪元(1970-01-01T00:00:00Z)以来的毫秒数表示。
updated_atnumber终端用户信息最后更新时间。该时间以自 Unix 纪元(1970-01-01T00:00:00Z)以来的毫秒数表示。

其他 标准声明 (Claims) 包括 family_namegiven_namemiddle_namenicknamepreferred_usernameprofilewebsitegenderbirthdatezoneinfolocale 也会包含在 profile 权限 (Scope) 中,无需请求 userinfo 端点。与上表声明 (Claims) 不同的是,这些声明 (Claims) 仅在其值不为空时返回,而上表声明 (Claims) 的值为空时会返回 null

备注:

与标准声明 (Claims) 不同,created_atupdated_at 声明 (Claims) 使用的是毫秒而不是秒。

email

Claim nameTypeDescription
emailstring用户的电子邮件地址
email_verifiedboolean电子邮件地址是否已被验证

phone

Claim nameTypeDescription
phone_numberstring用户的电话号码
phone_number_verifiedboolean电话号码是否已被验证

address

关于 address 声明 (Claim) 的详细信息,请参阅 OpenID Connect Core 1.0

信息:

带有 (默认) 标记的权限 (Scopes) 总是由 Logto SDK 请求。当请求相应权限 (Scope) 时,标准 OIDC 权限 (Scopes) 下的声明 (Claims) 总是包含在 ID 令牌 (ID token) 中——无法关闭。

扩展权限 (Scopes)

以下权限 (Scopes) 由 Logto 扩展,并将通过 userinfo 端点 返回声明 (Claims)。这些声明 (Claims) 也可以通过 控制台 > 自定义 JWT 配置为直接包含在 ID 令牌 (ID token) 中。详见 自定义 ID 令牌 (ID token)

custom_data

Claim nameTypeDescriptionIncluded in ID token by default
custom_dataobject用户的自定义数据

identities

Claim nameTypeDescriptionIncluded in ID token by default
identitiesobject用户关联的身份
sso_identitiesarray用户关联的 SSO 身份

roles

Claim nameTypeDescriptionIncluded in ID token by default
rolesstring[]用户的角色 (Roles)

urn:logto:scope:organizations

Claim nameTypeDescriptionIncluded in ID token by default
organizationsstring[]用户所属的组织 (Organizations) ID
organization_dataobject[]用户所属的组织 (Organizations) 数据
备注:

这些组织 (Organizations) 声明 (Claims) 也可以在使用 不透明令牌 (Opaque token) 时通过 userinfo 端点获取。但不透明令牌 (Opaque tokens) 不能作为组织令牌 (Organization tokens) 用于访问组织专属资源。详见 不透明令牌 (Opaque token) 与组织 (Organizations)

urn:logto:scope:organization_roles

Claim nameTypeDescriptionIncluded in ID token by default
organization_rolesstring[]用户所属组织 (Organizations) 的角色 (Roles),格式为 <organization_id>:<role_name>

API 资源与组织 (Organizations)

我们建议首先阅读 🔐 基于角色的访问控制 (RBAC),以了解 Logto RBAC 的基本概念以及如何正确设置 API 资源。

请求 API 资源访问权限

如需访问特定 API 资源,在设备授权请求中添加 resource 参数:

curl --request POST 'https://your.logto.endpoint/oidc/device/auth' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'scope=openid offline_access' \
--data-urlencode 'resource=https://your-api-resource-indicator'

用户完成授权并获得刷新令牌后,你可以为该 API 资源获取 JWT 访问令牌:

curl --request POST 'https://your.logto.endpoint/oidc/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'refresh_token=REFRESH_TOKEN' \
--data-urlencode 'resource=https://your-api-resource-indicator'

响应将包含 aud 为你的 API 资源指示器的 JWT access_token

备注:

仅在初始设备授权请求中包含 offline_access 权限 (Scope) 时才会返回 refresh_token。Logto 使用令牌轮换,请始终存储并使用最新的 refresh_token

获取组织令牌 (Organization tokens)

如果你对 组织 (Organizations) 不熟悉,请阅读 🏢 组织 (Organizations)(多租户) 以了解基础。

如需请求组织相关信息,在设备授权请求中添加 urn:logto:scope:organizations 权限 (Scope):

curl --request POST 'https://your.logto.endpoint/oidc/device/auth' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'scope=openid offline_access urn:logto:scope:organizations' \
--data-urlencode 'resource=urn:logto:resource:organizations'

用户登录后,你可以使用刷新令牌获取组织令牌 (Organization token):

curl --request POST 'https://your.logto.endpoint/oidc/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'refresh_token=REFRESH_TOKEN' \
--data-urlencode 'organization_id=your-organization-id'

响应将包含限定于指定组织的访问令牌。

组织 API 资源

如需获取组织内 API 资源的访问令牌,请同时包含 resourceorganization_id 参数:

curl --request POST 'https://your.logto.endpoint/oidc/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'refresh_token=REFRESH_TOKEN' \
--data-urlencode 'organization_id=your-organization-id' \
--data-urlencode 'resource=https://your-api-resource-indicator'

延伸阅读

终端用户流程:认证 (Authentication) 流程、账户流程和组织流程 配置连接器 (Connectors) 授权 (Authorization)