GitLab、Jenkins、Grafana、Harbor、Argo CD……只要是现代的 Web 应用,几乎没有不支持 OIDC 登录的。在 authentik 统一身份认证 的四种接入方式里,OIDC 永远是第一选择:它不仅让应用彻底碰不到员工的密码,还能顺滑地衔接飞书扫码,甚至能在高危应用的登录环节里再硬生生卡一道 MFA(二次验证)。
本篇我们将扒开 OIDC 登录的整个过程,看看从点击登录到进入页面的几百毫秒里到底发生了什么;同时盘点在 authentik 里对接 OIDC 时必须配对的几个关键参数,以及那些最容易让人踩坑的报错。如果你对 Application、Provider、Binding 等基础概念还不熟悉,建议先扫一眼 架构与接入方式选型。内容基于 authentik 2026.8 的官方文档。
TL;DR
- OIDC = OAuth 2.0 + 身份信息:authentik 是 OP(签发令牌),GitLab 等应用是 RP(信任令牌)。authentik 对飞书来说又是 RP。
- 授权码走浏览器,令牌走服务器直连:截获授权码没用,换令牌还要 client secret。换令牌这一步要求应用服务器能访问 authentik,WAF 不能拦。
- 三个最常见的坑:Provider 不选签名密钥(退化成 HS256,应用验签失败);Redirect URIs 留空;上线后修改 Subject mode(应用把同一个人当成新用户)。
- 用
entitlements映射应用内角色:比如 Grafana 的 Admin、Editor,不用在应用里手工维护。 - 应用有自己的会话:在 authentik 禁用员工,不会马上把他踢出 GitLab,要靠缩短会话或 back-channel logout。
Table of contents
Open Table of contents
1. OIDC 的本质是什么
如果说 OAuth 2.0 是一个授权框架(解决“让应用在用户同意的范围内访问数据”),那么 OpenID Connect(OIDC)就是在 OAuth 2.0 身上套了一层“身份马甲”,专门用来解决“让应用知道目前登录的到底是谁”。
在 OIDC 的世界里,永远有两个核心角色在交互:
- OpenID Provider(OP):负责验证用户身份并签发令牌(Token)。在这个架构里就是 authentik。
- Relying Party(RP):也叫客户端,它无条件信任 OP 的验证结果。这里指的就是 GitLab、Grafana 等具体应用。
有意思的是,在这个统一认证架构里,authentik 其实是个“双面人”:面对 GitLab 时,它是发牌的 OP;但面对飞书时,它又成了信任飞书验证结果的 RP。当员工点击登录 GitLab 时,其实是在经历一场 GitLab → authentik → 飞书的接力跑,验证通过后再原路拿着令牌返回。
2. 登录时究竟发生了什么
现代 Web 应用绝大多数采用的都是授权码模式(Authorization Code Flow):
一句话总结:授权码走浏览器暴露在明处,令牌走服务器直连藏在暗处。
看着步骤很多,其实只需记住这几个关键点:
- 分离机制保证了安全。 授权码(Code)会经过用户的浏览器,但它本身毫无价值。想要把 Code 换成真正的 Token,必须附带只有 GitLab 服务端才知道的
client_secret。因此,黑客就算在浏览器里截获了 Code 也拿不到 Token。 - 换令牌是“服务器对服务器”的背靠背请求。 GitLab 的后端服务器必须能在网络层面直接访问 authentik。如果 authentik 藏在内网,GitLab 所在的网络必须能解析并访问它;如果 authentik 前面挂了 WAF 或 CDN,绝对不能用人机验证去拦截 Discovery、JWKS、Token 这些纯后端的 API 请求,否则应用会因为拿不到数据而无限重定向或报错。
- 这就是单点登录(SSO)的魔力。 当员工刚刚登完 GitLab,转头又去打开 Grafana 时,由于浏览器里已经有了 authentik 的有效 Cookie,第 4 步和第 5 步(飞书扫码)会被直接跳过。authentik 会瞬间把新 Code 塞回给 Grafana,实现了“一次扫码,处处通行”。
- 应用有自己的会话(Session)。 登录成功后,GitLab 就开始维护它自己的会话,不再每次都去问 authentik。这意味着:你在 authentik 里一键禁用了某位离职员工,他并不会立刻从 GitLab 里被踢出去。如何处理这个脱节,请看第 7 节。
3. 令牌与 Scope:应用能拿到什么
换令牌成功后,authentik 到底给了应用什么东西?
| 令牌 | 作用 | 说明 |
|---|---|---|
| ID Token | 核心:告诉应用“登录的是谁” | 带有签名的 JWT 格式。包含 sub(用户唯一标识)、iss(签发者)、aud(指定给哪个应用)、exp(过期时间),以及按 Scope 返回的属性。 |
| Access Token | 权限证明:用来调用 userinfo 等 API | 有效期很短,仅作 Bearer Token 使用。 |
| Refresh Token | 续期:Access Token 过期后静默换发新 Token | 注意:从 authentik 2024.2 起默认不再发放。应用必须显式请求 offline_access Scope,且 Provider 层面也要配置相应的 Scope Mapping。 |
在这个过程中,应用需要通过请求不同的 Scope 来声明自己想要哪些用户信息:
| Scope | 返回的信息 |
|---|---|
openid | 必须包含。这标志着当前请求是一个 OIDC 请求而非普通 OAuth2。 |
profile | 用户的基本信息:用户名、姓名、所属组等。 |
email | 用户的邮箱地址。 |
entitlements | 用户在该应用里的权益(用来灵活映射应用内角色)。 |
offline_access | 申请颁发 Refresh Token。 |
其中,entitlements (权益) 是一个极其好用的特性。
举个例子:Grafana 允许全员登录,但只有运维组才是管理员。你不用去 Grafana 里挨个设权限,而是直接在 authentik 给 Grafana 应用建两个权益 Grafana Admins 和 Grafana Editors,分别绑定对应的运维组和研发负责人组。然后在 Grafana 侧这样配置映射规则:
role_attribute_path = contains(entitlements[*], 'Grafana Admins') && 'Admin' || contains(entitlements[*], 'Grafana Editors') && 'Editor' || 'Viewer'
另外要注意一个新版本的行为变更:从 2025.10 版本起,email_verified 字段默认返回 false。有些老派应用比较死板,看到 false 就拒绝登录,或者拒绝按邮箱关联存量账号。遇到这种应用时,需要参考官方文档的 Verify email addresses 章节进行额外配置。
4. authentik 的 OIDC 地址清单
当应用需要手工配置 OIDC 参数时,照着这个表填准没错:
| 用途 | 对应的 API 地址 |
|---|---|
| 自动发现(Discovery) | /application/o/<应用 slug>/.well-known/openid-configuration |
| 授权(Authorize) | /application/o/authorize/ |
| 换令牌(Token) | /application/o/token/ |
| 获取用户信息(UserInfo) | /application/o/userinfo/ |
| 退出登录(End Session) | /application/o/<应用 slug>/end-session/ |
| 签名公钥(JWKS) | /application/o/<应用 slug>/jwks/ |
如果应用比较现代,支持自动发现(Discovery),那你只需要填第一行的地址,再加上 Client ID 和 Client Secret 就够了。
小提示:应用的 Slug(URL 后缀)不能随意起名,绝对避开 authorize、token、device、userinfo 等保留字,否则会和系统的全局路由撞车。
5. 最容易出错的配置要点
在 authentik 里配置 OIDC Provider 时,这几个参数最容易让人栽跟头:
- 客户端类型(Client Type):GitLab、Jenkins 这类后端能保守秘密的应用,选 Confidential,走 Client Secret 验证;如果是纯前端 SPA 或无法保存 Secret 的命令行工具,选 Public,并且必须强制开启 PKCE 机制来防篡改。
- Redirect URIs(回调地址):必须填应用实际的回调地址,连末尾的斜杠都必须完全一致。千万不要留空。留空意味着 authentik 会“智能”地保存第一次登录时用到的回调地址;如果黑客精心构造了第一次恶意请求,回调地址就会被永久污染固定。如果应用有多个回调地址,可以使用正则表达式(记得把
.写成\.)。 - 签名密钥(Signing Key):必须手动选一个证书。选了证书,authentik 就会用标准的 RS256 算法签名,应用能通过 JWKS 地址拿到公钥验签;如果你留空,authentik 会退化成用 Client Secret 做 HS256 对称签名,且不提供 JWKS,导致很多认死理的应用直接验签失败。
- 签发者(Issuer Mode):默认情况下,每个应用都有独立的签发者,格式为
https://auth.company.com/application/o/<应用 slug>/。当应用要求手工填 Issuer 时,必须和这个值一字不差(包括末尾的/)。 - 用户标识(Subject Mode):这决定了 ID Token 里的
sub字段取什么值,默认是内部用户 ID 的哈希值。因为绝大多数应用都是靠sub来在本地数据库里关联用户的,所以系统上线后绝对不要去改这个值。一旦改了,应用就会把以前的熟人当成全新用户,导致账号重复和数据断层。 - 高危应用加二次 MFA:OIDC 最强大的地方,就是它把认证流程解耦了。员工扫码只能保证飞书账号没丢,但对于 JumpServer 或生产发布环境,你可以专门给这个应用的 Provider 换一个流程(Authorization Flow),在这个流程里插入一个 Authenticator Validation 步骤。这样,员工扫完码后,会被强制再验一次 TOTP 或硬件安全密钥。这是 OIDC 相比于 LDAP 和 RADIUS 碾压级的安全优势。
更细粒度的访问控制:如果你想玩点高级的,除了在应用层面绑定用户组,你还可以通过表达式策略(Expression Policy)来拦截特定的 Scope 请求。比如限制只有 ops-admin 组的人才能拿到某个特定的管理员 Scope:
if "my-admin-scope" in request.context["oauth_scopes"]:
return ak_is_group_member(request.user, name="ops-admin")
return True
6. 排障指南:那些常见的报错
| 现象 | 最可能的原因 |
|---|---|
报错 redirect_uri_mismatch | Redirect URIs 和浏览器实际发起的回调地址不一致。重点检查 http/https 是否匹配、端口号、以及末尾有没有多或少一个 /。 |
报错 invalid_issuer | 应用里填的 Issuer 和 Discovery 接口返回的不一致。如果 Discovery 返回的是 http://,请检查 authentik 的 Base URL 配置以及 Nginx/Ingress 是否正确传递了 X-Forwarded-Proto 等头信息。 |
| 令牌验签失败(Invalid Signature) | Provider 里没选签名密钥(Signing Key);或者应用服务器的时间不准,导致令牌里的 exp(过期时间)被判为已过期。 |
| 应用提示“邮箱未验证” | email_verified 字段变成了 false(参考第 3 节的说明)。 |
| 登录成功,但在系统里多出一个全新账号 | 应用用来关联老账号的规则(比如是靠用户名还是邮箱)和 authentik 传过去的字段没对上。 |
| 请求 Discovery 或 JWKS 时返回 403 | WAF 或防护规则拦截了服务器端发出的直接请求。请对这几个固定的接口地址开放白名单,不要为了绕过拦截而整体关闭防护。 |
7. 生产环境的取舍与防坑经验
存量账号迁移才是真正的修罗场。 协议本身很难出 Bug,但 GitLab、Jenkins 里面那些盘根错节的本地老账号极容易翻车。接入 SSO 后,如果系统没认出“新身份”和“老账号”其实是同一个人,就会直接为你创建一个空空如也的重复账号,而你原来的项目、权限、历史记录全被锁死在老账号里。因此,每接一个新系统前,务必先导出存量账号列表和 authentik 进行比对,查清该系统到底是按 sub、用户名还是邮箱来关联身份的,并在深夜找几个真实员工进行小范围试登录。
禁用账号拦得住新人进门,拦不住屋里的人。 员工离职被禁用后,authentik 确实会立刻拒绝他的任何新登录尝试。但他昨天在 GitLab 里建立的会话,只要没过期就依然有效!对于高危系统,务必从两方面补漏:一是缩短会话本身的有效期;二是如果应用支持 Back-channel Logout(后端静默登出),请务必打开,authentik 会在账号被禁用时主动发通知给应用去销毁会话。另外,员工在应用里自行生成的 Personal Access Token 等凭证不受 authentik 控制,必须在系统内单独吊销(完整的离职处理见 架构与接入方式选型)。
扫码的安全上限,就是你飞书账号的安全上限。 既然一切权限的源头都收敛到了飞书扫码,那么飞书侧就必须强制全员开启登录二次验证;而对于 JumpServer、生产环境 Argo CD 这类“只要登进去就能搞破坏”的核弹级应用,必须在扫码之后再强制追加一道 authentik 自己的 MFA(参考第 5 节的配置)。
请放过员工登录,不要把它用来做服务间调用。 OIDC 授权码模式是专门用来解决“活生生的人”登录系统的。如果你的诉求是让微服务 A 去调用微服务 B,请把鉴权任务交给 API 网关 的令牌校验,或者 Service Mesh 的 mTLS。不要试图用 OIDC 去套用这种机器对机器的场景。
8. 参考资料
- authentik OAuth 2.0 provider 官方文档
- authentik 常用系统集成指南目录:包含 GitLab、Grafana 等系统的保姆级接入配置。
- OpenID Connect Core 1.0 规范