This is an automated email from the ASF dual-hosted git repository.

yasithdev pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airavata-portals.git


The following commit(s) were added to refs/heads/main by this push:
     new b8323b191 fix(portal): keep the DB session user in the token-auth 
browser bridge (Track D, D5) (#182)
b8323b191 is described below

commit b8323b1913a08e4b6f5ace2fd050faee0de5e7b3
Author: Yasith Jayawardana <[email protected]>
AuthorDate: Mon Jun 8 22:00:17 2026 -0400

    fix(portal): keep the DB session user in the token-auth browser bridge 
(Track D, D5) (#182)
    
    `KeycloakTokenAuthentication` derived a lightweight non-DB `KeycloakUser` 
from
    the token for every request, including browser-session requests bridged via 
the
    session-stored access token. That broke every view that depends on the
    DB-backed user the session login established — e.g. `UserViewSet.current` 
does
    `redirect(... pk=request.user.id)` and 500'd with
    `'KeycloakUser' object has no attribute 'id'`; the user/profile serializers
    likewise read `request.user.user_profile`.
    
    When the token comes from the session (no `Authorization: Bearer` header), 
reuse
    the DB-backed user the Django session login already established. A pure 
Bearer
    request (API/desktop client) has no session user, so it still gets the 
non-DB
    `KeycloakUser` derived from the verified token claims. The token is 
validated the
    same way in both cases.
    
    Verified live: `/auth/users/current/` 500 -> 302 -> user-detail 200 (full
    UserSerializer payload); `/workspace/dashboard` 200.
---
 .../django_airavata/apps/auth/token_authentication.py    | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git 
a/airavata-django-portal/django_airavata/apps/auth/token_authentication.py 
b/airavata-django-portal/django_airavata/apps/auth/token_authentication.py
index a78313d7d..e41ddd60e 100644
--- a/airavata-django-portal/django_airavata/apps/auth/token_authentication.py
+++ b/airavata-django-portal/django_airavata/apps/auth/token_authentication.py
@@ -67,12 +67,14 @@ class 
KeycloakTokenAuthentication(authentication.BaseAuthentication):
         header = request.META.get('HTTP_AUTHORIZATION', '')
         if header.startswith('Bearer '):
             token = header[len('Bearer '):].strip()
+            bearer = True
         else:
             # Browser bridge: the session login flow stores the Keycloak access
             # token in the session; use it when no Authorization header is sent
             # so the existing browser session authenticates against the
             # token-only API. (Final state: the frontend sends the token as a
             # Bearer header and the session login is removed.)
+            bearer = False
             session = getattr(request, 'session', None)
             token = session.get('ACCESS_TOKEN') if session is not None else 
None
             if not token:
@@ -86,7 +88,19 @@ class 
KeycloakTokenAuthentication(authentication.BaseAuthentication):
             logger.warning("Keycloak token validation failed: %s", e)
             raise exceptions.AuthenticationFailed("Invalid or expired token")
 
-        user = KeycloakUser(claims)
+        # Browser-session bridge: when the token comes from the session (no
+        # Bearer header), the Django session login already established a
+        # DB-backed user that the user/profile views depend on
+        # (``request.user.id``, ``request.user.user_profile``). Reuse it. A 
pure
+        # Bearer request (API/desktop client) has no session user, so derive a
+        # lightweight non-DB user straight from the verified token claims.
+        session_user = None
+        if hasattr(request, '_request'):
+            session_user = getattr(request._request, 'user', None)
+        if not bearer and session_user is not None and 
session_user.is_authenticated:
+            user = session_user
+        else:
+            user = KeycloakUser(claims)
         authz_token = AuthzToken(
             accessToken=token,
             claimsMap={'gatewayID': settings.GATEWAY_ID,

Reply via email to