服务器操作指北(14)Authentik 邮件通知与外部身份源
创建于 2026-11-14
更新于 2026-11-14
科技
docker
authentik
oidc
microsoft
google
24880 字 · 约 83 分钟

前言

前两篇把 Authentik 部署起来,并配置了 DoraTiger 的登录、注册、MFA 和用户设置流程。接下来补上流程之外的连接:让服务器发邮件,让现有账号通过 Google/Microsoft 登录,以及把系统异常发送给管理员。

这部分遇到的问题比较具体:容器无法访问 Google,OAuth 回调不匹配,外部登录误建账号,收到的品牌邮件顶部图片加载失败。下面按最终配置整理,仍以 2026.8.5 为准,域名和发件账号使用示例。

前置文章:服务器操作指北(12)Authentik 基本概念、默认配置与部署、服务器操作指北(13)Authentik DoraTiger 自建流程与默认配置清理。示例部署目录沿用 /srv/example/authentik,Compose 服务名为 server、worker、postgresql。

各功能的配置位置不同。SMTP 和模板挂载在宿主机部署目录修改;通知传输、匹配策略、规则和身份源在 Authentik 管理员界面创建;Microsoft、Google 的 OAuth 客户端则在各自的云控制台注册。用户的外部账号绑定从 Authentik 用户设置中完成。

本文继续使用 doratiger- 作为对象名称前缀。流程的 Slug、Source 的 Slug 都会进入 URL;阶段、策略、传输和规则表中的名称填入各自的 Name。中文用途放在名称旁说明,避免把标识符与界面字段混在一起。

一、全局 SMTP

1.1 阿里云企业邮箱

本次使用中国内地站点的阿里云企业邮箱,邮箱侧先启用 SMTP 并准备授权码。示例发件地址为 noreply@example.com,接收测试及异常通知的是管理员用户已保存的邮箱。

先在“目录 → 用户”打开管理员账号,确认 Email 字段是实际接收通知的邮箱。发件邮箱 noreply@example.com 配置在 SMTP 中,收件地址来自用户资料,两者不必相同。

登录部署宿主机,在 /srv/example/authentik 中编辑已有 .env,保留原数据库密码和 Authentik 密钥,追加以下配置。阿里云内地站点的 SMTP 主机与 SSL 端口可对照服务商说明:

dotenv
1
2
3
4
5
6
7
8
AUTHENTIK_EMAIL__HOST=smtp.qiye.aliyun.com AUTHENTIK_EMAIL__PORT=465 AUTHENTIK_EMAIL__USERNAME=noreply@example.com AUTHENTIK_EMAIL__PASSWORD=REPLACE_WITH_SMTP_AUTHORIZATION_CODE AUTHENTIK_EMAIL__USE_TLS=false AUTHENTIK_EMAIL__USE_SSL=true AUTHENTIK_EMAIL__TIMEOUT=30 AUTHENTIK_EMAIL__FROM=DoraTiger Auth <noreply@example.com>

授权码只保存在自己的部署环境中,特殊字符的引用与转义按 Compose 语法处理。邮箱服务商、站点不同,主机和 TLS 参数也可能不同;不要照抄到海外邮箱或其他服务商。

只写 .env 不会自动把任意变量放进容器。第一篇使用共享环境变量锚点,因此还要在 compose.yaml 的 x-authentik-environment 中补上以下键;这是插入片段,不是整份文件:

yaml
1
2
3
4
5
6
7
8
AUTHENTIK_EMAIL__HOST: ${AUTHENTIK_EMAIL__HOST} AUTHENTIK_EMAIL__PORT: ${AUTHENTIK_EMAIL__PORT} AUTHENTIK_EMAIL__USERNAME: ${AUTHENTIK_EMAIL__USERNAME} AUTHENTIK_EMAIL__PASSWORD: ${AUTHENTIK_EMAIL__PASSWORD} AUTHENTIK_EMAIL__USE_TLS: ${AUTHENTIK_EMAIL__USE_TLS} AUTHENTIK_EMAIL__USE_SSL: ${AUTHENTIK_EMAIL__USE_SSL} AUTHENTIK_EMAIL__TIMEOUT: ${AUTHENTIK_EMAIL__TIMEOUT} AUTHENTIK_EMAIL__FROM: ${AUTHENTIK_EMAIL__FROM}

465 使用隐式 TLS,对应 USE_SSL;587 常见 STARTTLS,对应 USE_TLS。两者不要同时开启。全局 SMTP 在部署配置中设置,UI 的 Email Stage 可以使用自己的 SMTP,但那不会替代系统通知使用的全局配置。官方邮件配置

1.2 应用与验证

在宿主机部署目录执行:

bash
1
2
3
4
chmod 600 .env docker compose config -q docker compose up -d --no-deps server worker docker compose exec worker ak test_email admin@example.com

最后一条会真实发送邮件,应换成自己的测试收件地址。先检查收件箱和垃圾邮件,再从实际注册或找回密码流程测试。命令成功、任务入队、SMTP 提交成功和最终收件是不同检查点。

再进入“流程和阶段 → 阶段”,分别编辑注册验证邮件阶段 doratiger-enrollment-email-verification 和密码恢复邮件阶段 doratiger-recovery-email,开启 Use global connection settings 并保存。这样两个阶段都使用部署中的 SMTP 参数,不需要重复填写授权码。

不要连带修改它们的激活行为:注册验证阶段的 Activate user on success 保持开启,密码恢复阶段保持关闭。全局发信测试通过后,再用测试账号分别走一次注册验证和密码恢复,检查收件及链接执行结果。

二、统一邮件品牌模板

2.1 文件与挂载

网页品牌 Logo 不会自动替换所有邮件图片。自定义邮件模板放在 /templates,本例由宿主机 ./custom-templates 挂入 server 和 worker。

