跳到主要内容

为你的 Webflow 应用添加认证 (Authentication)

本指南将向你展示如何将 Logto 集成到你的 Webflow 网站中。

提示:

示例项目可在 Webflow 项目预览 查看。

前提条件​

  1. 将 Logto 与 Webflow 集成需要 Webflow 的“自定义代码”功能,这至少需要“基础”计划。
  2. 一个 Webflow 网站,可以使用现有网站或创建一个新网站。

集成​

初始化 Logto 提供者​

备注:

在以下步骤中,我们假设你的 Webflow 网站运行在 https://your-awesome-site.webflow.io。

在此步骤中,我们将为你的 Webflow 网站添加全局级别的自定义代码。由于 Webflow 不支持 NPM,我们将使用 jsdelivr.com CDN 服务来导入 Logto SDK。

打开“Site settings”页面,导航到“Custom code”部分。将以下代码添加到“Head code”部分。

<script type="module">
// 从 jsdelivr CDN 导入 `@logto/browser` SDK
import LogtoClient from 'https://esm.run/@logto/browser';

// 将 `logtoClient` 实例分配给 window 对象,
// 以便在其他页面中全局使用
window.logtoClient = new LogtoClient({
endpoint: '<your-logto-endpoint>', // 例如 http://localhost:3001
appId: '<your-application-id>',
});
</script>

实现登录​

在我们深入细节之前,下面是终端用户体验的快速概览。登录流程可以简化为如下:

  1. 你的应用调用登录方法。
  2. 用户被重定向到 Logto 登录页面。对于原生应用,会打开系统浏览器。
  3. 用户完成登录后被重定向回你的应用(配置为重定向 URI)。

关于基于重定向的登录​

  1. 此认证 (Authentication) 过程遵循 OpenID Connect (OIDC) 协议,Logto 强制执行严格的安全措施以保护用户登录。
  2. 如果你有多个应用程序,可以使用相同的身份提供商 (IdP)(日志 (Logto))。一旦用户登录到一个应用程序,当用户访问另一个应用程序时,Logto 将自动完成登录过程。

要了解有关基于重定向的登录的原理和好处的更多信息,请参阅 Logto 登录体验解释。


配置登录重定向 URI​

让我们切换到 Logto Console 的应用详情页面。添加一个重定向 URI https://your-awesome-site.webflow.io/callback 并点击“保存更改”。

Logto Console 中的重定向 URI

实现登录按钮​

返回到你的 Webflow 设计器,拖放一个“登录”按钮到主页,并为其分配一个 ID “sign-in”,以便稍后使用 getElementById() 进行引用。

<script type="module">
const signInButton = document.getElementById('sign-in');
const onClickSignIn = () => logtoClient.signIn('https://your-awesome-site.webflow.io/callback');
signInButton.addEventListener('click', onClickSignIn);
</script>

处理重定向​

我们快完成了!在最后一步中,我们使用 https://your-awesome-site.webflow.io/callback 作为重定向 URI,现在我们需要正确处理它。

首先,让我们在 Webflow 中创建一个“Callback”页面,并简单地放置一些静态文本“正在重定向...”。然后将以下页面级自定义代码添加到“Callback”页面。

<script type="module">
(async () => {
// 通过调用 SDK 方法处理登录回调逻辑
await logtoClient.handleSignInCallback(window.location.href);

// 处理完成后重定向回主页
window.location.assign('https://your-awesome-site.webflow.io');
})();
</script>

实现登出​

调用 .signOut() 将清除内存和 localStorage 中的所有 Logto 数据(如果存在)。

注销后,最好将用户重定向回你的网站。让我们在管理控制台中将 https://your-awesome-site.webflow.io 添加为注销后 URI(显示在重定向 URI 下),并在调用 .signOut() 时使用该 URL 作为参数。

实现一个注销按钮​

返回 Webflow 设计器,在你的主页上添加一个“注销”按钮。同样,为按钮分配一个 ID “sign-out”,并将以下代码添加到页面级自定义代码中。

