Skip to content

Commit

Permalink
fix: merge latest (#782)
Browse files Browse the repository at this point in the history
* fix: add exp and iat to JWT payloads without scientific notation (#765)

* adding dev-v6.0.9 tag to this commit to ensure building

* fix: fix handling of b64 and b64url encoded access tokens (#767)

* adding dev-v6.0.10 tag to this commit to ensure building

* Update release.md

* Update release.md

* fix: ee featureflag cron job (#778)

* fix: ee featureflag cron job

* fix: test

* fix: tests

* fix: tests

* adding dev-v6.0.11 tag to this commit to ensure building

* fix: test (#779)

* adding dev-v6.0.11 tag to this commit to ensure building

* fix: test (#780)

* fix: test

* fix: test

* adding dev-v6.0.11 tag to this commit to ensure building

* fix: test (#781)

* adding dev-v6.0.11 tag to this commit to ensure building

---------

Co-authored-by: Mihály Lengyel <[email protected]>
Co-authored-by: rishabhpoddar <[email protected]>
  • Loading branch information
3 people authored Sep 1, 2023
1 parent 5e0fb6f commit de76ce8
Show file tree
Hide file tree
Showing 14 changed files with 187 additions and 22 deletions.
7 changes: 7 additions & 0 deletions .github/ISSUE_TEMPLATE/release.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ labels:
- [ ] Change [checklist in contributing guide for which tables to pick when migrating data from dev to prod instance](https://test.supertokens.com/docs/contribute/checklists/saas/tables-to-consider-for-data-migration-dev-to-prod).
- [ ] Update license key used for cores to include nea feature.
- [ ] Update table schema in mysql / postgresql section for self hosted in docs
- [ ] Update API that returns the list of paid features in saas dashboard
- [ ] [supertokens-node:X.Y](https://github.com/supertokens/supertokens-node/tree/X.Y)
- [ ] [supertokens-golang:X.Y](https://github.com/supertokens/supertokens-golang/tree/X.Y)
- [ ] [supertokens-website:X.Y](https://github.com/supertokens/supertokens-website/tree/X.Y)
Expand Down Expand Up @@ -183,6 +184,12 @@ curl --location --request POST 'https://try.supertokens.com/recipe/dashboard/use
--header 'Content-Type: application/json' \
--data-raw '{"email": "[email protected]","password": "abcd1234"}'
curl --location --request POST 'https://try.supertokens.com/recipe/dashboard/user' \
--header 'rid: dashboard' \
--header 'api-key: <YOUR-API-KEY>' \
--header 'Content-Type: application/json' \
--data-raw '{"email": "[email protected]","password": "abcd1234"}'
curl --location --request PUT 'https://try.supertokens.com/recipe/multitenancy/tenant' \
--header 'Content-Type: application/json' \
--data-raw '{
Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,18 @@ ALTER TABLE emailpassword_pswd_reset_tokens ADD CONSTRAINT emailpassword_pswd_re
ALTER TABLE emailpassword_pswd_reset_tokens ADD COLUMN email VARCHAR(256);
```

## [6.0.11] - 2023-08-16

- Fixed feature flag cron job

## [6.0.10] - 2023-08-16

- Fixed an encoding/decoding issue for certain access token payloads

## [6.0.9] - 2023-08-14

- Now using decimal notation to add numbers into the access token payload (instead of scientific notation)

## [6.0.8] - 2023-08-01

- Fixes CUD validation starting with number.
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ compileTestJava { options.encoding = "UTF-8" }
// }
//}

version = "6.0.8"
version = "6.0.11"


repositories {
Expand Down
Binary file modified cli/jar/cli.jar
Binary file not shown.
Binary file modified downloader/jar/downloader.jar
Binary file not shown.
Binary file modified ee/jar/ee.jar
Binary file not shown.
4 changes: 2 additions & 2 deletions ee/src/main/java/io/supertokens/ee/EEFeatureFlag.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@
import java.util.List;

public class EEFeatureFlag implements io.supertokens.featureflag.EEFeatureFlagInterface {
public static final int INTERVAL_BETWEEN_SERVER_SYNC = 1000 * 3600 * 24; // 1 day.
private static final long INTERVAL_BETWEEN_DB_READS = (long) 1000 * 3600 * 4; // 4 hour.
public static final int INTERVAL_BETWEEN_SERVER_SYNC = 3600 * 24; // 1 day (in seconds).
private static final long INTERVAL_BETWEEN_DB_READS = (long) 1000 * 3600 * 4; // 4 hour (in millis).
public static final String REQUEST_ID = "licensecheck";

public static final String FEATURE_FLAG_KEY_IN_DB = "FEATURE_FLAG";
Expand Down
Binary file renamed jar/core-6.0.8.jar → jar/core-6.0.11.jar
Binary file not shown.
3 changes: 1 addition & 2 deletions src/main/java/io/supertokens/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,8 @@ private void init() throws IOException, StorageQueryException {
}
}
}
FeatureFlag.initForBaseTenant(this, CLIOptions.get(this).getInstallationPath() + "ee/");

MultitenancyHelper.init(this);
FeatureFlag.initForBaseTenant(this, CLIOptions.get(this).getInstallationPath() + "ee/");

try {
// load all configs for each of the tenants.
Expand Down
7 changes: 3 additions & 4 deletions src/main/java/io/supertokens/jwt/JWTSigningFunctions.java
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,8 @@ public static String createJWTToken(JWTSigningKey.SupportedAlgorithms supportedA
headerClaims.put("kid", keyToUse.keyId);

// Add relevant claims to the payload, note we only add/override ones that we absolutely need to.
Map<String, Object> jwtPayload = new Gson().fromJson(payload, HashMap.class);
if (jwksDomain != null) {
jwtPayload.putIfAbsent("iss", jwksDomain);
if (jwksDomain != null && !payload.has("iss")){
payload.addProperty("iss", jwksDomain);
}

JWTCreator.Builder builder = com.auth0.jwt.JWT.create();
Expand All @@ -141,7 +140,7 @@ public static String createJWTToken(JWTSigningKey.SupportedAlgorithms supportedA
if (jwksDomain != null) {
builder.withIssuer(jwksDomain);
}
builder.withPayload(jwtPayload);
builder.withPayload(payload.toString());

return builder.sign(signingAlgorithm);
}
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/io/supertokens/utils/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,9 @@ public static String convertToBase64(String str) {
return new String(Base64.getEncoder().encode(stringToBytes(str)), StandardCharsets.UTF_8);
}

// This function deserializes both B64 and B64URL encodings
public static String convertFromBase64(String str) {
return new String(Base64.getDecoder().decode(stringToBytes(str)), StandardCharsets.UTF_8);
return new String(Base64.getDecoder().decode(stringToBytes(str.replace("-", "+").replace("_", "/"))), StandardCharsets.UTF_8);
}

public static String throwableStacktraceToString(Throwable e) {
Expand Down
88 changes: 84 additions & 4 deletions src/test/java/io/supertokens/test/CronjobTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import io.supertokens.multitenancy.MultitenancyHelper;
import io.supertokens.pluginInterface.STORAGE_TYPE;
import io.supertokens.pluginInterface.Storage;
import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo;
import io.supertokens.pluginInterface.multitenancy.*;
import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException;
import io.supertokens.storageLayer.StorageLayer;
Expand All @@ -39,11 +40,9 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.reflections.Reflections;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;

import static org.junit.Assert.*;

Expand Down Expand Up @@ -780,4 +779,85 @@ public void testThatCoreAutomaticallySyncsToConfigChangesInDb() throws Exception
process.kill();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));
}

@Test
public void testThatNoCronJobIntervalIsMoreThanADay() throws Exception {
String[] args = {"../"};

TestingProcessManager.TestingProcess process = TestingProcessManager.start(args);
process.startProcess();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));

if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) {
return;
}

// ensure none of the tasks have an interval more than a day
for (CronTask task : Cronjobs.getInstance(process.getProcess()).getTasks()) {
assertTrue(task.getIntervalTimeSeconds() <= 3600 * 24);
assertTrue(task.getInitialWaitTimeSeconds() <= 3600 * 24);
}
process.kill();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));
}

@Test
public void testThatThereAreTasksOfAllCronTaskClassesAndHaveCorrectIntervals() throws Exception {
String[] args = {"../"};

TestingProcessManager.TestingProcess process = TestingProcessManager.start(args);
process.startProcess();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));

if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) {
return;
}

Reflections reflections = new Reflections("io.supertokens");
Set<Class<? extends CronTask>> classes = reflections.getSubTypesOf(CronTask.class);

Set<String> classNames = new HashSet<>();

for (Class cls : classes) {
if (!cls.getName().contains("io.supertokens.test")) {
classNames.add(cls.getName());
}
}

// Note that the time is in seconds
Map<String, Integer> intervals = new HashMap<>();
intervals.put("io.supertokens.ee.cronjobs.EELicenseCheck", 86400);
intervals.put("io.supertokens.cronjobs.syncCoreConfigWithDb.SyncCoreConfigWithDb", 60);
intervals.put("io.supertokens.cronjobs.deleteExpiredSessions.DeleteExpiredSessions", 43200);
intervals.put("io.supertokens.cronjobs.deleteExpiredPasswordResetTokens.DeleteExpiredPasswordResetTokens", 3600);
intervals.put("io.supertokens.cronjobs.deleteExpiredEmailVerificationTokens.DeleteExpiredEmailVerificationTokens", 43200);
intervals.put("io.supertokens.cronjobs.deleteExpiredPasswordlessDevices.DeleteExpiredPasswordlessDevices", 3600);
intervals.put("io.supertokens.cronjobs.deleteExpiredTotpTokens.DeleteExpiredTotpTokens", 3600);
intervals.put("io.supertokens.cronjobs.deleteExpiredDashboardSessions.DeleteExpiredDashboardSessions", 43200);
intervals.put("io.supertokens.cronjobs.telemetry.Telemetry", 86400);
intervals.put("io.supertokens.cronjobs.deleteExpiredAccessTokenSigningKeys.DeleteExpiredAccessTokenSigningKeys", 86400);

Map<String, Integer> delays = new HashMap<>();
delays.put("io.supertokens.ee.cronjobs.EELicenseCheck", 86400);
delays.put("io.supertokens.cronjobs.syncCoreConfigWithDb.SyncCoreConfigWithDb", 0);
delays.put("io.supertokens.cronjobs.deleteExpiredSessions.DeleteExpiredSessions", 0);
delays.put("io.supertokens.cronjobs.deleteExpiredPasswordResetTokens.DeleteExpiredPasswordResetTokens", 0);
delays.put("io.supertokens.cronjobs.deleteExpiredEmailVerificationTokens.DeleteExpiredEmailVerificationTokens", 0);
delays.put("io.supertokens.cronjobs.deleteExpiredPasswordlessDevices.DeleteExpiredPasswordlessDevices", 0);
delays.put("io.supertokens.cronjobs.deleteExpiredTotpTokens.DeleteExpiredTotpTokens", 0);
delays.put("io.supertokens.cronjobs.deleteExpiredDashboardSessions.DeleteExpiredDashboardSessions", 0);
delays.put("io.supertokens.cronjobs.telemetry.Telemetry", 0);
delays.put("io.supertokens.cronjobs.deleteExpiredAccessTokenSigningKeys.DeleteExpiredAccessTokenSigningKeys", 0);

List<CronTask> allTasks = Cronjobs.getInstance(process.getProcess()).getTasks();
assertEquals(10, allTasks.size());

for (CronTask task : allTasks) {
assertEquals(intervals.get(task.getClass().getName()).intValue(), task.getIntervalTimeSeconds());
assertEquals(delays.get(task.getClass().getName()).intValue(), task.getInitialWaitTimeSeconds());
}

process.kill();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));
}
}
39 changes: 39 additions & 0 deletions src/test/java/io/supertokens/test/FeatureFlagTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import io.supertokens.ProcessState;
import io.supertokens.cronjobs.CronTask;
import io.supertokens.cronjobs.CronTaskTest;
import io.supertokens.cronjobs.Cronjobs;
import io.supertokens.cronjobs.syncCoreConfigWithDb.SyncCoreConfigWithDb;
import io.supertokens.emailpassword.EmailPassword;
import io.supertokens.featureflag.EE_FEATURES;
import io.supertokens.featureflag.FeatureFlag;
Expand All @@ -35,6 +39,7 @@
import io.supertokens.session.Session;
import io.supertokens.storageLayer.StorageLayer;
import io.supertokens.test.httpRequest.HttpRequestForTesting;
import io.supertokens.test.multitenant.api.TestMultitenancyAPIHelper;
import io.supertokens.webserver.WebserverAPI;
import org.junit.*;
import org.junit.rules.TestRule;
Expand Down Expand Up @@ -702,4 +707,38 @@ public void testPaidFeaturesAreEnabledIfUsingInMemoryDatabase() throws Exception
process.kill();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));
}

@Test
public void testNetworkCallIsMadeInCoreInit() throws Exception {
String[] args = {"../"};

TestingProcessManager.TestingProcess process = TestingProcessManager.start(args);
process.startProcess();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));

if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) {
return;
}

if (StorageLayer.isInMemDb(process.getProcess())) {
return;
}

// While adding license
TestMultitenancyAPIHelper.addLicense(OPAQUE_KEY_WITH_MULTITENANCY_FEATURE, process.getProcess());
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.LICENSE_KEY_CHECK_NETWORK_CALL));
ProcessState.getInstance(process.getProcess()).clear();

process.kill(false);


// Restart core and check if the call was made during init
process = TestingProcessManager.start(args);
process.startProcess();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.LICENSE_KEY_CHECK_NETWORK_CALL));

process.kill();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));
}
}
44 changes: 36 additions & 8 deletions src/test/java/io/supertokens/test/session/AccessTokenTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.supertokens.ProcessState.EventAndException;
import io.supertokens.ProcessState.PROCESS_STATE;
import io.supertokens.exceptions.AccessTokenPayloadError;
Expand Down Expand Up @@ -256,7 +257,8 @@ public void inputOutputTest() throws Exception {
EventAndException e = process.checkOrWaitForEvent(PROCESS_STATE.STARTED);
assertNotNull(e);
JsonObject jsonObj = new JsonObject();
jsonObj.addProperty("key", "value");
String testValue = "asdf???123";
jsonObj.addProperty("key", testValue);

// db key
long expiryTime = System.currentTimeMillis() + 1000;
Expand All @@ -268,10 +270,15 @@ public void inputOutputTest() throws Exception {
assertEquals("userId", info.recipeUserId);
assertEquals("refreshTokenHash1", info.refreshTokenHash1);
assertEquals("parentRefreshTokenHash1", info.parentRefreshTokenHash1);
assertEquals("value", info.userData.get("key").getAsString());
assertEquals(testValue, info.userData.get("key").getAsString());
assertEquals("antiCsrfToken", info.antiCsrfToken);
assertEquals(expiryTime / 1000 * 1000, info.expiryTime);

JsonObject payload = (JsonObject) new JsonParser()
.parse(io.supertokens.utils.Utils.convertFromBase64(newToken.token.split("\\.")[1]));
// This throws if the number is in scientific (E) format
assertEquals(expiryTime / 1000, Long.parseLong(payload.get("exp").toString()));

JWT.JWTPreParseInfo jwtInfo = JWT.preParseJWTInfo(newToken.token);
assertNotNull(jwtInfo.kid);
assertEquals(jwtInfo.version, AccessToken.getLatestVersion());
Expand All @@ -286,22 +293,29 @@ public void inputOutputTestStatic() throws Exception {
EventAndException e = process.checkOrWaitForEvent(PROCESS_STATE.STARTED);
assertNotNull(e);
JsonObject jsonObj = new JsonObject();
jsonObj.addProperty("key", "value");
String testValue = "asdf???123";
jsonObj.addProperty("key", testValue);

// db key
long expiryTime = System.currentTimeMillis() + 1000;
TokenInfo newToken = AccessToken.createNewAccessToken(process.getProcess(), "sessionHandle", "userId",
"refreshTokenHash1", "parentRefreshTokenHash1", jsonObj, "antiCsrfToken", expiryTime,
AccessToken.getLatestVersion(), true);
System.out.println(newToken.token);
AccessTokenInfo info = AccessToken.getInfoFromAccessToken(process.getProcess(), newToken.token, true);
assertEquals("sessionHandle", info.sessionHandle);
assertEquals("userId", info.recipeUserId);
assertEquals("refreshTokenHash1", info.refreshTokenHash1);
assertEquals("parentRefreshTokenHash1", info.parentRefreshTokenHash1);
assertEquals("value", info.userData.get("key").getAsString());
assertEquals(testValue, info.userData.get("key").getAsString());
assertEquals("antiCsrfToken", info.antiCsrfToken);
assertEquals(expiryTime / 1000 * 1000, info.expiryTime);

JsonObject payload = (JsonObject) new JsonParser()
.parse(io.supertokens.utils.Utils.convertFromBase64(newToken.token.split("\\.")[1]));
// This throws if the number is in scientific (E) format
assertEquals(expiryTime / 1000, Long.parseLong(payload.get("exp").toString()));

JWT.JWTPreParseInfo jwtInfo = JWT.preParseJWTInfo(newToken.token);
assertNotNull(jwtInfo.kid);
assertEquals(jwtInfo.version, AccessToken.getLatestVersion());
Expand All @@ -315,7 +329,8 @@ public void inputOutputTestV2() throws Exception {
EventAndException e = process.checkOrWaitForEvent(PROCESS_STATE.STARTED);
assertNotNull(e);
JsonObject jsonObj = new JsonObject();
jsonObj.addProperty("key", "value");
String testValue = "asdf???123";
jsonObj.addProperty("key", testValue);

// db key
long expiryTime = System.currentTimeMillis() + 1000;
Expand All @@ -327,9 +342,15 @@ public void inputOutputTestV2() throws Exception {
assertEquals("userId", info.recipeUserId);
assertEquals("refreshTokenHash1", info.refreshTokenHash1);
assertEquals("parentRefreshTokenHash1", info.parentRefreshTokenHash1);
assertEquals("value", info.userData.get("key").getAsString());
assertEquals(testValue, info.userData.get("key").getAsString());
assertEquals("antiCsrfToken", info.antiCsrfToken);
assertEquals(expiryTime, info.expiryTime);

JsonObject payload = (JsonObject) new JsonParser()
.parse(io.supertokens.utils.Utils.convertFromBase64(newToken.token.split("\\.")[1]));
// This throws if the number is in scientific (E) format
assertEquals(expiryTime, Long.parseLong(payload.get("expiryTime").toString()));

process.kill();
}

Expand All @@ -343,7 +364,8 @@ public void inputOutputTestv1() throws InterruptedException, InvalidKeyException
EventAndException e = process.checkOrWaitForEvent(PROCESS_STATE.STARTED);
assertNotNull(e);
JsonObject jsonObj = new JsonObject();
jsonObj.addProperty("key", "value");
String testValue = "asdf???123";
jsonObj.addProperty("key", testValue);

// db key
TokenInfo newToken = AccessToken.createNewAccessTokenV1(process.getProcess(), "sessionHandle", "userId",
Expand All @@ -353,8 +375,14 @@ public void inputOutputTestv1() throws InterruptedException, InvalidKeyException
assertEquals("userId", info.recipeUserId);
assertEquals("refreshTokenHash1", info.refreshTokenHash1);
assertEquals("parentRefreshTokenHash1", info.parentRefreshTokenHash1);
assertEquals("value", info.userData.get("key").getAsString());
assertEquals(testValue, info.userData.get("key").getAsString());
assertEquals("antiCsrfToken", info.antiCsrfToken);

JsonObject payload = (JsonObject) new JsonParser()
.parse(io.supertokens.utils.Utils.convertFromBase64(newToken.token.split("\\.")[1]));
// This throws if the number is in scientific (E) format
Long.parseLong(payload.get("expiryTime").toString());

process.kill();
}

Expand Down

0 comments on commit de76ce8

Please sign in to comment.