Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: revoke APIs #1041

Merged
merged 32 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
8c24a51
fix: revoke consent sessions
sattvikc Sep 16, 2024
59a13cd
fix: revoke token
sattvikc Sep 16, 2024
110f2f7
Merge branch 'feat/oauth/pass-thru-apis' into feat/oauth/revoke-apis
sattvikc Sep 18, 2024
eefbc2b
fix: revoke impl
sattvikc Sep 18, 2024
abc95ef
fix: revoke session
sattvikc Sep 18, 2024
1dee4cc
fix: introspect impl after revoke
sattvikc Sep 18, 2024
9e3caf5
Merge branch 'feat/oauth/pass-thru-apis' into feat/oauth/revoke-apis
sattvikc Sep 19, 2024
a529072
fix: revoke by client_id
sattvikc Sep 19, 2024
8e34a75
fix: refresh token check in token exchange
sattvikc Sep 19, 2024
2577ee2
fix: at hash
sattvikc Sep 19, 2024
74e55ee
fix: sqlite impl
sattvikc Sep 19, 2024
8e79a0f
fix: client props whitelist
sattvikc Sep 19, 2024
a07e69c
fix: status and query null check
sattvikc Sep 20, 2024
7c2842e
fix: plugin interface update
sattvikc Sep 20, 2024
49c51d7
fix: logout api
sattvikc Sep 20, 2024
5aa95fe
fix: ext
sattvikc Sep 20, 2024
64dc34c
fix: accept consent
sattvikc Sep 20, 2024
5582efa
fix: accept consent
sattvikc Sep 20, 2024
d9d4a8d
fix: introspect in token api
sattvikc Sep 20, 2024
bb6c92d
fix: keep fragment while updating query params
sattvikc Sep 23, 2024
c0ec93d
fix: count creds and pr comment
sattvikc Sep 23, 2024
882e172
fix: oauth stats
sattvikc Sep 23, 2024
e9770fa
fix: oauth cleanup cron task
sattvikc Sep 23, 2024
930cd2c
fix: gid in refresh token
sattvikc Sep 24, 2024
c58d162
fix: inememory impl
sattvikc Sep 24, 2024
bec7d4d
feat: add initial payload fields to accept consent
porcellus Sep 24, 2024
70a15a5
fix: revoke cleanup
sattvikc Sep 24, 2024
08d25c9
fix: stats
sattvikc Sep 24, 2024
ea9ed88
fix: client credentials basic
sattvikc Sep 24, 2024
d964272
fix: authorization header
sattvikc Sep 24, 2024
4b327e4
fix: authorizaion header in revoke
sattvikc Sep 24, 2024
e91d8fe
fix: missing table
sattvikc Sep 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions ee/src/main/java/io/supertokens/ee/EEFeatureFlag.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import io.supertokens.pluginInterface.KeyValueInfo;
import io.supertokens.pluginInterface.STORAGE_TYPE;
import io.supertokens.pluginInterface.Storage;
import io.supertokens.pluginInterface.StorageUtils;
import io.supertokens.pluginInterface.authRecipe.AuthRecipeStorage;
import io.supertokens.pluginInterface.dashboard.sqlStorage.DashboardSQLStorage;
import io.supertokens.pluginInterface.exceptions.StorageQueryException;
Expand All @@ -32,6 +33,7 @@
import io.supertokens.pluginInterface.multitenancy.TenantIdentifier;
import io.supertokens.pluginInterface.multitenancy.ThirdPartyConfig;
import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException;
import io.supertokens.pluginInterface.oauth.OAuthStorage;
import io.supertokens.pluginInterface.session.sqlStorage.SessionSQLStorage;
import io.supertokens.storageLayer.StorageLayer;
import io.supertokens.utils.Utils;
Expand Down Expand Up @@ -338,6 +340,28 @@ private JsonObject getAccountLinkingStats() throws StorageQueryException, Tenant
return result;
}

