Reputation: 17
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 roles in APP Registration
Airflow Webserver no role 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
Reputation: 16064
Note that: If you are calling
/me
endpoint, thenUser.Read
API permission is required.
User.Read
allows the app to read the profile of signed-in users.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:
Now grant API permissions:
Go to APIs my organization support -> Search your application -> Application permissions
Assign the role to the user in Enterprise application:
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
When decoded I can the access token contains the scp and role of the user:
Based on your requirement you can pass the scopes to generate the access token.
Upvotes: 0