这次使用的脱敏模板整理成了附件:邮件模板包。包含 base.html,以及邀请、账号验证、密码恢复、事件通知四组 HTML/TXT 模板;域名统一替换为 auth.example.com,不含账号、邀请令牌和 Logo 图片。

把下载的压缩包放到部署目录,解压后应为:

bash
1
2
mkdir -p custom-templates tar -xzf doratiger-mail-templates.tar.gz -C custom-templates
text
1
2
3
4
5
6
7
custom-templates/email/doratiger/ ├── base.html ├── invitation.html / invitation.txt ├── account_confirmation.html / account_confirmation.txt ├── password_reset.html / password_reset.txt ├── event_notification.html / event_notification.txt └── logo.png # 自行放入现有品牌 PNG

先检查并替换模板中的 auth.example.com 为自己的认证域名,再使用。HTML 的用户字段保持 Django 自动转义,链接由 Authentik 提供。HTML 与同名 TXT 一起存在时,可提供 HTML 和纯文本邮件。模板说明

共用模板负责页头、Logo、正文容器和页脚,四种子模板只写各自内容。注册验证和密码恢复不要混用文案:前者继续注册,后者设置新密码。

核对 compose.yaml 中 server 和 worker 的 volumes。沿用基础部署时已经有下面这一项;如果没有,在两个服务的原有列表中各追加一次,不覆盖数据或证书挂载:

yaml
1
- ./custom-templates:/templates:ro

宿主机的 custom-templates/email/doratiger/invitation.html 因此对应容器内的 /templates/email/doratiger/invitation.html。UI 和策略里的模板名称填写 email/doratiger/invitation.html,不带 /templates/ 前缀。若已有同一目录的挂载,直接保留,不重复添加。

替换域名、放好 Logo 并完成挂载后再重建容器。可以先在部署目录检查文件和模板引用:

bash
1
2
3
test -f custom-templates/email/doratiger/base.html test -f custom-templates/email/doratiger/invitation.html rg -n 'auth\.example\.com|cid:logo' custom-templates/email/doratiger

第一项搜索用于发现尚未替换的示例域名,第二项用于确认内嵌 Logo 引用仍然存在。

当前版本的邮件发送代码识别正文中的 cid:logo,并从 web/dist/assets/icons/icon_left_brand.png 读取图片作为内嵌附件。于是将共用模板的图片改为:

html
1
2
<img src="cid:logo" width="258" alt="DoraTiger Auth" style="display:block;max-width:100%;height:auto;border:0;">

在 server 和 worker 原有 volumes 列表中各追加这一项,其他数据、证书和模板挂载保留:

yaml
1
- ./custom-templates/email/doratiger/logo.png:/web/dist/assets/icons/icon_left_brand.png:ro

这里利用的是 2026.8.5 的原生 CID 发送机制,但图片路径属于镜像内部实现,不是一个“邮件品牌 Logo”配置项。升级后应复核路径及收件效果。该挂载也会影响其他引用此内嵌 Logo 的邮件。

确认源文件确实是 PNG,再重建应用容器:

bash
1
2
3
test -f custom-templates/email/doratiger/logo.png docker compose config -q docker compose up -d --no-deps server worker

修改后重新发送新邮件,Outlook 和 Gmail 都已确认显示 Logo。旧邮件不会自动随模板更新。

2.3 邮件模板引用

使用位置 Template
管理员邀请策略 email/doratiger/invitation.html
doratiger-enrollment-email-verification email/doratiger/account_confirmation.html
doratiger-recovery-email email/doratiger/password_reset.html
管理员邮件通知传输 email/doratiger/event_notification.html

具体引用需要在各自对象中保存:

  1. 注册验证:在“流程和阶段 → 阶段”编辑 doratiger-enrollment-email-verification,Template 改为 email/doratiger/account_confirmation.html,保留全局 SMTP、有效期和激活设置。
  2. 密码恢复:编辑 doratiger-recovery-email,Template 改为 email/doratiger/password_reset.html,保持 Activate user on success 关闭。
  3. 管理员通知:编辑邮件通知传输 doratiger-admin-email,在 Template 中选择或填写 email/doratiger/event_notification.html。
  4. 管理员邀请:在邀请表达式策略的 ak_send_email() 调用中,将 template 参数设为 email/doratiger/invitation.html。原生邀请页的发信按钮不通过这段自定义策略,不能只改策略就认定所有邀请邮件都会使用同一模板。

对象尚未创建时,在对应创建步骤中填写这个路径即可。保存后发送新邮件检查,不能用收件箱里的旧邮件验证模板更新。

邮件日期用 Asia/Shanghai 显示,标明北京时间;后台日志可能显示 UTC,排查时要转换后比较。品牌模板不改变令牌生成、账号激活或密码验证逻辑。

三、管理员系统通知

通知链路由四个部分组成:事件记录描述发生了什么,Event Matcher 策略筛选事件,通知规则指定接收者和级别,通知传输决定通过邮件还是站内通知送达。登录或异常发生后系统会记录事件,不需要先手工创建一个长期保留的“异常事件”。

3.1 通知传输与事件匹配策略

进入“事件 → 通知传输”,点击创建。先创建邮件传输,Name 填 doratiger-admin-email,Mode 选择 Email,填写主题前缀和模板,关闭 Send once 并保存。再创建站内传输,Name 填 doratiger-admin-local,Mode 选择 authentik inbuilt notifications,同样关闭 Send once。

这两个对象分别是“管理员邮件通道”和“管理员站内通道”,字段汇总如下:

名称 类型 设置
doratiger-admin-email Email 主题前缀 [DoraTiger Auth] ;使用上节事件模板;Send once 关闭
doratiger-admin-local authentik inbuilt notifications 站内通知;Send once 关闭

Send once 不是“同类异常限流”。当前实现启用时会在一个传输的收件人循环中发送一次后停止;要让管理员组每个成员收到通知,保持关闭。

