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

linxinyuan pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/texera.git


The following commit(s) were added to refs/heads/main by this push:
     new f90848e557 feat(auth): removed token refresh functionality (#3765)
f90848e557 is described below

commit f90848e5572c46e944e4a655d70a0dfd1be1333a
Author: Victor Fawole <[email protected]>
AuthorDate: Wed Sep 24 22:52:14 2025 -0400

    feat(auth): removed token refresh functionality (#3765)
    
    ## Description
    ---
    This PR mitigates the security issue described in #3737, by removing the
    token refresh functionality.
    
    ## Problem
    ---
    When user's sign in, they receive a new token. This token has a TTL of 2
    days (2880 minutes), as defined by `expiration-in-minutes` in
    `auth.conf`. However, a user's token is refreshed if they make any
    action that creates a request to the backend, if the token has not
    already expired. The refresh time is the same as the original TTL, thus,
    a user could theoretically be signed in indefinitely. From a security
    perspective this is unsafe.
    
    ## Solution
    ---
    The token refresh functionality was removed from `auth.service.ts`.
    Additionally, since the `refreshToken` in `auth.service.ts` called the
    `/refresh` route from the backend, the route and any classes/functions
    it used were removed (`AuthResource.scala`,
    `RefreshTokenRequest.scala`). The TTL of the token was then changed to 7
    days (10080 minutes) to improve user experience.
    
    ## Side Effects
    ---
    As noted in #3738, when a user's token is expired, but are not logged
    out, they are still able to access some data, but are unable to make any
    changes.
    Because tokens are no longer refreshed, the user is more likely to
    encounter this case.
    
    Fixes #3737
---
 .../http/request/auth/RefreshTokenRequest.scala    | 22 --------------
 .../texera/web/resource/auth/AuthResource.scala    | 21 ++-----------
 core/config/src/main/resources/auth.conf           |  2 +-
 .../src/app/common/service/user/auth.service.ts    | 34 ----------------------
 4 files changed, 3 insertions(+), 76 deletions(-)

diff --git 
a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/http/request/auth/RefreshTokenRequest.scala
 
b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/http/request/auth/RefreshTokenRequest.scala
deleted file mode 100644
index 521073afa9..0000000000
--- 
a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/http/request/auth/RefreshTokenRequest.scala
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package edu.uci.ics.texera.web.model.http.request.auth
-
-case class RefreshTokenRequest(accessToken: String)
diff --git 
a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/auth/AuthResource.scala
 
b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/auth/AuthResource.scala
index 272886e654..ccccdd768a 100644
--- 
a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/auth/AuthResource.scala
+++ 
b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/auth/AuthResource.scala
@@ -19,19 +19,10 @@
 
 package edu.uci.ics.texera.web.resource.auth
 
-import edu.uci.ics.texera.auth.JwtAuth.{
-  TOKEN_EXPIRE_TIME_IN_MINUTES,
-  jwtClaims,
-  jwtConsumer,
-  jwtToken
-}
+import edu.uci.ics.texera.auth.JwtAuth.{TOKEN_EXPIRE_TIME_IN_MINUTES, 
jwtClaims, jwtToken}
 import edu.uci.ics.texera.config.UserSystemConfig
 import edu.uci.ics.texera.dao.SqlServer
-import edu.uci.ics.texera.web.model.http.request.auth.{
-  RefreshTokenRequest,
-  UserLoginRequest,
-  UserRegistrationRequest
-}
+import edu.uci.ics.texera.web.model.http.request.auth.{UserLoginRequest, 
UserRegistrationRequest}
 import edu.uci.ics.texera.web.model.http.response.TokenIssueResponse
 import edu.uci.ics.texera.dao.jooq.generated.Tables.USER
 import edu.uci.ics.texera.dao.jooq.generated.enums.UserRoleEnum
@@ -107,14 +98,6 @@ class AuthResource {
     }
   }
 
-  @POST
-  @Path("/refresh")
-  def refresh(request: RefreshTokenRequest): TokenIssueResponse = {
-    val claims = jwtConsumer.process(request.accessToken).getJwtClaims
-    
claims.setExpirationTimeMinutesInTheFuture(TOKEN_EXPIRE_TIME_IN_MINUTES.toFloat)
-    TokenIssueResponse(jwtToken(claims))
-  }
-
   @POST
   @Path("/register")
   def register(request: UserRegistrationRequest): TokenIssueResponse = {
diff --git a/core/config/src/main/resources/auth.conf 
b/core/config/src/main/resources/auth.conf
index ef82fc8ace..c99db10c85 100644
--- a/core/config/src/main/resources/auth.conf
+++ b/core/config/src/main/resources/auth.conf
@@ -19,7 +19,7 @@
 # Configuration for JWT Authentication. Currently it is used by the 
FileService to parse the given JWT Token
 auth {
     jwt {
-        expiration-in-minutes = 2880
+        expiration-in-minutes = 10080
         expiration-in-minutes = ${?AUTH_JWT_EXPIRATION_IN_MINUTES}
 
         256-bit-secret = "8a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d"
diff --git a/core/gui/src/app/common/service/user/auth.service.ts 
b/core/gui/src/app/common/service/user/auth.service.ts
index 57f90658de..2f47036712 100644
--- a/core/gui/src/app/common/service/user/auth.service.ts
+++ b/core/gui/src/app/common/service/user/auth.service.ts
@@ -48,7 +48,6 @@ export class AuthService {
   public static readonly GOOGLE_LOGIN_ENDPOINT = "auth/google/login";
 
   private tokenExpirationSubscription?: Subscription;
-  private refreshTokenSubscription?: Subscription;
 
   constructor(
     private http: HttpClient,
@@ -112,7 +111,6 @@ export class AuthService {
   public logout(): undefined {
     AuthService.removeAccessToken();
     this.tokenExpirationSubscription?.unsubscribe();
-    this.refreshTokenSubscription?.unsubscribe();
     return undefined;
   }
 
@@ -145,7 +143,6 @@ export class AuthService {
     }
 
     this.registerAutoLogout();
-    this.registerAutoRefreshToken();
     return {
       uid: this.jwtHelperService.decodeToken(token).userId,
       name: this.jwtHelperService.decodeToken(token).sub,
@@ -157,37 +154,6 @@ export class AuthService {
     };
   }
 
-  /**
-   * Refreshes the current accessToken to get a new accessToken
-   * // TODO: for better security, use a separate refresh token to perform 
this refresh
-   */
-  private refreshToken(): Observable<Readonly<{ accessToken: string }>> {
-    return this.http.post<Readonly<{ accessToken: string }>>(
-      `${AppSettings.getApiEndpoint()}/${AuthService.REFRESH_TOKEN}`,
-      { accessToken: AuthService.getAccessToken() }
-    );
-  }
-
-  private registerAutoRefreshToken() {
-    this.refreshTokenSubscription?.unsubscribe();
-    const TOKEN_REFRESH_INTERVAL_IN_MIN = 
this.config.env.expirationTimeInMinutes - 1;
-    // Token Refresh Interval set to Token Expiration Time - 1
-    this.refreshTokenSubscription = interval(TOKEN_REFRESH_INTERVAL_IN_MIN * 
60 * 1000)
-      .pipe(startWith(0)) // to trigger immediately for the first time.
-      .subscribe(() => {
-        this.refreshToken().subscribe(
-          ({ accessToken }) => {
-            AuthService.setAccessToken(accessToken);
-            this.registerAutoLogout();
-          },
-          (_: unknown) => {
-            // failed to refresh the access token, logout instantly.
-            this.logout();
-          }
-        );
-      });
-  }
-
   private registerAutoLogout() {
     this.tokenExpirationSubscription?.unsubscribe();
     const expirationTime = 
this.jwtHelperService.getTokenExpirationDate()?.getTime();

Reply via email to