Keycloak SSO嵌入Grafana至Flask应用时遇state缺失错误
login.OAuthLogin(missing saved state)错误 已完成Grafana与Keycloak的独立集成(可直接通过Keycloak登录Grafana),但将Grafana以iframe嵌入Flask Web应用,且Web应用已通过Keycloak登录后,访问嵌入的Grafana时出现login.OAuthLogin(missing saved state)错误,同时重定向到Grafana认证环节时触发500错误。
现有配置
Flask配置
{ "web": { "issuer": "http://localhost:8080/realms/internal", "auth_uri": "http://localhost:8080/realms/internal/protocol/openid-connect/auth", "client_id": "flask", "client_secret": "nlY4o3kIrReiwwsYo0FrKFDHIZvfdXd5", "redirect_uris": [ "http://localhost:5000/*" ], "token_uri": "http://localhost:8080/realms/internal/protocol/openid-connect/token", "token_introspection_uri": "http://localhost:8080/auth/realms/internal/protocol/openid-connect/token/introspect", "userinfo_uri": "http://localhost:8080/realms/internal/protocol/openid-connect/userinfo" } }
Grafana配置
[auth.generic_oauth] enabled = true name = OAuth allow_sign_up = true client_id = grafana client_secret = CA6OIr8z9v3ZPY4yhPWMSwZWJIPWaRK7 scopes = openid email profile ;email_attribute_name = admin@test.com ;email_attribute_path = admin@test.com auth_url = http://localhost:8080/realms/internal/protocol/openid-connect/auth token_url = http://localhost:8080/realms/internal/protocol/openid-connect/token api_url = http://localhost:8080/realms/internal/protocol/openid-connect/userinfo ;allowed_domains = ;team_ids = ;allowed_organizations = role_attribute_path = "contains(roles[*], 'Admin') && 'Admin' || contains(roles[*], 'Editor') && 'Editor' || 'Viewer'" ;tls_skip_verify_insecure = false ;tls_client_cert = ;tls_client_key = ;tls_client_ca =
Cookie与嵌入设置
cookie_samesite = lax allow_embedding = true
问题原因及修复方案
核心原因
iframe跨域场景下,浏览器的SameSite Cookie策略会阻止Grafana存储OAuth state的会话Cookie传递,导致回调时找不到state值,触发missing saved state;同时Flask的认证上下文未正确传递给iframe中的Grafana,引发500错误。
具体修复步骤
1. 调整Grafana的Cookie配置
修改Grafana的grafana.ini中Cookie相关设置:
cookie_samesite = none cookie_secure = true allow_embedding = true
注意:
cookie_secure = true要求Grafana通过HTTPS访问,本地测试可使用ngrok或自签名证书开启HTTPS,否则浏览器会拒绝SameSite=None的Cookie。
2. 配置Keycloak的跨域信任
- 登录Keycloak控制台,进入
internalrealm,找到grafana客户端:- 设置
Access Type为confidential - 在Valid Redirect URIs中添加
http://localhost:3000/login/generic_oauth(Grafana默认OAuth回调地址) - 在Web Origins中添加Flask应用域名
http://localhost:5000,允许跨域请求
- 设置
- 同样找到
flask客户端,在Web Origins中添加Grafana域名http://localhost:3000
3. 开启Grafana自动登录
在Grafana的[auth.generic_oauth]配置中添加:
auto_login = true
让已通过Keycloak认证的用户自动登录Grafana,跳过手动授权步骤。
4. 传递Flask认证上下文到Grafana(可选,推荐使用auth_proxy模式)
如果希望完全复用Flask的登录状态,可将Flask作为Grafana的反向代理,启用Grafana的auth.proxy模式:
- Grafana配置新增:
[auth.proxy] enabled = true header_name = X-WEBAUTH-USER header_property = email auto_sign_up = true
- Flask侧在转发Grafana请求时,添加请求头
X-WEBAUTH-USER,值为当前登录用户的邮箱(需和Keycloak中用户邮箱一致)
5. 验证OAuth回调URL一致性
确认Grafana的auth_url、token_url与Keycloak实际端点完全匹配,且Keycloak中grafana客户端的Valid Redirect URIs包含Grafana的回调地址。
验证流程
- 重启Grafana和Flask应用,确保配置生效
- 通过HTTPS访问Flask应用,完成Keycloak登录
- 查看嵌入的Grafana iframe是否自动登录,无
missing saved state错误
内容的提问来源于stack exchange,提问作者Abhishek