进入“自定义 → 策略”,点击创建,类型选择 Event Matcher(事件匹配器)。以系统异常为例,Name 填 doratiger-match-system-exception,Action 选 system_exception,其他过滤条件留空,保存。依次按下表创建其余策略;一条策略对应一种事件动作,不把多个 Action 拼成一个字符串:

策略名称 Action 中文用途
doratiger-match-system-exception system_exception 系统异常
doratiger-match-system-task-exception system_task_exception 后台任务异常
doratiger-match-policy-exception policy_exception 策略执行异常
doratiger-match-property-mapping-exception property_mapping_exception 属性映射异常
doratiger-match-configuration-error configuration_error 配置错误
doratiger-match-configuration-warning configuration_warning 配置警告
doratiger-match-update update_available 可用版本更新

3.2 通知规则与收件组

先进入“目录 → 组”确认实际的管理员组及其成员,再在“目录 → 用户”检查这些成员的 Email。这里选择已有管理员组,不新建同名空组,也不因配置收件人而授予新的管理员权限。

在“事件 → 通知规则”点击创建,Name 填下表中的规则名称,Severity 选对应级别。Destination group 选择现有管理员组,Destination event user/Send notification to event user 关闭,Transports 同时选择 doratiger-admin-email 和 doratiger-admin-local,Policy engine mode 选 Any,保存。

名称 级别 绑定策略
doratiger-notify-system-errors Alert 前五条异常策略,任意一条匹配即可
doratiger-notify-configuration-warning Warning 配置警告策略
doratiger-notify-update Notice 版本更新策略

规则保存后,还需要绑定匹配策略:

  1. 回到通知规则列表,展开 doratiger-notify-system-errors 左侧的箭头,点击“创建或绑定(Create or bind)”。
  2. 选择“绑定已有策略”,选中 doratiger-match-system-exception,Order 填 10,Enabled 开启、Negate 关闭,保存。
  3. 用同样方式依次绑定后台任务、策略、属性映射异常和配置错误策略,Order 分别填 20、30、40、50。
  4. 确认系统异常规则的 Policy engine mode 为 Any。一个事件通常只具有一种 Action,这里需要任意一种异常命中即发送,不能要求五种 Action 同时匹配。
  5. 展开配置警告规则,绑定 doratiger-match-configuration-warning;展开版本更新规则,绑定 doratiger-match-update。两条绑定的 Order 均可填 10,Enabled 开启、Negate 关闭。

策略绑定在通知规则上,不放进登录流程或 Prompt Stage 的 Validation Policies。保存后逐条展开规则,检查收件组、两个传输和策略绑定都存在。官方通知规则配置

确认管理员用户资料里有正确邮箱。事件是谁触发的,与通知发给谁是两回事;这里固定通知管理员组,不发给每个事件的操作者。

规则没有包含 login_failed 或 email_sent,因此不会把普通输错密码和通知发送成功也当作系统异常。当前逐事件通知,没有合并风暴或同类异常限流。

3.3 测试与默认通知清理

检查时先确认全局 SMTP 测试能够收件,再核对通知规则的配置。触发或遇到符合规则的事件后,按以下顺序查看:

  1. “事件 → 日志”:找到原始事件,确认 Action 与策略中的值一致。
  2. 管理员用户的通知列表:检查站内通知是否出现,级别是否符合规则。
  3. “事件 → 日志”及后台任务:检查对应邮件发送记录或任务失败信息。
  4. 管理员邮箱:检查实际收件、主题前缀、正文与 Logo。

已有真实错误日志可用于核对事件字段,但不能仅凭旧事件存在就判断新规则已成功发送通知。不要通过故意填错生产数据库或 SMTP 凭据来制造异常。

本次通过一条明确标注测试的配置错误事件检查整条链路,确认生成一条站内通知,并产生对应的 email_sent 记录。邮件采用内嵌品牌 Logo。发送成功事件说明 SMTP 提交完成,实际收件仍需检查邮箱。

验证后停用 Default - Events Transport & Rules 蓝图,删除旧的配置错误、配置警告、异常规则及对应匹配策略。版本更新保留功能,使用上面自建的规则和策略;当所有规则都已使用自建传输时,再删除 default-email-transport 和 default-local-transport。

这套通知依赖 Authentik 和 worker 仍能运行、SMTP 仍能连接。整台服务器停机需要外部监控,不能指望停机的服务给自己发信。

四、Microsoft 个人账号登录

4.1 外部身份认证流程

进入“流程和阶段 → 流程”创建一个供外部身份返回后使用的流程:

字段 填写内容
Name DoraTiger 外部身份认证
Slug doratiger-source-authentication
Title 完成身份验证
Designation Authentication(认证)
Authentication requirement Require unauthenticated(要求未登录)

保存后打开“阶段绑定”,两次选择“绑定已有阶段”:Order 10 选择 MFA 验证阶段 doratiger-login-mfa,Order 100 选择会话建立阶段 doratiger-login-session。这两个阶段已经在本地登录配置中创建,不再复制一份。上游 Source 完成身份验证后,该流程继续验证本地 MFA 并建立会话。

在“自定义 → 策略”创建 Expression Policy(表达式策略),Name 填 doratiger-source-authentication-if-sso,Expression 填:

python
1
return ak_is_sso_flow

保存策略后,返回“DoraTiger 外部身份认证”流程详情,打开“策略/组/用户绑定”,点击创建或绑定,选择已有策略 doratiger-source-authentication-if-sso,Order 填 0,Enabled 开启、Negate 关闭并保存。

这里绑定到整个流程,不绑定到某个阶段,也不放进表单校验。它限定从外部身份认证上下文进入,避免把仅含 MFA 和登录的流程作为任意直接登录入口。直接打开流程 URL 被拒绝不代表配置失败,测试应从 Source 按钮发起。

Google 和 Microsoft 的 Authentication flow 都选择它;Enrollment flow 留空,User matching mode 选择 identifier。本文使用的是登录页 Source 按钮,不是另行把 Source Stage 插入一个外层流程,两种集成的恢复执行机制不能混用。

