Authagonal

文档

开始使用 Authagonal 所需的一切——从创建您的第一个租户到接入 SSO、SCIM 和自定义品牌。

快速入门

Authagonal 为每个租户提供完全符合标准的 OIDC 服务器。每个租户拥有自己的发行者 URL、发现文档和令牌端点——租户之间不共享基础设施。您可以在 5 分钟内从零开始完成登录流程的搭建。

创建租户

authagonal.io 注册并为您的租户选择一个 slug。该 slug 将成为您的发行者域名:{slug}.authagonal.io。创建账户后,验证您的电子邮箱以激活租户。

Authagonal signup page showing tenant slug input and email verification

在注册时为您的租户选择唯一的 slug

注册客户端

在门户侧边栏中导航到客户端,然后点击创建。为您的应用输入 clientIdclientName。然后配置至少一个重定向 URI——这是用户完成身份验证后跳转的地址。例如:https://app.example.com/callback

Client creation form with clientId, clientName, and redirect URI fields

在门户中注册新的 OAuth 客户端

本地开发

本地开发时使用 http://localhost:3000/callback 作为重定向 URI。Authagonal 允许 localhost 来源使用非 HTTPS 重定向 URI。

您的首次登录

最快的集成方式是使用 oidc-client-ts,一个轻量级的 JavaScript 和 TypeScript 应用 OIDC 客户端库。

oidc-client-ts integration
import { UserManager } from 'oidc-client-ts';

const mgr = new UserManager({
  authority: 'https://acme.authagonal.io',
  client_id: 'my-app',
  redirect_uri: 'https://app.example.com/callback',
  response_type: 'code',
  scope: 'openid profile email',
});

// Redirect to login
mgr.signinRedirect();

// On callback page
const user = await mgr.signinRedirectCallback();
console.log(user.profile); // { sub, email, name, ... }

如果您更喜欢不使用库的最简方式,可以使用标准的 OAuth 2.0 授权码流程配合原生 fetch

Minimal fetch-based flow
// 1. Redirect the user to the authorization endpoint
const authorizeUrl = new URL('https://acme.authagonal.io/connect/authorize');
authorizeUrl.searchParams.set('client_id', 'my-app');
authorizeUrl.searchParams.set('redirect_uri', 'https://app.example.com/callback');
authorizeUrl.searchParams.set('response_type', 'code');
authorizeUrl.searchParams.set('scope', 'openid profile email');
authorizeUrl.searchParams.set('code_challenge', codeChallenge);
authorizeUrl.searchParams.set('code_challenge_method', 'S256');
window.location.href = authorizeUrl.toString();

// 2. On the callback page, exchange the code for tokens
const res = await fetch('https://acme.authagonal.io/connect/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    grant_type: 'authorization_code',
    code: new URLSearchParams(window.location.search).get('code')!,
    redirect_uri: 'https://app.example.com/callback',
    client_id: 'my-app',
    code_verifier: codeVerifier,
  }),
});

const tokens = await res.json();
// tokens.id_token, tokens.access_token, tokens.refresh_token
Authagonal login page with email and password fields, branded with tenant logo

您的租户默认登录页面

沙箱模式

先在沙箱模式中测试您的集成。沙箱租户使用单独的 URL({slug}-sandbox.authagonal.io),可以随时从生产环境刷新而不影响正式用户。

仪表板

门户仪表板为您提供租户的实时概览。它展示最重要的指标——用户增长、身份验证活动,以及快速导航到门户中的每个功能。

概览

仪表板顶部显示欢迎消息和当前用户总数。下方的日活跃用户图表显示过去 7 天每天完成身份验证的唯一用户数,让您快速了解参与趋势。

Full dashboard view showing welcome banner, user count, and Daily Active Users line chart

仪表板主屏幕,包含 DAU 图表和活动概览

活动指标

活动指标面板显示四张统计卡片,汇总关键身份验证事件:

  • 成功登录——已完成的身份验证流程总数
  • 失败登录——凭证错误、账户锁定或策略拒绝
  • 活跃用户——所选时间段内完成身份验证的唯一用户数
  • SCIM 操作——来自已连接身份提供商的用户和组配置事件

使用时间范围筛选器在24 小时3 天7 天30 天之间切换。所有统计卡片和图表会更新以反映所选时间窗口。

Activity metrics panel with four stat cards and a time range filter bar showing 24h, 3d, 7d, and 30d options

可配置时间范围的活动指标

快速导航

指标面板下方,导航卡片直接链接到每个主要功能:客户端、用户、组、角色、SSO、SCIM、品牌和设置。每张卡片都显示简要说明,方便新团队成员快速了解。

客户端

OAuth 客户端代表通过您的租户进行用户身份验证的应用。每个客户端都有自己的重定向 URI、作用域、授权类型、令牌有效期和 MFA 策略配置。

客户端列表

客户端页面显示所有已注册客户端的表格。每行显示 clientId、显示名称、以彩色标签形式显示的允许授权类型,以及是否启用了 PKCE。点击任意行可打开完整的配置编辑器。

Clients table showing clientId, name, grant type badges, and PKCE status for each registered application

带有授权类型标签和 PKCE 指示器的客户端列表

创建客户端

