From 9213fd41406453681adaaff7758746bdfdf9a5dd Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Mon, 14 Oct 2024 12:54:39 +0530 Subject: [PATCH] fix: updates user active when issuing tokens --- .../webserver/api/oauth/OAuthAuthAPI.java | 47 +++++++++++++++ .../webserver/api/oauth/OAuthTokenAPI.java | 57 ++++++++++++++++--- 2 files changed, 97 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/supertokens/webserver/api/oauth/OAuthAuthAPI.java b/src/main/java/io/supertokens/webserver/api/oauth/OAuthAuthAPI.java index 7ff44dcfc..c33e7d48f 100644 --- a/src/main/java/io/supertokens/webserver/api/oauth/OAuthAuthAPI.java +++ b/src/main/java/io/supertokens/webserver/api/oauth/OAuthAuthAPI.java @@ -20,14 +20,22 @@ import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; +import io.supertokens.ActiveUsers; import io.supertokens.Main; import io.supertokens.multitenancy.exception.BadPermissionException; import io.supertokens.oauth.HttpRequestForOry; import io.supertokens.oauth.OAuth; +import io.supertokens.oauth.OAuthToken; import io.supertokens.pluginInterface.RECIPE_ID; import io.supertokens.pluginInterface.Storage; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; +import io.supertokens.pluginInterface.session.SessionInfo; +import io.supertokens.pluginInterface.useridmapping.UserIdMapping; +import io.supertokens.session.Session; +import io.supertokens.storageLayer.StorageLayer; +import io.supertokens.useridmapping.UserIdType; import io.supertokens.webserver.InputParser; import io.supertokens.webserver.WebserverAPI; import jakarta.servlet.ServletException; @@ -98,6 +106,26 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I String redirectTo = response.headers.get("Location").get(0); redirectTo = OAuth.transformTokensInAuthRedirect(main, appIdentifier, storage, redirectTo, iss, accessTokenUpdate, idTokenUpdate, useDynamicKey); + + + if (redirectTo.contains("#")) { + String tokensPart = redirectTo.substring(redirectTo.indexOf("#") + 1); + String[] parts = tokensPart.split("&"); + for (String part : parts) { + if (part.startsWith("access_token=")) { + String accessToken = java.net.URLDecoder.decode(part.split("=")[1], "UTF-8"); + try { + JsonObject accessTokenPayload = OAuthToken.getPayloadFromJWTToken(appIdentifier, main, accessToken); + if (accessTokenPayload.has("sessionHandle")) { + updateLastActive(appIdentifier, accessTokenPayload.get("sessionHandle").getAsString()); + } + } catch (Exception e) { + // ignore + } + } + } + } + List responseCookies = response.headers.get("Set-Cookie"); JsonObject finalResponse = new JsonObject(); @@ -120,4 +148,23 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I throw new ServletException(e); } } + + private void updateLastActive(AppIdentifier appIdentifier, String sessionHandle) { + try { + TenantIdentifier tenantIdentifier = new TenantIdentifier(appIdentifier.getConnectionUriDomain(), + appIdentifier.getAppId(), Session.getTenantIdFromSessionHandle(sessionHandle)); + Storage storage = StorageLayer.getStorage(tenantIdentifier, main); + SessionInfo sessionInfo = Session.getSession(tenantIdentifier, storage, sessionHandle); + + UserIdMapping userIdMapping = io.supertokens.useridmapping.UserIdMapping.getUserIdMapping( + appIdentifier, storage, sessionInfo.userId, UserIdType.ANY); + if (userIdMapping != null) { + ActiveUsers.updateLastActive(appIdentifier, main, userIdMapping.superTokensUserId); + } else { + ActiveUsers.updateLastActive(appIdentifier, main, sessionInfo.userId); + } + } catch (Exception e) { + // ignore + } + } } diff --git a/src/main/java/io/supertokens/webserver/api/oauth/OAuthTokenAPI.java b/src/main/java/io/supertokens/webserver/api/oauth/OAuthTokenAPI.java index d9e36b214..e16804c50 100644 --- a/src/main/java/io/supertokens/webserver/api/oauth/OAuthTokenAPI.java +++ b/src/main/java/io/supertokens/webserver/api/oauth/OAuthTokenAPI.java @@ -18,12 +18,16 @@ import com.auth0.jwt.exceptions.JWTCreationException; import com.google.gson.*; + +import io.supertokens.ActiveUsers; import io.supertokens.Main; +import io.supertokens.exceptions.TryRefreshTokenException; import io.supertokens.featureflag.exceptions.FeatureNotEnabledException; import io.supertokens.jwt.exceptions.UnsupportedJWTSigningAlgorithmException; import io.supertokens.multitenancy.exception.BadPermissionException; import io.supertokens.oauth.HttpRequestForOry; import io.supertokens.oauth.OAuth; +import io.supertokens.oauth.OAuthToken; import io.supertokens.oauth.exceptions.OAuthAPIException; import io.supertokens.pluginInterface.RECIPE_ID; import io.supertokens.pluginInterface.Storage; @@ -31,10 +35,17 @@ import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; import io.supertokens.pluginInterface.oauth.OAuthClient; import io.supertokens.pluginInterface.oauth.exception.OAuthClientNotFoundException; +import io.supertokens.pluginInterface.session.SessionInfo; +import io.supertokens.pluginInterface.useridmapping.UserIdMapping; +import io.supertokens.session.Session; +import io.supertokens.session.info.SessionInformationHolder; import io.supertokens.session.jwt.JWT.JWTException; +import io.supertokens.storageLayer.StorageLayer; +import io.supertokens.useridmapping.UserIdType; import io.supertokens.webserver.InputParser; import io.supertokens.webserver.WebserverAPI; import jakarta.servlet.ServletException; @@ -163,14 +174,14 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I if (grantType.equals("client_credentials")) { try { OAuth.addM2MToken(main, appIdentifier, storage, response.jsonResponse.getAsJsonObject().get("access_token").getAsString()); - } catch (Exception e) { - // ignore + } catch (TryRefreshTokenException e) { + throw new IllegalStateException("should never happen"); } } if (response.jsonResponse.getAsJsonObject().has("refresh_token")) { String newRefreshToken = response.jsonResponse.getAsJsonObject().get("refresh_token").getAsString(); - long exp = 0; + long refreshTokenExp = 0; { // Introspect the new refresh token to get the expiry @@ -191,26 +202,39 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I if (introspectResponse != null) { JsonObject refreshTokenPayload = introspectResponse.jsonResponse.getAsJsonObject(); - exp = refreshTokenPayload.get("exp").getAsLong(); + refreshTokenExp = refreshTokenPayload.get("exp").getAsLong(); + if (refreshTokenPayload.has("sessionHandle")) { + updateLastActive(appIdentifier, refreshTokenPayload.get("sessionHandle").getAsString()); + } + } else { - return; + throw new IllegalStateException("Should never come here"); } } if (inputRefreshToken == null) { // Issuing a new refresh token if (!oauthClient.enableRefreshTokenRotation) { - OAuth.createOrUpdateRefreshTokenMapping(main, appIdentifier, storage, newRefreshToken, newRefreshToken, exp); + OAuth.createOrUpdateRefreshTokenMapping(main, appIdentifier, storage, newRefreshToken, newRefreshToken, refreshTokenExp); } // else we don't need a mapping } else { // Refreshing a token if (!oauthClient.enableRefreshTokenRotation) { - OAuth.createOrUpdateRefreshTokenMapping(main, appIdentifier, storage, inputRefreshToken, newRefreshToken, exp); + OAuth.createOrUpdateRefreshTokenMapping(main, appIdentifier, storage, inputRefreshToken, newRefreshToken, refreshTokenExp); response.jsonResponse.getAsJsonObject().remove("refresh_token"); } else { OAuth.deleteRefreshTokenMappingIfExists(main, appIdentifier, storage, inputRefreshToken); } } + } else { + try { + JsonObject accessTokenPayload = OAuthToken.getPayloadFromJWTToken(appIdentifier, main, response.jsonResponse.getAsJsonObject().get("access_token").getAsString()); + if (accessTokenPayload.has("sessionHandle")) { + updateLastActive(appIdentifier, accessTokenPayload.get("sessionHandle").getAsString()); + } + } catch (Exception e) { + // ignore + } } } catch (IOException | InvalidConfigException | TenantOrAppNotFoundException | StorageQueryException | InvalidKeyException | NoSuchAlgorithmException | InvalidKeySpecException | JWTCreationException | JWTException | StorageTransactionLogicException | UnsupportedJWTSigningAlgorithmException e) { @@ -229,4 +253,23 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I OAuthProxyHelper.handleOAuthClientNotFoundException(resp); } } + + private void updateLastActive(AppIdentifier appIdentifier, String sessionHandle) { + try { + TenantIdentifier tenantIdentifier = new TenantIdentifier(appIdentifier.getConnectionUriDomain(), + appIdentifier.getAppId(), Session.getTenantIdFromSessionHandle(sessionHandle)); + Storage storage = StorageLayer.getStorage(tenantIdentifier, main); + SessionInfo sessionInfo = Session.getSession(tenantIdentifier, storage, sessionHandle); + + UserIdMapping userIdMapping = io.supertokens.useridmapping.UserIdMapping.getUserIdMapping( + appIdentifier, storage, sessionInfo.userId, UserIdType.ANY); + if (userIdMapping != null) { + ActiveUsers.updateLastActive(appIdentifier, main, userIdMapping.superTokensUserId); + } else { + ActiveUsers.updateLastActive(appIdentifier, main, sessionInfo.userId); + } + } catch (Exception e) { + // ignore + } + } }