Abhijit
Abhijit

Reputation: 17

Airflow AUTH_ROLES_MAPPING is not working for Outh2 with Azure EntraId [App Registration] Authentication

I am working on Integration Azure EntraId AUthentication with Airflow [2.9.3] by OAuth authorization. I am able to authenticate and authorize from AIrflow properly. But the roles mapping [AUTH_ROLES_MAPPING] is not working. Only default role from AUTH_USER_REGISTRATION_ROLE = "Public" is considering. I am using Helm [1.15.0] for airflow deployment. My Airflow is deployed in AKS [K8 1.29.7].

I have validated the App roles in App Registration and in airflow Webserver config file like the spelling, case sensitivity etc. but everything is fine I believe. The token is also fetching desired user roles [Admin, Viewer or Op] under "role_keys" attribute.

If I mention AUTH_USER_REGISTRATION_ROLE = 'Viewer' then it is logging in with Viewer role. But not taking roles from mapping which is defined in App Registration in Azure.

List of API permissions in App Registration

List of API permissions in App Registration

List of roles in APP Registration

Roles in App Registration

Airflow Webserver no role massage.

Airflow Webserver Massage

Webconfig configuration code.

from __future__ import annotations

import os
import logging
import base64
import hashlib
import secrets

from airflow.www.fab_security.manager import AUTH_OAUTH
# from airflow.www.security import AirflowSecurityManager
from airflow.auth.managers.fab.security_manager.override import FabAirflowSecurityManagerOverride
from airflow.utils.log.logging_mixin import LoggingMixin
from urllib.parse import urlencode

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 = <Client ID>
AAD_CLIENT_SECRET = <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': 'User.read openid email profile'
        },
        'access_token_url': f"https://login.microsoftonline.com/{AAD_TENANT_ID}/oauth2/v2.0/token",
        "access_token_params": {
            'scope': 'User.read openid email profile',
            #'code_verifier': code_verifier  # Include code verifier in token exchange
        },
        'authorize_url': f"https://login.microsoftonline.com/{AAD_TENANT_ID}/oauth2/v2.0/authorize",
        "authorize_params": {
            'scope': 'User.read openid email profile',
            #'code_challenge': code_challenge,  # Include code challenge in the authorization request
            #'code_challenge_method': 'S256'  # Specify the challenge method
        },
        'client_id': f"{AAD_CLIENT_ID}",
        'client_secret': f"{AAD_CLIENT_SECRET}",
        'jwks_uri': 'https://login.microsoftonline.com/common/discovery/v2.0/keys',
        'redirect_url': '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_aaa_admin": ["Admin"],
    "airflow_bbb_ops": ["Op"],
    "airflow_ccc_viewer": ["Viewer"],
}

class AzureCustomSecurity(FabAirflowSecurityManagerOverride, LoggingMixin):
    def get_oauth_user_info(self, provider, response=None):
        
        logging.info(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()
            logging.info(e)
        
        logging.info(f"Parse JWT token : {me}")
        
        return {
            "name": me["first_name"] + " " + me["last_name"],
            "email": me["email"],
            "first_name": me["first_name"],
            "last_name": me["last_name"],
            "id": me["email"],
            "username": me["first_name"],
            "role_keys": me.get("role_keys", ["Public"])
        }
    
# 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

Upvotes: -1

Views: 235

Answers (1)

Rukmini
Rukmini

Reputation: 16064

Note that: If you are calling /me endpoint, then User.Read API permission is required.

  • User.Read allows the app to read the profile of signed-in users.
  • In your scenario to resolve the error, configure the AUTH_ROLE_MAPPING correctly and pass User.Read as scope.

But passing scope as User.read openid email profile, do not display the app roles in the access token,

To display the app roles in the access token, you need to pass scope as api://ClientID/.default or ClientID/.default.

For sample, exposed and API and created app role like below:

enter image description here

Now grant API permissions:

Go to APIs my organization support -> Search your application -> Application permissions

enter image description here

Assign the role to the user in Enterprise application:

enter image description here

Generated access token by passing below parameters:

GET https://login.microsoftonline.com/TenantID/oauth2/v2.0/token

client_id : ClientID
scope : ClientID/.default
grant_type : authorization_code
code : code
redirect_uri : RedirectURL
code_verifier : S256

enter image description here

When decoded I can the access token contains the scp and role of the user:

enter image description here

Based on your requirement you can pass the scopes to generate the access token.

Upvotes: 0

Related Questions