点击创建客户端以注册新应用。您需要提供两个字段:

  • clientId——客户端的唯一标识符(例如 my-spa
  • clientName——易于阅读的显示名称
Create client form with clientId and clientName input fields

注册新的 OAuth 客户端

删除客户端

要删除客户端,请打开客户端配置,然后点击页面底部的删除客户端按钮。系统会要求您在永久删除前进行确认。已删除客户端的所有活跃会话和令牌将立即失效。

客户端配置参考

每个客户端都有一组全面的配置选项,分为多个部分。

常规设置

设置项描述默认值
clientName在同意屏幕和门户中显示的名称
requirePkce在授权码流程中要求 Proof Key for Code Exchange开启
requireClientSecret令牌请求时要求客户端密钥(对公共客户端如 SPA 禁用此项)关闭
allowOfflineAccess允许客户端通过 offline_access 作用域请求刷新令牌关闭
alwaysIncludeUserClaimsInIdToken将所有用户声明直接包含在 ID 令牌中,而非要求调用 UserInfo关闭
includeGroupsInTokens在 ID 令牌中以 groups 声明形式包含用户的组成员关系关闭

PKCE 安全性

禁用 PKCE 会降低授权码流程的安全性。仅对不支持 PKCE 的旧版客户端禁用此项。所有现代应用都应保持 PKCE 启用。

URI

URI 字段使用标签输入——输入值后按回车逗号添加。点击标签上的 X 可将其移除。

设置项描述
redirectUris身份验证后允许的回调 URL。必须与授权请求中的 redirect_uri 参数完全匹配。
postLogoutRedirectUris注销后允许重定向到的 URL。
allowedCorsOrigins允许对令牌和 UserInfo 端点进行跨域请求的源。
URI configuration section showing tag inputs for redirect URIs, post-logout URIs, and CORS origins

用于配置 URI 的标签输入字段

作用域与授权类型

设置项选项
allowedScopesopenid profile email offline_access
allowedGrantTypesauthorization_code client_credentials refresh_token device_code

令牌有效期

设置项描述默认值
accessTokenLifetimeSeconds访问令牌的有效时长1800(30 分钟)
identityTokenLifetimeSecondsID 令牌的有效时长300(5 分钟)
authorizationCodeLifetimeSeconds授权码可用于交换的有效时长300(5 分钟)
absoluteRefreshTokenLifetimeSeconds刷新令牌的最大有效期,不受活动状态影响2592000(30 天)
slidingRefreshTokenLifetimeSeconds刷新令牌在每次使用时重置过期时间,但不超过绝对有效期1296000(15 天)
Token lifetime configuration fields with numeric inputs for each lifetime setting

按客户端配置令牌有效期

登出 URI

客户端可以注册 back-channel 和 front-channel 登出 URI。两者均为可选——配置与您的应用清理会话方式相匹配的那一个或两个。

设置项描述
backChannelLogoutUri带签名登出令牌的服务器到服务器 POST。即使用户浏览器离线也可靠。
frontChannelLogoutUri在登出时加载到隐藏 iframe 中,使浏览器清理 cookie 和本地存储。
frontChannelLogoutSessionRequired启用时,登出 URL 会收到 isssid 查询参数,以便应用将登出与特定会话关联。

一起使用

Back-channel 保证服务器收到通知;front-channel 清理浏览器。大多数应用都会受益于同时配置两者。

MFA 策略

每个客户端可以使用按客户端设置覆盖租户范围的 MFA 策略。MFA 策略下拉菜单提供三个选项:

策略行为
已禁用此客户端不会提示 MFA
已启用用户可以选择注册 MFA;已注册的用户在登录时会收到提示
必需所有用户必须完成 MFA 才能通过此客户端进行身份验证
MFA policy dropdown showing Disabled, Enabled, and Required options on the client configuration page

按客户端的 MFA 策略覆盖

企业 SSO

企业 SSO 让您的客户使用自己的身份提供商。Authagonal 支持 SAML 2.0 和 OIDC 联合身份验证,配合基于域的路由,用户可以根据其电子邮箱地址自动跳转到正确的身份提供商。

SSO domain routing flow — shows how email domains are matched to SAML or OIDC identity providers

基于域的 SSO 路由

SAML 2.0 连接

要创建 SAML 连接,请导航到 SSO 页面并选择 SAML 选项卡。提供以下信息:

字段描述
connectionName此连接的可读名称(例如「Acme Corp Okta」)
entityId您的 SP 实体 ID。请在您的 IdP 中将此值原样注册为应用程序的标识符(Entity ID);断言必须将其作为 Audience
metadataUrlIdP 的 SAML 元数据 XML 文档的 URL
metadataXml粘贴的 IdP 元数据 XML,适用于没有元数据 URL 的 IdP(Google Workspace)或其 URL 无法从互联网访问的情况。此项与 metadataUrl 二选一,不能同时提供
nameIdFormat可选的、向 IdP 请求的 NameID 格式。留空使用默认的 emailAddress,或设为 "none" 以完全省略 NameIDPolicy(ADFS 推荐)

保存连接后,Authagonal 会获取元数据文档并导入身份提供商的签名证书、SSO 端点 URL 和名称标识符格式。元数据会定期刷新以获取证书轮换。

SAML connection creation form with fields for connection name, entity ID, and metadata URL

创建 SAML 2.0 SSO 连接

OIDC 连接

要创建 OIDC 联合身份验证连接,请选择 OIDC 选项卡并提供:

字段描述
connectionName此连接的可读名称
discoveryUrlOpenID Connect 发现 URL(例如 https://login.microsoftonline.com/{tenant}/v2.0/.well-known/openid-configuration)
clientId在外部身份提供商中为此联合注册的 client ID
clientSecret外部身份提供商注册的 client secret
OIDC connection creation form with fields for connection name, discovery URL, client ID, and client secret

创建 OIDC 联合身份验证连接

域路由

域路由根据用户的电子邮箱域名自动将用户重定向到正确的身份提供商。当用户在登录页面输入电子邮箱时,Authagonal 会检查域名部分(例如 acme.com)是否匹配任何已配置的 SSO 连接。如果匹配,用户将被无缝重定向到其组织的身份提供商。

电子邮箱域名SSO 提供商协议
acme.comAcme Corp OktaSAML 2.0
contoso.comContoso Azure ADOIDC
example.orgExample OneLoginSAML 2.0
Domain routing table showing email domains mapped to SSO connections with protocol type

域路由将电子邮箱域名映射到身份提供商

SP 发起的流程

SP 发起的流程是默认模式——用户从您的登录页面开始,自动路由到正确的身份提供商。用户也可以通过 /saml/{connectionId}/login/oidc/{connectionId}/login 直接链接到特定连接。

JIT 配置

默认情况下,当用户首次通过 SSO 登录且在您的租户中不存在时,Authagonal 会自动创建其账户(即时配置)。可以通过在创建或编辑连接时勾选禁用 JIT 配置来按连接禁用此功能。

禁用 JIT 配置后,只有预先配置的用户——通过 SCIM、门户的用户页面或 API——才能通过该连接登录。未知用户会收到 access_denied 错误,并被引导联系其管理员。

按连接设置

JIT 配置是按 SSO 连接控制的,而非租户范围。您可以让一个连接允许 JIT(例如,用于自行管理用户的合作伙伴组织),而另一个连接要求预先配置(例如,用于使用 SCIM 同步的企业客户)。

推广前测试

在向生产用户推广前,使用沙箱模式测试 SSO 连接。这可以让您在不影响正式身份验证流程的情况下验证身份提供商配置、属性映射和域路由。

用户

用户页面让您管理租户中的所有终端用户。您可以搜索用户、查看用户详情、创建新账户,并查看每个用户的配置来源。

搜索栏支持按电子邮箱或用户 ID 筛选。搜索有 300 毫秒的防抖延迟,因此在您输入时结果会更新,而不会过度调用 API。结果以每页 50 个用户分页——使用表格底部的导航控件在页面之间切换。

用户表格

用户表格为每个用户显示以下列:

描述
电子邮箱用户的电子邮箱地址,如果邮箱已确认则显示验证标识
用户 ID分配给用户的唯一标识符
全名名字和姓氏的组合
状态ActiveInactive ——表示账户是否已启用
MFAEnabledOff ——是否已注册多因素身份验证
来源SCIMLocal ——用户的创建方式
创建时间用户账户的创建日期
User list table with columns for email, user ID, name, status, MFA, source, and created date

带搜索栏和分页的用户列表

创建用户

点击创建用户以添加新的本地用户。表单需要填写:

字段描述
email用户的电子邮箱地址(在租户内必须唯一)
password初始密码(至少 8 个字符,必须符合您的租户密码策略)
firstName用户的名字
lastName用户的姓氏
language首选语言。设置用户的界面和邮件语言;可选,默认回退到英语。
Create user form with email, password, first name, last name, and preferred Language fields

创建新的本地用户

SCIM 配置的用户

通过 SCIM 创建的用户会带有「SCIM」标签,且不能通过门户更改密码。其生命周期——创建、更新和停用——完全由上游身份提供商管理。

首选语言

每个用户都有一个首选语言,它同时决定其托管界面以及 Authagonal 发送给他们的事务性电子邮件(验证、密码重置、欢迎等)。您可以在创建用户时设置它,并随时从用户详情页面更改。如果用户未设置首选语言,Authagonal 将回退到英语。选择器提供所有受支持的语言:英语、德语、法语、西班牙语、葡萄牙语、越南语和简体中文。

User detail edit form with profile fields including a preferred Language selector

在详情页面设置用户的首选语言

用户详情

点击用户列表中的任意一行以打开其详情页。您可以在此编辑个人资料、管理角色、重置 MFA、查看自定义属性,以及删除用户。

User detail page showing profile fields, status, assigned roles, and custom attributes

个人资料

编辑用户的邮箱、姓/名、电话、公司、外部 ID 和激活状态。邮箱更改必须在租户内保持唯一;如果已被占用,API 会返回 email_in_use

角色

分配和取消在角色页面定义的角色。当客户端启用 includeRolesInTokens 时,角色成员关系会出现在 ID 和访问令牌中。

多因素认证

查看为用户注册的每个 MFA 凭据——身份验证器应用(TOTP)、WebAuthn/通行密钥以及恢复码——每个都有注册和最后使用时间戳。您可以删除单个凭据,或重置所有 MFA。重置会强制用户在下次登录时重新注册。

自定义属性

附加到用户的任意键/值数据。键必须唯一。属性通过用户资料 API 和 SCIM 公开,并可通过配置自定义作用域的 userClaims 映射到访问令牌声明。

组织

该用户所属的组织。它是您自己的一个自由文本标识符,会作为 org_id 声明出现在该用户的令牌上,以及出现在 /connect/userinfo 上(需带 profile 作用域)。Authagonal 从不推导这个值:它由您的配置应用设置,或由创建该用户的 SCIM 凭据设置,也可以在此处手动设置。

字段旁边的链接会列出共享该值的所有人,同一个筛选条件在 API 上也可用,形式为 GET /api/v1/users?organizationId=。用它来查明谁属于某个特定客户,而不必翻遍整个用户目录。

删除用户

永久删除该用户及其所有 MFA 凭据。输入用户邮箱以确认——无法撤销。

组让您组织用户并在令牌中包含组成员关系。组可以在门户中手动创建,也可以通过 SCIM 从外部身份提供商自动配置。

组列表

组页面显示租户中的所有组及以下信息:

描述
组名组的显示名称
成员当前组中的用户数量
来源SCIMManual ——组的创建方式
创建时间组的创建日期
Groups list table showing group name, member count, source badge, and created date

带来源指示器的组列表

创建组

点击创建组并输入组的 displayName。组名应具有描述性且在租户内唯一(例如「Engineering」、「Billing Admins」、「Beta Testers」)。

组详情与成员

点击任意组可打开详情视图。在这里您可以查看所有当前成员并管理成员关系:

  • 添加成员——输入用户 ID 将用户添加到组。
  • 移除成员——点击任意成员旁边的移除按钮即可单独移除。
Group detail view showing member list with user IDs and a field to add new members

在详情视图中管理组成员关系

令牌中的组

当客户端启用 includeGroupsInTokens 时,ID 令牌中会包含一个 groups 声明,其中包含用户的组成员关系。每个条目包含组的 idname

groups claim in ID token
{
  "sub": "user-123",
  "email": "[email protected]",
  "groups": [
    { "id": "grp-001", "name": "Engineering" },
    { "id": "grp-002", "name": "Beta Testers" }
  ]
}

按客户端启用

includeGroupsInTokens 设置在每个客户端上单独配置。导航到客户端的常规设置以启用它。

角色

角色支持应用中的基于角色的访问控制(RBAC)。在 Authagonal 中定义角色,将其分配给用户,并使用令牌中的 roles 声明在您的应用逻辑中执行授权。

管理角色

角色页面显示所有已定义角色的表格,支持内联编辑。每个角色包含:

描述
名称角色的唯一标识符(例如 "admin"、"editor"、"viewer")
描述角色所授予权限的可读描述
创建时间角色的创建日期

创建角色

点击创建角色并提供名称和描述。角色名称应简洁,并在应用中遵循一致的命名约定(例如,使用小写加连字符:billing-admin)。

内联编辑

角色支持直接在表格中内联编辑。点击任意角色上的铅笔图标进入编辑模式——名称和描述字段变为可编辑。修改值后,点击对勾图标保存。更改立即生效。

删除角色

点击任意角色上的删除图标将其移除。系统会要求您在永久删除前进行确认。移除角色不会追溯使现有令牌失效——该角色将不会出现在删除后签发的新令牌中。

Roles table with inline editing active, showing editable name and description fields with save and cancel icons

在角色表格中内联编辑角色

令牌中的角色

分配给用户的角色作为 roles 声明包含在 ID 令牌中。您的应用可以读取此声明来做出授权决策:

roles claim in ID token
{
  "sub": "user-123",
  "email": "[email protected]",
  "roles": ["admin", "billing-admin"]
}

SCIM 用户配置

SCIM 2.0(跨域身份管理系统)支持从 Okta、Azure AD、OneLogin 和 JumpCloud 等企业身份提供商自动配置用户和组。配置完成后,用户账户和组成员关系会自动从上游身份提供商同步到您的 Authagonal 租户。

SCIM provisioning flow — shows user lifecycle events flowing from enterprise IdP through Authagonal SCIM API to your app via TCC webhooks

SCIM 用户生命周期同步与下游配置

设置步骤

按照以下步骤为客户端启用 SCIM 用户配置:

  1. 选择客户端应用——选择 SCIM 配置将关联的 OAuth 客户端。
  2. 生成 SCIM 令牌——提供描述和到期天数,然后生成令牌。
  3. 立即复制令牌——原始令牌值仅显示一次。在关闭对话框前复制它。
  4. 配置您的身份提供商——在您的身份提供商的 SCIM 设置中,输入基础 URL 和 Bearer 令牌。
  5. 测试用户同步——从您的身份提供商触发测试同步,并验证用户是否出现在 Authagonal 门户中。

SCIM 基础 URL

使用以下基础 URL 配置您的身份提供商:

SCIM Base URL
https://{slug}.authagonal.io/scim/v2

{slug} 替换为您的租户 slug。

SCIM configuration page showing client selector, token generation form, and base URL

SCIM 设置页面,包含令牌生成功能

令牌管理

SCIM 令牌用于验证来自身份提供商的配置请求。您可以为每个客户端管理多个令牌:

字段描述
描述用于标识令牌的标签(例如「Okta Production SCIM」)
过期时间令牌有效期(天数,1 到 3650)。不需要频繁轮换的令牌可留空或设置较大的值。
状态活跃令牌正在使用中。已撤销的令牌显示 Revoked 标识且无法再用于验证请求。

要撤销令牌,请点击令牌旁边的撤销按钮。已撤销的令牌在列表中仍然可见(用于审计目的),但会立即停止接受请求。

SCIM token list showing active and revoked tokens with description, expiry date, and revoke button

令牌管理,包含活跃和已撤销令牌指示器

立即复制令牌

原始 SCIM 令牌在创建时仅显示一次。请立即复制——之后无法再次获取。如果您丢失了令牌,需要生成新令牌并更新您的身份提供商配置。

测试连接

通过查询 ServiceProviderConfig 端点验证您的 SCIM 集成是否正常工作:

Test SCIM connectivity
curl -H "Authorization: Bearer YOUR_TOKEN" \
  https://acme.authagonal.io/scim/v2/ServiceProviderConfig

成功的响应会返回一个 JSON 文档,描述支持的 SCIM 功能,包括批量操作、筛选和更改密码功能。

首选语言

SCIM 的 preferredLanguage 属性(回退到 locale)会映射到用户已存储的语言。通过 SCIM 配置的 SSO 用户会自动收到其 IdP 所发送语言的本地化邮件。

一个凭据能看到什么

一个 SCIM 凭据只能看到它自己配置的用户和组。读取、更新或删除由其他连接器创建的用户会返回 404,列表只返回它自己的,而一个组的成员也只能是同一个连接器配置过的用户。以任何其他方式创建的账户,无论是由管理员创建、通过自助注册创建,还是通过 SSO 即时配置创建,对 SCIM 都完全不可见。

这条边界是按客户端划分的,而不是按令牌。在同一个客户端上签发的两个凭据是拥有两个密钥的同一个身份,每一个都可以修改另一个创建的内容。请给互不信任的连接器各配一个客户端。externalId 的作用范围也是如此,因此两个连接器可以各自把 ext-001 用在不同的人身上而不会冲突。

取消配置

DELETE /scim/v2/Users/{id} 会停用该账户并将其标记为已删除。记录会按 RFC 7644 允许的方式保留,但对之后的每一次操作都返回 404,并且不会出现在列表中。该用户的 MFA 注册和组成员关系会被清除,其 externalId 映射会被释放,已签发的令牌全部被撤销,因此访问权限立即终止,而不是等到令牌下次过期。

如果同一个人后来被重新雇用并重新创建,他们会获得一个新的用户 id。标识符从不重复使用:该 id 是您签发过的每一个令牌中的 subject,因此重用它会在每个信任该 id 的应用中,悄无声息地把前一位持有者的历史交给新入职的人。

为已同步的用户标记组织

SCIM 没有提供任何方式让连接器说明它正在同步您的哪一个客户。核心 SCIM 未定义组织属性,因此一次普通的用户创建只会告诉您此人的姓名和邮箱,不会说明他们属于谁。如果多个客户都向您的租户配置用户,他们的用户到达时无法区分。

回答这个问题的是凭据,而不是请求本身。创建 SCIM 令牌时,请将组织设置为您自己的一个标识符。通过该令牌配置的每个用户都会被标记上它,此后它会作为 org_id 声明出现在这些用户的令牌上。为每个客户在同一个客户端上签发一个凭据,他们同步过来的用户就能被正确归属,而不必为每个客户单独注册一个客户端。留空则用户不带标记,这也是在此功能出现之前每个凭据的行为方式。

打标记不等于隔离

所有权是按客户端强制执行的,而不是按令牌。同一个客户端上的两个凭据是拥有两个密钥的同一个身份:每一个都可以读取、重命名、停用和删除另一个创建的用户。当所有凭据都由您自己保管时,这没有问题。如果每个客户自己的 IT 团队各自保管他们的凭据,就为他们各配一个客户端,并同样在他们的凭据上设置组织。

标记只在用户被创建时应用,绝不会在之后的更新中应用,因此一次例行的增量同步不会悄悄改动已有账户的归属。如果您同时还运行一个配置应用,凭据上显式设置的标记优先:/try 响应只会填充仍然为空的组织。

支持的架构

我们实现了 SCIM 2.0 的核心 UserGroup 架构(RFC 7643)。支持的用户属性有 userNamename.givenNamename.familyNamedisplayNameemailsactiveexternalIdpreferredLanguage / locale

enterprise user 扩展实现,因此 departmentmanageremployeeNumbercostCenterdivisionorganization 会被接受并忽略,而不是被存储,create、replace 和 PATCH 都是如此。Entra 和 Okta 默认都会映射其中一部分,因此您无需把它们从属性映射中删除。要把某个用户归属到您的某个客户,请改为在 SCIM 凭据上设置组织:enterprise 的 organization 属性是由您客户的身份提供商断言的,它被有意设计为不会成为该用户的 org_id

OAuth 作用域

作用域允许客户端请求用户数据或权限的特定部分。Authagonal 支持标准 OIDC 作用域和您为自己的 API 定义的自定义作用域。

内置作用域

作用域描述
openid任何 OpenID Connect 流程所必需。颁发 ID 令牌。
profile返回标准个人资料声明(name、given_name、family_name)。
email返回用户的电子邮件地址和验证状态。
offline_access与访问令牌一同颁发刷新令牌。

自定义作用域

在作用域页面定义您自己的作用域。每个作用域描述客户端可以请求的权限或资源(例如 billing.read、orders.write)。

Custom scope creation form with name, display name, description and User Claims fields
字段描述
name在令牌请求中发送的作用域标识符(例如 billing.read)。
displayName在同意屏幕上显示的可读标签。
description同意时显示名称下方的较长说明。
userClaims授予此作用域时添加到访问令牌的额外声明。
showInDiscoveryDocument如果启用,作用域将出现在 /.well-known/openid-configuration 中。
emphasize在同意屏幕上将作用域作为敏感项突出显示。
required防止用户在同意时取消选择该作用域。

同意集成

设置 RequireConsent: true 的客户端在首次请求时会提示用户。删除作用域不会撤销已颁发的令牌——如有需要请显式撤销。

令牌上的自定义声明

自定义声明包含两半。来源是按用户的数据:每个 AuthUser 都有一个 customAttributes 字典,可以通过 Portal(用户 → 用户 → 自定义属性)、SCIM 或 TCC 配置钩子来填充。释放按作用域进行:每个作用域的 userClaims 列表声明允许离开服务器的键。

当客户端请求作用域时,Authagonal 会遍历已授予的作用域,合并其 userClaims 列表,仅从用户的 customAttributes 中输出这些键。未知键会被静默丢弃 — 客户端无法通过猜测名称读取属性。标准 OIDC 声明 (subemailname 等) 遵循规范,不受白名单限制。

Example: project-scope claims on an access token
# 1. On the user (Portal → Users → {user} → Custom Attributes)
department = "engineering"
employee_id = "E-1042"
seat_tier = "enterprise"

# 2. On a custom scope (Portal → Scopes → projects.read)
name        = "projects.read"
userClaims  = ["department", "seat_tier"]   # <-- whitelist

# 3. Client requests scope=openid projects.read
# Decoded access token (relevant fields only):
{
  "sub": "u-9b…",
  "scope": "openid projects.read",
  "department": "engineering",
  "seat_tier":  "enterprise"
  // employee_id is NOT emitted — it's not in the whitelist for any granted scope.
}

联合声明按会话覆盖

当用户通过上游 IdP(SAML/OIDC SSO)登录时,从 IdP 进入的会话级声明 — 例如从 SAML 断言映射出的 department 属性 — 流经同一作用域白名单,但在键冲突时会战胜已持久化的 customAttributes。它们出现在该会话的令牌上(并在刷新轮换中存活),但不会回写到用户记录。

将作用域分配给客户端

客户端 → 作用域与授权选项卡中添加允许的作用域。客户端只能请求已被授予的作用域;未知的作用域将被拒绝,返回 invalid_scope

品牌

自定义您的租户登录页面的外观和感觉。品牌设置让您将身份验证体验与产品的视觉形象相匹配——从 Logo 和颜色到高级 CSS 覆盖。

外观

设置项描述
appName显示在登录页面标题和浏览器标签上的应用名称
logoUrl您的 Logo 图片 URL。显示在登录页面顶部。推荐尺寸:200x60px 或类似宽高比。
primaryColor用于按钮、链接和焦点状态的主品牌颜色。通过颜色选择器或十六进制输入设置。更改值时实时预览会更新。
customCssUrl在默认样式之后加载的外部 CSS 文件 URL。用于高级样式覆盖。
Branding appearance settings with app name input, logo URL field, color picker with hex input, and custom CSS URL field

带实时颜色预览的外观设置

联系信息

设置项描述
supportEmail显示在登录页面上的支持电子邮箱地址。用户在需要帐户帮助时会看到此地址。

登录页面切换

控制租户登录页面上显示的元素:

开关描述默认值
showForgotPassword在登录表单上显示「忘记密码?」链接开启
showRegistration显示「注册」链接以支持自助用户注册开启
showPoweredBy在登录页面底部显示「Powered by Authagonal」标识开启
A customized login page showing a branded logo, custom primary color on the sign-in button, and support email in the footer

应用了自定义品牌的示例登录页面

自定义 CSS

如需完全控制登录页外观,请在品牌设置中提供 CSS 文件 URL。该文件会在默认样式之后加载,因此您的规则优先。

CSS 自定义属性

登录页面支持 CSS 自定义属性(变量)以便进行常见覆盖。在您的 CSS 文件中设置这些变量,即可修改颜色、字体和形状,无需编写复杂的选择器。
/* your-custom-styles.css */
:root {
--auth-bg: #1a1a2e;
--auth-card-bg: #16213e;
--auth-heading: #e0e0e0;
--auth-radius: 12px;
--auth-font: 'Inter', sans-serif;
}
变量描述默认
--auth-bg页面背景色#f3f4f6
--auth-card-bg登录卡片背景white
--auth-heading标题文字颜色#111827
--auth-radius卡片圆角半径0.5rem
--auth-font字体族inherit

深色模式

登录应用提供浅色、深色和跟随系统三种主题。用户在登录页面上通过切换按钮选择;选择会在会话间保留。当设置为 system 时,SPA 会实时跟踪 prefers-color-scheme

浅色值在 :root 中声明;深色覆盖仅限于 .dark。通过 customCssUrl 设置的租户品牌样式始终优先——无论用户选择何种主题,您的颜色都会保留。

元素选择器

如需更细粒度的控制,请使用 data-auth 属性定位特定元素。这些选择器在更新之间保持稳定 —— 在我们更改内部类名时不会失效。
选择器元素
[data-auth="page"]整页背景容器
[data-auth="header"]Logo 和应用名称区域
[data-auth="logo"]Logo 图像
[data-auth="app-name"]应用名称标题(未设置 Logo 时)
[data-auth="content"]主要内容区域(表单、消息)
[data-auth="login-form"]登录表单元素
[data-auth="email-field"]电子邮件输入包装器
[data-auth="password-field"]密码输入包装器
[data-auth="submit-button"]登录按钮
[data-auth="languages"]语言选择器栏

设置

配置租户范围的安全策略、Webhook 和环境设置。这些设置全局适用于所有客户端,除非在客户端级别被覆盖。

密码策略

定义租户中所有用户的密码复杂度要求:

设置项范围默认值
minPasswordLength6 – 1288
requireUppercase开 / 关开启
requireLowercase开 / 关开启
requireDigit开 / 关开启
requireSpecialChar开 / 关开启
Password policy settings showing minimum length slider and toggle switches for character requirements

密码策略配置

MFA 策略

租户范围的 MFA 策略设置默认的多因素身份验证行为。单个客户端可以覆盖此设置。

策略行为
DisabledMFA 不可用。用户无法注册 MFA。
EnabledMFA 为可选。用户可以选择注册,已注册的用户在登录时会收到提示。
RequiredMFA 为必需。所有用户必须注册 MFA 并在每次登录时完成第二因素验证。

会话与锁定

控制会话持续时间和账户锁定行为:

设置项范围默认值
sessionLifetimeMinutes5 – 43,200(30 天)60
maxFailedAttempts1 – 1005
lockoutDurationMinutes1 – 1,440(24 小时)10
Session and lockout settings with numeric inputs for session lifetime, max failed attempts, and lockout duration

会话和锁定配置

Webhook

Webhook 让你实时响应认证事件。两个事件 (onUserAuthenticatedonTokenIssued) 是可强制的 — 默认情况下它们异步触发,不会阻塞用户,但你可以按事件启用强制,使非 2xx 响应或 {"allow": false} 响应体拒绝该操作。其余事件都是通知 — 永远是触发即忘,从不阻塞。

事件类型描述
onUserAuthenticated可强制登录成功后触发。默认触发即忘,不影响登录延迟。开启 <code>webhookEnforceUserAuthenticated</code> 即可使其变为阻塞 — 非 2xx 响应或 <code>{"allow": false}</code> 响应体将拒绝登录。
onTokenIssued可强制在签发令牌之前触发 (authorization_code、refresh_token、client_credentials)。默认触发即忘。开启 <code>webhookEnforceTokenIssued</code> 即可使其变为阻塞 — 非 2xx 响应或 <code>{"allow": false}</code> 响应体将阻止令牌签发。
onUserCreated通知当新用户注册或通过 SCIM 配置时的即发即忘通知。
onUserUpdated通知当用户记录被更新(资料变更、角色变更、SCIM 更新)时的触发即忘通知。
onUserDeleted通知当用户被删除时的触发即忘通知(来自 Portal/SCIM,或由保留策略触发)。
onLoginFailed通知当登录尝试因凭证错误、锁定或策略拒绝而失败时的即发即忘通知。

其他 Webhook 设置:

设置项范围默认值描述
webhookTimeoutSeconds1 – 305等待强制执行 Webhook 响应的最大时间(超时前)
webhookFailOpen开 / 关开启启用后,如果强制执行 Webhook 不可达或超时,操作将被允许继续
Webhook configuration section showing URL inputs for each event type, timeout slider, and fail-open toggle

Webhook 事件配置

强制执行 Webhook 可用性

强制执行 Webhook 可以阻止身份验证流程。如果您的 Webhook 端点不可用且 webhookFailOpen 被禁用,则没有用户能够登录。除非您有严格的合规要求需要在 Webhook 失败时阻止,否则请使用失败开放模式。

验证 Webhook

一旦配置了任何 Webhook URL,Authagonal 就会为每个租户生成一个签名密钥(一个 whsec_… 值,在设置 → Webhook 下以只读方式显示)。每次出站投递都会带有一个 X-Authagonal-Signature: t=<unix>,v1=<hex> 头,其中 v1HMAC-SHA256(secret, "{t}.{body}"),基于原始请求体计算。在你的端点上重新计算它并进行常量时间比较,以确认请求确实来自 Authagonal 且未被篡改 —— 并拒绝 t 过于陈旧的投递以阻止重放攻击。

验证 Webhook (Node.js)
import crypto from 'node:crypto';

// rawBody MUST be the exact bytes Authagonal sent — verify before any JSON re-serialization.
function verifyAuthagonalWebhook(signatureHeader, rawBody, signingSecret) {
  const parts = Object.fromEntries(signatureHeader.split(',').map((p) => p.split('=')));
  const { t, v1 } = parts;

  // Replay protection: reject deliveries older than 5 minutes.
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;

  const expected = crypto
    .createHmac('sha256', signingSecret)
    .update(`${t}.${rawBody}`)
    .digest('hex');

  return v1.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}

轮换签名密钥

使用签名密钥旁的重新生成来轮换它 —— 例如在怀疑泄露之后。先前的密钥会立即失效,因此请用新值更新你的验证器,否则进行中的投递将开始无法通过签名校验。

维护窗口

为证书轮换和基础设施更新等破坏性操作设置首选维护窗口。选择一个 UTC 时间(0-23)——门户还会显示对应的本地时区时间以方便参考。

注册与访问

谁可以成为您租户中的用户,以及他们在什么条件下可以登录。

设置项默认值描述
公开注册开启是否允许任何人自助注册。关闭后会隐藏注册页面并拒绝注册 API,这一点很重要:仅仅隐藏链接会让端点依然敞开。当您通过 SCIM、API 或邀请自行开通用户时,请使用它。
登录前要求验证电子邮箱开启未确认的地址无法登录。如果改由您自己的应用来对验证把关,可以关闭它;无论哪种方式,email_verified 声明都仍会随令牌一起发出,因此这个信号不会丢失。
动态客户端注册关闭允许客户端在运行时按 RFC 7591 自行注册,AI 智能体或 MCP 连接器在发起流程之前正需要这一点。默认关闭,启用后也只为您自己的租户打开这道门。无论如何,注册始终受速率限制,并始终要求 PKCE 和用户授权同意。
门户 MFA 策略禁用针对您自己团队登录门户的多因素设置,与面向终端用户的策略相互独立。可设为禁用、登录时可选,或强制要求。
用户数上限您方案的上限用户总数的硬性上限,在唯一的创建收口处强制执行,因此每一条路径都受其约束:自助注册、API、门户创建、SCIM、即时(JIT)配置、批量导入和团队邀请。

不活跃用户保留

可选地停用并随后删除长期未使用的账户,避免目录里永远堆积着休眠身份。两者在您设置之前都处于关闭状态,且删除是永久性的。

设置项默认值描述
停用阈值(不活跃天数)从不停用在这段时间内未登录过的账户。记录会被保留,可以重新激活。
删除阈值(不活跃天数)从不永久删除该账户。没有撤销这一说,因此在开启它之前,请先设置下面的预警时间窗和 Webhook。
预警天数7在停用或删除发生前多少天触发预警 Webhook,给您留出干预的时间。
保留策略 Webhook-这些预警以及已执行的动作会被投递到哪里,方便您通知本人或让账户继续保留。

审计导出与远程备份

两种按计划、而非按需申请取出数据的方式。审计导出会把每一个已结束整天的审计事件推送到您指定的 URL,并附带签名以便您验证它来自我们,可用于接入 SIEM 或合规归档。远程备份会把您每日和每周备份的副本发送到您掌控的目的地,可选择不认证、基本认证或 Bearer 令牌。两者都在设置中配置,数据归您保管,与我们保存的备份彼此独立。

沙箱环境

沙箱环境是生产租户的完整克隆,可通过单独的 URL 访问。使用它来测试配置更改、SSO 集成和 Webhook 端点,而不影响正式用户。

操作描述
启用沙箱创建生产租户的沙箱副本。沙箱 URL 是您的租户 slug 加 -sandbox 后缀。
从正式环境刷新将沙箱环境与当前生产配置和用户数据同步。
禁用沙箱永久删除沙箱环境及其所有数据。

沙箱可通过 {slug}-sandbox.authagonal.io 访问。

Sandbox settings showing enable/disable toggle, refresh from live button, and sandbox URL display

沙箱环境控制

计费

通过门户的计费页面管理您的订阅和账单。此页面提供当前方案的概览,并提供访问 Stripe 计费门户以管理支付方式、发票和方案变更。

订阅信息

计费页面一目了然地显示您当前的订阅详情。您会看到一个状态标签,指示您的订阅状态——activetrialingpast_duecanceledunpaid——以及您的方案名称、当前计费周期(起止日期),以及您的订阅是否设置在当前周期结束时取消。

管理订阅

点击管理订阅按钮,在新窗口中打开 Stripe 计费门户。在那里您可以更新支付方式、查看和下载发票、更改方案或取消订阅。

如果尚未创建订阅,则会显示设置计费引导操作,指导您选择方案并输入支付信息。

Billing page showing subscription status, plan name, billing period, and Manage Subscription button

计费页面显示您当前的订阅详情并提供 Stripe 访问入口

支付安全

所有计费通过 Stripe 处理。您的支付信息绝不会存储在 Authagonal 服务器上。

自定义域名

通过您自己的域名(例如 auth.yourdomain.com)而非默认的 {slug}.authagonal.io 提供身份验证页面。自定义域名为您的用户提供无缝的品牌化身份验证体验。

添加域名

在添加域名表单中输入您要使用的主机名(例如 auth.yourdomain.com)。添加后,该域名将以 pending_verification 状态显示在您的域名列表中。

DNS 验证

创建一条 CNAME 记录,将您的域名指向 {slug}.authagonal.io。DNS 记录就位后,点击验证以检查 DNS 传播。

CNAME record
auth.yourdomain.com.  CNAME  acme.authagonal.io.

DNS 传播

DNS 传播最多可能需要 48 小时。如果验证失败,请等待后重试。

TLS 证书

域名验证通过后,您需要 TLS 证书以便用户可以通过 HTTPS 安全连接。Authagonal 支持两种选项:

自动(cert-manager)——Authagonal 使用 cert-manager 自动配置和续期 TLS 证书。这是大多数用户的推荐选项。无需额外配置。

自带证书(BYO)——以 PEM 格式上传您自己的证书和私钥。如果您的组织要求使用特定证书颁发机构的证书,此选项很有用。系统会跟踪证书到期时间,以便您在过期前续期。

域名状态

每个域名显示一个状态标签,指示其当前状态:pending_verification(DNS 尚未确认)、verified(DNS 已确认,TLS 待配置)、active(完全可用)或 failed(检测到配置问题)。

Domain list showing domains with status badges and verification controls

域名列表显示每个自定义域名及其当前状态

BYO certificate upload form with certificate and private key PEM fields

以 PEM 格式上传您自己的 TLS 证书和私钥

BYO 证书续期

请保持您的 BYO 证书已续期。过期的证书会导致用户的浏览器显示安全警告。

电子邮件配置

配置您的租户如何发送事务性电子邮件 —— 验证、密码重置和 MFA 通知。可在默认共享发件人、通过 Resend 验证的自定义域名,或自己的 SMTP 服务器之间进行选择。

Email settings showing provider options (default, Resend domain, custom SMTP) and a test-send

本地化邮件

事务性电子邮件以收件人的首选语言发送。验证、密码重置、账户已存在、欢迎、账单和管理员邀请邮件均有七种语言的模板:英语、德语、法语、西班牙语、葡萄牙语、越南语和简体中文。当收件人的语言没有对应模板时,邮件将回退到英语。

语言在发送时根据收件人已存储的偏好来确定。该偏好可来自多个位置:

  • 注册和创建账户 —— 取自用户在托管登录界面上所选的语言。
  • 门户中的用户页面 —— 由管理员在创建或编辑用户时设置。
  • SCIM 配置 —— 当用户通过 SSO 同步时,从 IdP 的 preferredLanguage 映射而来。
  • 自助账户页面 —— 由用户本人在 /login/account 上选择。

无需配置

本地化是自动的,并适用于所有提供商模式(默认、Resend 自定义域名和 SMTP)。无需启用任何内容。

电子邮件提供商

提供商描述设置
Default电子邮件通过我们共享的 Resend 基础设施从 [email protected] 发送。无需配置 —— 开箱即用。
Resend Custom Domain通过 Resend 从您自己已验证的域名发送电子邮件。注册您的域名,添加 DNS 记录,验证所有权。
Custom SMTP通过您自己的 SMTP 服务器发送电子邮件。提供 SMTP 主机、端口、凭证和 TLS 设置。

发件人身份

发件人电子邮件和名称在所有提供商模式中共享。发件人电子邮件为必填;名称为空时将回退到租户名称。

字段描述
senderEmail发出电子邮件的 From 地址。在 Resend 自定义域名模式下必须位于已验证的域名下。
senderName收件人收件箱中显示的名称。

Resend 自定义域名

仅需在 Resend 验证一次您的发送域名,然后即可用作此租户的 From 地址。DNS TXT 记录(SPF、DKIM)由"域名"页面提供;Resend 会自动验证。

自定义 SMTP

使用您自己的 SMTP 服务器 —— 适用于内部中继、Resend 未覆盖的供应商或法规要求固定的场景。

字段描述
hostSMTP 服务器主机名(例如 smtp.example.com)。
port连接端口。STARTTLS 使用 587,隐式 TLS 使用 465,未认证的内部中继使用 25。
username认证用户名(可选 —— 未认证中继请留空)。
password认证密码。以加密方式存储在租户设置密钥中。
useTls要求 TLS。除非指向可信的内部中继,否则请保持启用。

自定义发送域名

使用 Resend 提供商时,您可以注册自己的域名,让电子邮件来自您的品牌(例如 [email protected]),而不是 @authagonal.io。

  1. 进入"设置 → 电子邮件",选择 Resend 自定义域名提供商。
  2. 输入您的域名并点击"注册"。
  3. 将显示的 DNS 记录(DKIM、SPF 和 return path)添加到您域名的 DNS 中。
  4. 点击"检查验证"—— DNS 生效后(通常 1–10 分钟),域名状态将变为已验证。

DNS 传播

DNS 变更在全球传播最多可能需要 48 小时,但大多数提供商会在几分钟内更新。您可以根据需要多次检查验证状态。

测试

使用"设置 → 电子邮件"中的"发送测试邮件"按钮验证配置。系统将使用当前保存的设置向您的管理员邮箱发送测试邮件。

审计日志

审计日志提供对租户执行的所有管理操作的只读记录。通过门户或 API 进行的每项更改都会被记录完整的上下文,为合规和故障排除提供完整的追踪记录。

日志列

描述
时间戳操作发生的日期和时间
执行者执行操作的管理员的电子邮箱地址,自动操作显示为 "system"
操作执行的操作类型(例如 Client Created、Settings Updated)
实体操作目标,格式为 type:id(例如 client:my-app)
详情关于变更的附加上下文

跟踪的操作

以下管理操作会记录在审计日志中:

类别操作
客户端Client Created、Client Updated、Client Deleted
SSO 连接SAML Connection Created、SAML Connection Deleted、OIDC Connection Created、OIDC Connection Deleted
用户User Created、User Updated
设置Settings Updated、Branding Updated
域名Domain Added、Domain Verified、Domain Deleted
SCIMSCIM Token Created、SCIM Token Revoked
角色Role Created、Role Updated、Role Deleted
Group Created、Group Deleted
团队Team Member Invited、Team Member Removed
Audit log table showing timestamped administrative actions with actor, action, entity, and detail columns

审计日志提供所有管理操作的完整记录

保留

审计日志在租户的整个生命周期内保留,不可修改或删除。

备份

Authagonal 每小时自动备份您的租户数据。备份包括所有用户、组、角色、客户端、SSO 连接、SCIM 令牌、品牌信息和设置。您可以在"备份"页面查看备份历史并下载最新的完整备份。

Backups page showing backup history with timestamps, types, and entity counts

备份工作原理

  • 每日运行一次完整备份,捕获租户存储分片中的每个表。
  • 增量备份每小时运行一次,仅捕获自上次备份以来发生变化的行。
  • 备份存储在 Azure Blob Storage 中,使用与您的租户相同的托管标识。
  • 已删除的记录通过 tombstone 跟踪并包含在备份中,以确保审计的完整性。

下载备份

点击"下载最新"以获取 ZIP 文件,其中包含最新的完整备份以及随后合并的所有增量备份。每个表都导出为 JSONL 文件(每行一个 JSON 对象)。

备份格式

备份以 JSONL(JSON Lines)格式导出 —— 每个表每行一个实体。此格式易于解析、比较并导入到其他系统。

配置应用

预配应用是你自己的服务。每当创建用户时,Authagonal 都会调用它们,让它们可以开通账户、分配许可证、决定该用户属于哪个组织,或者直接拒绝这次注册。

工作原理

创建用户时,Authagonal 会按 TCC(Try/Confirm/Cancel)模式调用你的预配应用的回调 URL。在其中任何一个应用被提交之前,所有应用都必须在 Try 阶段接受,这样多个下游系统可以达成一致,或者由其中一个否决,而不会留下创建了一半的账户。

阶段端点用途
/tryPOST {callbackUrl}/try检查应用是否可以处理该用户。返回 200 表示接受,4xx 表示拒绝。
/confirmPOST {callbackUrl}/confirm在所有应用接受 /try 阶段后提交操作。
/cancelPOST {callbackUrl}/cancel如果在 /try 阶段另一个应用失败,则回滚操作。

预配何时运行

每一条创建用户的路径都会运行预配,不只是自助注册。已经完成预配的应用与用户组合会被跳过,因此一个应用对每个用户只会看到一次。

创建路径何时触发
POST /api/auth/register自助注册
SAML ACS 回调新用户的首次 SSO 登录(JIT)
OIDC 回调新用户的首次 SSO 登录(JIT)
POST /scim/v2/Users连接器从客户的目录中预配用户
门户和管理后台创建用户运维人员手动创建或邀请用户

Try 请求

Authagonal 会把这段 JSON POST 到 <code>{callbackUrl}/try</code>。没有值的字段会被省略,而不是以 null 发送。

字段类型描述
transactionIdstring标识本次预配事务。同一个值也会发送给 /confirm 和 /cancel,因此请围绕它暂存你的工作,并在那次调用到达时提交或丢弃。
userIdstring该用户的 Authagonal id。这就是你会在他们的令牌中看到的 subject。
emailstring该用户的邮箱地址。
firstNamestring名,在创建路径提供了它时。
lastNamestring姓,在创建路径提供了它时。
organizationIdstring该用户已经所属的组织,如果有的话。只有在此前已有某处为其分配过组织时才会出现;首次注册时它不存在,这正是提示你去分配组织的信号。
customAttributesobject该用户已保存的自定义属性。对于通过 SSO 创建的用户,其中包含 federated_connection,即为其做出担保的那个连接的名称。

通过 SSO 到来的用户是最值得提前考虑的常见情况:他们还没有组织,而 federated_connection 会告诉你他们来自你的哪一个客户。

通过 SSO 连接到来的用户的 Try 请求
{
  "transactionId": "8f14e45fceea167a5a36dedd4bea2543",
  "userId": "0f6b1c8e-3d2a-4f51-9e77-2c1a4b5d6e7f",
  "email": "[email protected]",
  "firstName": "Ada",
  "lastName": "Lovelace",
  "customAttributes": {
    "federated_connection": "acme-okta"
  }
}

Try 响应

你的应用返回 200 和一个 JSON 主体。这个主体不只是一次确认收到:下游应用正是通过它来分配最终出现在用户令牌上的组织和属性。

字段类型描述
approvedboolean此应用是否接受该用户。省略时默认为 true。false 会拒绝这次注册,新建的账户会被删除。
reasonstring该用户被拒绝的原因。会呈现给调用创建路径的一方。
organizationIdstring该用户所属的组织。会保存在用户上,并作为 org_id 声明发到其令牌中。仅在用户尚未拥有组织时才会应用,因此第一个作答的应用胜出,后续应用会看到这次分配。
customAttributesobject要按键逐个合并到用户上的属性。通过某个 scope 的 UserClaims 配置发到令牌中。
emailVerifiedboolean你的应用担保它已验证过这个地址,例如通过兑换发送到该地址的邀请。Authagonal 会把账户标记为已确认,并跳过自己的验证邮件。
分配组织的 Try 响应
{
  "approved": true,
  "organizationId": "org_acme",
  "customAttributes": { "org_role": "member" }
}

org_id 就是从这里来的

令牌上以及 /connect/userinfo 上的 org_id 声明,就是你的预配应用作为 organizationId 返回的那个值。Authagonal 从不推导它:没有 Organization 对象,没有唯一性规则,也没有格式要求。它是你的标识符,保存在用户上,并在每个令牌上交还给你。你也可以用 PUT /api/v1/users/{userId} 直接设置它,并用 GET /api/v1/users?organizationId= 按它过滤。

为用户打上正确的组织标记

在这里、在同一个地方决定组织,而不是在每条创建路径上各决定一次。SSO 用户会带上 federated_connection,它标识出为其完成认证的连接,因而也标识出客户,并且在同一个客户联合了多个邮箱域名时依然正确。受邀用户没有连接,因此请用你签发的邀请来匹配他们。两条路径都会在用户存在之前到达 /try,因此一段逻辑就能覆盖两者,也不会有两套规则各自漂移。

在 Try 回调中解析组织
// POST {callbackUrl}/try
app.post('/provisioning/try', async (req, res) => {
  const { email, customAttributes } = req.body;

  // An SSO user carries the connection that vouched for them. That is the
  // customer, and it stays right when one customer has several domains.
  const connection = customAttributes?.federated_connection;

  const org = connection
    ? await orgByConnection(connection)
    : await orgByPendingInvite(email);

  if (!org) return res.json({ approved: false, reason: 'No organization for this user' });

  // Stamped on the user and emitted as org_id on every token from now on.
  res.json({ approved: true, organizationId: org.id });
});

拒绝会删除账户

只要有任何一个应用返回 approved: false,刚创建的用户就会被删除,不会留下预配到一半的账户。API 创建路径会返回 422 以及你给出的 reason;SAML 和 OIDC 回调返回 400。对于你无需做任何处理的用户,请返回 approved: true

添加配置应用

要添加配置应用,请提供名称回调 URL 和可选的API 密钥。API 密钥作为 Bearer 令牌在每个 Webhook 请求的 Authorization 头中发送,允许您的应用验证来自 Authagonal 的请求。

测试

点击任意配置应用旁边的测试,向您的回调 URL 发送测试请求。测试结果显示 HTTP 状态码和响应正文,帮助您验证应用是否正确接收和处理 Webhook。

Provisioning apps page showing configured apps with test results displaying HTTP status and response body

测试配置应用以验证 Webhook 交付和响应处理

方案限制

每个租户的最大配置应用数量可配置,默认限制为 6 个。如果您的工作流程需要更多配置目标,管理员可以调整此限制。

API 密钥身份验证

如果设置了 API 密钥,它将作为 Bearer 令牌在 Authorization 头中发送。使用此密钥验证来自 Authagonal 的 Webhook 请求。

团队

团队页面管理门户管理员——可以通过管理门户访问和配置您的租户的人员。所有团队成员对租户配置的各个方面拥有完全管理权限。

管理员列表

管理员列表显示每位团队成员的姓名、电子邮箱和添加日期。当前用户行旁边会显示「您」标识,方便您识别自己的账户。

邀请管理员

要邀请新团队成员,请提供其电子邮箱姓名临时密码(至少 8 个字符)。受邀用户使用临时密码登录,应在首次登录时更改密码。

邀请字段

管理员邀请将创建一个完全配置的用户——无需邮件往返。

字段描述
email新管理员的邮箱地址。在租户中必须唯一。
name在管理员列表中显示的名称。
tempPassword受邀者首次登录时使用的临时密码。系统会要求他们更改。留空则自动生成并通过邮件发送。

移除管理员

点击任意团队成员旁边的移除以撤销其访问权限。移除确认前会显示确认对话框。您不能移除自己——团队中必须始终至少有一名管理员。

Team page showing the admin list with name, email, date added columns, a You indicator, and invite/remove controls

在团队页面管理门户管理员

无所有者角色

没有「所有者」角色的区分。所有门户管理员对租户配置拥有完全访问权限。请谨慎邀请人员。

支持

无需离开门户,即可向 Authagonal 团队提交支持请求。每个请求都是一段连续的对话,从首次反馈到最终解决,您和我们的团队始终保持同步。

您的请求

支持页面会列出您提交的每一个请求,最新动态排在最前。借助状态标识,您可以一眼看出哪些在等您、哪些在等我们。

Support ticket list showing each ticket's subject, status badge, priority, and last-activity time, with a New ticket button

您的支持请求,含主题、状态、优先级和最近动态

  • 每一行显示主题、当前状态(进行中、等待回复、已解决或已关闭)、优先级以及最近动态的时间。
  • 点击新建请求即可创建一个,然后填写主题、优先级和您的第一条留言。
  • 彩色状态标识让您能轻松扫一眼,找出需要您处理的请求。

请求对话

打开请求即可看到完整对话。回复按顺序显示,我们团队的新消息无需刷新页面即可出现。

Support ticket thread showing the conversation between the customer and the Authagonal team with a reply box and file attachment control

您与 Authagonal 团队之间的请求对话

  • 您与 Authagonal 团队之间的对话消息按时间先后顺序显示。
  • 可在对话中直接回复并附加文件,分享日志、截图或配置。
  • 对话实时更新,我们团队一发出回复就会立即显示。
  • 如果您改为直接回复通知邮件,您的消息也会自动归入对话

回复如何送达您

Authagonal 工作人员在管理端处理请求。每当团队回复时,您都会收到邮件通知,因此无需一直开着门户就能跟进对话。

面向您用户的支持台

除了您从我们这里获得的支持之外,Authagonal 还可以为您的终端用户运行一个支持台。他们在您租户自有品牌主机上的账户页面里提交工单,您的团队则在门户中回复。

它之所以存在,是因为登录不了的人,恰恰就是够不到那些必须登录才能用的支持工具的人。支持台紧挨着登录页面,所以被挡在门外的用户仍有一条路可走,而且每张工单送达时就已经关联到您目录中的真实账户,而不是关联到某人随手填写的地址。

启用它

在门户中打开设置并启用支持门户。在您启用之前,您的用户看不到任何内容。

设置项作用
支持门户总开关。关闭时,终端用户页面和您的客服收件箱都会隐藏,对应的 API 返回 404。
允许匿名工单让未登录的访客也能提交工单,也就是被挡在门外的那种情况。该入口由人机验证保护,而且由于没有账户可供登录,后续往来通过电子邮件加一个私密链接继续进行。
通知工单到达或用户回复时需要提醒的电子邮箱地址,这样就不必有人一直盯着收件箱。
终端用户默认语言当消息太短、无法从中检测出语言时使用。回退到英语。
支持 Webhook用于 POST 工单事件的 URL,可把事件推送进您自己的工具链。

免费方案不提供

支持台是免费方案唯一不包含的能力,因为入站邮件和翻译带有实实在在的按次成本。所有付费方案都包含它。身份验证功能从不会以这种方式设限:单点登录、SCIM、多因素、自定义域名、品牌定制和审计在每个方案上都有,包括免费方案。

您的用户看到什么

已登录用户会在您租户主机上的账户页面里看到一块带有您品牌的支持区域。他们可以提交工单、查看自己提交过的全部工单,并在对话中回复。您团队的回复也会通过电子邮件送达,因此用户不必反复回来查看。

启用匿名工单后,登录不了的人仍然可以提交工单。他们留下一个电子邮箱地址和自己的问题描述,随后拿到一个通往该对话的私密链接。这个链接是唯一的入口,因此请把它当作凭据看待:它无法被猜出,而任何持有它的人都可以阅读并回复那一个对话。

客服收件箱

您的团队在门户的支持收件箱中回复。它与您向我们提交的工单是彼此独立的两个地方,并且对 tenant:support 及以上角色开放,因此您可以只把支持台的权限给某位客服人员,而不必把门户的其余部分也交出去。

操作作用
回复发布到对话中。用户会收到电子邮件,如果他们正开着页面,也会实时看到。
分配把工单指派给您团队中的某位具体成员,避免两个人回复同一张工单。
状态与优先级让工单在进行中、等待回复、已解决和已关闭之间流转,并标记它有多紧急。已关闭的工单会在保留期结束后删除,而不是永久留存。
内部备注只有您的团队可见、用户永远看不到的备注。编辑和删除都会记入审计日志。
代用户创建工单从您的目录中选出某位用户并与其开启一段对话,适用于交流最初发生在别处的情况。
升级给 Authagonal如果一张工单最终是关于 Authagonal 而不是您的产品,可以把它升级上来。这会在我们团队这边打开一张关联工单,并可选择带上目前为止的对话内容,同时把两者关联起来,方便您同时跟进。一张工单只能升级一次。

用户用自己的语言书写

工单按用户书写时使用的语言保存,而对话中的每个人都用自己的语言阅读它。您的客服人员看到的是被翻译成其门户语言的消息,用户看到的是被翻译成其语言的回复,原文始终保留在旁。语言从第一条消息中检测得出,并固定到该工单上。每种语言的译文只计算一次并复用,因此长对话不会反复重新翻译。

时区

工单会记录用户提交时所在的时区。此后每条消息都会在您的时间旁边显示对方的当地时间,于是 14:32 发出的回复,对等待它的人来说会显示为实际的 02:32。当时区未知时,例如通过电子邮件到达的工单,或者时区与您相同时,则不显示任何内容。

Webhook

设置一个支持 Webhook URL,即可在工单事件发生时接收它们,用于触发告警或把工单镜像到您自己的系统中。投递内容带有签名,便于您验证它确实来自我们。

事件作用
support.ticket.created用户提交了一张工单。
support.ticket.message用户在对话中作了回复。
support.ticket.replied您的某位客服人员作了回复。
support.ticket.assigned工单被分配给了某位团队成员。
support.ticket.escalated工单被升级给了 Authagonal。

导入与迁移

将现有的身份系统迁移到您的 Authagonal 租户。支持两种来源 —— Duende IdentityServer(一个 SQL Server 数据库)和 Auth0(Management API)。每种来源都会运行只读预览,让您在提交之前准确查看将要复制的内容。

从 Duende IdentityServer 导入

将现有的 Duende IdentityServer SQL Server 数据库中的 Clients、Scopes、用户和角色迁移到您的 Authagonal 租户。导入分为两个阶段 —— 预览和提交 —— 这样您可以在任何更改之前查看将要复制的内容。

导入的内容

导入器从 Duende 的 ConfigurationDb 和 ASP.NET Identity 表中读取数据,将映射的行写入您的租户。短时效资源(如 persisted grants、device codes 和签名密钥)会被跳过。

实体源表备注
ClientsClients、ClientSecrets、ClientGrantTypes、ClientScopes、ClientRedirectUris已禁用的 Client 将以禁用状态导入。已过期的 secret 会被跳过。
ScopesApiScopes、ApiResources、IdentityResources可识别的用户声明映射会被保留。
用户AspNetUsers、AspNetUserClaims密码哈希(ASP.NET Identity V3)原样复制,首次登录时重新哈希。
角色AspNetRoles、AspNetUserRoles角色分配会被保留。
外部登录AspNetUserLogins仅作参考保存;导入后请通过 SSO 重新连接上游 IdP。

提交前预览

粘贴您的 Duende ConfigurationDb / IdentityDb 连接字符串,然后点击 运行预览。预览会以只读方式连接并统计将要导入的每一行 —— 不会写入任何数据。

  • Clients、Scopes、用户、角色和角色分配的实体计数。
  • 当目标租户已存在匹配的 Clients、角色或 Scopes 时发出覆盖警告。
  • 对未知表和未映射列的警告,便于您了解哪些数据将被丢弃。
Import preview panel showing entity counts and warnings before committing the import

带有计数和警告的预览面板

密码哈希

Duende 使用 ASP.NET Identity V3(PBKDF2)存储密码。Authagonal 的 PasswordHasher 可直接验证该格式,并在首次成功登录时重新哈希为原生格式 —— 用户无需重置流程即可保留现有密码。

用户 ID 协调

如果此租户中已存在的用户与传入记录拥有相同的电子邮件,导入会在导入前将该账户的 userId 轮换为源 sub,这样导入的角色、登录和声明就会附加到现有账户上,而已经通过其源 sub 引用该用户的应用在切换后仍能继续解析。该账户现有的密码和个人资料会被保留;源角色会叠加合并到其上。预览会在您提交之前列出每一个将被协调的账户。

执行导入

在查看预览后,点击 开始导入。提交阶段会将 Clients、Scopes、用户、角色和外部登录引用写入您的租户存储。重复的 clientIdscope nameemailrole name 会被跳过 —— 导入器可安全重复运行。

不会导入的内容

  • Persisted grants、device codes、server-side sessions —— 短时效,自动重新生成。
  • 签名密钥 —— Authagonal 为每个租户签发自己的密钥。
  • 自定义列和表 —— 任何超出 Duende 标准 schema 的内容都会以警告形式出现,以便您了解数据已被丢弃。
  • 已禁用的 Client —— 以禁用状态导入;准备好后可在 Clients 页面重新启用。

沙盒模式下不可用

导入仅对正式租户运行。导入前请退出沙盒模式。

从 Auth0 导入

将 Authagonal 连接到您的 Auth0 租户的 Management API,迁移您的应用、API、角色、用户和企业连接。导入的用户和应用 ID 会被保留,因此切换之后现有的 sub 和 client_id 引用仍可正常解析。

您需要准备的内容

在 Auth0 中创建一个授权访问 Management API 的机器到机器(Machine-to-Machine)应用,并授予以下读取 scope:read:usersread:clientsread:resource_serversread:rolesread:connectionsread:client_grants。将其域名、client ID 和 client secret 粘贴到导入表单中 —— 它们仅用于本次导入。

导入的内容

实体源表备注
应用clients、client-grants自动检测 public 与 confidential。client secrets 会被重新哈希,以便继续可用。
API 与 scopesresource-serversaudiences 和 scopes 会根据各 client 的 grants 分配给它们。
角色roles + 分配关系保留每个用户的角色分配关系。
用户users + identities档案和元数据会一并迁移;社交/企业身份将成为已关联的登录方式。
连接connections(OIDC)企业 OIDC 连接会成为联合提供方。SAML、社交和数据库连接会被跳过并给出警告。

密码

Auth0 的 Management API 从不返回密码哈希。如果您拥有 Auth0 支持团队协助导出的批量密码(NDJSON),请一并提供 —— bcrypt 哈希会原样导入,您的用户无需重置即可保留密码。该文件还包含您的完整用户集合,从而突破 Auth0 API 列表 1,000 个用户的上限。若不提供,用户将以档案形式导入,并在首次登录时设置新密码。

相同的预览、轮换与限制

上文所述的预览、owner-userId 轮换、可重复执行的提交以及沙盒限制,同样适用于 Auth0 导入。

API 参考

每个租户在 https://{slug}.authagonal.io 上暴露一个符合标准的 OIDC 服务器。所有端点遵循 OAuth 2.0 和 OpenID Connect 规范。此参考涵盖您的应用可能需要交互的每个端点。

OIDC Authorization Code Flow with PKCE — sequence diagram showing the 9-step flow between your app, the browser, and Authagonal

带 PKCE 的授权码流程

OIDC 发现和 JWKS

发现文档让 OIDC 客户端库可以自动配置自身。这两个端点均不需要身份验证。

GET /.well-known/openid-configuration

返回 OpenID Provider 配置文档。响应包含客户端与此租户交互所需的所有元数据。

字段描述
issuer租户发行者 URL
authorization_endpoint授权请求 URL
token_endpoint令牌交换 URL
userinfo_endpoint获取用户声明的 URL
jwks_uriJSON Web Key Set URL
revocation_endpoint令牌撤销 URL
introspection_endpoint令牌内省 URL
end_session_endpoint注销 / 结束会话 URL
device_authorization_endpoint设备授权请求 URL
pushed_authorization_request_endpoint推送授权请求端点的 URL (RFC 9126)。
require_pushed_authorization_requests租户是否在全局要求 PAR。即使此值为 false,单个客户端仍可以设置 RequirePushedAuthorizationRequests = true
scopes_supported支持的作用域列表
response_types_supported支持的响应类型
grant_types_supported支持的授权类型
code_challenge_methods_supported支持的 PKCE 方法(S256)
backchannel_logout_supported是否支持后通道注销

GET /.well-known/openid-configuration/jwks

返回用于验证令牌签名的 JSON Web Key Set。响应包含一个 keys 数组,其中每个 RSA 公钥包含 ktyusekidalgne 字段。

Fetch discovery document
curl https://acme.authagonal.io/.well-known/openid-configuration

授权端点

GET /connect/authorize

发起授权码流程。用户必须有活跃会话,否则将被重定向到登录页面。成功后,用户将携带授权码被重定向回您的应用。

参数必需描述
response_type必须为 "code"
client_id您已注册的客户端标识符
redirect_uri必须与已注册的重定向 URI 完全匹配
scope以空格分隔的作用域列表(例如 "openid profile email")
state推荐用于 CSRF 防护的不透明值,在重定向中原样返回
code_challengePKCE 时必需code_verifier 的 SHA-256 哈希值经 Base64url 编码
code_challenge_methodPKCE 时必需必须为 "S256"
nonce可选绑定到 ID 令牌的值,用于防止重放攻击
login_hint可选预填充登录页面的电子邮箱字段

成功响应:302 重定向到 redirect_uri,附带 codestate 查询参数。

错误响应:302 重定向,附带 errorerror_descriptionstate 查询参数。

PKCE 必需

所有客户端默认要求 PKCE。生成一个 code_verifier(43 个或更多字符的随机字符串),使用 SHA-256 哈希,然后进行 Base64url 编码以创建 code_challenge

推送授权请求 (PAR)

RFC 9126。客户端不再把每个授权参数放在 URL 上,而是用普通的客户端认证将它们 POST 到 /connect/par,并取回一个短期的不透明 request_uri。浏览器随后访问 /connect/authorize?client_id=...&request_uri=... — 其它内容不会出现在浏览器历史、服务器日志或 Referer 头中,并且服务器已在客户端认证下完成参数完整性校验。

POST /connect/par

客户端认证与 /connect/token 相同:使用 HTTP Basic 携带 client_id/client_secret,或者通过表单字段提交凭据。公共客户端无需 secret 直接提交。请求体携带的参数与你通常发送给 /connect/authorize 的相同;request_uri 本身会被拒绝(规范 §2.1 禁止链式 PAR)。返回 201 Created

参数必需描述
client_id你的客户端 ID。必须与已认证的客户端一致。
client_secret机密客户端你的客户端密钥。机密客户端必填。
response_type必须为 "code"
redirect_uri必须与已注册的重定向 URI 完全匹配
scope以空格分隔的作用域列表(例如 "openid profile email")
code_challengePKCE 时必需code_verifier 的 SHA-256 哈希值经 Base64url 编码
code_challenge_methodPKCE 时必需必须为 "S256"
state推荐用于 CSRF 防护的不透明值,在重定向中原样返回
nonce可选绑定到 ID 令牌的值,用于防止重放攻击

响应

字段描述
request_uri一次性不透明引用,例如 <code>urn:ietf:params:oauth:request_uri:abc123…</code>。将其作为 <code>request_uri</code> 传给 <code>/connect/authorize</code>。
expires_in<code>request_uri</code> 的存活时间(秒)。默认为 90 — 与典型参考 IdP 一致。

在后续的 GET /connect/authorize?client_id=…&request_uri=… 调用中,所有其它参数都从已推送的载荷中读取,多余的查询参数会被忽略。授权调用上的 client_id 必须与推送请求时的客户端一致。一旦被消费(或 expires_in 到期),request_uri 会从存储中移除。

按客户端强制 PAR

在客户端上启用 要求 PAR(Portal → 客户端 → 客户端 → 高级),即可拒绝该客户端发起的普通 /connect/authorize 调用。对高风险客户端,推荐姿势是 RequirePushedAuthorizationRequests = true 与 PKCE 组合使用 — 这样就彻底消除了 URL 栏作为攻击面。
Push an authorization request and follow up
# 1. Push parameters (server returns request_uri + expires_in)
curl -X POST https://acme.authagonal.io/connect/par \
  -u "my-app:CLIENT_SECRET" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "response_type=code" \
  -d "redirect_uri=https://app.example.com/callback" \
  -d "scope=openid profile email" \
  -d "state=$(openssl rand -hex 16)" \
  -d "code_challenge=YOUR_CODE_CHALLENGE" \
  -d "code_challenge_method=S256"

# 2. Send the user to /authorize with only client_id + request_uri
# https://acme.authagonal.io/connect/authorize?client_id=my-app&request_uri=urn:ietf:params:oauth:request_uri:abc123...

令牌端点

POST /connect/token

用凭证交换令牌。请求必须使用 Content-Type: application/x-www-form-urlencoded。客户端身份验证可以通过 HTTP Basic auth(Authorization: Basic base64(client_id:client_secret))或表单正文参数(client_id + client_secret)提供。

授权码授权

参数必需描述
grant_type"authorization_code"
code来自重定向的授权码
redirect_uri必须与授权请求中使用的 URI 匹配
code_verifierPKCE 时必需用于生成 code_challenge 的原始随机字符串
client_id您的客户端标识符(如果未使用 Basic auth)
client_secret机密客户端您的客户端密钥(如果未使用 Basic auth)

刷新令牌授权

参数必需描述
grant_type"refresh_token"
refresh_token要交换的刷新令牌
client_id您的客户端标识符
client_secret机密客户端您的客户端密钥

客户端凭证授权

参数必需描述
grant_type"client_credentials"
client_id您的客户端标识符
client_secret您的客户端密钥
scope可选以空格分隔的请求作用域

设备代码授权

参数必需描述
grant_type"urn:ietf:params:oauth:grant-type:device_code"
device_code来自设备授权响应的设备代码
client_id您的客户端标识符
client_secret机密客户端您的客户端密钥

令牌响应:

字段描述
access_token用于 API 调用的访问令牌
token_type"Bearer"
expires_in令牌有效期(秒)
id_tokenOpenID Connect ID 令牌(请求 openid 作用域时)
refresh_token刷新令牌(授予 offline_access 作用域时)
Exchange authorization code with PKCE
curl -X POST https://acme.authagonal.io/connect/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "code=AUTHORIZATION_CODE" \
  -d "redirect_uri=https://app.example.com/callback" \
  -d "client_id=my-app" \
  -d "code_verifier=YOUR_CODE_VERIFIER"

UserInfo 端点

GET /connect/userinfo

返回已验证用户的声明。需要具有 openid 作用域的有效访问令牌。

字段类型描述
substring唯一用户标识符
emailstring用户电子邮箱地址
email_verifiedboolean电子邮箱是否已验证
given_namestring名字
family_namestring姓氏
namestring完整显示名称
phone_numberstring电话号码(如已提供)
org_idstring该用户所属的组织。由你自己的预配应用分配(见预配应用一节),或用 PUT /api/v1/users/{userId} 设置;Authagonal 从不推导它。在 profile scope 下发放,用户没有组织时不出现。
rolesstring[]已分配角色的数组
groupsobject[]组成员关系数组,每项包含 id 和 name
Fetch user info
curl https://acme.authagonal.io/connect/userinfo \
  -H "Authorization: Bearer ACCESS_TOKEN"

令牌内省(RFC 7662)

POST /connect/introspect

验证令牌并返回其元数据。需要客户端凭证(Basic auth 或表单正文参数)。

参数必需描述
token要内省的令牌
token_type_hint可选关于令牌类型的提示(例如 "refresh_token")

活跃令牌响应:

字段描述
activetrue
sub主体(用户 ID)
client_id令牌签发给的客户端
scope已授予的以空格分隔的作用域
iss发行者
exp过期时间(Unix 时间戳)
iat签发时间(Unix 时间戳)
aud受众
token_type令牌类型(例如 "Bearer")

非活跃令牌响应:{ "active": false }

始终返回 200 OK

根据 RFC 7662,内省端点始终返回 200 OK——从不返回 401 或 403。这可以防止令牌枚举攻击。无效或过期的令牌只会返回 active: false
Introspect a token
curl -X POST https://acme.authagonal.io/connect/introspect \
  -u "my-app:CLIENT_SECRET" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "token=ACCESS_OR_REFRESH_TOKEN"

令牌撤销(RFC 7009)

POST /connect/revocation

撤销先前签发的令牌。需要客户端凭证。

参数必需描述
token要撤销的令牌
token_type_hint可选关于令牌类型的提示(例如 "refresh_token")

根据 RFC 7009 规范,该端点始终返回 200 OK,即使令牌无效或已被撤销。

仅支持刷新令牌

目前支持刷新令牌撤销。访问令牌是无状态 JWT,无法被撤销——它们在自然过期前始终有效。

设备授权(RFC 8628)

POST /connect/deviceauthorization

为输入受限设备(CLI、智能电视、IoT 设备)发起设备授权流程。设备向用户显示一个代码,用户随后在另一台有浏览器的设备上批准请求。

参数必需描述
client_id您的客户端标识符
client_secret机密客户端您的客户端密钥
scope可选以空格分隔的作用域(默认为 "openid")

响应:

字段描述
device_code设备验证码(用于轮询)
user_code面向用户的代码,格式为 XXXX-XXXX
verification_uri用户访问以输入代码的 URL
verification_uri_complete预填充了 user_code 的 URL
expires_in600(秒——代码有效期为 10 分钟)
interval5(秒——最小轮询间隔)

批准流程:用户访问 verification_uri,输入 user_code,然后批准请求。同时,设备使用 device_code 轮询令牌端点。

轮询错误代码:

错误含义
authorization_pending用户尚未批准——继续轮询
expired_token设备代码已过期——重新开始流程
access_denied用户拒绝了授权请求
Request device authorization
curl -X POST https://acme.authagonal.io/connect/deviceauthorization \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "client_id=my-cli" \
  -d "scope=openid profile email"

结束会话 / 注销

GET POST /connect/endsession

注销当前用户会话,触发对所有注册了 BackChannelLogoutUri 的客户端的后通道注销,并撤销所有授权。

参数必需描述
id_token_hint可选ID 令牌——用于验证 post_logout_redirect_uri
post_logout_redirect_uri可选注销后重定向的目标地址(必须已注册)
state可选在重定向中返回的不透明值

如果提供了有效的 post_logout_redirect_uri 且匹配已注册的 URI,用户将收到 302 重定向。否则,将返回 JSON 响应确认会话已结束。

后通道注销

当用户注销时,Authagonal 向每个客户端的 BackChannelLogoutUri 发送签名 JWT。JWT 包含 subaudisshttp://schemas.openid.net/event/backchannel-logout 事件声明。您的应用应在收到此通知时使用户的本地会话失效。

SCIM 2.0 API 参考

Authagonal 支持 SCIM 2.0 协议,用于自动化用户和组的配置。Okta、Azure AD 和 OneLogin 等身份提供商可以使用此 API 将您的 Authagonal 租户与企业目录保持同步。

基础 URL:https://{slug}.authagonal.io/scim/v2

身份验证:所有请求需要 Bearer 令牌。在门户的设置 > SCIM 用户配置中生成 SCIM 令牌。

通用请求头:

请求头
AuthorizationBearer SCIM_TOKEN
Content-Typeapplication/scim+json

列表端点通过 startIndex(从 1 开始)和 count(最大 200)查询参数支持分页,并通过 filter 参数支持筛选(例如 userName eq "[email protected]")。

用户

GET /scim/v2/Users — 列出用户,支持可选的分页和筛选。

查询参数描述
startIndex第一个结果的从 1 开始的索引(默认:1)
count每页最大结果数(最大:200)
filterSCIM 筛选表达式(例如 userName eq "[email protected]")

GET /scim/v2/Users/{id} — 通过 Authagonal 用户 ID 获取单个用户。

POST /scim/v2/Users — 创建新用户。返回 201 Created。

字段必需描述
userName电子邮箱地址(在租户内必须唯一)
name.givenName名字
name.familyName姓氏
displayName完整显示名称
active用户是否处于活跃状态(默认:true)
externalId来自上游身份提供商的标识符

PUT /scim/v2/Users/{id} — 完全替换用户资源。必须提供所有字段。

PATCH /scim/v2/Users/{id} — 使用 SCIM PatchOp 进行部分更新。

操作支持的路径示例值
replaceactive, name.givenName, name.familyName, externalIdtrue / false,或字符串值
addname.givenName, name.familyName, externalId一个字符串值
removeexternalId(无需值)

DELETE /scim/v2/Users/{id} — 软删除用户(停用账户并撤销所有令牌)。返回 204 No Content。

Create a user via SCIM
curl -X POST https://acme.authagonal.io/scim/v2/Users \
  -H "Authorization: Bearer SCIM_TOKEN" \
  -H "Content-Type: application/scim+json" \
  -d '{
    "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
    "userName": "[email protected]",
    "name": {
      "givenName": "Jane",
      "familyName": "Smith"
    },
    "displayName": "Jane Smith",
    "active": true,
    "externalId": "ext-12345"
  }'

GET /scim/v2/Groups — 列出所有组,支持可选的分页和筛选。

GET /scim/v2/Groups/{id} — 通过 ID 获取单个组,包括其成员列表。

POST /scim/v2/Groups — 创建新组。返回 201 Created。

字段必需描述
displayName组显示名称
members成员对象数组,每个对象包含一个 value 字段(用户 ID)
externalId来自上游身份提供商的标识符

PUT /scim/v2/Groups/{id} — 完全替换组资源(包括其成员列表)。

PATCH /scim/v2/Groups/{id} — 用于添加或移除组成员的部分更新。

DELETE /scim/v2/Groups/{id} — 硬删除组。返回 204 No Content。

Add members to a group via PATCH
curl -X PATCH https://acme.authagonal.io/scim/v2/Groups/GROUP_ID \
  -H "Authorization: Bearer SCIM_TOKEN" \
  -H "Content-Type: application/scim+json" \
  -d '{
    "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
    "Operations": [
      {
        "op": "add",
        "path": "members",
        "value": [
          { "value": "USER_ID_1" },
          { "value": "USER_ID_2" }
        ]
      }
    ]
  }'

