authentik:Application、Provider 与 Flow 如何组成身份入口
登录页出现了,协议对象仍可能没有接好
authentik 不是“另一个 Keycloak 皮肤”。Application 是用户看到的应用入口,Provider 定义 OIDC、SAML、LDAP、Proxy 等协议,Flow 组织认证与授权阶段,Policy 决定绑定条件;Proxy、LDAP、RADIUS 与 RAC Provider 还需要 Outpost 承载协议逻辑。把这些对象混成一个“client”,会让登录成功、token 签发、应用访问和边缘代理之间的责任无法解释。
本地实验先确定问题范围。只验证 JWT 解析不必启动完整身份平台;需要验证 authentik 的 Flow、Policy、Application/Provider 或 Outpost 时,才值得承担 PostgreSQL、worker、签名密钥和管理入口的成本。
从官方 Compose 建立可重建输入
官方 Docker Compose 安装文档将此路径定位为测试与小规模生产入口,并要求 Compose v2、至少 2 核与 2 GB 内存。文档首页提供的 compose.yml 会随发行线变化;下面的 OIDC 实验固定使用既经隔离验证的 authentik 版本标识 2026.5.5,因此从 authentik 2026.5 版本发行说明所指向的同发行线 Compose取得输入,并显式检查 server、worker 的最终镜像标签。这个旧补丁只用作本地协议复现,不能当成当前生产安全版本;共享或生产部署应按目标发行说明选择维护补丁、审查 Compose 差异并重测客户端。
# Linux 测试机上的普通账号;需有 Docker 权限、curl、OpenSSL、sed 和 jq。
set -euo pipefail
umask 077
mkdir authentik-oidc-lab
cd authentik-oidc-lab
curl -q --fail-with-body -LsS -o compose.yml \
https://goauthentik.io/version/2026.5/lifecycle/container/compose.yml
printf 'PG_PASS=%s\n' "$(openssl rand -base64 36 | tr -d '\n')" > .env
printf 'AUTHENTIK_SECRET_KEY=%s\n' "$(openssl rand -base64 60 | tr -d '\n')" >> .env
AUTHENTIK_LAB_VERSION='2026.5.5' # authentik 版本标识
printf 'AUTHENTIK_TAG=%s\n' "$AUTHENTIK_LAB_VERSION" >> .env
printf '%s\n' \
'COMPOSE_PORT_HTTP=127.0.0.1:9000' \
'COMPOSE_PORT_HTTPS=127.0.0.1:9443' >> .env
chmod 600 .env
# 此实验不创建自动 Outpost,删除 worker 的 Docker socket 挂载。
sed -i '\#/var/run/docker.sock:/var/run/docker.sock#d' compose.yml
docker compose -p authentik-oidc-lab config --format json | jq -e \
--arg image "ghcr.io/goauthentik/server:$AUTHENTIK_LAB_VERSION" '
.services.server.image == $image and
.services.worker.image == $image and
(.services.server.ports | length == 2 and all(.host_ip == "127.0.0.1")) and
([.services[] | .volumes[]? | select(.source == "/var/run/docker.sock")] | length == 0)'
docker compose -p authentik-oidc-lab pull
docker compose -p authentik-oidc-lab up -d
docker compose -p authentik-oidc-lab ps若 JSON 检查没有输出 true,先看下载文件的镜像标签、端口和 worker 挂载,不要执行 up -d。docker compose ps 应显示 PostgreSQL、server、worker 运行;server、worker 的健康状态可能稍后才就绪,可用 docker compose -p authentik-oidc-lab logs --tail=80 server worker 查启动错误。仅在这台测试机本地打开 http://localhost:9000,为 akadmin 建立密码。.env 不进入 Git,也不复用其他实例的密钥。默认 Compose 会把 Docker socket 挂给 worker,用于自动创建和管理 Outpost;这是宿主机控制面权限。本实验在启动前删除并核对了挂载。需要 Outpost 但不接受 socket 风险时,按官方 手工 Compose 部署单独运行,并给它范围受限的服务账号 token。
Application 与 Provider 要分别验明
从 Applications 进入 New Provider,向导可以一起创建 Application 与 OAuth2/OIDC Provider。使用普通测试用户,不以 akadmin 承担应用身份。下面以 authentik 版本 2026.5.5 的本地 OIDC 接入为例;已有实例只新建这些实验对象,不覆盖其他 Provider:
| 配置对象 | 本地值与作用 |
|---|---|
| Application | 名称 OIDC local lab,slug te-web,关联下面的 Provider |
| OAuth2/OIDC Provider | client type 选 Public,client ID 为 te-web,授权类型启用 Authorization Code |
| redirect URI | strict 匹配 http://localhost:5173/callback;不要留空或使用通配符 |
| Authentication / Authorization Flow | 使用已有默认认证流程,以及默认显式同意或隐式同意的授权流程 |
| Invalidation Flow | 选择 default-provider-invalidation-flow,用于 Provider 的会话失效流程 |
| Signing Key | 选择带私钥的 RSA 非对称签名证书;保持 RS256,不配置 token encryption |
| Scope mappings | 关联内置 openid 与 profile;不要添加覆盖 aud 或 scope 的自定义映射 |
| Issuer mode | 保持每个 Provider 独立 issuer |
| Application binding | 绑定一个只包含虚构测试用户的实验组,另留一个组外用户检查拒绝 |
OAuth2 Provider 文档解释了严格回调、PKCE 与 issuer mode。没有非对称 Signing Key 时,Provider 可能用 client secret 做对称签名,无法按这套公开 JWKS 的 RS256 路径接入。
issuer: http://localhost:9000/application/o/te-web/
discovery: http://localhost:9000/application/o/te-web/.well-known/openid-configuration
jwks: http://localhost:9000/application/o/te-web/jwks/
token: http://localhost:9000/application/o/token/不要从 URL 外形猜 issuer。请求 discovery,记录它返回的 issuer、jwks_uri 与 token_endpoint,再检查 JWT 的 iss、kid、aud 和 exp:
curl -q --noproxy '*' --fail-with-body -sS \
http://localhost:9000/application/o/te-web/.well-known/openid-configuration |
jq '{issuer,jwks_uri,token_endpoint}'
curl -q --noproxy '*' --fail-with-body -sS \
http://localhost:9000/application/o/te-web/jwks/ |
jq '.keys[] | {kid,kty,alg}'资源服务应信任 discovery 返回的 issuer。若浏览器用 localhost、容器却用内部 DNS,先选定双方都可解析的外部身份,再配置反向代理与内部访问;关闭 issuer 校验只会把配置错误变成安全缺口。
登录后把 access token 送入资源接口
下载本地 OIDC 客户端与资源端样例。在安装 Node.js 24、npm 和 unzip 的 Linux 主机运行,普通宿主用户即可;authentik 继续由前面的 Compose 提供。它与 Keycloak 样例共用协议代码,但 issuer、客户端和 audience 均按当前 Provider 独立配置。
unzip oidc-local-lab.zip
cd oidc-local-lab
npm ci
export OIDC_ISSUER=http://localhost:9000/application/o/te-web/
export API_AUDIENCE=te-web
npm run resourceauthentik 这组配置的 access token 和 ID token 都以 Provider 的 client ID te-web 作为 aud,不能照搬 Keycloak 示例的 te-api。资源端验证 RS256 签名、精确 issuer、audience、有效期与主体,并要求 access token 的 scope 含 openid。ID token 用于客户端确认用户登录;access token 才传给 API。这一版本默认 access token 额外带 scope 等字段,两类令牌的生成区别可查 authentik 令牌实现。生产接入要制定具体 API 的受众与业务 scope,不把本地演示的 openid 当成业务授权,也不自行给 ID token 添加 access-token scope。
第二个终端进入解压目录,启动客户端:
export OIDC_ISSUER=http://localhost:9000/application/o/te-web/
export OIDC_CLIENT_ID=te-web
npm run client浏览器打开 http://localhost:5173/login,用实验组内的普通用户登录并按所选授权 Flow 同意。客户端通过 discovery 找授权端点,生成一次性 state、nonce、PKCE verifier 和 S256 challenge。返回 callback 后先校验与当前浏览器绑定的 state,再以原 verifier 换取令牌;ID token 的签名、nonce 和 audience 都通过后,才将 access token 通过 HTTP 送入资源接口。
若 Linux/Node 将 localhost 优先解析为 ::1 而实验端口只绑定在 127.0.0.1,两个 Node 终端均设置 NODE_OPTIONS=--dns-result-order=ipv4first 后重启;不要在一侧单独改成 127.0.0.1 却保留另一侧的 localhost issuer。先比较 discovery 返回的精确 issuer,确保浏览器、客户端与资源端使用同一身份 URL。
浏览器 → authentik 登录与授权 Flow → callback(code, state)
客户端 → token endpoint(code, verifier) → ID token + access token
客户端:验证 ID token 的签名、iss、aud、exp、nonce
资源接口:验证 access token 的签名、iss、aud、exp、scope → 200 / 401资源接口的签名由 JOSE jwtVerify执行,不是本地解码后信任字段。回调页只显示以下结果:
{"idTokenVerified":true,"access":200,"tampered":401,"absent":401,"idTokenAsAccess":401}后面三项依次用篡改 payload 但未重新签名的令牌、无令牌、ID token 请求同一个 API。若 access 为 401,先比较实际 discovery issuer(包括末尾斜杠)、API_AUDIENCE 与 client ID,再检查 signing key 和 scope mappings;不要改成忽略验签。若 ID token 也被接受,说明资源端缺少令牌用途约束,应核对自定义 scope 映射,恢复默认配置后重试。若登录直接被拒绝,先看 Application binding、用户组和授权 Flow 的事件,不要把应用权限问题误判成签名问题。
在浏览器新建隔离会话,用组外用户再次打开 /login。预期停留在 authentik 的访问拒绝,不会进入资源接口的 access:200。随后在 Events 中按 Application 与测试用户查对应结果。伪造 callback 的 state 应由本地客户端返回 400;code/state 都是一次性数据,失败后重新进入 /login。
本地程序只监听回环、在内存保留五分钟登录上下文,不存储 refresh token,不承担生产会话或登出管理。结束时在两个 Node 终端按 Ctrl+C,在 authentik 退出测试会话并移除实验 Application、Provider、组和用户。已有共享实例不执行删卷清理。
Flow、Policy 与 Outpost 各有边界
Flow 决定认证、授权、注册或恢复过程中执行哪些 Stage;Policy 绑定在 Flow、Application 或其他对象上,决定某个主体能否继续。调试时先看实际绑定与评估结果,不要把“用户存在”当作必然能进入 Application。
Outpost 是可独立部署的 authentik 组件。官方说明 Proxy、LDAP、RADIUS 与 RAC Provider 需要它;创建 Outpost 会生成服务账号和 token,只授予读取所绑定 Application/Provider 配置所需的权限。Outpost 通过 API 与 WebSocket 接收配置和报告健康状态。核心实例与 Outpost 升级必须保持同版本,不能把旧边缘组件留在外面静默漂移。
OIDC Provider 本身由核心服务处理,通常不需要为了 OIDC 再建一个 Proxy Outpost。只有应用要经过 authentik Proxy、暴露 LDAP/RADIUS 或使用 RAC 时,才引入相应 Outpost,并分别验证网络、token、证书和应用绑定。
数据、密钥与退出
PostgreSQL 保存用户、Application、Provider、Flow、Policy 与审计对象;media、证书、签名材料和 Outpost token 也有独立生命周期。删除 server 容器不会删除这些状态,docker compose down -v 又会把整个本地身份环境清空。运行前先确认 Compose project 只属于当前实验。
测试用户必须使用虚构资料。PG_PASS、AUTHENTIK_SECRET_KEY、client secret、Outpost token、authorization code、JWT、Cookie 与签名私钥不进入仓库、日志和截图。Blueprint 或数据库备份也可能携带身份属性和 secret,不能因为是“配置导出”就默认可提交。
本地退出要停止并删除测试 Application/Provider、用户、Flow 绑定、Outpost、服务账号 token、反向代理路由和 DNS。仅当 authentik-oidc-lab 是上面新建的独占实验目录且不需要保留测试用户与签名材料时,在该目录核对 docker compose -p authentik-oidc-lab ps 后执行 docker compose -p authentik-oidc-lab down -v;它会删除本实验的 PostgreSQL 卷,不能照搬到共享实例。若准备转入共享或生产,进入 authentik 身份平台继续完成外部数据库、备份恢复、HA、升级与密钥轮换;本地联调成功不能代替这些验收。
权威资料与规范地址
安装、版本与 Outpost
- Docker Compose 安装:https://docs.goauthentik.io/install-config/install/docker-compose/
- 2026.5 发行说明:https://docs.goauthentik.io/releases/2026.5/
- 同发行线 Compose:https://goauthentik.io/version/2026.5/lifecycle/container/compose.yml
- 手工部署 Outpost:https://docs.goauthentik.io/add-secure-apps/outposts/manual-deploy-docker-compose/
OIDC 与令牌验证
- OAuth2/OIDC Provider:https://docs.goauthentik.io/add-secure-apps/providers/oauth2/
- authentik 2026.5.5 令牌实现:https://github.com/goauthentik/authentik/blob/version/2026.5.5/authentik/providers/oauth2/id_token.py
- JOSE
jwtVerify:https://github.com/panva/jose/blob/main/docs/jwt/verify/functions/jwtVerify.md
