melicheradam commented on issue #51362:
URL: https://github.com/apache/airflow/issues/51362#issuecomment-3590124648
I almost have a workaround to get user from a custom token. I fetch the
public key from KC in some code above, that is irrelevant.
Basically what I want to achieve is, to be able to authenticate with the
FAB, but using a custom token (from KC). I want to be able to auto-register
users on the API, thats why im using the configured Oauth.
```python
from jwt import InvalidTokenError
from airflow.api_fastapi.auth.managers.models.base_user import BaseUser
from airflow.providers.fab.auth_manager.fab_auth_manager import
FabAuthManager
class KeycloakAuthManager(FabAuthManager):
"""Custom Auth Manager to use Keycloak as OAuth provider as a secondary
method"""
async def get_user_from_keycloak_token(self, token: str) -> BaseUser:
try:
payload: dict[str, Any] = jwt.decode(token, public_key,
algorithms=['HS256', 'RS256'])
except InvalidTokenError as e:
log.error("JWT Keycloak token is not valid: %s", e)
raise e
try:
scopes = payload.get("scope", "").split(" ") # unsafe
roles = []
for scope in scopes:
roles += API_AUTH_ROLES_MAPPING.get(scope, [])
if len(roles) < 1:
roles = ["airflow_public"]
userinfo = {
"username": payload.get("sub"),
"email": payload.get("email", f"{payload.get('sub')}@null"),
# email must be filled and must be unique
"first_name": payload.get("given_name", "unknown-api"),
"last_name": payload.get("family_name", "unknown-api"),
"role_keys": roles,
}
with current_app.app_context():
user = self.security_manager.auth_user_oauth(userinfo)
if user is not None:
res = login_user(user, remember=False)
else:
raise InvalidTokenError("User was not retreived from
auth_user_oauth!")
return user
except (ValueError, KeyError) as e:
log.error("Couldn't deserialize user from token, JWT Keycloak
token is not valid: %s", e)
raise InvalidTokenError(str(e))
async def get_user_from_token(self, token: str) -> BaseUser:
"""Verify the JWT token is valid and create a user object from it if
valid."""
try:
return await super().get_user_from_token(token)
except Exception as e:
log.info("Token is not valid for default auth manager, trying
Keycloak...")
return await self.get_user_from_keycloak_token(token)
```
But no matter what I do, I get this error, and im feeling pretty stuck here.
The `with current_app.app_context()` is there. Why im getting the error? Maybe
somebody with more experience with Airflow 3 internals or Flask can help.
```python
File
"/home/airflow/.local/lib/python3.11/site-packages/airflow/api_fastapi/core_api/security.py",
line 99, in resolve_user_from_token
return await get_auth_manager().get_user_from_token(token_str)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/airflow/airflow_config/keycloak_auth.py", line 102, in
get_user_from_token
return await self.get_user_from_keycloak_token(token)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/airflow/airflow_config/keycloak_auth.py", line 82, in
get_user_from_keycloak_token
with current_app.app_context():
^^^^^^^^^^^^^^^^^^^^^^^
File
"/home/airflow/.local/lib/python3.11/site-packages/werkzeug/local.py", line
316, in __get__
obj = instance._get_current_object()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File
"/home/airflow/.local/lib/python3.11/site-packages/werkzeug/local.py", line
513, in _get_current_object
raise RuntimeError(unbound_message) from None
RuntimeError: Working outside of application context.
This typically means that you attempted to use functionality that needed
the current application. To solve this, set up an application context
with app.app_context(). See the documentation for more information.
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]