SCIM 错误响应

当 SCIM 请求失败时,响应正文遵循 SCIM 错误模式:{ "schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"], "status": "400", "detail": "..." }。常见状态码包括 400(错误请求)、404(资源未找到)、409(冲突 / 重复)和 429(频率限制)。

Portal API(自动化)

Portal API 让您自己的后端能够自动化您在门户中所能执行的一切操作——管理用户、客户端、组、角色、作用域、SSO 连接和设置——通过一种机器对机器(M2M)凭据完成。它与门户 UI 调用的是同一套 API。

基础 URL:https://portal-api.<your-domain>/api/v1。请求通过 Bearer 访问令牌进行身份验证;租户取自令牌,而非 URL。

创建 API 凭据

在门户中,打开 Clients → Create API credential,选择一个访问级别并为其命名。Authagonal 会生成一个为 Portal API 配置好的 OAuth client_credentials 客户端,并返回 Client ID 和密钥。

立即复制密钥

客户端密钥仅显示一次,就在创建之后。请在关闭对话框前将其保存到您的密钥管理器中——如果丢失,请删除该凭据并重新创建一个。

访问级别

作用域授予权限
tenant:owner完全访问,包括破坏性的、仅限所有者的操作,例如删除整个租户。
tenant:admin管理除仅限所有者操作之外的一切——用户、客户端、SSO、组、角色、品牌和设置。
tenant:developer管理客户端、作用域和配置应用。
tenant:support为支持任务读取和管理用户。