4.2 Microsoft 应用注册

在 Microsoft Entra 管理中心进入“应用程序 → 应用注册”,点击“新注册”。Name 可填 DoraTiger Auth,用于在微软控制台辨认这个 OAuth 客户端。

Supported account types 要包含个人 Microsoft 账号。本例只允许个人账号登录,按控制台可用选项选择个人账号类型;如果选择“任何组织目录中的账号和个人 Microsoft 账号”,它允许的范围更大,仍需用身份源端点限定本例的登录入口。应用注册需要有相应目录和应用管理权限,个人 Hotmail 邮箱作为登录身份与管理应用注册的权限是两件事。

为应用添加 Web 平台,注册以下回调地址。这里约定 Authentik Source 的 slug 为 microsoft:

text
1
https://auth.example.com/source/oauth/callback/microsoft/

完成注册后,在 Overview 记录 Application(client)ID。进入“Certificates & secrets → Client secrets → New client secret”,填写用途和有效期,创建后保存密钥的 Value,不是 Secret ID。密钥只在创建时完整显示,到期前需要更换。

在 API permissions 中检查 Microsoft Graph 的委托权限,身份识别使用 openid、profile、email 和 User.Read;本例不同步组织组,不申请 GroupMember.Read.All。组织目录要求管理员同意时,由对应管理员处理;个人账号登录不要照搬组织组同步配置。Authentik Entra OAuth 配置

4.3 Microsoft 身份源

回到 Authentik 管理员界面,进入“目录 → 联合认证与社交登录(Federation and Social login)”,点击创建,类型选择 Entra ID OAuth Source。按表填写:

字段 填写内容 用途
Name Microsoft 后台和身份源按钮的名称
Slug microsoft 必须与微软端回调路径末段一致
Enabled 开启 启用身份源
Consumer key 微软 Application(client)ID 标识 OAuth 客户端
Consumer secret 客户端密钥 Value 服务端交换令牌时使用
Authentication flow DoraTiger 外部身份认证 选择已创建的自建流程
Enrollment flow 留空 禁止该身份源创建新用户
User matching mode Use the source-specific identifier 仅按已建立的外部身份关联匹配

Consumer key 和 secret 属于 Protocol settings。展开 URL settings,检查下面的授权和令牌端点,填写后保存,再打开身份源详情核对 Callback URL。

微软端点中的租户部分也要与应用支持的账号类型匹配:

端点中的值 适用账号
consumers 个人 Microsoft 账号
organizations 工作或学校账号
common 工作或学校账号,以及个人账号;还受应用注册的账号类型限制
具体租户 ID 指定组织租户

本次最终使用 consumers。对应的授权和令牌端点为:

text
1
2
https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize https://login.microsoftonline.com/consumers/oauth2/v2.0/token

用户资料接口使用 https://graph.microsoft.com/v1.0/me,属性映射保留 Entra ID Source 的原生配置;不要为个人账号额外创建组织组映射。common 并不专指个人账号;不能只改端点而忽略应用注册中的账号类型。微软文档对这些取值有具体说明。

4.4 Microsoft 账号绑定

  1. 先用本地密码和 MFA 登录要绑定的 Authentik 账号,确认当前用户正确。
  2. 切换到用户界面,进入“Settings → Sources”,找到 Microsoft,点击连接。
  3. 在微软页面选择自己的个人账号,完成授权并返回 Authentik。
  4. 在外部身份列表确认 Microsoft 已连接,再用无痕窗口从 Microsoft 登录入口测试。
  5. 登录后核对用户名和原有权限,并检查用户列表没有新增同邮箱账号。

如果已经误建了用户,先检查外部身份连接归属,再处理解绑和重新绑定,不要直接把新用户升成管理员来掩盖账号重复的问题。本次已经将 Microsoft 身份绑定到原管理员,并关闭了该 Source 的注册流程。

4.5 登录页身份源入口

登录页上的按钮由认证流程中的 Identification Stage 决定。Source 创建成功后,还要把它加入这个阶段的 Sources 列表。这一步不需要编写 Expression Policy。

在管理员界面按以下顺序操作:

  1. 打开 Flows and Stages → Flows。
  2. 进入 doratiger-authentication。
  3. 打开 Stage Bindings,找到 doratiger-login-identification。
  4. 点击 Edit Stage,在 Source settings → Sources 中把 Microsoft 移到 Selected Sources。Google 创建完成后也在这里添加,保留已经选中的其他身份源。
  5. 开启 Show source labels,让按钮显示名称,然后保存。

这里编辑的是已绑定的阶段,不需要新建一个同名 Flow,也不需要删除原有的用户名、密码认证阶段。如果使用了自定义 Brand 或认证 Flow,应先确认该入口实际使用的 Flow,再修改其中的 Identification Stage。

保存后打开无痕窗口访问自建登录页:

text
1
https://auth.example.com/if/flow/doratiger-authentication/

本次 Microsoft 和 Google 都已加入该阶段。匿名访问自建流程时,返回的登录挑战中包含两个身份源入口。自建流程的配置位置也可对照 官方登录页配置说明。

显示按钮只决定入口是否可见,不等于允许注册。 外部身份能否找到本地用户、能否创建新用户,要继续看 Source 自己的匹配模式与注册流程。

五、Google 账号登录

5.1 Google OAuth 客户端

在 Google Cloud Console选择或新建一个项目,确认当前项目正确。进入 Google Auth platform;如果尚未初始化,按引导填写应用信息。在已有配置中,分别检查 Branding、Audience 和 Data Access:Google 同意屏幕配置

配置位置 填写内容
Branding → App name DoraTiger Auth
User support email、联系邮箱 自己可接收通知的邮箱
Authorized domains 自己控制的域名,示例为 example.com
Audience External,供个人 Google 账号使用
Test users 测试状态下加入实际参与测试的账号
Data Access 仅保留登录需要的基本身份信息,不增加 Drive、Gmail 等业务权限