private JsonObject getOAuthStats() throws StorageQueryException, TenantOrAppNotFoundException {
JsonObject result = new JsonObject();

OAuthStorage oAuthStorage = StorageUtils.getOAuthStorage(StorageLayer.getStorage(
this.appIdentifier.getAsPublicTenantIdentifier(), main));

result.addProperty("totalNumberOfClients", oAuthStorage.countTotalNumberOfClientsForApp(appIdentifier));
result.addProperty("numberOfClientCredentialsOnlyClients", oAuthStorage.countTotalNumberOfClientCredentialsOnlyClientsForApp(appIdentifier));
result.addProperty("numberOfM2MTokensAlive", oAuthStorage.countTotalNumberOfM2MTokensAlive(appIdentifier));

long now = System.currentTimeMillis();
JsonArray tokensCreatedArray = new JsonArray();
for (int i = 1; i <= 31; i++) {
long timestamp = now - (i * 24 * 60 * 60 * 1000L);
int numberOfTokensCreated = oAuthStorage.countTotalNumberOfM2MTokensCreatedSince(this.appIdentifier, timestamp);
tokensCreatedArray.add(new JsonPrimitive(numberOfTokensCreated));
}
result.add("numberOfM2MTokensCreated", tokensCreatedArray);

return result;
}

private JsonArray getMAUs() throws StorageQueryException, TenantOrAppNotFoundException {
JsonArray mauArr = new JsonArray();
long now = System.currentTimeMillis();
Expand Down Expand Up @@ -395,6 +419,10 @@ public JsonObject getPaidFeatureStats() throws StorageQueryException, TenantOrAp
if (feature == EE_FEATURES.SECURITY) {
usageStats.add(EE_FEATURES.SECURITY.toString(), new JsonObject());
}

if (feature == EE_FEATURES.OAUTH) {
usageStats.add(EE_FEATURES.OAUTH.toString(), getOAuthStats());
}
}