您只能授予自己拥有的权限

凭据的权限不能高于创建它的人。管理员无法签发所有者级别的凭据,且平台管理作用域永远无法授予凭据。

获取令牌

在您的管理租户令牌端点——https://<your-tenant>.<your-domain>/connect/token——用该凭据换取访问令牌,然后将令牌作为 Bearer 请求头发送给 Portal API。令牌有效期为一小时。

获取令牌,然后调用 API
# 1. Exchange the credential for an access token (your tenant's token endpoint)
curl -X POST https://acme.authagonal.io/connect/token \
  -d grant_type=client_credentials \
  -d client_id=api-3f2a... \
  -d client_secret=YOUR_CLIENT_SECRET \
  -d scope=tenant:admin

# Response: { "access_token": "ey...", "token_type": "Bearer", "expires_in": 3600 }

# 2. Call the Portal API with the access token
curl https://portal-api.authagonal.io/api/v1/users \
  -H "Authorization: Bearer $ACCESS_TOKEN"

端点

所有路径均相对于基础 URL,并需要 Bearer 访问令牌。每个分组旁的作用域是该凭据所需的最低访问级别。列表端点接受 startIndex 和 count 查询参数。

客户端tenant:developer

GET/api/v1/clients— 列出 OAuth 客户端。

GET/api/v1/clients/{id}— 按 ID 获取单个客户端。