旧版控制台从“API 与服务 → OAuth 同意屏幕”进入。下面是 Authentik 官方教程保留的旧版入口截图,不是当前实例的截图;新版对应 Google Auth platform 的配置页面。

官方旧版 Google OAuth 同意屏幕入口

同意屏幕配置完成后,在 Clients 中创建 OAuth 客户端;旧版位置为“凭据 → 创建凭据 → OAuth 客户端 ID”。Application type 选择 Web application,Name 可填 DoraTiger Auth,在 Authorized redirect URIs 添加:

text
1
https://auth.example.com/source/oauth/callback/google/

官方 Google Web application 客户端与回调地址示例

图中的 authentik.company 是官方示例,本文使用 auth.example.com。回调应填在 Authorized redirect URIs,不填到 Authorized JavaScript origins。保存后记录 Client ID 和 Client Secret,供 Authentik 使用。

5.2 Google 身份源

在 Authentik 的“目录 → 联合认证与社交登录”点击创建,选择 Google OAuth Source,填写:

字段 填写内容
Name Google
Slug google
Enabled 开启
Consumer key Google OAuth 客户端的 Client ID
Consumer secret 对应的 Client Secret
Authentication flow DoraTiger 外部身份认证(doratiger-source-authentication)
Enrollment flow 留空
User matching mode Use the source-specific identifier(identifier)

Google Source 的协议端点和基本身份映射采用原生配置,不额外添加用户名或邮箱自动关联策略。保存后核对 Callback URL 与 Google 客户端完全一致。Google Source 配置

保存及登录过程中,Authentik 服务端需要访问 Google 的发现文档和令牌接口。若容器无法访问,先完成容器出站代理配置并验证网络,再回来保存和测试身份源。浏览器能打开 Google 不能替代这项检查。

5.3 注册限制与账号匹配

创建时就保持 Enrollment flow 为空、匹配模式为 identifier。已有 Google Source 则按下面的方法检查并修改,避免测试匿名登录时创建第二个账号。

在管理员界面打开 Directory → Federation and Social login,编辑 Google OAuth Source。不同语言界面可能将该菜单翻译为“联合认证与社交登录”,需要修改的是 Source 本身。

配置项 本次设置 作用
Enabled 开启 保留 Google 身份源可用
Authentication flow doratiger-source-authentication 已关联用户完成外部认证后继续登录
Enrollment flow 清空,不选择任何流程 禁止通过这个 Source 注册本地新用户
User matching mode Use the source-specific identifier(identifier) 按已建立的来源身份关联识别用户

找到 Enrollment flow,移除原来选中的 default-source-enrollment,使这个字段为空,然后保存。这里清空的是 Google Source 对注册流程的引用,不要删除全局的 default-source-enrollment Flow,其他身份源可能仍在使用它。

保留 Authentication flow,并将 User matching mode 设为 identifier。这样,已绑定的 Google 身份通过已有连接找到对应本地用户;未绑定身份不会仅因邮箱相同就自动连到本地账号。

几个容易混淆的匹配选项如下:

匹配方式 行为与本次取舍
identifier 使用身份源唯一标识及已保存的关联;本次采用
email_link 按相同邮箱自动关联已有用户;本次不采用
username_link 按相同用户名自动关联已有用户;本次不采用
email_deny / username_deny 遇到重复邮箱或用户名时拒绝注册;这不等于关闭所有新用户注册

单独清空 Enrollment flow 只控制该身份源的注册路径,不会关闭整个 Authentik 的其他注册入口。也不能只看界面上有没有“注册”按钮来判断是否允许注册。

5.4 Google 账号绑定

采用上述设置后,已有本地账号仍可主动绑定 Google。操作顺序是:

  1. 先用本地密码登录要绑定的 Authentik 账号,确认当前用户正确。
  2. 在用户界面进入 Settings → Sources,找到 Google 并选择连接。当前版本的设置入口也可通过 /if/user/#/settings;page-sources 进入。
  3. 跳转到 Google 后选择自己的账号,完成授权并返回 Authentik。
  4. 在外部身份列表中确认 Google 已连接,再用无痕窗口从登录页测试 Google 登录。

这里验证了本地账号和外部账号两边的身份,随后保存关联。未登录时直接点 Google,是尝试用 Google 登录本地账号,不是给某个指定本地账号添加绑定。

管理员为其他人提供这种登录方式时,应先创建其本地用户,并让用户完成本地登录后自行绑定。不要为了省掉绑定步骤,把 email_link 当作关闭注册的替代方案。若需要进一步限定哪些本地用户可以绑定某个 Source,再单独配置 Source 的策略或组限制;本次没有增加额外的 Expression Policy。

5.5 登录入口与功能验证

进入“流程和阶段 → 阶段”,编辑本地登录识别阶段 doratiger-login-identification,在 Sources 中加入 Google,保留 Microsoft,开启 Show source labels 并保存。Source 的创建和用户绑定都不会自动完成这一步。

按下表分别验证入口、账号归属和注册限制:

场景 预期结果
无痕窗口打开自建登录页 能看到 Microsoft 和 Google
用已绑定的 Google 账号完成登录 回到原来的 Authentik 用户,不创建第二个用户
用未绑定的 Google 账号在未登录状态下尝试登录 因该 Source 未配置注册流程而被拒绝,不创建新用户
已有本地用户先登录,再从设置连接 Google 可以建立外部身份关联;仍受该 Source 的访问策略约束

本次服务端已确认 Google 的 Enrollment flow 为空、匹配模式为 identifier,自建登录挑战包含 Google,原管理员绑定保持不变。部署版本对未关联身份进入注册分支的处理是返回“Source is not configured for enrollment.”。这项限制来自 Source 配置,无需改写默认注册 Flow 的内部阶段。

