-
Notifications
You must be signed in to change notification settings - Fork 16.3k
Description
Apache Airflow version
3.1.2
If "Other Airflow 2/3 version" selected, which one?
3.1.2, 3.1.1
What happened?
When login in with Azure Oauth SSO the initial authentication delay causes an error message to pop up on screen, when the authentication is successful 1 second after the error message dissapears.
Logs:
api-server INFO: 10.9.155.17:60082 - "GET /api/v2/version HTTP/1.1" 200 OK api-server INFO: 10.9.155.17:34212 - "GET /api/v2/version HTTP/1.1" 200 OK api-server INFO: 10.9.155.17:34218 - "GET /api/v2/version HTTP/1.1" 200 OK api-server INFO: 10.128.1.206:43444 - "GET /api/v2/auth/login?next=***" 307 Temporary Redirect api-server Provider: None api-server DEBUG: Provider: None [views.py:633] api-server INFO: 10.128.1.206:43444 - "GET /auth/login/?next=***" 200 OK api-server INFO: 10.128.1.206:43444 - "GET /auth/static/appbuilder/css/fontawesome/fontawesome.min.css HTTP/1.1" 304 Not Modified api-server INFO: 10.128.1.206:43444 - "GET /auth/static/appbuilder/css/fontawesome/solid.min.css HTTP/1.1" 304 Not Modified ... api-server INFO: 10.128.1.206:43548 - "GET /auth/static/dist/flash.5583a9e0cf11f2be93da.css HTTP/1.1" 304 Not Modified api-server INFO: 10.128.1.206:43492 - "GET /auth/static/dist/main.3cf3be1a0c5439bb640d.js HTTP/1.1" 304 Not Modified ... api-server Provider: azure api-server DEBUG: Provider: azure [views.py:633] api-server DEBUG: Going to call authorize for: azure [views.py:646] api-server INFO: 10.128.1.206:43578 - "GET /auth/login/azure?next=***" 302 Found api-server INFO: 10.9.155.17:44558 - "GET /api/v2/version HTTP/1.1" 200 OK ... api-server DEBUG: Authorized init [views.py:676] api-server DEBUG: OAUTH Authorized resp: { 'token_type': 'Bearer', 'scope': 'email openid profile User.Read', 'access_token': '***', 'id_token': '***', 'userinfo': { 'email': '[REDACTED]', 'name': '[REDACTED]', ... } } [views.py:690] api-server DEBUG: Parsing JWT token for provider: azure [webserver_config.py:105] api-server DEBUG: User info from Azure: { 'email': '[REDACTED]', 'name': '[REDACTED]', 'roles': ['role1', 'role2', ...] } [override.py:2112] api-server DEBUG: _____________________role_keys start__________________________________ [webserver_config.py:115] api-server DEBUG: Parsed JWT token: { 'email': '[REDACTED]', 'username': '[REDACTED]', 'role_keys': [...] } [webserver_config.py:116] api-server DEBUG: Role Keys: [...] [webserver_config.py:117] api-server DEBUG: _____________________role_keys end____________________________________ [webserver_config.py:118] api-server DEBUG: User info retrieved from azure: { 'name': '[REDACTED]', 'email': '[REDACTED]', 'role_keys': [...] } [views.py:699] api-server DEBUG: No whitelist for OAuth provider [views.py:712] api-server DEBUG: Calculated new roles for user='[REDACTED]' as: [Public, Admin] [override.py:2043] api-server INFO: Updated user [REDACTED] [override.py:1449] api-server INFO: 10.128.1.1:38716 - "GET /auth/oauth-authorized/azure?code=***&state=***&session_state=*** HTTP/1.1" 302 Found api-server INFO: 10.128.1.1:38716 - "GET /dags/test_remote_logging/runs/manual__*** HTTP/1.1" 200 OK api-server INFO: 10.9.155.17:59236 - "GET /api/v2/version HTTP/1.1" 200 OK ... api-server INFO: 10.128.1.1:38716 - "GET /ui/config HTTP/1.1" 401 Unauthorized api-server INFO: 10.128.1.1:38716 - "GET /api/v2/auth/login?next=***" 307 Temporary Redirect api-server DEBUG: Provider: None [views.py:633] api-server DEBUG: Already authenticated [REDACTED] [views.py:635] api-server INFO: 10.128.1.1:38716 - "GET /auth/login/?next=*** HTTP/1.1" 302 Found api-server INFO: 10.128.1.1:38716 - "GET /auth/ HTTP/1.1" 302 Found api-server INFO: 10.128.1.1:38716 - "GET / HTTP/1.1" 200 OK api-server INFO: 10.128.1.1:38716 - "GET /ui/config HTTP/1.1" 200 OK api-server INFO: 10.128.1.1:38716 - "GET /static/i18n/locales/en/dag.json HTTP/1.1" 304 Not Modified api-server INFO: 10.128.1.1:38716 - "GET /ui/auth/menus HTTP/1.1" 200 OK ... api-server INFO: 10.128.1.1:50736 - "GET /ui/dashboard/historical_metrics_data?start_date=*** HTTP/1.1" 200 OK api-server INFO: 10.128.1.1:50740 - "GET /api/v2/assets/events?limit=6&order_by=-timestamp×tamp_gte=***×tamp_lte=*** HTTP/1.1" 200 OK api-server INFO: 10.128.1.1:50668 - "GET /api/v2/dags/~/dagRuns/~/hitlDetails?state=deferred&response_received=false HTTP/1.1" 200 OK api-server INFO: 10.128.1.1:50666 - "GET /api/v2/dags/~/dagRuns?state=running&state=queued HTTP/1.1" 200 OK api-server INFO: 10.128.1.1:50700 - "GET /ui/dags?limit=10&is_favorite=true HTTP/1.1" 200 OK ...
What you think should happen instead?
The ui should not render the error message and should wait for the login outcome.
How to reproduce
Setup Airflow 3.1.2 on python3.12
Operating System
Debian GNU/Linux 12 (bookworm)
Versions of Apache Airflow Providers
No response
Deployment
Official Apache Airflow Helm Chart
Deployment details
Deploy using official helm chart to k8s on azure (aks)
Anything else?
This occurs each time a login happens, This is the apiserver_config.py used for the oauth towards azure, worked on airlfow 3.0.6 without issues.
#
## https://gist.github.com/wallyhall/915fedb4dfc766b61f442a32c95e1c29#file-apache_airflow_sso_howto-md
## https://gist.github.com/wallyhall/915fedb4dfc766b61f442a32c95e1c29?permalink_comment_id=5253211#gistcomment-5253211
#
from __future__ import annotations
import os
# Airflow 3.1.x and FAB provider >=2.6.0
#! https://gist.github.com/wallyhall/915fedb4dfc766b61f442a32c95e1c29?permalink_comment_id=5623661#gistcomment-5623661
from airflow.providers.fab.auth_manager.security_manager.override import FabAirflowSecurityManagerOverride
from airflow.utils.log.logging_mixin import LoggingMixin
from flask_appbuilder.security.manager import AUTH_OAUTH
basedir = os.path.abspath(os.path.dirname(__file__))
print("------------------------------------STARTING AUTH------------------------------------")
# ------------------------------------------------------------------------------
# General Flask AppBuilder / Airflow Auth Config
# ------------------------------------------------------------------------------
WTF_CSRF_ENABLED = True
WTF_CSRF_TIME_LIMIT = None
# ------------------------------------------------------------------------------
# Azure AD OAuth Settings
# ------------------------------------------------------------------------------
AAD_TENANT_ID = os.getenv("AAD_TENANT_ID")
AAD_CLIENT_ID = os.getenv("AAD_CLIENT_ID")
AAD_CLIENT_SECRET = os.getenv("AAD_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}",
"request_token_url": None,
"request_token_params": {"scope": "openid profile email"},
"access_token_url": f"https://login.microsoftonline.com/{AAD_TENANT_ID}/oauth2/v2.0/token",
"authorize_url": f"https://login.microsoftonline.com/{AAD_TENANT_ID}/oauth2/v2.0/authorize",
"client_id": f"{AAD_CLIENT_ID}",
"client_secret": f"{AAD_CLIENT_SECRET}",
"client_kwargs": {"scope": "openid profile email"},
"jwks_uri": "https://login.microsoftonline.com/common/discovery/v2.0/keys",
},
}
]
# ------------------------------------------------------------------------------
# User registration and role sync
# ------------------------------------------------------------------------------
AUTH_USER_REGISTRATION = True
AUTH_USER_REGISTRATION_ROLE = "Public"
AUTH_ROLES_SYNC_AT_LOGIN = True
# This gets injected via Helm values usually, First you MUST create a role like"Admin with value Admin" in the AppRegistration "App Roles" section in the Azure Portal under Microsoft EntraID.
# Then groups MUST be linked from the Microsoft Entra ID "EnterpriseApplication" 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"
# https://airflow.apache.org/docs/apache-airflow/2.4.3/security/access-control.html
# AUTH_ROLES_MAPPING = {
# "airflow-admin": ["Admin"],
# "airflow-user": ["User"],
# }
PLACEHOLDER_FOR_AUTH_ROLES_MAPPING
# ------------------------------------------------------------------------------
# Custom Security Manager
# ------------------------------------------------------------------------------
class AzureCustomSecurity(FabAirflowSecurityManagerOverride, LoggingMixin):
"""
Custom FAB security manager for Azure AD OAuth.
Compatible with Airflow 3.1.x (API server).
"""
def oauth_user_info_save(self, userinfo):
"""
Override Airflow 3.1+ user creation to reuse existing users by email.
"""
email = userinfo.get("email")
if not email:
self.log.warning("OAuth userinfo missing email — cannot authenticate user.")
return None
existing_user = self.find_user(email=email)
if existing_user:
self.log.debug(f"User {email} already exists, reusing record instead of creating new one.")
return existing_user
self.log.debug(f"No existing user found for {email}, creating new user.")
return super().oauth_user_info_save(userinfo)
def get_oauth_user_info(self, provider, response=None):
self.log.debug(f"Parsing JWT token for provider: {provider}")
try:
me = super().get_oauth_user_info(provider, response)
except Exception as e:
import traceback
traceback.print_exc()
self.log.debug(e)
return None
self.log.debug("_____________________role_keys start__________________________________")
self.log.debug(f"Parsed JWT token: {me}")
self.log.debug(f"Role Keys: {me.get('role_keys')}")
self.log.debug("_____________________role_keys end____________________________________")
#! Example output from: self.log.debug(f"Parse JWT token : {me}")
# DEBUG - Parse JWT token : {'email': 'aj@a.com',
# 'first_name': 'A',
# 'last_name': 'J',
# 'username': 'ebxxxxxxxxxxxxxxxxxxxxxxxxxxae7',
# 'role_keys': ['airflow-user','airflow-admin']}
return {
"name": f"{me.get('first_name', '')} {me.get('last_name', '')}",
"email": me.get("email"),
"first_name": me.get("first_name"),
"last_name": me.get("last_name"),
"id": me.get("username"),
"username": me.get("email"), # email as username avoids duplicates
"role_keys": me.get("role_keys"),
}
# ------------------------------------------------------------------------------
# Register the Auth Manager for Airflow 3.1.x API Server
# ------------------------------------------------------------------------------
FAB_SECURITY_MANAGER_CLASS = "webserver_config.AzureCustomSecurity"
SECURITY_MANAGER_CLASS = AzureCustomSecurity
Are you willing to submit PR?
- Yes I am willing to submit a PR!
Code of Conduct
- I agree to follow this project's Code of Conduct