POST/api/v1/clients— 创建客户端。返回客户端 ID;对于机密客户端,还会返回一次性密钥。

PUT/api/v1/clients/{id}— 更新客户端(重定向 URI、授权类型、令牌有效期、PKCE/PAR 要求)。

DELETE/api/v1/clients/{id}— 删除客户端。

POST/api/v1/clients/api-credential— 创建机器对机器的 Portal API 凭据。

用户tenant:support

GET/api/v1/users— 列出用户。支持 count、search(邮箱或姓名前缀)、organizationId(筛选出单个组织)以及 after(游标分页)。

GET/api/v1/users/count— 租户的用户总数。

GET/api/v1/users/stats/mfa— MFA 注册统计。

GET/api/v1/users/{id}— 获取单个用户。

POST/api/v1/users— 使用邮箱和密码创建用户。

PUT/api/v1/users/{id}— 更新用户(资料、邮箱、启用/封禁状态、organizationId)。

DELETE/api/v1/users/{id}— 删除用户。

GET/api/v1/users/{id}/mfa— Get a user's enrolled MFA methods.

DELETE/api/v1/users/{id}/mfa— Reset a user's MFA enrollment.

角色tenant:admin

GET/api/v1/roles— 列出角色。

POST/api/v1/roles— 创建角色。