const signOutButton = document.getElementById('sign-out');
const onClickSignOut = () => logtoClient.signOut('https://your-awesome-site.webflow.io');
signOutButton.addEventListener('click', onClickSignOut);

处理认证 (Authentication) 状态​

在 Logto SDK 中,我们通常可以使用 logtoClient.isAuthenticated() 方法来检查认证 (Authentication) 状态,如果用户已登录,值将为 true;否则,将为 false。

在你的 Webflow 网站中,你也可以使用它来通过编程方式显示和隐藏登录和注销按钮。应用以下自定义代码以相应地调整按钮的 CSS。

const isAuthenticated = await logtoClient.isAuthenticated();

signInButton.style.display = isAuthenticated ? 'none' : 'block';
signOutButton.style.display = isAuthenticated ? 'block' : 'none';

检查点:测试你的 Webflow 网站​

现在,测试你的网站:

  1. 部署并访问你的网站 URL,登录按钮应该是可见的。
  2. 点击登录按钮,SDK 将启动登录过程,将你重定向到 Logto 登录页面。
  3. 登录后,你将被重定向回你的网站,看到用户名和退出按钮。
  4. 点击退出按钮以退出。

获取用户信息​

你可以使用这些 Logto 方法以编程方式检索用户信息:

  • 获取 ID 令牌 (ID token) 声明 (Claims):通过解码本地 ID 令牌获取用户信息。某些声明可能不可用。
  • 获取用户信息:通过向 userinfo endpoint 发送请求获取用户信息。

需要注意的是,可以检索的用户信息声明取决于用户在登录时使用的权限,并且考虑到性能和数据大小,ID 令牌可能不包含所有用户声明,某些用户声明仅在 userinfo endpoint 中可用(请参阅下面的相关列表)。

Logto 使用 OIDC 权限 (Scopes) 和声明 (Claims) 约定 来定义用于从 ID 令牌 (ID token) 和 OIDC userinfo 端点 获取用户信息的权限 (Scopes) 和声明 (Claims)。"scope" 和 "claim" 都是 OAuth 2.0 和 OpenID Connect (OIDC) 规范中的术语。

对于标准 OIDC 声明 (Claims),其在 ID 令牌 (ID token) 中的包含严格由所请求的权限 (Scopes) 决定。扩展声明 (Claims)(如 custom_data 和 organizations)可以通过 自定义 ID 令牌 (Custom ID token) 设置额外配置到 ID 令牌 (ID token) 中。

简而言之,当你请求某个权限 (Scope) 时,你将在用户信息中获得相应的声明 (Claims)。例如,如果你请求 `email` 权限 (Scope),你将获得用户的 `email` 和 `email_verified` 数据。

默认情况下,Logto SDK 总是会请求三个权限 (Scopes):`openid`、`profile` 和 `offline_access`,并且无法移除这些默认权限 (Scopes)。但你可以在配置 Logto 时添加更多权限 (Scopes):

<script type="module">
// 从 jsdelivr CDN 导入 `@logto/browser` SDK
import LogtoClient from 'https://esm.run/@logto/browser';

// 将 `logtoClient` 实例分配给 window 对象,
// 以便在其他页面中全局使用
window.logtoClient = new LogtoClient({
endpoint: '<your-logto-endpoint>', // 例如 http://localhost:3001
appId: '<your-application-id>',
scopes: [
UserScope.Email,
UserScope.Phone,
UserScope.CustomData,
UserScope.Identities,
UserScope.Organizations,
],
});
</script>

