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]

Reply via email to