DELETE/api/v1/roles/{id}— 删除角色。

POST/api/v1/roles/assign— 为用户分配角色。

POST/api/v1/roles/unassign— 移除用户的角色。

tenant:admin

GET/api/v1/groups— 列出组。

GET/api/v1/groups/{id}— 获取组及其成员。

POST/api/v1/groups— 创建组。

POST/api/v1/groups/{id}/members— 向组中添加成员。

DELETE/api/v1/groups/{groupId}/members/{userId}— 从组中移除成员。

DELETE/api/v1/groups/{id}— 删除组。

GET/api/v1/group-role-mappings— 列出组到角色的映射(根据组成员身份在签发令牌时授予的角色)。

作用域tenant:developer

GET/api/v1/scopes— 列出 API 作用域。

POST/api/v1/scopes— 创建作用域。

DELETE/api/v1/scopes/{name}— 删除作用域。

SSO 连接tenant:admin

GET/api/v1/saml/connections— 列出 SAML 连接。

POST/api/v1/saml/connections— 创建 SAML 连接。

DELETE/api/v1/saml/connections/{id}— 删除 SAML 连接。

GET/api/v1/oidc/connections— 列出 OIDC 连接。

POST/api/v1/oidc/connections— 创建 OIDC 连接。

DELETE/api/v1/oidc/connections/{id}— 删除 OIDC 连接。

GET/api/v1/sso/domains— 列出路由到 SSO 连接的域名(home-realm 发现)。

品牌tenant:admin

GET/api/v1/branding— 获取租户品牌设置(颜色、logo、支持的语言)。

PUT/api/v1/branding— 更新租户品牌设置。

设置tenant:admin

GET/api/v1/settings— 获取租户设置(webhook、公开注册、令牌策略)。

PUT/api/v1/settings— 更新租户设置。

POST/api/v1/settings/webhook-secret/regenerate— 轮换 webhook 签名密钥。

POST/api/v1/settings/test-email— 使用当前邮件配置发送测试邮件。

自定义域名和邮件tenant:admin

GET/api/v1/custom-domains— 列出自定义登录域名及其验证状态。

POST/api/v1/custom-domains— 添加自定义域名。

POST/api/v1/custom-domains/{domain}/verify— 为自定义域名触发 DNS 验证。

DELETE/api/v1/custom-domains/{domain}— 移除自定义域名。

GET/api/v1/email/domains— 列出发件人邮件域名。

审计日志tenant:admin

GET/api/v1/audit— 查询租户审计日志。

通过 SCIM 预配用户

如需从 IdP(Entra、Okta)批量预配用户和组,请使用 SCIM 2.0 API,而不是这些端点。

示例:创建用户

POST /api/v1/users
curl -X POST https://portal-api.authagonal.io/api/v1/users \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "password": "S3cure-temp-passw0rd",
    "firstName": "Ada",
    "lastName": "Lovelace"
  }'

# 200 OK
# { "userId": "8f3a...", "email": "[email protected]" }

UI 能做的一切

Portal API 公开了门户 UI 所使用的同一套端点,因此您在门户中能执行的任何操作都可以自动化——但受凭据访问级别的限制。

登录页面

这些是您的终端用户在您租户的认证服务器上看到的托管页面。Authagonal 提供每一个开箱即用的页面,让您无需构建任何 UI 即可获得完整、安全的登录体验。本页将逐一介绍每个页面,并说明哪些门户设置可以控制它。

完全白标

这里的每个页面都由您租户的品牌设置渲染——您的 Logo、配色、应用名称和自定义 CSS。这些页面还遵循 prefers-color-scheme,会随用户设备在浅色与深色之间自动切换。

登录

Hosted sign-in screen with an email field, Continue button, single sign-on provider buttons, and forgot-password and create-account links
  • 邮箱优先的两步式流程:用户先输入邮箱并点击继续,然后出现密码输入框。
  • 当存在 SSO 连接时,「使用 {provider} 继续」单点登录按钮会自动出现。
  • 忘记密码创建账户链接,二者均可显示或隐藏。
  • 可选的 Cloudflare Turnstile 人机验证,用于阻止自动化登录尝试。

在门户管理后台中控制

  • 品牌设置用于设定 Logo、配色、应用名称、支持邮箱和自定义 CSS。
  • 显示或隐藏忘记密码注册链接(品牌设置)。
  • SSO 连接会添加社交登录按钮(SSO 页面)。
  • 会话有效期和锁定阈值(设置 → 安全)。

注册

Account registration screen with first and last name fields, email, password, and a live password-policy checklist
  • 收集名字和姓氏(可选)、邮箱以及密码
  • 实时密码策略清单会随用户输入而更新,让要求在提交前一目了然。
  • 可选的 Cloudflare Turnstile 人机验证。
  • 为已有账户的用户提供「登录」链接。

在门户管理后台中控制

  • 显示或隐藏注册链接(品牌设置)。
  • 您租户的密码策略决定了清单内容。
  • 品牌设置为整个页面设定样式。

忘记密码

Forgot-password screen with an email field and a neutral check-your-email confirmation state
  • 用户输入邮箱后,会看到一条中性的「请查收邮件」确认提示。
  • 该页面绝不透露账户是否存在,从而挫败账户枚举探测。
  • 「返回登录」链接可将用户带回登录页面。

在门户管理后台中控制

  • 显示或隐藏忘记密码链接(品牌设置)。
  • 您租户的邮件投递负责发送重置邮件。
  • 品牌设置为整个页面设定样式。

重置密码

Reset-password screen with new and confirm password fields and a live per-rule requirement checklist
  • 新密码确认密码输入框,配有逐条规则的实时要求清单
  • 当重置令牌不再有效时,会显示清晰的链接无效或已过期状态。
  • 确认密码已更改的成功状态

在门户管理后台中控制

  • 您租户的密码策略决定了清单内容。
  • 品牌设置为整个页面设定样式。

MFA 验证

MFA challenge screen with a method switcher, a six-digit authenticator code field, recovery-code entry, and a passkey button
  • 在身份验证器应用、通行密钥和恢复码之间切换的方式切换器
  • 6 位 TOTP 输入框,所有数字输入完毕后自动提交。
  • 为丢失身份验证器访问权限的用户提供恢复码输入
  • 用于硬件支持验证的通行密钥按钮。

在门户管理后台中控制

  • MFA 策略按应用设置(客户端 → 安全)。
  • 任何已注册验证因素的用户都会始终被要求验证,无论策略如何。

MFA 设置

MFA setup screen showing enrolled-method status, authenticator QR code and manual key, passkey enrolment, and recovery-code generation
  • 显示已注册方式的状态,让用户知道哪些已经配置好。
  • 通过二维码、手动密钥备用方案和确认步骤完成身份验证器设置
  • 用于硬件支持认证的通行密钥注册
  • 用于账户恢复的恢复码生成
  • 当 MFA 为自助而非强制时,提供可选的跳过

在门户管理后台中控制

  • MFA 策略按应用设置;强制会在登录时强制完成设置(客户端 → 安全)。
  • 品牌设置为整个页面设定样式。

设备授权

Device authorization screen with a centered user-code entry field, an Approve button, and an approved confirmation state
  • 居中的用户代码输入框,用于输入设备上显示的代码。
  • 授权该设备的批准步骤。
  • 当用户尚未认证时显示的登录过渡页
  • 设备授权完成后的已批准确认

在门户管理后台中控制

  • 在应用上启用设备代码授权(客户端 → 授权类型)。
  • 设置设备代码有效期(客户端 → 令牌)。
Consent screen showing the requesting application's logo and name, a per-scope permission list, and Allow and Deny buttons
  • 显示发起请求的客户端 Logo 和名称
  • 逐项权限列表,为每项权限配有友好、易读的标签。
  • 用于授予或拒绝访问的允许拒绝按钮。
  • 解释该决定含义的授权提示页脚

在门户管理后台中控制

  • 按应用开启要求授权同意(客户端 → 安全)。
  • Logo、名称和 URL来自应用自身的元数据。
  • 品牌设置渲染授权同意卡片。

已连接应用(授权)

Connected apps screen listing the applications a user has authorized with their scopes and granted date, plus a revoke control
  • 列出用户已授权的每个应用,含其名称、权限范围和授权日期
  • 撤销对某个应用的访问权限,生效前会有确认步骤。
  • 当用户尚未授权任何应用时,显示友好的空状态

在门户管理后台中控制

  • 该列表由需要授权同意的应用填充。
  • 品牌设置为整个页面设定样式。

账户

位于 /login/account 的托管自助账户页面,已登录用户可在此管理自己的个人资料和首选语言,无需门户访问权限。

Self-service account screen with editable profile fields and a preferred Language selector
  • 编辑名字和姓氏、公司和电话;电子邮件地址以只读方式显示。
  • 从受支持的语言中选择首选语言;界面会即时预览所选语言,并在保存时保留该选择。
  • 已保存的语言决定用户的托管界面以及他们所收到的事务性电子邮件的语言。

在门户管理后台中控制

  • 品牌设置为整个页面设定样式。
  • 管理员也可在门户的用户页面上编辑同一个首选语言。

身份验证流程

身份验证流程涵盖终端用户如何与您的 Authagonal 租户交互——登录、注册、重置密码和设置 MFA。这些端点由托管登录页面使用,如果您正在构建自定义登录 UI,也可以直接调用。

登录

POST /api/auth/login

使用电子邮箱和密码验证用户身份。成功后,签发会话 Cookie 并返回用户配置文件。如果配置了 MFA,响应会指示在会话完全建立之前需要第二因素。

请求正文:

Login request
{
  "email": "[email protected]",
  "password": "correct-horse-battery-staple"
}

成功响应:

字段类型描述
userIdstring唯一用户标识符
emailstring用户电子邮箱地址
namestring完整显示名称
mfaAvailableboolean用户是否已注册 MFA 方法

MFA 必需响应:当用户已注册 MFA 时,响应包含 mfaRequired: true 以及 challengeId 和列出可用 MFA 方法的 methods 数组。

MFA 设置必需响应:当租户要求 MFA 但用户尚未注册时,响应包含 mfaSetupRequired: true 和用于注册流程的 setupToken

错误响应:

错误代码HTTP 状态描述
invalid_credentials401电子邮箱或密码不正确
account_disabled403账户已被管理员停用
email_not_confirmed403用户尚未验证其电子邮箱地址
locked_out423账户已被临时锁定(包含 retryAfter 秒数)
sso_required409电子邮箱域名已配置 SSO(包含 redirectUrl)

SSO 检查:如果用户的电子邮箱域名配置了 SSO 连接,登录端点将返回 sso_requiredredirectUrl。客户端应将用户重定向到 SSO 提供商。

账户锁定:连续 maxFailedAttempts 次登录失败后,账户将被锁定 lockoutDurationMinutes。这两个值均可在租户设置中配置。

托管登录页面

登录端点通常由托管登录页面调用,而非直接由您的应用调用。使用 OIDC 授权码流程发起身份验证——您的用户将自动被重定向到托管登录页面。

