Airflow 2.9.3集成Azure EntraID授权失败问题求助
问题概况
- 部署环境:Airflow 2.9.3,通过Helm Chart 1.15.0部署
- 现象:认证流程完成后,授权阶段报错:
The request to sign in was denied. - Pod日志关键错误:
ERROR - Error authorizing OAuth access token: Invalid JSON Web Key Set.
已完成的配置
Azure安全组
airflow_nonprod_admin airflow_nonprod_op airflow_nonprod_viewer
Azure企业应用SAML配置
Identifier (Entity ID) :: https://airflow.xyz.com/ Reply URL (Assertion Consumer Service URL) :: https://airflow.xyz.com/oauth-authorized/azure Sign on URL :: https://airflow.xyz.com/login/ Relay State (Optional) :: https://airflow.xyz.com/home Logout Url (Optional) :: https://airflow.xyz.com/logout
Airflow webserver_config.py代码片段
from __future__ import annotations import os from airflow.www.fab_security.manager import AUTH_OAUTH from airflow.auth.managers.fab.security_manager.override import FabAirflowSecurityManagerOverride from airflow.utils.log.logging_mixin import LoggingMixin basedir = os.path.abspath(os.path.dirname(__file__)) # Flask-WTF flag for CSRF WTF_CSRF_ENABLED = True WTF_CSRF_TIME_LIMIT = None AAD_TENANT_ID = <tenant id> AAD_CLIENT_ID = <APP Registration client id> AAD_CLIENT_SECRET = <App Registration client secret> AUTH_TYPE = AUTH_OAUTH OAUTH_PROVIDERS = [{ 'name':'azure', 'token_key':'access_token', 'icon':'fa-windows', 'remote_app': { 'api_base_url': f"https://login.microsoftonline.com/{AAD_TENANT_ID}", 'client_kwargs': { "scope": "User.read name preferred_username email profile upn", "resource": f"{AAD_CLIENT_ID}", # Optionally enforce signature JWT verification "verify_signature": False }, 'request_token_url': None, 'request_token_params': { 'scope': 'openid email profile' }, 'access_token_url': f"https://login.microsoftonline.com/{AAD_TENANT_ID}/oauth2/v2.0/token", "access_token_params": { 'scope': 'openid email profile' }, 'authorize_url': f"https://login.microsoftonline.com/{AAD_TENANT_ID}/oauth2/v2.0/authorize", "authorize_params": { 'scope': 'openid email profile' }, 'client_id': f"{AAD_CLIENT_ID}", 'client_secret': f"{AAD_CLIENT_SECRET}", 'jwks_uri': 'https://login.microsoftonline.com/common/discovery/v2.0/keys', 'redirect_uri': 'https://airflow.xyz.com/oauth-authorized/azure' } }] AUTH_USER_REGISTRATION_ROLE = "Public" AUTH_USER_REGISTRATION = True AUTH_ROLES_SYNC_AT_LOGIN = True # First you MUST create a role like"Admin with value Admin" in the App Registration "App Roles" section in the Azure Portal under Microsoft Entra ID. # Then groups MUST be linked from the Microsoft Entra ID "Enterprise Application" section in the Azure Portal under the "Users and Groups" section. # Each groups or users MUST be assigned a role e.g.: Admin, Op, Viewer in the "Users and Groups" AUTH_ROLES_MAPPING = { "airflow_nonprod_admin": ["Admin"], "airflow_nonprod_op": ["Op"], "airflow_nonprod_viewer": ["Viewer"], } class AzureCustomSecurity(FabAirflowSecurityManagerOverride, LoggingMixin): def get_oauth_user_info(self, provider, response=None): self.log.debug(f"Parsing JWT token for provider : {provider}") try: # the try and except are optional - strictly you only need the me= line. me = super().get_oauth_user_info(provider, response) except Exception as e: import traceback traceback.print_exc() self.log.debug(e) self.log.debug(f"Parse JWT token : {me}") return { "name": me["userprincipalname"], "email": me["mail"], "first_name": me["givenname"], "last_name": me["surname"], "id": me["userprincipalname"], "username": me["givenname"], "role_keys": me["groups"] } # the first of these two appears to work with older Airflow versions, the latter newer. FAB_SECURITY_MANAGER_CLASS = 'webserver_config.AzureCustomSecurity' SECURITY_MANAGER_CLASS = AzureCustomSecurity
排查步骤
修正JWKS URI
当前配置的jwks_uri使用了通用租户地址https://login.microsoftonline.com/common/discovery/v2.0/keys,替换为租户专属的URI:'jwks_uri': f"https://login.microsoftonline.com/{AAD_TENANT_ID}/discovery/v2.0/keys"通用地址可能返回不匹配的密钥集,导致验证失败。
清理重复的Scope配置
当前在client_kwargs、request_token_params等多个字段重复定义scope,合并统一为:'client_kwargs': { "scope": "openid email profile User.read", # 移除resource参数,v2.0 OAuth endpoint不使用该参数 "verify_signature": False },同时删除
request_token_params、access_token_params、authorize_params中的scope字段,避免冲突。检查SAML与OAuth配置冲突
当前Airflow使用OAuth2认证,但Azure企业应用配置了SAML。若采用OAuth2,无需配置SAML,只需确保Azure应用注册的重定向URI与Airflow的redirect_uri完全一致,并且应用注册已添加正确的API权限(如User.Read)。修正角色映射的组标识
Azure OAuth返回的groups字段是组的对象ID而非显示名称,当前AUTH_ROLES_MAPPING使用的是组名称,会导致映射失效。有两种解决方式:- 把
AUTH_ROLES_MAPPING中的键替换为对应组的对象ID - 在Azure应用注册中配置可选声明,添加
groups声明并选择"将组名称作为声明值返回"
- 把
验证Token签名验证逻辑
虽然设置了verify_signature: False,但部分版本的authlib可能依然会尝试获取JWKS。修正JWKS URI后,若问题仍存在,可检查Airflow依赖的authlib版本,确保与Airflow 2.9.3兼容。确认重定向URI一致性
确保Azure应用注册中的重定向URI与Airflow配置的redirect_uri完全一致,包括HTTPS协议、域名、路径,无大小写或字符差异。
内容的提问来源于stack exchange,提问作者Abhijit

