You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Airflow 2.9.3集成Azure EntraID授权失败问题求助

排查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

排查步骤

  1. 修正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"
    

    通用地址可能返回不匹配的密钥集,导致验证失败。

  2. 清理重复的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字段,避免冲突。

  3. 检查SAML与OAuth配置冲突
    当前Airflow使用OAuth2认证,但Azure企业应用配置了SAML。若采用OAuth2,无需配置SAML,只需确保Azure应用注册的重定向URI与Airflow的redirect_uri完全一致,并且应用注册已添加正确的API权限(如User.Read)。

  4. 修正角色映射的组标识
    Azure OAuth返回的groups字段是组的对象ID而非显示名称,当前AUTH_ROLES_MAPPING使用的是组名称,会导致映射失效。有两种解决方式:

    • 把AUTH_ROLES_MAPPING中的键替换为对应组的对象ID
    • 在Azure应用注册中配置可选声明,添加groups声明并选择"将组名称作为声明值返回"
  5. 验证Token签名验证逻辑
    虽然设置了verify_signature: False,但部分版本的authlib可能依然会尝试获取JWKS。修正JWKS URI后,若问题仍存在,可检查Airflow依赖的authlib版本,确保与Airflow 2.9.3兼容。

  6. 确认重定向URI一致性
    确保Azure应用注册中的重定向URI与Airflow配置的redirect_uri完全一致,包括HTTPS协议、域名、路径,无大小写或字符差异。

内容的提问来源于stack exchange,提问作者Abhijit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 03:02:34