注册

POST /api/auth/register

创建新用户账户。验证邮件会自动发送——用户必须在登录前验证其电子邮箱。

请求正文:

Registration request
{
  "email": "[email protected]",
  "password": "a-strong-password-here",
  "firstName": "Jane",
  "lastName": "Smith"
}
字段必需描述
email电子邮箱地址(必须唯一)
password必须符合租户密码策略
firstName名字
lastName姓氏

成功:201 Created,返回新账户的 userId。使用已注册的邮箱注册同样返回 201:我们绝不透露某个邮箱是否已存在(以防止账户枚举),而是改为通过邮件通知真正的账户持有者。

错误响应:

错误代码HTTP 状态描述
weak_password400密码不符合租户密码策略
rate_limited429注册尝试次数过多
provisioning_rejected422配置 Webhook 拒绝了该注册

密码策略

在提交前通过 GET /api/auth/password-policy 检查租户的密码要求。返回最小长度、所需字符类别以及是否启用了泄露密码检查。

密码重置

POST /api/auth/forgot-password

请求密码重置邮件。无论电子邮箱是否存在,端点始终返回成功响应,以防止电子邮箱枚举。

Forgot password request
{
  "email": "[email protected]"
}

POST /api/auth/reset-password

使用邮件链接中的令牌重置用户密码。

Reset password request
{
  "token": "RESET_TOKEN_FROM_EMAIL",
  "newPassword": "new-strong-password"
}

成功密码重置的副作用:

  • 失败登录尝试计数器重置为零
  • 所有现有刷新令牌被撤销
  • 生成新的安全戳(使所有现有会话失效)

MFA 设置与验证

Authagonal 支持三种 MFA 方法:TOTP(身份验证器应用)、WebAuthn(安全密钥和生物识别)和一次性恢复代码。

TOTP 设置

POST /api/auth/mfa/totp/setup — 返回 QR 码数据 URI 和手动输入密钥。用户使用身份验证器应用(Google Authenticator、Authy、1Password 等)扫描 QR 码,然后确认注册。

POST /api/auth/mfa/totp/confirm — 通过验证身份验证器应用中的 6 位数代码来确认 TOTP 注册。

Confirm TOTP enrollment
{
  "code": "123456"
}

WebAuthn 设置

POST /api/auth/mfa/webauthn/setup——返回 WebAuthn API 的凭证创建选项。浏览器使用这些选项调用 navigator.credentials.create()

POST /api/auth/mfa/webauthn/confirm — 通过提交浏览器的证明响应来确认 WebAuthn 注册。

恢复代码

POST /api/auth/mfa/recovery/generate — 生成 10 个一次性 8 字符恢复代码。每个代码只能使用一次以绕过 MFA。

恢复代码仅显示一次

恢复代码仅在生成时显示,之后无法再次获取。如果用户同时丢失了身份验证器设备和恢复代码,管理员必须在门户中手动删除其 MFA 凭证后用户才能再次登录。

MFA 验证

POST /api/auth/mfa/verify — 在成功密码登录后完成 MFA 挑战。

字段必需描述
challengeId来自登录响应的挑战 ID
method"totp"、"recovery" 或 "webauthn"
codeTOTP / 恢复代码6 位 TOTP 代码或 8 字符恢复代码
assertionWebAuthn来自 navigator.credentials.get() 的断言响应

MFA 状态

GET /api/auth/mfa/status — 返回用户当前已注册的 MFA 方法。

SSO 登录流程

Authagonal 支持 SAML 2.0 和基于 OIDC 的 SSO 连接。基于域的路由根据用户的电子邮箱地址自动检测使用哪个 SSO 提供商。

SSO 检查

GET /api/auth/[email protected]

字段类型描述
ssoRequiredboolean该电子邮箱域名是否需要 SSO
providerTypestring"saml" 或 "oidc"
connectionIdstringSSO 连接标识符
redirectUrlstring将用户重定向到 SSO 登录的 URL

SAML 流程

用户被重定向到 GET /saml/{connectionId}/login,该端点向身份提供商发送 SAML AuthnRequest。身份提供商验证用户身份后将 SAML 响应发回 Assertion Consumer Service (ACS) 端点。Authagonal 验证断言、创建或更新用户并签发会话 Cookie。

用于配置身份提供商的 SAML 元数据可在 GET /saml/{connectionId}/metadata 获取。

OIDC 流程

用户被重定向到 GET /oidc/{connectionId}/login,该端点使用 PKCE 重定向到上游身份提供商。用户完成身份验证后,回调端点 /oidc/callback 交换授权码、验证 ID 令牌并创建或更新用户。

JIT 配置:SAML 和 OIDC 流程均支持即时配置。如果用户在租户中不存在,将根据身份提供商的声明自动创建。如果用户已存在,其配置文件属性将更新为与提供商的最新值匹配。

基于域的路由

基于域的路由意味着您的用户无需知道他们使用的是哪个 SSO 提供商。只需输入电子邮箱地址即可——Authagonal 会将域名匹配到正确的 SSO 连接并自动重定向。

Backend-for-Frontend(BFF)

BFF 让 OAuth 令牌完全不进入浏览器。您的单页应用只持有一个 httpOnly 会话 Cookie,由您自己后端上的机密客户端完成 OpenID Connect 流程,并在服务端持有令牌。

单页应用能读到的任何东西,跨站脚本都能偷走,其中就包括内存里的访问令牌和 localStorage 里的刷新令牌。把令牌存在浏览器中还会压低它们的有效期,因为脚本触手可及的长期刷新令牌始终是一项悬着的风险。IETF 的 OAuth 2.0 for Browser-Based Apps 当前最佳实践,正是出于这个原因推荐这一模式。

作为交换,您得到的是:一个不出现任何令牌、却能跨页面刷新继续存在的会话;由服务端替您完成的令牌刷新;通过后通道注销实现的即时撤销;以及一个已认证的代理,让您的 API 永远不必解析一个浏览器有可能篡改过的令牌。

创建客户端

在门户中打开客户端,选择创建 BFF 应用。填入您的应用对外提供服务的基础 URL,例如 https://app.acme.com,Authagonal 会为您注册一个配置正确的机密客户端,而不是把拼装的活儿丢给您:

设置项
重定向 URI{appBaseUrl}/bff/callback
注销后重定向 URI{appBaseUrl}/
后通道注销 URI{appBaseUrl}/bff/backchannel-logout
授权类型authorization_code, refresh_token
作用域openid, profile, email, offline_access
PKCE 与客户端密钥两者均为必需

密钥只显示一次

响应中携带 clientIdclientSecretauthority,之后密钥再也无法找回,因为我们只保存它的哈希值。请把它直接放进后端的配置或密钥存储中。如果丢失了,请另建一个客户端,不要试图找回这一个。

接入您的后端

支持两种运行时,行为完全一致:面向 .NET 的 Authagonal.Bff 和面向 Node 的 @authagonal/bff,后者带有 Express 和 Next.js 适配器。把其中任意一个指向门户刚刚给出的那些值即可。

.NET
// dotnet add package Authagonal.Bff
builder.Services.AddAuthagonalBff(o =>
{
    o.Authority    = "https://acme.authagonal.io";       // your tenant auth host
    o.ClientId     = builder.Configuration["Bff:ClientId"]!;
    o.ClientSecret = builder.Configuration["Bff:ClientSecret"]!;
    o.Scope        = ["openid", "profile", "email", "offline_access"];
    o.PostLogoutRedirectUri = "https://app.acme.com/";
});

var app = builder.Build();

app.UseForwardedHeaders();   // required behind a proxy or ingress, see the note below
app.MapAuthagonalBff();
app.MapFallbackToFile("index.html");   // your SPA
app.Run();
Node(Express)
// npm install @authagonal/bff
import express from 'express';
import { authagonalBff } from '@authagonal/bff/express';

const app = express();

app.use(authagonalBff({
  authority: 'https://acme.authagonal.io',
  clientId: process.env.BFF_CLIENT_ID,
  clientSecret: process.env.BFF_CLIENT_SECRET,
  scope: ['openid', 'profile', 'email', 'offline_access'],
  postLogoutRedirectUri: 'https://app.acme.com/',
}));

app.listen(8080);

位于代理之后时,请信任转发标头

几乎每种部署都会把 BFF 放在 ingress 或负载均衡器之后,由它终结 TLS,并以明文 HTTP 与您的进程通信。如果不处理转发标头,BFF 会认为请求是不安全的,于是签发的 __Host- 会话 Cookie 不带 Secure 属性,浏览器随后会悄悄丢弃它。症状是登录干净利落地完成,会话却始终不出现。在 .NET 中,请在 MapAuthagonalBff() 之前调用 app.UseForwardedHeaders();在 Node 中,请设置框架的 trust-proxy 选项。

端点

默认挂载在 /bff 之下。它们必须由与您 SPA 相同的来源提供服务,因为会话 Cookie 是 httpOnly 且同源的:请把它们放在同一个主机名之下,而不是放在单独的 API 域名上。

路由用途
GET /bff/login?returnUrl=/发起登录并重定向到 Authagonal。随后把用户送回 returnUrl。
GET /bff/callbackOIDC 重定向 URI。由 BFF 代为处理,您无需自己编写。
GET /bff/user返回 isAuthenticated、会话声明和 sessionExpiresAt。需要携带防伪标头。
GET|POST /bff/logout在本地和 Authagonal 侧同时结束会话。
POST /bff/backchannel-logout接收来自 Authagonal 的注销通知,因此在别处退出登录也会结束此处的会话。

在浏览器中调用

每个非导航请求都必须携带一个固定的防伪标头。它与 Cookie 的 SameSite 属性一起防御跨站请求伪造:跨站的表单提交无法设置自定义标头,因此缺少该标头的请求会被拒绝。

查询当前登录的是谁
const me = await fetch('/bff/user', {
  headers: { 'X-Authagonal-Bff': '1' },
}).then(r => r.json());

if (!me.isAuthenticated) {
  // Navigate, do not fetch: this is a redirect to your identity provider.
  window.location.href = '/bff/login?returnUrl=' + encodeURIComponent(location.pathname);
}

登录和注销请通过页面导航完成,而不是 fetch:location.href = '/bff/login'。这些路由返回的是跳转到您身份提供商的重定向,而这样的重定向链并不是 fetch 能够有效跟随的。

调用您的 API

