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

mridulpathak pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 2d5c3bdf70 Fixed: REST refresh-token endpoint now rejects 
disabled/deleted accounts (#1646)
2d5c3bdf70 is described below

commit 2d5c3bdf70a20d52fec954cee5436d1788feec98
Author: Mridul Pathak <[email protected]>
AuthorDate: Mon Aug 17 10:32:47 2026 +0530

    Fixed: REST refresh-token endpoint now rejects disabled/deleted accounts 
(#1646)
    
    AuthenticationResource.refreshToken() minted new access/refresh tokens 
directly from a validated JWT's claims, with no check that the UserLogin still 
existed or was active — so disabling or deleting an account did not revoke a 
holder's ability to keep renewing API tokens with a refresh token issued 
beforehand. It also swallowed refresh-token validation errors instead of 
returning 401. Both are fixed: the userLoginId is now looked up and confirmed 
active via the same LoginWorker.isUser [...]
    
    Thanks: Pavel Kohout, Aisle Research for reporting.
---
 .../ws/rs/resources/AuthenticationResource.java    | 44 ++++++++++++++++++++--
 1 file changed, 41 insertions(+), 3 deletions(-)

diff --git 
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/resources/AuthenticationResource.java
 
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/resources/AuthenticationResource.java
index 799c88346f..5fa0c35d8d 100644
--- 
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/resources/AuthenticationResource.java
+++ 
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/resources/AuthenticationResource.java
@@ -20,11 +20,17 @@ package org.apache.ofbiz.ws.rs.resources;
 
 import java.util.Map;
 
+import org.apache.ofbiz.base.util.Debug;
 import org.apache.ofbiz.base.util.UtilMisc;
+import org.apache.ofbiz.base.util.UtilValidate;
 import org.apache.ofbiz.entity.Delegator;
+import org.apache.ofbiz.entity.GenericEntityException;
 import org.apache.ofbiz.entity.GenericValue;
+import org.apache.ofbiz.entity.util.EntityQuery;
 import org.apache.ofbiz.entity.util.EntityUtilProperties;
+import org.apache.ofbiz.service.ModelService;
 import org.apache.ofbiz.webapp.control.JWTManager;
+import org.apache.ofbiz.webapp.control.LoginWorker;
 import org.apache.ofbiz.ws.rs.annotation.AuthToken;
 import org.apache.ofbiz.ws.rs.util.RestApiUtil;
 
@@ -53,6 +59,8 @@ import jakarta.ws.rs.core.Response;
 @Tag(name = "Authentication Token Generating Resource", description = 
"Intended to provide generation of authentication tokens.")
 public class AuthenticationResource {
 
+    private static final String MODULE = 
AuthenticationResource.class.getName();
+
     @Context
     private ServletContext servletContext;
 
@@ -146,12 +154,17 @@ public class AuthenticationResource {
         httpRequest.setAttribute("dispatcher", delegator);
         Map<String, Object> claims = 
JWTManager.validateRefreshToken(delegator, refreshToken);
 
-        // Fetch delegator, dispatcher, and userLogin
-        if (claims.containsKey("errorMessage")) {
-            System.out.println("Error with JWT token: ");
+        if (claims.containsKey(ModelService.ERROR_MESSAGE)) {
+            return 
RestApiUtil.error(Response.Status.UNAUTHORIZED.getStatusCode(), 
Response.Status.UNAUTHORIZED.getReasonPhrase(),
+                    "Unauthorized: " + claims.get(ModelService.ERROR_MESSAGE));
         }
 
         String userLoginId = (String) claims.get("userLoginId");
+        GenericValue userLogin = getActiveUserLogin(delegator, userLoginId);
+        if (userLogin == null) {
+            return 
RestApiUtil.error(Response.Status.UNAUTHORIZED.getStatusCode(), 
Response.Status.UNAUTHORIZED.getReasonPhrase(),
+                    "Unauthorized: Invalid refresh token.");
+        }
 
         String newAccessToken = JWTManager.createJwt(delegator, 
UtilMisc.toMap("userLoginId", userLoginId));
         String newRefreshToken = JWTManager.createRefreshToken(delegator, 
userLoginId);
@@ -161,4 +174,29 @@ public class AuthenticationResource {
 
         return RestApiUtil.success("Token refreshed.", tokenPayload);
     }
+
+    /**
+     * Looks up the {@code UserLogin} named by a validated refresh token's 
claims and confirms
+     * the account is still present and active, so a disabled or deleted 
account cannot keep
+     * renewing tokens on the strength of a refresh token issued before the 
account changed.
+     * @param delegator the delegator
+     * @param userLoginId the userLoginId claim from a cryptographically 
validated refresh token
+     * @return the active userLogin, or {@code null} if it is missing, 
deleted, or disabled
+     */
+    private GenericValue getActiveUserLogin(Delegator delegator, String 
userLoginId) {
+        if (UtilValidate.isEmpty(userLoginId)) {
+            return null;
+        }
+        GenericValue userLogin;
+        try {
+            userLogin = 
EntityQuery.use(delegator).from("UserLogin").where("userLoginId", 
userLoginId).queryOne();
+        } catch (GenericEntityException e) {
+            Debug.logError(e, "Unable to get UserLogin information from 
refresh token: " + e.getMessage(), MODULE);
+            return null;
+        }
+        if (userLogin == null || !LoginWorker.isUserLoginActive(userLogin)) {
+            return null;
+        }
+        return userLogin;
+    }
 }

Reply via email to