跳到主要内容

Protected App — 非 SDK 认证 (Authentication) 集成

Protected App 旨在通过将认证 (Authentication)层与应用程序分离,消除SDK 集成的复杂性。我们负责认证 (Authentication),让你专注于核心功能。一旦用户通过认证 (Authentication),Protected App 就会从你的服务器提供内容。

Protected App 的工作原理

Protected App 由 Cloudflare 提供支持,在全球边缘网络上运行,确保你的应用低延迟和高可用性。

Protected App 会维护会话状态和用户信息。如果用户未通过认证 (Authentication),Protected App 会将其重定向到登录页面。认证 (Authentication) 成功后,Protected App 会将用户的请求附带认证 (Authentication) 和用户信息后转发到源服务器。

此过程可通过以下流程图进行可视化:

保护你的源服务器

源服务器可以是物理或虚拟设备,不属于 Logto 的 Protected App,存放你的应用内容。类似于内容分发网络 (CDN) 服务器,Protected App 管理认证 (Authentication) 流程并从你的源服务器获取内容。因此,如果用户可以直接访问你的源服务器,就能绕过认证 (Authentication),你的应用将不再受保护。

因此,保护源服务器连接非常重要,这可以防止攻击者在未认证 (Authentication) 的情况下发现并访问你的源服务器。常见的做法有:

  1. HTTP 头验证
  2. JSON Web Token (JWT) 验证

HTTP 头验证

你可以使用 HTTP 基本认证 (Authentication) 来保护你的源服务器。

每个来自 Protected App 的请求都会包含如下头部:

Authorization: Basic base64(appId:appSecret)

通过验证此头部,你可以确认请求来自 Protected App,并拒绝所有未包含此头部的请求。

如果你使用 Nginx 或 Apache,可以参考以下指南在源服务器上实现 HTTP 基本认证 (Authentication):

  1. Nginx: 配置 HTTP 基本认证 (Authentication)
  2. Apache: 认证 (Authentication) 与授权 (Authorization)

如需在应用程序中检查头部,请参考 Cloudflare 提供的 HTTP Basic Authentication 示例,了解如何使用 HTTP Basic 方案限制访问。

JSON Web Token (JWT) 验证

另一种保护源服务器的方法是使用 JSON Web Token (JWT)。

每个通过认证 (Authentication) 的 Protected App 请求都会包含如下头部:

Logto-ID-Token: <JWT>

该 JWT 被称为 ID 令牌 (ID token),由 Logto 签名并包含用户信息。通过验证此 JWT,你可以确认请求来自 Protected App,并拒绝所有未包含此头部的请求。

该令牌采用 JWS 令牌格式进行加密和签名。

验证步骤如下:

  1. 验证 JWT
  2. 验证 JWS 签名
  3. 令牌的发行者 (Issuer) 是 https://<your-logto-domain>/oidc(由你的 Logto 认证 (Authentication) 服务器签发)
const express = require('express');
const jwksClient = require('jwks-rsa');
const jwt = require('jsonwebtoken');

const ISSUER = 'https://<your-logto-domain>/oidc';
const CERTS_URL = 'https://<your-logto-domain>/oidc/jwks';

const client = jwksClient({
jwksUri: CERTS_URL,
});

const getKey = (header, callback) => {
client.getSigningKey(header.kid, function (err, key) {
callback(err, key?.getPublicKey());
});
};

const verifyToken = (req, res, next) => {
const token = req.headers['Logto-ID-Token'];

// 确保传入请求包含我们的令牌头部
if (!token) {
return res.status(403).send({ status: false, message: '缺少必需的 Logto-ID-Token 头部' });
}

jwt.verify(token, getKey, { issuer: ISSUER }, (err, decoded) => {
if (err) {
return res.status(403).send({ status: false, message: '无效的 ID 令牌' });
}

req.user = decoded;
next();
});
};

const app = express();

app.use(verifyToken);

app.get('/', (req, res) => {
res.send('Hello World!');
});

app.listen(3000);

获取认证 (Authentication) 状态和用户信息

如果你需要为应用获取认证 (Authentication) 和用户信息,也可以使用 Logto-ID-Token 头部。

如果你只想解码令牌,可以使用如下代码:

const express = require('express');

const decodeIdToken = (req, res, next) => {
const token = req.headers['Logto-ID-Token'];

if (!token) {
return res.status(403).send({
status: false,
message: '缺少必需的 Logto-ID-Token 头部',
});
}

const parts = token.split('.');
if (parts.length !== 3) {
throw new Error('无效的 ID 令牌');
}

const payload = parts[1];
const decodedPayload = atob(payload.replace(/-/g, '+').replace(/_/g, '/'));
const claims = JSON.parse(decodedPayload);

req.user = claims;
next();
};

const app = express();

app.use(decodeIdToken);

app.get('/', (req, res) => {
res.json(req.user);
});

app.listen(3000);

自定义 ID 令牌 (ID token) 声明 (Claims)

默认情况下,Logto-ID-Token 头部包含标准 OIDC 声明 (Claims)(如 subnameemail)。如需包含扩展声明 (Claims)(如角色或组织数据),需同时配置以下两项:

  1. 租户开关:在 控制台 > 自定义 JWT > ID 令牌 (ID token) 中启用该声明 (Claim)。
  2. Protected App 权限 (Scopes):在 Protected App 设置中,于 ID 令牌 (ID token) 声明 (Claims) > 附加权限 (Scopes) 选择对应权限 (Scope)。

仅当在自定义 JWT 中启用声明 (Claim) 且为 Protected App 选择了对应权限 (Scope) 时,扩展声明 (Claims) 才会包含在转发的 ID 令牌 (ID token) 中。完整扩展权限 (Scopes) 和声明 (Claims) 列表见 自定义 ID 令牌 (ID token)

权限 (Scope)声明 (Claims)
custom_datacustom_data
identitiesidentities, sso_identities
rolesroles
urn:logto:scope:organizationsorganizations, organization_data
urn:logto:scope:organization_rolesorganization_roles

获取原始主机

如果你需要获取客户端请求的原始主机,可以使用 Logto-Hostx-forwarded-host 头部。

自定义认证 (Authentication) 规则

默认情况下,Protected App 会保护所有路由。如果你需要自定义认证 (Authentication) 规则,可以在控制台设置“自定义认证 (Authentication) 规则”字段。

支持正则表达式,以下是两种场景示例:

  1. 仅保护 /admin/privacy 路由:^/(admin|privacy)/.*
  2. 排除 JPG 图片不进行认证 (Authentication):^(?!.*\.jpg$).*$

本地开发

Protected App 设计用于与你的源服务器配合使用。如果你的源服务器无法公开访问,可以使用 ngrokCloudflare Tunnels 等工具将本地服务器暴露到互联网。

迁移到 SDK 集成

Protected App 旨在简化认证 (Authentication) 流程。但如果你希望获得更好的控制和自定义,可以选择迁移到 SDK 集成。你可以在 Logto 中创建新应用并配置SDK 集成。为顺利迁移,你可以复用 Protected App 的应用配置。Protected App 实际上是 Logto 中的“传统 Web 应用”,你可以在应用设置中找到 “AppId” 和 “AppSecret”。迁移完成后,可以将 Protected App 从你的应用中移除。

Protected App:几步构建你的应用认证 (Authentication),无需代码。

Protected App 背后的动机

构建认证 (Authentication) 系统的最快方式