BFF 可以在自己的基础路径下转发您的 API,并在转发途中附上会话的访问令牌。浏览器发送的是 Cookie,您的 API 收到的是一个按常规校验的 Bearer 令牌,而该令牌的任何部分对页面既不可见,也无法伪造。注册一个上游后,发往 /bff/api/** 的请求就会以已认证的身份到达它。把列表留空,代理则被完全禁用。

转发一个上游 API
o.Upstreams.Add(new BffUpstream
{
    Prefix        = "/orders",                        // matches /bff/api/orders/**
    TargetBaseUrl = "https://api.internal.acme.com",
    StripPrefix   = false,                            // keep /orders in the forwarded path
});
选项默认值作用
Upstreams[]代理转发到的那些 API。留空则禁用代理端点。
Prefix/该上游负责处理的、位于 /bff/api 之后的路径前缀,例如 /orders。
TargetBaseUrl-请求被转发到的基础 URL。
StripPrefixfalse转发前去掉匹配到的前缀。这样您可以用一个人为设定的路由前缀,把一个 BFF 分发到共用同一路径命名空间的多个后端。
AllowAnonymousProxyRequestsfalse当请求没有可用会话时,不再拒绝它,而是在不带 Authorization 标头的情况下转发。适用于同时服务已登录调用方和匿名调用方的 API。
RequiredAuthority[]以 type:action 对给出的权限门槛,例如 email:send。设置后,代理会在转发前检查外发令牌的 RFC 9396 authorization details。
AuthorityLocation-当权限是针对某个公开资源标识符授予的,而该标识符与代理实际调用的内部地址不同时,此上游对外所用的 RFC 9396 locations 根地址。
StrictAuthorityfalse当调用带有代理无法判定的授权约束时,拒绝该调用而不是转发它。代理是盲转发的,不会推导出任何约束上下文,因此该选项默认关闭。
ExchangeRoutes[]这些代理路由在调用上游时使用的是绑定了上下文的交换令牌,而不是会话的主访问令牌。第一个匹配到的模式生效。

WebSocket

WebSocket 握手既无法携带自定义标头,也无法携带 Bearer 令牌,因此防伪标头和代理在这里都帮不上忙。启用票据后,SPA 就可以调用 GET /bff/ws-ticket,把这个短时效的一次性票据放到连接 URL 上,再由您的 API 去兑换。请在每次连接之前才签发一个:它在首次使用后即被删除,并在数秒内过期。

选项默认值作用
WsTicketsEnabledfalse启用 ws-ticket 端点。默认关闭。
WsTicketLifetime30s票据的有效时长。由于它要在 URL 中传输,这里刻意保持很短。
TicketExchangeParams[]票据请求可以转发进令牌交换的查询参数,使票据绑定到该上下文,而不是对一切都有效。

有意把令牌交给浏览器

BFF 的全部意义就在于浏览器不持有任何令牌,因此这项能力需要主动开启,适用面也很窄。它只为 Cookie 模型够不到的那一种情况而存在:位于另一个来源的资源服务器,例如您嵌入在 iframe 中的应用,必须用 Bearer 令牌来调用。启用后,GET /bff/token?resource=… 会返回一个交换得到的令牌:会话的令牌被降权到某一个白名单资源,并绑定到任何白名单上下文参数。浏览器永远看不到会话自身的令牌,而它拿到的那个令牌时效很短,且只面向单一受众。指定了白名单之外资源的请求会被拒绝,正是这一点让它不会变成一台通用的令牌铸造机。

选项默认值作用
TokenEndpointEnabledfalse启用 token 端点。默认关闭。
TokenEndpointResources[]令牌可以指向的 resource 取值。其他一律拒绝。
TokenEndpointExchangeParams[]作为上下文绑定转发进交换的查询参数,例如 project_id。

用一个 BFF 服务多个租户

设置一个租户查询参数,一套部署就能服务众多租户:/bff/login?slug=acme 选定租户,解析器提供该租户的 authority 和客户端凭据,这个键随关联 Cookie 一路进入会话,后通道注销则从令牌的签发者解析出租户。默认解析器让单租户行为保持逐字节一致,因此只要您不使用这项功能,就不必为它付出任何代价。

值得了解的选项

完整清单。Node 包以驼峰形式镜像这些选项,因此 BasePath 对应 basePath,其余同理。

选项默认值作用
Authority-您的租户认证主机。OIDC 元数据从这里发现。除非您是多租户,由解析器提供它,否则此项必填。
ClientId-为该 BFF 注册的机密客户端 id。
ClientSecret-客户端密钥。BFF 是机密客户端,因此此项必填。
Scopeopenid profile offline_access请求的作用域。必须包含 offline_access,否则不会有刷新令牌,会话会随访问令牌一起结束。
BasePath/bffBFF 路由挂载的位置。
CallbackPath/bff/callbackOIDC 重定向 URI 的路径。必须与客户端注册时使用的一致。
CookieName__Host-agbff会话 Cookie 的名称。__Host- 前缀要求使用 HTTPS,因此在明文 HTTP 上做本地开发时需要换一个名称。
SessionLifetime8h会话最长可以存活多久。请把它设为与刷新令牌的绝对有效期一致,否则闲置的用户会在手里的凭据仍然有效时被登出。
PersistentCookiefalse关闭浏览器后 Cookie 是否仍然保留。无论哪种设置,刷新令牌都留在服务端。
CorrelationLifetime30m从发起登录到回调之间,一次登录最长可以花多久。它限定了承载 state、nonce 和 PKCE verifier 的那个 Cookie 的生存期,因此用户把登录页开着离开、过一会儿再回来,正是它必须扛得住的情况。
RefreshThresholdSeconds60在访问令牌过期前多少秒对其进行刷新。
AntiForgeryHeaderX-Authagonal-Bff浏览器在非导航请求上必须发送的标头名称。
PostLogoutRedirectUri-注销完成后浏览器最终停留的位置。
ReturnUrlAllowlist[]非相对的 returnUrl 可以指向的绝对来源。相对路径始终允许,其余一律强制归为 /,因此无法通过登录路由构造出开放重定向。
LoginPassthroughParams[]从 /bff/login 转发到 authorize 请求的查询参数,例如 prompt,好让「立即开始」这类链接直接进入注册而不是登录。
TenantQueryParam-设置它即可用一个 BFF 服务多个租户。参见上文。

两种运行时行为一致

.NET 包和 Node 包实现的是同一份协议契约,因此端点、Cookie、防伪标头和刷新行为都完全相同。哪一个与您的后端匹配就选哪一个。上面给出的是 .NET 的写法,Node 使用对应的驼峰形式。

每个部件都可替换

这些部件都是接口,因此您无需 fork 就能把 BFF 搬到自己的基础设施上:IBffSessionStore 决定会话存放在哪里,ICookieProtector 负责 Cookie 加密(默认使用 ASP.NET Data Protection),ITokenClient 负责与令牌端点和撤销端点通信。通过 IBffTenantResolver,一个 BFF 还可以服务多个租户:登录时从查询参数选择租户,后通道注销时则从令牌的签发者解析出租户。

用 AI 助手来操作您的门户

把 AI 助手连到您的租户,然后让它去做那些您原本要一路点击才能完成的事:找出一个登录不了的用户,查看他们是否还有第二验证因素,邀请某个人,看看谁持有管理员角色。

这是一次普通的 OAuth 连接,而不是一把 API 密钥。每个人都以自己的身份登录并批准该访问,因此助手能做的恰好是那个人在门户中能做的事,不会更多。不会新建任何可能泄露的东西,撤销某一个助手也不会影响其他任何人。

启用它

打开设置并启用 AI 助手访问。在您启用之前它是关闭的,而在关闭期间,该端点并不存在,而不只是拒绝请求。启用之后,面板会显示要粘贴到您的 AI 客户端里的 URL:

您的 MCP URL
https://portal-api.authagonal.io/api/v1/mcp/{your-tenant}

助手是怎么拿到权限的

助手必须先自行注册,才能发起一次登录,而允许它这么做的正是启用 AI 助手访问这一项。您不需要再打开别的开关,尤其不要为此在面向最终用户的租户上启用动态客户端注册:那项设置管的是为您自己的用户提供服务的租户,而助手并不在那里登录。注册并不等于拿到权限。刚注册好的助手什么都拿不到,直到您团队里的某个人登录并批准它,此后每个工具都会重新核对那个人的角色。

助手能做什么

恰好是连接它的那个人能做的事,按每次调用依据那个人自己的令牌来判定。tenant:support 坐席拿到的是诊断类和日常类工具;管理员工具不只是对他们隐藏起来,而是即便直接点名调用也会被拒绝。门户里受限的事情在这里同样受限:修改用户的电子邮箱地址在那里需要管理员角色,在这里也需要。

因为这是一份普通的授权,您就按普通方式管理它。撤销它,助手会立即停止工作,而不是等到令牌下次过期。助手做的每一件事都会以它所代表的那个人的名义写进您的审计日志,因此这条线索还是您一直在看的那一条。

这些工具

本次发布共二十个。由您的 AI 客户端决定启用哪些,因此如果您只想让助手做读取,就可以只把读取类工具交给它。

工具角色作用
find_user支持按电子邮箱、姓名前缀或 id 查找用户。其他一切的起点。
get_user支持一个用户的完整信息,包括是否处于活动状态、是否已确认以及是否被锁定。
get_user_mfa支持某个人已注册了哪些第二验证因素。
get_user_sessions支持用户当前在哪些地方处于登录状态。
search_audit支持按操作者、动作或被操作的对象检索审计日志。
list_users支持列出目录,可选择只筛选某一个组织。
get_user_stats支持您有多少用户,以及其中有多少人使用第二验证因素。
invite_user支持通过电子邮件邀请某个人。
resend_invite支持再发一次邀请。
send_verification_email支持再发一次电子邮箱验证邮件。
update_user支持更新一份资料。邮箱、确认状态和组织仍然需要管理员权限。
revoke_user_sessions支持让一个用户在所有地方退出登录。
list_roles管理员您租户中定义的角色。
list_role_members管理员谁持有指定的角色。
assign_role管理员给一个用户授予某个角色。
unassign_role管理员收回一个角色。
reset_user_mfa管理员移除全部第二验证因素,供丢失了验证器的人使用。
get_settings管理员您租户的配置。
list_sso_connections管理员您的 SSO 连接以及它们覆盖的域。
list_clients开发者您租户中已注册的 OAuth 客户端。

读取与写入都有标注

每个工具都会告诉您的客户端:它是否只读,是否会改动什么,以及那次改动是否具有破坏性。好的客户端会据此在不打扰您的情况下执行一次查询,并在诸如移除第二验证因素这类操作之前先停下来。请把它当作一种便利,而不是一种管控:真正决定助手可以做什么的,是您自己的角色,每次调用都会检查。

目前还没有包含的内容

删除用户、创建或编辑 SSO 连接、计费、备份和客户端密钥在本次发布中都没有。删除属于您的数据擦除队列,而不是摆在它旁边;SSO 连接的影响面大到一次改错就会把整支员工队伍挡在门外,所以它们目前是只读的;而一个会返回客户端密钥的工具,等于把密钥留在助手的对话记录里。请告诉我们您想要其中哪些,以及按什么顺序。

MCP 服务器身份验证

如果您对外提供 Model Context Protocol 服务器,Authagonal 可以充当它背后的授权服务器。AI 助手发起连接,它背后的人登录并授予访问权限,您的服务器则收到一个普通的 bearer 令牌,像验证任何其他 API 一样验证它。

另一种做法是把 API 密钥粘贴进助手的配置里,那是一份背后没有用户的凭据,没有有效期,没有同意步骤,也没有办法在不为所有人轮换密钥的前提下撤销某一个连接器。改用 OAuth 意味着这份授权属于一个具名的人,会出现在您的审计日志里,并且可以在门户中撤销,而不必动其他任何东西。

连接是怎么建立的

整个交换过程都由发现机制驱动,因此符合规范的客户端除了您服务器的 URL 之外不需要配置任何东西。

步骤发生了什么
1连接器不带令牌调用您的 MCP 服务器,得到一个 401,其中写明去哪里查找。
2它获取您的受保护资源元数据,其中指明您的 Authagonal 租户就是授权服务器。
3它获取该租户的授权服务器元数据,并且由于从未在任何地方注册过,就自行注册。
4它把用户送去登录并批准该访问,同时指明您的 MCP 服务器就是它想要令牌的那个资源。
5它带着拿到的 bearer 令牌再次调用您的服务器,该令牌只对您的服务器和那位用户有效。

这个顺序里没有一步是 Authagonal 特有的:它就是 MCP 授权规范,建立在 RFC 9728(资源元数据)、RFC 8414(发现授权服务器)、RFC 7591(注册)和 RFC 8707(指明资源)之上。遵循该规范的连接器无需为我们做特殊处理就能工作。

让连接器自行注册

您从未打过交道的连接器无法使用您手工创建的客户端,因此它会在运行时注册一个。该功能默认关闭。在设置中打开动态客户端注册,注册端点就会出现在您的发现文档里;保持关闭,该端点就不会被公布,并且会拒绝请求。启用它只为您自己的租户打开注册,绝不会为别人的租户打开。

防护发生了什么
授权类型只能注册 authorization code 流程和 refresh 流程。自行注册无法铸造出一个完全绕过用户的机器对机器客户端。
PKCE对每个已注册的客户端强制启用,无论注册请求要的是什么。
授权同意同样强制。已注册的连接器在有人看过它所请求的内容并批准之前,拿不到令牌。
作用域OIDC 内置的作用域始终可用。您自己的任何作用域都必须先写进允许列表,自行注册的客户端才可以请求它。
速率限制每个 IP 地址每小时十次注册,因此开放的端点无法被用来塞满您的客户端库。

两条发现路径都提供服务

MCP 客户端通过 /.well-known/oauth-authorization-server(RFC 8414)解析授权服务器,而 OIDC 客户端使用 /.well-known/openid-configuration。您的租户在两条路径上都返回相同的元数据,因此遵循 MCP 规范的连接器无需别人告知去哪里查找,就能找到您。

您的 MCP 服务器要实现什么

两件小事,之后它就是一台普通的资源服务器。第一,发布受保护资源元数据,指明您的租户是授权服务器。把它放在 well-known 路径上提供;如果您的 MCP 端点位于子路径上,也要在带路径后缀的形式上提供,因为客户端两种都会试。

受保护资源元数据
GET https://your-app.example/.well-known/oauth-protected-resource

{
  "resource": "https://your-app.example/mcp",
  "authorization_servers": ["https://acme.authagonal.io"],
  "bearer_methods_supported": ["header"],
  "scopes_supported": ["mcp"]
}

第二,当一次调用没有带有效令牌时,返回 401 并附上指向那份元数据的 WWW-Authenticate 头。正是这个头把一次拒绝变成一次连接:没有它,客户端就无从发现该去哪里认证,只会直接失败。

验证令牌
// Validate the connector's token like any other resource server.
builder.Services.AddAuthentication().AddJwtBearer("McpBearer", o =>
{
    o.Authority = "https://acme.authagonal.io";              // your tenant
    o.TokenValidationParameters.ValidAudience = "https://your-app.example/mcp";
});

// Unauthenticated? Point the connector at the metadata rather than just refusing.
http.Response.Headers.WWWAuthenticate =
    "Bearer resource_metadata=\"https://your-app.example/.well-known/oauth-protected-resource\"";
return Results.Unauthorized();

检查受众,而不只是签名

要验证令牌是为您的服务器签发的。连接器把您的 MCP 服务器指明为资源,所以令牌的受众就是您的资源 URL。只检查签名和签发者的资源服务器,会接受同一租户内为另一个资源签发的令牌,一个连接器的访问权限就是这样变成了另一个连接器的。

作用域、方案与撤销

为您的 MCP 接口定义一个作用域,把它加入允许注册的作用域,它就会出现在授权同意屏幕上,让用户看到自己在批准什么。令牌携带用户的角色以及您配置的任何声明,因此您的服务器可以决定这个具体的人能做什么,而不是把每个连接器都一视同仁。

因为这是一份普通的 OAuth 授权,撤销也就按普通方式进行:找到该用户,撤销他们的会话或那份授权,连接器会立即停止工作,而不是等到令牌下次过期。审计日志会记录注册、同意和签发,因此您可以看到是哪个助手在什么时候请求了什么。

设置发生了什么
Dynamic client registration允许连接器自行注册的门户设置。默认关闭。
Auth:DynamicClientRegistrationScopes自行注册的客户端可以请求的、OIDC 内置之外的作用域。
resource连接器发送的、用来指明您的 MCP 服务器的参数,它把令牌的受众收窄到该服务器。

构建自定义登录界面

用您自己的界面替换 Authagonal 托管的登录、注册、密码重置和 MFA 界面,同时让 Authagonal 继续处理身份验证、MFA、SSO、会话和令牌签发。有两种方式:使用我们的 React 组件库,或从任意框架直接调用认证 API。此功能需主动启用——请先在租户设置中启用自定义登录界面

A fully customized hosted login page with a tenant logo, brand color, and support email in the footer

前提条件:在您的根域名上启用自定义域名

登录会话是第一方 Cookie,因此您的界面与 Authagonal 认证服务器必须共享同一个可注册域名。请在与您应用运行所用的相同根域名下,将一个自定义认证域名指向 Authagonal——例如认证位于 login.acme.com,应用位于 app.acme.com。在存在已启用的自定义域名之前,自定义登录界面设置将保持禁用状态。

您的界面认证主机是否可用?
app.acme.comlogin.acme.com✅ 相同根域名
acme.comauth.acme.com✅ 相同根域名
app.acme.comacme.authagonal.io❌ 跨站点
myapp.iologin.acme.com❌ 跨站点

为什么必须使用自定义域名

跨站点的会话 Cookie 会成为第三方 Cookie——而浏览器(Safari、Chrome)正在逐步淘汰它。将认证保留在您自己的根域名下可使 Cookie 成为第一方且面向未来,这也是平台所强制的:跨域认证调用只有来自与认证主机共享根域名的来源时才会被接受。

此外,请将您界面的来源(例如 https://app.acme.com)添加到您 OAuth 客户端的允许的 CORS 来源中——与您为令牌交换设置的列表相同。

React:@authagonal/login

npm i @authagonal/login 将认证逻辑和界面作为同一个包提供——与 Authagonal 托管登录所基于的包相同。选择适合您的层级:

  • 完整应用——直接引入 App 并通过品牌设置为其设定主题。
  • 组合页面——在您自己的布局中使用 LoginPageMfaChallengePageResetPasswordPage
  • 基础组件 + 逻辑——使用 AuthLayout/Button/Input 和 API 客户端(loginmfaVerifyforgotPassword…)构建您自己的界面。
使用 @authagonal/login API 的自定义界面
import { AuthLayout, Input, Button, login, ApiRequestError } from '@authagonal/login';

function MyLogin() {
  async function onSubmit(email: string, password: string) {
    try {
      const res = await login({ email, password });        // POST /login (sets the session cookie)
      if (res.mfaRequired) {/* render your MFA step → mfaVerify(...) */}
      else window.location.href = res.returnUrl;            // hand off to /connect/authorize
    } catch (e) {
      if (e instanceof ApiRequestError) {/* show e.message */}
    }
  }
  return <AuthLayout>{/* your own markup + <Input/> <Button/> */}</AuthLayout>;
}

任意框架:调用认证 API

不使用 React?可直接调用认证流程端点(位于 /api/auth 下),然后移交给标准的 OIDC /connect/authorize 流程。请发送 credentials: 'include' 以便存储会话 Cookie。

端点用途
POST /api/auth/login进行身份验证;返回 mfaRequired 或返回 URL
POST /api/auth/register自助注册(启用时)
POST /api/auth/forgot-password发起密码重置
POST /api/auth/reset-password完成密码重置
GET /api/auth/password-policy密码策略(用于渲染规则)
POST /api/auth/mfa/*MFA 设置 + 验证(TOTP、WebAuthn、恢复码)

使用 credentials: 'include'

会话是一个 Cookie,因此您的请求必须发送凭据。跨域调用只有在启用了自定义登录界面,且您的来源与认证主机共享根域名时才会成功——否则将被以 403 拒绝。
先进行身份验证,然后移交给 OIDC
# 1. Authenticate (browser fetch — credentials:'include' so the session cookie is stored)
curl -i -X POST https://login.acme.com/api/auth/login \
  -H "Content-Type: application/json" \
  -H "Origin: https://app.acme.com" \
  --data '{"email":"[email protected]","password":"..."}'
# (handle {"mfaRequired":true} → POST /api/auth/mfa/verify, then continue)

# 2. Hand off to the OAuth flow — top-level navigation to the authorize endpoint with PKCE:
# https://login.acme.com/connect/authorize?client_id=my-app&redirect_uri=...&response_type=code
#   &scope=openid%20profile%20email&code_challenge=...&code_challenge_method=S256
# The session cookie (same-site) authenticates the user; you get back a code → exchange at /connect/token.

方案与限额

Authagonal 提供四个方案级别。所有方案包含所有功能——唯一的区别是月活跃用户(MAU)限额和超额定价。

方案级别

方案MAU 限额超额超额费用/用户
入门版1,000
专业版5,000$0.04/用户
规模版25,000$0.025/用户
企业版100,000$0.015/用户

月活跃用户(MAU)

月活跃用户是在计费月内至少成功完成一次身份验证的唯一用户。通过 SCIM 配置但尚未登录的用户不计入您的 MAU 总数。

超额——如果您的方案支持超额,超出 MAU 限额的用户将按上方方案表中显示的每用户费率计费。您可以设置超额上限以限制计费周期的最大支出。

强制执行——如果您的方案不支持超额(入门版),超出 MAU 限额的用户在下一个计费周期或您升级到支持超额的方案之前无法登录。

每个方案均包含完整功能

所有方案包含完整功能集——SSO、SCIM、MFA、自定义域名、品牌、Webhook、审计日志和门户。