Google 和 Microsoft 后续都已完成账号持有人的浏览器登录测试,返回原账号,MFA 也正常。配置检查和入口显示只完成了其中一部分,实际授权仍需分别测试。

六、容器出站代理

6.1 代理服务

最终使用 MetaCubeXD 完整服务端管理 Mihomo,它包含 WebUI、配置管理 Agent 和内核。完整订阅作为远程 Profile 导入,端口、管理密钥等放在独立的本地覆盖 Profile 中,原生刷新间隔设置为 1440 分钟。

完整订阅在 /profiles 页面作为远程 Profile 管理,部署方式见 MetaCubeXD 官方说明。下面以代理服务已经可用为前提,配置 Authentik 与它的连接。

接入 Authentik 前,代理服务应已经运行,能通过所选节点访问 Google。以下示例约定它加入名为 egress-proxy 的 Docker 自定义网络,网络别名也为 egress-proxy,容器内 HTTP 代理端口为 7890。

6.2 共享 Docker 网络

同一自定义网络中的容器可以通过网络别名访问彼此,因此 Authentik 的代理地址直接使用:

text
1
http://egress-proxy:7890

这条连接不经过宿主机的公开端口,也不需要把代理端口开放到公网。容器之间使用容器监听端口,WebUI 的宿主端口映射与这条连接无关。

这里的网络名和别名是示例约定,不是安装 Mihomo 后自动出现的名称。先检查代理容器加入的 Docker 网络、它在该网络中的别名以及实际 HTTP 监听端口,再将示例中的 egress-proxy 和 7890 替换成一致的值。代理必须允许同网段容器连接;若只监听代理容器自己的回环地址,其他容器仍无法使用。

如果当前部署没有这个外部网络,需要在代理服务的部署配置中建立并加入共享网络、配置别名,再执行 Authentik 的覆盖配置。仅在 Authentik 侧声明 external 网络不会自动把代理容器加入其中。

在 Authentik 部署目录新增 compose.override.yaml。如果已有覆盖文件,应将下面字段合并进去,保留原设置:

yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
x-outbound-proxy: &outbound-proxy HTTP_PROXY: http://egress-proxy:7890 HTTPS_PROXY: http://egress-proxy:7890 NO_PROXY: localhost,127.0.0.1,::1,server,worker,postgresql,egress-proxy,auth.example.com,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 http_proxy: http://egress-proxy:7890 https_proxy: http://egress-proxy:7890 no_proxy: localhost,127.0.0.1,::1,server,worker,postgresql,egress-proxy,auth.example.com,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 services: server: environment: *outbound-proxy networks: [default, egress-proxy] worker: environment: *outbound-proxy networks: [default, egress-proxy] networks: egress-proxy: external: true name: egress-proxy

这里保留 default 网络,以继续连接 PostgreSQL;只给 server 和 worker 增加代理网络。HTTPS_PROXY 的值仍以 http:// 开头,表示通过 HTTP 代理的 CONNECT 隧道访问 HTTPS 目标。NO_PROXY 中还应补上自己实际使用的内部域名和网段。

应用配置会重建 server 和 worker,安排在允许短暂中断登录服务时执行:

bash
1
2
3
docker network inspect egress-proxy >/dev/null docker compose config -q docker compose up -d --no-deps server worker

6.3 在 Authentik 容器内验证

测试要放在实际发起请求的容器里。以下命令会使用容器中配置的环境代理:

bash
1
2
3
4
5
6
7
8
for service in server worker; do docker compose exec -T "$service" python -c ' import requests r = requests.get("https://accounts.google.com/.well-known/openid-configuration", timeout=30) r.raise_for_status() print(r.status_code, r.json()["issuer"]) ' done

本次两个容器均返回 200,issuer 为 https://accounts.google.com;Authentik 的 server、worker 和 PostgreSQL 健康检查也正常。

七、管理员邀请入口

7.1 输入与流程

原生邀请管理能创建邀请,但每次都要填写固定数据 JSON,再点发送邮件。我另外建了一个只供管理员使用的入口,仍在 Authentik 内完成。

先进入“流程和阶段 → 输入”创建受邀邮箱字段:

字段 值
Name doratiger-invitation-email
Field key invitation_email
Label 受邀邮箱
Type Email
Required 开启
Order 10
Initial value 留空

再进入“流程和阶段 → 流程”,创建“DoraTiger 发送邀请”,Slug 填 doratiger-send-invitation,Title 填“邀请新用户”,Designation 选 Stage configuration,Authentication requirement 选 Require superuser。

保存后打开阶段绑定,选择“创建并绑定新阶段”,类型选 Prompt,Name 填 doratiger-invitation-recipient-prompt,Fields 只选择刚创建的受邀邮箱输入,绑定 Order 填 10。这时表单已经有了,但尚不能创建邀请;需要保存发信策略并关联到这个表单。

7.2 邀请创建与发信策略

在“自定义 → 策略”点击创建,类型选 Expression Policy,Name 填 doratiger-admin-create-invitation,Expression 填入下面的代码。这里的中文用途是“管理员创建邀请并发送邮件”。

下面是实际策略的脱敏版本。替换 auth.example.com,流程 slug、输入字段键和模板路径需要与前文一致。它会写入邀请并发邮件,不是纯粹的布尔判断,因此专门限制了执行入口。

