oauth2-proxy 对接 login.gov:美国联邦政府 OIDC 身份认证集成与源码解析
oauth2-proxy 对接 login.gov美国联邦政府 OIDC 身份认证集成与源码解析【免费下载链接】oauth2-proxyA reverse proxy that provides authentication with Google, Azure, OpenID Connect and many more identity providers.项目地址: https://gitcode.com/GitHub_Trending/oa/oauth2-proxyoauth2-proxy 内置了login.gov提供商用于为面向美国联邦政府用户的应用接入 login.gov 这一政府级 OIDC 身份提供商。本文基于仓库中的官方集成文档 login_gov.md完整覆盖应用注册、代理启动参数、密钥管理的实操步骤并结合 providers/logingov.go 的源码实现深入解析其 JWT 客户端断言client assertion、nonce 校验与邮箱验证等区别于普通 OIDC 提供商的独特机制帮助你在代理层正确完成 login.gov 认证并理解其底层原理。一、login.gov 与 oauth2-proxy 的集成背景login.gov 是美国联邦政府的 OIDC 身份提供商。根据官方文档如果你的机构是美国联邦政府机构US Government agency可以通过 login.gov 开发者的联系方式与其团队沟通获取集成测试账号与生产环境的访问权限其开发者指南developers.login.gov介绍了注册流程而oauth2-proxy 本身承担了除在 login.gov 仪表盘中注册应用之外的一切工作——即授权码交换、JWT 签名断言、令牌校验、会话管理等全部代理侧逻辑都已完成。在 providers/providers.go 中login.gov被注册为受支持的提供商类型之一由NewLoginGovProvider构造// providers/providers.go节选 return NewLoginGovProvider(providerData, providerConfig.LoginGovConfig)其专属配置项定义在 pkg/apis/options/providers.gotype LoginGovOptions struct { // JWTKey is a private key in PEM format used to sign JWT, JWTKey string yaml:jwtKey,omitempty // JWTKeyFile is a path to the private key file in PEM format used to sign the JWT JWTKeyFile string yaml:jwtKeyFile,omitempty // PubJWKURL is the JWK pubkey access endpoint PubJWKURL string yaml:pubjwkURL,omitempty }也就是说login.gov 提供商在通用 OAuth2 参数之外额外强制要求两样东西一把用于签名 JWT 的 RSA 私钥jwt-key或jwt-key-file和IdP 公钥 JWK 端点pubjwk-url。这是由 login.gov 的客户端认证方式决定的后文会结合源码详解。二、在 login.gov 仪表盘注册应用官方文档给出的演示假设待保护应用运行在http://localhost:3000/oauth2-proxy 启动在http://localhost:4180/且你已有一个机构集成测试账号。首先在 login.gov 的仪表盘dashboard中注册应用文档列出的关键配置项如下配置项取值Identity protocolOpenID ConnectIssuer按 OIDC 要求填写该字符串后文记为${LOGINGOV_ISSUER}Public key由 2048 位 RSA 私钥生成的自签名证书.pem 格式Return to App URLhttp://localhost:4180/Redirect URIshttp://localhost:4180/oauth2/callbackAttribute Bundle必须勾选 email关于 Public key 一项文档给出了快速生成方式openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem \ -days 3650 -nodes -subj /CUS/STWashington/LDC/OGSA/OU18F/CNlocalhost其中key.pemPEM 格式的 RSA 私钥内容记为${OAUTH2_PROXY_JWT_KEY}它正是启动参数-jwt-key所需的值而cert.pem是自签名证书用于在 login.gov 仪表盘注册公钥。需要注意Attribute Bundle中 email 属性是必选项从源码看login.gov 提供商获取用户邮箱不依赖id_token而是调用 userinfo 端点并强制要求email_verified为 true见 providers/logingov.go 的emailFromUserInfoemail : emailData.Email if email { return , fmt.Errorf(missing email) } if !emailData.EmailVerified { return , fmt.Errorf(email %s not listed as verified, email) }因此若注册时未将 email 加入 Attribute Bundle令牌交换阶段会直接报missing email或not listed as verified错误。三、启动 oauth2-proxy 的完整参数应用注册完成后官方文档给出的启动命令如下完整继承自原文档可复制使用./oauth2-proxy -provider login.gov \ -client-id${LOGINGOV_ISSUER} \ -redirect-urlhttp://localhost:4180/oauth2/callback \ -oidc-issuer-urlhttps://idp.int.identitysandbox.gov/ \ -cookie-securefalse \ -email-domaingsa.gov \ -upstreamhttp://localhost:3000/ \ -cookie-secretsomerandomstring12341234567890AB \ -cookie-domainlocalhost \ -skip-provider-buttontrue \ -pubjwk-urlhttps://idp.int.identitysandbox.gov/api/openid_connect/certs \ -profile-urlhttps://idp.int.identitysandbox.gov/api/openid_connect/userinfo \ -jwt-key${OAUTH2_PROXY_JWT_KEY}结合 pkg/apis/options/legacy_options.go 中的 flag 定义与 providers/logingov.go 中的默认值各参数含义如下参数说明-provider login.gov选择 login.gov 专属提供商而非通用oidc提供商-client-idlogin.gov 仪表盘中的 Issuer 字符串-redirect-url回调地址必须与仪表盘的 Redirect URIs 一致-oidc-issuer-url沙箱环境为https://idp.int.identitysandbox.gov/-cookie-securefalse演示用 http生产环境应使用 https 并去掉该项-email-domaingsa.gov仅允许指定邮箱域名的用户登录-upstream受保护的上游应用地址-cookie-secret会话 Cookie 加密密钥务必使用随机强密钥-skip-provider-buttontrue登录页不显示通过 login.gov 登录按钮直接进入认证流程-pubjwk-urlIdP 公钥 JWK 端点用于校验id_token并做 nonce 校验login.gov 必填-profile-urluserinfo 端点用于获取经验证的邮箱-jwt-key2048 位 RSA 私钥 PEM 内容${OAUTH2_PROXY_JWT_KEY}用于向 IdP 发送 JWT 客户端断言login.gov 必填其中-jwt-key、-jwt-key-file、-pubjwk-url三个 flag 的官方描述均标注 required by login.gov见 pkg/apis/options/legacy_options.go。此外login.gov 提供商内置了一组默认端点与 scope定义于 providers/logingov.go并有测试 providers/logingov_test.go 验证在需要对接生产环境时可用-login-url、-redeem-url、-validate-url、-scope覆盖// providers/logingov.go 中的默认端点 // 默认登录地址https://secure.login.gov/openid_connect/authorize // 默认换令牌地址https://secure.login.gov/api/openid_connect/token // 默认 profile/validate 地址https://secure.login.gov/api/openid_connect/userinfo // 默认 scopeemail openid四、源码级解析login.gov 提供商的独特机制login.gov 提供商与通用 OIDC 提供商providers/oidc.go相比有三处本质区别全部可以在 providers/logingov.go 中找到对应实现。4.1 JWT 客户端断言替代 client_secret通用 OAuth2 换令牌流程通过client_idclient_secret证明客户端身份而 login.gov 要求客户端用 RSA 私钥签名一个 JWT 作为client_assertion。Redeem方法providers/logingov.go的实现如下claims : jwt.RegisteredClaims{ Issuer: p.ClientID, Subject: p.ClientID, Audience: jwt.ClaimStrings{p.RedeemURL.String()}, ExpiresAt: jwt.NewNumericDate(time.Now().Add(5 * time.Minute)), } token : jwt.NewWithClaims(jwt.GetSigningMethod(RS256), claims) ss, err : token.SignedString(p.JWTKey) params : url.Values{} params.Add(client_assertion, ss) params.Add(client_assertion_type, urn:ietf:params:oauth:client-assertion-type:jwt-bearer) params.Add(code, code) params.Add(grant_type, authorization_code)即以ClientID同时作为签发者iss与主题sub以换令牌端点 URL 作为受众aud有效期 5 分钟用-jwt-key提供的 RSA 私钥以 RS256 签名后作为client_assertion随授权码一并 POST 到令牌端点。私钥的加载逻辑在configure方法中providers/logingov.go有三条硬规则jwt-key与jwt-key-file互斥同时设置会报错cannot set both jwt-key and jwt-key-file options两者都不设置会报错login.gov provider requires a private key for signing JWTsPEM 解析失败会返回明确的解析错误便于排查密钥格式问题。4.2 nonce 注入与强校验login.gov 要求授权请求携带 nonce且id_token中的 nonce 必须与之匹配。提供商在构造时生成 32 位随机字母 nonceproviders/logingov.go 中Nonce: randSeq(32)并在GetLoginURL中强制注入providers/logingov.gofunc (p *LoginGovProvider) GetLoginURL(redirectURI, state, _ string, extraParams url.Values) string { if len(extraParams[acr_values]) 0 { acr : http://idmanagement.gov/ns/assurance/loa/1 extraParams.Add(acr_values, acr) } extraParams.Add(nonce, p.Nonce) a : makeLoginURL(p.ProviderData, redirectURI, state, extraParams) return a.String() }这里有两点值得注意除 nonce 外还默认注入了acr_valueshttp://idmanagement.gov/ns/assurance/loa/1登录保证等级 Level of Assurance 1如果用户通过-login-url-parameters显式指定了 acr_values则保留用户值。换令牌成功后checkNonceproviders/logingov.go会从pubjwk-url拉取 IdP 公钥集用第一把公钥解析id_token然后比对claims.Nonce ! p.Nonce即返回nonce validation failed。测试 providers/logingov_test.go 中的TestLoginGovProviderBadNonce用httptest模拟了 IdP 的令牌、userinfo 与 JWK 三个端点专门验证了错误 nonce 会导致Redeem失败——这正是 nonce 防重放机制的可执行证据。4.3 邮箱必须来自 userinfo 且已验证如第二节所述Redeem在换取令牌后并不从id_token提取邮箱而是携带 access token 调用profile-urluserinfosession : sessions.SessionState{ AccessToken: jsonResponse.AccessToken, IDToken: jsonResponse.IDToken, Email: email, // 来自 userinfo 且 email_verifiedtrue }会话过期时间则取自令牌响应的expires_in。ValidateSessionproviders/logingov.go以 Bearer 方式携带 access token 请求validate-url默认与 profile 端点相同。五、环境变量与 Docker 部署中的密钥管理所有启动参数都可以通过OAUTH2_PROXY_前缀的环境变量设置便于云/Docker 环境使用。官方文档特别指出一个实际痛点Docker 的 env-file 不支持多行变量因此 PEM 私钥无法直接以OAUTH2_PROXY_JWT_KEY多行环境变量的形式传入。文档给出的解决方案是改用文件方式在仓库顶层目录创建jwt_signing_key.pem内容为 PEM 格式的私钥执行 docker build 时构建过程会把该文件拷贝进镜像运行时设置环境变量OAUTH2_PROXY_JWT_KEY_FILE/etc/ssl/private/jwt_signing_key.pem或直接在命令行使用--jwt-key-file/etc/ssl/private/jwt_signing_key.pem。从源码看providers/logingov.gojwt-key-file的取值会经os.ReadFile读取后与内联 PEM 走同一条jwt.ParseRSAPrivateKeyFromPEM解析路径行为完全等价。对应地在 v7 的 alpha 配置文件中等价写法是providers[0].loginGovConfig.jwtKeyFile与jwtKeypkg/apis/options/providers.go 的 yaml tag而 legacy 命令行 flag 与 alpha 配置之间的映射见 pkg/apis/options/legacy_options.go。六、运行验证与生产化建议启动后按官方文档的验收路径操作浏览器访问http://localhost:4180/被重定向到 login.gov 集成沙箱认证服务器完成登录认证通过后请求被代理转发至http://localhost:3000/上的应用。文档同时给出了真实部署的两条要求用防火墙等手段把上游应用保护起来使其只能从代理访问防止绕过认证直连上游并且生产环境应使用真实的主机名意味着回调地址、-redirect-url、-cookie-secure等都要改为 https 与正式域名。七、附录Skip OIDC discovery不支持发现文档的 IdP原文档最后附有一节通用补充知识当某 OIDC 提供商不通过 issuer URL 提供 OIDC discovery 文档时oauth2-proxy 无法从/.well-known/openid-configuration元数据中自动获取授权、令牌与 JWKS 端点。此时可设置--skip-oidc-discovery并手动提供各端点。文档给出的示例如下针对通用oidc提供商同样适用于任何不支持 discovery 的 IdP-provider oidc -client-id oauth2-proxy -client-secret proxy -redirect-url http://127.0.0.1:4180/oauth2/callback -oidc-issuer-url http://127.0.0.1:5556 -skip-oidc-discovery -login-url http://127.0.0.1:5556/authorize -redeem-url http://127.0.0.1:5556/token -oidc-jwks-url http://127.0.0.1:5556/keys -cookie-securefalse -email-domain example.com即通过-login-url、-redeem-url、-oidc-jwks-url三个参数显式指定授权端点、令牌端点和公钥集端点跳过发现步骤。login.gov 本身支持 discovery示例中显式给出了 issuer 与 jwks 地址但在对接自建或行为非标的 OIDC IdP 时该技巧可直接复用。八、小结与延伸阅读集成入口-provider login.gov-jwt-key或-jwt-key-file-pubjwk-url三个专属参数缺一不可核心机制RS256 JWT 客户端断言证明客户端身份、强制 nonce 防重放、userinfo 邮箱必须经验证三者均在 providers/logingov.go 中实现并由 providers/logingov_test.go 的TestLoginGovProviderSessionData、TestLoginGovProviderBadNonce、TestLoginGovProviderGetLoginURL等用例验证部署要点Docker env-file 无法承载多行 PEM应使用OAUTH2_PROXY_JWT_KEY_FILE指向镜像内拷贝的密钥文件。延伸阅读仓库中的相关文档提供商列表索引、通用 OIDC 提供商、alpha 配置参考。【免费下载链接】oauth2-proxyA reverse proxy that provides authentication with Google, Azure, OpenID Connect and many more identity providers.项目地址: https://gitcode.com/GitHub_Trending/oa/oauth2-proxy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考