以下是支持的权限 (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_name、given_name、middle_name、nickname、preferred_username、profile、website、gender、birthdate、zoneinfo 和 locale 也会包含在 profile 权限 (Scope) 中,无需请求 userinfo 端点。与上表声明 (Claims) 不同的是,这些声明 (Claims) 仅在其值不为空时返回,而上表声明 (Claims) 的值为空时会返回 null。

备注:

与标准声明 (Claims) 不同,created_at 和 updated_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 资源​

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

配置 Logto 客户端​

一旦你设置了 API 资源,就可以在应用中配置 Logto 时添加它们:

<script type="module">
// 从 jsdelivr CDN 导入 `@logto/browser` SDK
import LogtoClient from 'https://esm.run/@logto/browser';

// 将 `logtoClient` 实例分配给 window 对象,
// 以便在其他页面中全局使用
window.logtoClient = new LogtoClient({
endpoint: '<your-logto-endpoint>', // 例如 http://localhost:3001
appId: '<your-application-id>',
resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], // 添加 API 资源 (API resources)
});
</script>

每个 API 资源都有其自己的权限(权限)。

例如,https://shopping.your-app.com/api 资源具有 shopping:read 和 shopping:write 权限,而 https://store.your-app.com/api 资源具有 store:read 和 store:write 权限。

要请求这些权限,你可以在应用中配置 Logto 时添加它们:

<script type="module">
// 从 jsdelivr CDN 导入 `@logto/browser` SDK
import LogtoClient from 'https://esm.run/@logto/browser';

window.logtoClient = new LogtoClient({
endpoint: '<your-logto-endpoint>',
appId: '<your-application-id>',
scopes: ['shopping:read', 'shopping:write', 'store:read', 'store:write'],
resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'],
});
</script>

你可能会注意到权限是与 API 资源分开定义的。这是因为 OAuth 2.0 的资源指示器 指定请求的最终权限将是所有目标服务中所有权限的笛卡尔积。

因此,在上述情况下,权限可以从 Logto 中的定义简化,两个 API 资源都可以拥有 read 和 write 权限,而无需前缀。然后,在 Logto 配置中:

<script type="module">
// 从 jsdelivr CDN 导入 `@logto/browser` SDK
import LogtoClient from 'https://esm.run/@logto/browser';

window.logtoClient = new LogtoClient({
endpoint: '<your-logto-endpoint>',
appId: '<your-application-id>',
scopes: ['read', 'write'],
resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'],
});
</script>

对于每个 API 资源,它将请求 read 和 write 权限。

备注:

请求 API 资源中未定义的权限是可以的。例如,即使 API 资源没有可用的 email 权限,你也可以请求 email 权限。不可用的权限将被安全地忽略。

成功登录后,Logto 将根据用户的角色向 API 资源发布适当的权限。

获取 API 资源的访问令牌 (Access token)​

要获取特定 API 资源的访问令牌 (access token),你可以使用 getAccessToken 方法:

const isAuthenticated = await logtoClient.isAuthenticated();

if (isAuthenticated) {
(async () => {
const token = await logtoClient.getAccessToken();
console.log(token);
})();
}

此方法将返回一个 JWT 访问令牌 (access token),当用户具有相关权限时,可以用来访问 API 资源。如果当前缓存的访问令牌 (access token) 已过期,此方法将自动尝试使用刷新令牌 (refresh token) 获取新的访问令牌 (access token)。

获取组织令牌 (Organization tokens)​

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

在配置 Logto 客户端时,你需要添加 UserScope.Organizations 权限:

import LogtoClient, { UserScope } from 'https://esm.run/@logto/browser';

window.logtoClient = new LogtoClient({
// ...other configs
scopes: [UserScope.Organizations],
});

用户登录后,你可以获取用户的组织令牌:

import { UserScope } from 'https://esm.run/@logto/browser';

const isAuthenticated = await logtoClient.isAuthenticated();

(async () => {
if (!isAuthenticated) {
return;
}
const claims = await logtoClient.getIdTokenClaims();

console.log('ID 令牌声明 (ID token claims):', claims);
console.log('组织 (Organization) IDs:', claims.organizations);

// 假设至少有一个组织 (Organization),我们取第一个
const organizationId = claims.organizations[0];
const token = await logtoClient.getOrganizationToken(organizationId);

console.log('组织 (Organization) 访问令牌 (access token):', token);
})();

延伸阅读​

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