python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import re from datetime import timedelta from uuid import uuid4 from zoneinfo import ZoneInfo from django.core.exceptions import ValidationError from django.core.validators import validate_email from django.db import transaction from django.utils import timezone from django.utils.text import slugify from authentik.core.models import User from authentik.flows.models import Flow from authentik.stages.invitation.models import Invitation http = request.http_request # 只允许管理员在指定流程提交表单时执行。 # 策略测试和 GET 请求不能创建邀请或发信。 if ( request.debug or http is None or http.method != "POST" or http.path != "/api/v3/flows/executor/doratiger-send-invitation/" ): ak_message("请从管理员邀请流程提交邮箱。") return False actor = http.user if not actor.is_authenticated or not actor.is_active or not actor.is_superuser: ak_message("只有超级管理员可以发送邀请。") return False email = ( request.context.get("prompt_data", {}).get("invitation_email") or "" ).strip() try: validate_email(email) except ValidationError: ak_message("请输入有效的邮箱地址。") return False flow = Flow.objects.filter( slug="doratiger-enrollment", designation="enrollment", ).first() if flow is None: ak_message("未找到 doratiger-enrollment 注册流程。") return False if User.objects.filter(email__iexact=email).exists(): ak_message("该邮箱已有账号,请先在用户列表中确认。") return False # 名称只用于识别,去重使用实际邮箱。 # 示例:akadmin-someone-at-gmail-com-20261114-173228-a1b2c3d4 inviter = slugify(actor.username)[:40] or f"user-{actor.pk}" email_label = re.sub( r"[^a-z0-9-]+", "-", email.casefold().replace("@", "-at-"), ).strip("-") # 锁住管理员记录,避免同一管理员并发创建重复邀请。 with transaction.atomic(): User.objects.select_for_update().get(pk=actor.pk) now = timezone.now() existing = Invitation.objects.filter( created_by=actor, flow=flow, fixed_data__email__iexact=email, expires__gt=now, ).first() if existing: ak_message( "该邮箱已有有效邀请,本次未重复发送。" "如需重发,请到“目录 → 邀请”操作。" ) return False timestamp = now.astimezone( ZoneInfo("Asia/Shanghai") ).strftime("%Y%m%d-%H%M%S") short_id = uuid4().hex[:8] name = f"{inviter}-{email_label}-{timestamp}-{short_id}" invitation = Invitation.objects.create( name=name, created_by=actor, flow=flow, single_use=True, expiring=True, expires=now + timedelta(hours=48), fixed_data={"email": email}, ) # 先保存邀请,再提交邮件;失败时保留邀请供检查和重发。 url = ( "https://auth.example.com/if/flow/doratiger-enrollment/" f"?itoken={invitation.pk}" ) try: queued = ak_send_email( address=email, subject="邀请你加入 DoraTiger", template="email/doratiger/invitation.html", context={ "url": url, "expires": invitation.expires, "host": "auth.example.com", }, ) except Exception: ak_message( "邀请已创建,但邮件提交结果不确定。" "请检查系统任务,再从“目录 → 邀请”决定是否重发。" ) return False if not queued: ak_message( "邀请已创建,邮件未成功入队。" "请检查邮件配置,再从“目录 → 邀请”重发。" ) return False ak_message(f"邀请邮件已提交发送:{email}") return True

保存策略后,进入“流程和阶段 → 阶段”,编辑 doratiger-invitation-recipient-prompt,在 Validation Policies 中选择 doratiger-admin-create-invitation 并保存。不要绑定到流程入口,否则执行时还没有提交邮箱。也不要把它复制到其他表单,其 POST 路径检查只允许当前邀请流程。

回到流程详情,确认只有 Order 10 的输入阶段,无需添加 User Write、User Login 或 Redirect 阶段:管理员已经登录,用户账号由受邀者完成注册时才创建。

策略先检查真实 HTTP POST、准确的执行器路径及当前超级管理员身份,拒绝策略测试器和页面 GET 创建邀请。验证邮箱后,再检查该邮箱是否已有账号。

去重以“同一管理员、同一邮箱、同一注册流程、仍有效”为范围,用管理员记录的数据库锁串行处理并发点击。邀请名称采用“邀请人-邮箱标签-北京时间-8 位随机标识”,便于后台辨认;末尾是 UUID 随机片段,并不是邮箱哈希,去重也不依赖名称。

创建的邀请为单次使用、48 小时有效,固定数据为目标邮箱。数据库写入结束后才提交邮件任务。入队失败或结果不确定时保留邀请,管理员到邀请页检查并决定是否重发,不自动创建第二份。

这个策略使用当前版本 Django 模型和执行器路径,属于实例内自定义代码。跨管理员的全局去重、邀请被消耗后继续去重,以及邮件投递和数据库写入的跨系统事务,都不在这段实现范围内。升级后需要重测,不能仅凭策略返回 True 认为收件箱已经收到邮件。

7.3 应用入口与访问策略

进入“应用程序 → 应用程序”,从创建按钮的选项中选择 with existing provider。Name 填“邀请新用户”,Slug 可填 doratiger-invitation,Provider 保持未选择。这里创建的是应用列表中的链接入口,不需要创建 OIDC Provider。

在应用的启动地址(Launch URL)中填写下面的完整地址并保存。应用 Slug 只标识这张入口卡片;地址中的 doratiger-send-invitation 才是刚创建的流程 Slug。

Launch URL 填写:

text
1
https://auth.example.com/if/flow/doratiger-send-invitation/?next=%2Fif%2Fadmin%2F%23%2Fflow%2Fstages%2Finvitations

在“自定义 → 策略”创建 Expression Policy,Name 填 doratiger-admin-only,用于限制邀请入口仅供超级管理员访问。Expression 填:

python
1
return request.user.is_authenticated and request.user.is_superuser

保存后返回“邀请新用户”应用详情,打开“策略/组/用户绑定”,点击创建或绑定,选择已有策略 doratiger-admin-only,Order 填 0,Enabled 开启、Negate 关闭。确认没有其他会让普通用户放行的绑定,保存后再回用户界面检查应用卡片。

应用绑定控制入口可见和可访问范围,流程自身还要求超级管理员,写入策略再次检查真实用户。即使知道流程 URL,也不能绕过内部检查。

提交完成后,通过 Launch URL 中的 next 返回原生邀请管理页面。使用正常流程完成后的跳转,不额外添加 Redirect Stage。

7.4 邀请发送验证