usageStats.add("maus", getMAUs());
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/io/supertokens/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import io.supertokens.config.Config;
import io.supertokens.config.CoreConfig;
import io.supertokens.cronjobs.Cronjobs;
import io.supertokens.cronjobs.cleanupOAuthRevokeList.CleanupOAuthRevokeList;
import io.supertokens.cronjobs.deleteExpiredAccessTokenSigningKeys.DeleteExpiredAccessTokenSigningKeys;
import io.supertokens.cronjobs.deleteExpiredDashboardSessions.DeleteExpiredDashboardSessions;
import io.supertokens.cronjobs.deleteExpiredEmailVerificationTokens.DeleteExpiredEmailVerificationTokens;
Expand Down Expand Up @@ -256,6 +257,8 @@ private void init() throws IOException, StorageQueryException {
// starts DeleteExpiredAccessTokenSigningKeys cronjob if the access token signing keys can change
Cronjobs.addCronjob(this, DeleteExpiredAccessTokenSigningKeys.init(this, uniqueUserPoolIdsTenants));

Cronjobs.addCronjob(this, CleanupOAuthRevokeList.init(this, uniqueUserPoolIdsTenants));

// this is to ensure tenantInfos are in sync for the new cron job as well
MultitenancyHelper.getInstance(this).refreshCronjobs();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package io.supertokens.cronjobs.cleanupOAuthRevokeList;

import java.util.List;

import io.supertokens.Main;
import io.supertokens.cronjobs.CronTask;
import io.supertokens.cronjobs.CronTaskTest;
import io.supertokens.pluginInterface.Storage;
import io.supertokens.pluginInterface.StorageUtils;
import io.supertokens.pluginInterface.multitenancy.AppIdentifier;
import io.supertokens.pluginInterface.multitenancy.TenantIdentifier;
import io.supertokens.pluginInterface.oauth.OAuthStorage;
import io.supertokens.storageLayer.StorageLayer;

public class CleanupOAuthRevokeList extends CronTask {

public static final String RESOURCE_KEY = "io.supertokens.cronjobs.cleanupOAuthRevokeList" +
".CleanupOAuthRevokeList";

private CleanupOAuthRevokeList(Main main, List<List<TenantIdentifier>> tenantsInfo) {
super("CleanupOAuthRevokeList", main, tenantsInfo, true);
}

public static CleanupOAuthRevokeList init(Main main, List<List<TenantIdentifier>> tenantsInfo) {
return (CleanupOAuthRevokeList) main.getResourceDistributor()
.setResource(new TenantIdentifier(null, null, null), RESOURCE_KEY,
new CleanupOAuthRevokeList(main, tenantsInfo));
}

@Override
protected void doTaskPerApp(AppIdentifier app) throws Exception {
Storage storage = StorageLayer.getStorage(app.getAsPublicTenantIdentifier(), main);
OAuthStorage oauthStorage = StorageUtils.getOAuthStorage(storage);
oauthStorage.cleanUpExpiredAndRevokedTokens(app);
}

@Override
public int getIntervalTimeSeconds() {
if (Main.isTesting) {
Integer interval = CronTaskTest.getInstance(main).getIntervalInSeconds(RESOURCE_KEY);
if (interval != null) {
return interval;
}
}
// Every 24 hours.
return 24 * 3600;
}

@Override
public int getInitialWaitTimeSeconds() {
if (!Main.isTesting) {
return getIntervalTimeSeconds();
} else {
return 0;
}
}
}
96 changes: 82 additions & 14 deletions src/main/java/io/supertokens/inmemorydb/Start.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@
import io.supertokens.pluginInterface.multitenancy.exceptions.DuplicateThirdPartyIdException;
import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException;
import io.supertokens.pluginInterface.multitenancy.sqlStorage.MultitenancySQLStorage;
import io.supertokens.pluginInterface.oauth.exceptions.OAuth2ClientAlreadyExistsForAppException;
import io.supertokens.pluginInterface.oauth.sqlStorage.OAuthSQLStorage;
import io.supertokens.pluginInterface.passwordless.PasswordlessCode;
import io.supertokens.pluginInterface.passwordless.PasswordlessDevice;
Expand Down Expand Up @@ -107,7 +106,6 @@ public class Start
ActiveUsersSQLStorage, DashboardSQLStorage, AuthRecipeSQLStorage, OAuthSQLStorage {

private static final Object appenderLock = new Object();
private static final String APP_ID_KEY_NAME = "app_id";
private static final String ACCESS_TOKEN_SIGNING_KEY_NAME = "access_token_signing_key";
private static final String REFRESH_TOKEN_KEY_NAME = "refresh_token_key";
public static boolean isTesting = false;
Expand Down Expand Up @@ -3011,7 +3009,7 @@ public int countUsersThatHaveMoreThanOneLoginMethodOrTOTPEnabledAndActiveSince(A
}

@Override
public boolean doesClientIdExistForThisApp(AppIdentifier appIdentifier, String clientId)
public boolean doesClientIdExistForApp(AppIdentifier appIdentifier, String clientId)
throws StorageQueryException {
try {
return OAuthQueries.isClientIdForAppId(this, clientId, appIdentifier);
Expand All @@ -3021,19 +3019,11 @@ public boolean doesClientIdExistForThisApp(AppIdentifier appIdentifier, String c
}

@Override
public void addClientForApp(AppIdentifier appIdentifier, String clientId)
throws StorageQueryException, OAuth2ClientAlreadyExistsForAppException {
public void addOrUpdateClientForApp(AppIdentifier appIdentifier, String clientId, boolean isClientCredentialsOnly)
throws StorageQueryException {
try {
OAuthQueries.insertClientIdForAppId(this, clientId, appIdentifier);
OAuthQueries.insertClientIdForAppId(this, appIdentifier, clientId, isClientCredentialsOnly);
} catch (SQLException e) {

SQLiteConfig config = Config.getConfig(this);
String serverErrorMessage = e.getMessage();

if (isPrimaryKeyError(serverErrorMessage, config.getOAuthClientTable(),
new String[]{"app_id", "client_id"})) {
throw new OAuth2ClientAlreadyExistsForAppException();
}
throw new StorageQueryException(e);
}
}
Expand All @@ -3055,4 +3045,82 @@ public List<String> listClientsForApp(AppIdentifier appIdentifier) throws Storag
throw new StorageQueryException(e);
}
}

@Override
public void revoke(AppIdentifier appIdentifier, String targetType, String targetValue, long exp)
throws StorageQueryException {
try {
OAuthQueries.revoke(this, appIdentifier, targetType, targetValue, exp);
} catch (SQLException e) {
throw new StorageQueryException(e);
}

}

@Override
public boolean isRevoked(AppIdentifier appIdentifier, String[] targetTypes, String[] targetValues, long issuedAt)
throws StorageQueryException {
try {
return OAuthQueries.isRevoked(this, appIdentifier, targetTypes, targetValues, issuedAt);
} catch (SQLException e) {
throw new StorageQueryException(e);
}
}

@Override
public void addM2MToken(AppIdentifier appIdentifier, String clientId, long iat, long exp)
throws StorageQueryException {
try {
OAuthQueries.addM2MToken(this, appIdentifier, clientId, iat, exp);
} catch (SQLException e) {
throw new StorageQueryException(e);
}
}

@Override
public void cleanUpExpiredAndRevokedTokens(AppIdentifier appIdentifier) throws StorageQueryException {
try {
OAuthQueries.cleanUpExpiredAndRevokedTokens(this, appIdentifier);
} catch (SQLException e) {
throw new StorageQueryException(e);
}
}

@Override
public int countTotalNumberOfClientCredentialsOnlyClientsForApp(AppIdentifier appIdentifier)
throws StorageQueryException {
try {
return OAuthQueries.countTotalNumberOfClientsForApp(this, appIdentifier, true);
} catch (SQLException e) {
throw new StorageQueryException(e);
}
}

@Override
public int countTotalNumberOfClientsForApp(AppIdentifier appIdentifier) throws StorageQueryException {
try {
return OAuthQueries.countTotalNumberOfClientsForApp(this, appIdentifier, false);
} catch (SQLException e) {
throw new StorageQueryException(e);
}
}

@Override
public int countTotalNumberOfM2MTokensAlive(AppIdentifier appIdentifier) throws StorageQueryException {
try {
return OAuthQueries.countTotalNumberOfM2MTokensAlive(this, appIdentifier);
} catch (SQLException e) {
throw new StorageQueryException(e);
}
}

@Override
public int countTotalNumberOfM2MTokensCreatedSince(AppIdentifier appIdentifier, long since)
throws StorageQueryException {
try {
return OAuthQueries.countTotalNumberOfM2MTokensCreatedSince(this, appIdentifier, since);
} catch (SQLException e) {
throw new StorageQueryException(e);
}
}
}
12 changes: 11 additions & 1 deletion src/main/java/io/supertokens/inmemorydb/config/SQLiteConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -165,5 +165,15 @@ public String getDashboardSessionsTable() {
return "dashboard_user_sessions";
}

public String getOAuthClientTable(){ return "oauth_clients"; }
public String getOAuthClientsTable() {
return "oauth_clients";
}

public String getOAuthRevokeTable() {
return "oauth_revoke";
}

public String getOAuthM2MTokensTable() {
return "oauth_m2m_tokens";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -423,10 +423,27 @@ public static void createTablesIfNotExists(Start start, Main main) throws SQLExc
update(start, TOTPQueries.getQueryToCreateUsedCodesExpiryTimeIndex(start), NO_OP_SETTER);
}

if (!doesTableExists(start, Config.getConfig(start).getOAuthClientTable())) {
if (!doesTableExists(start, Config.getConfig(start).getOAuthClientsTable())) {
getInstance(main).addState(CREATING_NEW_TABLE, null);
update(start, OAuthQueries.getQueryToCreateOAuthClientTable(start), NO_OP_SETTER);
}

if (!doesTableExists(start, Config.getConfig(start).getOAuthRevokeTable())) {
getInstance(main).addState(CREATING_NEW_TABLE, null);
update(start, OAuthQueries.getQueryToCreateOAuthRevokeTable(start), NO_OP_SETTER);

// index
update(start, OAuthQueries.getQueryToCreateOAuthRevokeTimestampIndex(start), NO_OP_SETTER);
}

if (!doesTableExists(start, Config.getConfig(start).getOAuthM2MTokensTable())) {
getInstance(main).addState(CREATING_NEW_TABLE, null);
update(start, OAuthQueries.getQueryToCreateOAuthM2MTokensTable(start), NO_OP_SETTER);

// index
update(start, OAuthQueries.getQueryToCreateOAuthM2MTokenIatIndex(start), NO_OP_SETTER);
update(start, OAuthQueries.getQueryToCreateOAuthM2MTokenExpIndex(start), NO_OP_SETTER);
}
}


Expand Down
Loading
Loading