用超级管理员打开“邀请新用户”应用,填写一个尚未注册、没有有效重复邀请的测试邮箱并提交。到“目录 → 邀请”确认名称、创建者、注册流程、固定邮箱和 48 小时有效期,再检查收件箱。重复提交同一邮箱,应提示已有有效邀请;普通用户应无法执行这个邀请流程。

随后用未登录窗口打开邮件链接,完成邀请注册、邮箱验证和 MFA。检查新用户的邮箱与邀请一致。若邮件没有收到,先查看邀请是否已经创建,再决定重发;直接重新填写邮箱可能先命中重复邀请检查。

八、验证结果与维护

Microsoft、Google 都已完成绑定和登录测试;邀请创建与发信、注册验证、找回密码已测试成功。Outlook/Gmail 收件 Logo 经过更换内嵌图片后确认正常。管理员异常测试产生了站内通知和邮件发送成功事件,版本更新规则通过了事件匹配检查,没有伪造一个真实更新来证明发布版本存在。

维护时分别检查 OAuth 客户端密钥有效期、SMTP 授权码、后台任务和邮件模板挂载。品牌图片的内部覆盖路径与邀请策略的模型接口都与版本有关,升级后至少重测一次邀请、注册验证和恢复邮件。

默认配置清理按实际替代进度进行:Source 的自建认证流程验证后再停用默认 Source 蓝图,通知验证后再停用默认事件蓝图。最后保留的系统映射和 RBAC 蓝图,作用见第一篇;不要为了清掉默认名称而删除协议基础配置。

九、配置问题与解答

9.1 全局 SMTP 与阶段 SMTP

Q:UI 的邮件阶段可以填写 SMTP,为什么还需要修改部署环境变量?

A: Email Stage 的连接设置只作用于该阶段。系统通知和邀请策略使用的全局邮件连接仍由部署配置提供。本例统一配置全局 SMTP,再让两个 Email Stage 使用全局设置。只修改 .env 也不够,还需确认 Compose 已将变量传给 server 和 worker,并重建容器。

9.2 邮件顶部 Logo 破图

Q:公网图片地址可以访问,为什么 Outlook 和 Gmail 仍显示破图?

A: 本次图片文件完整,IPv4/IPv6 均能返回 PNG,日志也记录到 Gmail 代理成功下载;加版本号仍未解决,不能断言是文件不存在。最终使用原生 cid:logo 内嵌附件,并挂载品牌 PNG 替换镜像对应文件,重新发信后两种邮箱均确认显示。该路径属于版本内部实现,升级时需要复核。

9.3 通知测试事件没有命中规则

Q:日志里有测试事件,为什么系统异常规则没有发送邮件?

A: 先核对 Action、规则绑定的策略、Any 模式、收件组及其成员邮箱,再检查后台任务。当前版本用 Event.new() 直接传字符串 "configuration_error" 会得到 custom_configuration_error,与原生 Action 不同;需要构造原生测试事件时使用 EventAction.CONFIGURATION_ERROR 枚举。普通策略测试通过也不等于整条通知投递链路成功。

9.4 Microsoft 回调地址不匹配

Q:出现 redirect_uri 不合法或未注册的错误,应修改哪里?

A: 对照 Authentik Source 详情显示的 Callback URL,检查 Microsoft 应用注册中的 Web Redirect URI。协议、域名、路径、Source Slug 和末尾斜杠必须对应。https://auth.example.com/ 是站点入口,不是 OAuth 回调;本例回调末段为 /microsoft/。

9.5 外部登录创建了第二个用户

Q:Microsoft 授权成功后,为什么进入了新账号,而不是原管理员?

A: 未登录状态点击身份源按钮是在尝试外部登录。身份尚未绑定且 Source 允许注册时,可能新建本地用户。本例关闭 Enrollment flow,使用 identifier 匹配,先登录原本的本地账号,再从用户设置连接外部身份。误建账号时先核对连接归属,再解绑和重新绑定,不把新账号升为管理员来代替处理。

9.6 Google 发现文档请求超时

Q:浏览器可以打开 Google,为什么保存身份源时仍报连接超时?

A: 浏览器重定向和 Authentik 服务端出站请求不是同一条连接。本次失败目标是 accounts.google.com/.well-known/openid-configuration。应在 server 和 worker 容器内验证请求;需要代理时,检查环境变量、Docker 网络、别名和代理监听端口。两个容器都成功返回发现文档后,再测试实际账号授权。

9.7 完整订阅只加载节点

Q:订阅在桌面客户端包含策略组和规则,为什么放入 proxy-providers 后只剩节点?

A: proxy-providers 只提供节点集合,完整订阅则是包含节点、策略组和规则的 Profile。本次改为完整服务端的远程 Profile,并单独保留本地端口、密钥等覆盖配置。节点更新与完整 Profile 刷新不是一回事。

9.8 邀请入口直接跳转或地址不存在

Q:打开邀请应用时没有邮箱输入框,或跳转后提示 URL 不存在,如何检查?

A: 先检查应用 Launch URL 是否指向 doratiger-send-invitation,而不是直接指向后台邀请列表。流程内只保留邮箱输入阶段,通过正常完成后的 next 跳转,避免旧 Redirect Stage 留下的执行状态。当前邀请管理页为 #/flow/stages/invitations,不是 #/directory/invitations;修改后从新的流程入口重新测试。

9.9 重复邀请与邮件未送达

Q:再次输入邮箱提示重复邀请,但收件箱没有邮件,是否应删除邀请重新创建?

A: 策略先保存邀请,再提交邮件任务,邮件失败时会保留邀请。因此先到邀请页核对有效记录,再查看后台任务和 SMTP 结果,必要时使用重发操作。返回 True 表示提交发送成功,不代表最终投递;重复请求被拒绝也不能证明第一次邮件已经收到。

参考

手机扫码阅读
本文作者: 有次元袋的 tiger
本文链接: https://www.superheaoz.top/2026/11/58243/
版权声明: 本站点所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 我的个人天地!