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

feat: oauth/allow list #1067

Merged
merged 13 commits into from
Oct 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
41 changes: 24 additions & 17 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,21 @@ CREATE TABLE IF NOT EXISTS oauth_clients (
FOREIGN KEY(app_id) REFERENCES apps(app_id) ON DELETE CASCADE
);

CREATE TABLE IF NOT EXISTS oauth_revoke (
CREATE TABLE IF NOT EXISTS oauth_sessions (
gid VARCHAR(255),
app_id VARCHAR(64) DEFAULT 'public',
target_type VARCHAR(16) NOT NULL,
target_value VARCHAR(128) NOT NULL,
timestamp BIGINT NOT NULL,
client_id VARCHAR(255) NOT NULL,
session_handle VARCHAR(128),
external_refresh_token VARCHAR(255) UNIQUE,
internal_refresh_token VARCHAR(255) UNIQUE,
jti TEXT NOT NULL,
exp BIGINT NOT NULL,
PRIMARY KEY (app_id, target_type, target_value),
FOREIGN KEY(app_id) REFERENCES apps(app_id) ON DELETE CASCADE
PRIMARY KEY (gid),
FOREIGN KEY(app_id, client_id) REFERENCES oauth_clients(app_id, client_id) ON DELETE CASCADE
);

CREATE INDEX IF NOT EXISTS oauth_revoke_timestamp_index ON oauth_revoke(timestamp DESC, app_id DESC);
CREATE INDEX IF NOT EXISTS oauth_revoke_exp_index ON oauth_revoke(exp DESC);
CREATE INDEX IF NOT EXISTS oauth_session_exp_index ON oauth_sessions(exp DESC);
CREATE INDEX IF NOT EXISTS oauth_session_external_refresh_token_index ON oauth_sessions(app_id, external_refresh_token DESC);

CREATE TABLE IF NOT EXISTS oauth_m2m_tokens (
app_id VARCHAR(64) DEFAULT 'public',
Expand Down Expand Up @@ -104,18 +107,22 @@ CREATE TABLE IF NOT EXISTS oauth_clients (
FOREIGN KEY(app_id) REFERENCES apps(app_id) ON DELETE CASCADE
);

CREATE TABLE IF NOT EXISTS oauth_revoke (

CREATE TABLE IF NOT EXISTS oauth_sessions (
gid VARCHAR(255),
app_id VARCHAR(64) DEFAULT 'public',
target_type VARCHAR(16) NOT NULL,
target_value VARCHAR(128) NOT NULL,
timestamp BIGINT UNSIGNED NOT NULL,
exp BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (app_id, target_type, target_value),
FOREIGN KEY(app_id) REFERENCES apps(app_id) ON DELETE CASCADE
client_id VARCHAR(255) NOT NULL,
session_handle VARCHAR(128),
external_refresh_token VARCHAR(255) UNIQUE,
internal_refresh_token VARCHAR(255) UNIQUE,
jti TEXT NOT NULL,
exp BIGINT NOT NULL,
PRIMARY KEY (gid),
FOREIGN KEY(app_id, client_id) REFERENCES oauth_clients(app_id, client_id) ON DELETE CASCADE
);

CREATE INDEX oauth_revoke_timestamp_index ON oauth_revoke(timestamp DESC, app_id DESC);
CREATE INDEX oauth_revoke_exp_index ON oauth_revoke(exp DESC);
CREATE INDEX IF NOT EXISTS oauth_session_exp_index ON oauth_sessions(exp DESC);
CREATE INDEX IF NOT EXISTS oauth_session_external_refresh_token_index ON oauth_sessions(app_id, external_refresh_token DESC);

CREATE TABLE oauth_m2m_tokens (
app_id VARCHAR(64) DEFAULT 'public',
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/io/supertokens/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import io.supertokens.config.Config;
import io.supertokens.config.CoreConfig;
import io.supertokens.cronjobs.Cronjobs;
import io.supertokens.cronjobs.cleanupOAuthRevokeListAndChallenges.CleanupOAuthRevokeListAndChallenges;
import io.supertokens.cronjobs.cleanupOAuthRevokeListAndChallenges.CleanupOAuthSessionsAndChallenges;
import io.supertokens.cronjobs.deleteExpiredAccessTokenSigningKeys.DeleteExpiredAccessTokenSigningKeys;
import io.supertokens.cronjobs.deleteExpiredDashboardSessions.DeleteExpiredDashboardSessions;
import io.supertokens.cronjobs.deleteExpiredEmailVerificationTokens.DeleteExpiredEmailVerificationTokens;
Expand Down Expand Up @@ -257,7 +257,7 @@ 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, CleanupOAuthRevokeListAndChallenges.init(this, uniqueUserPoolIdsTenants));
Cronjobs.addCronjob(this, CleanupOAuthSessionsAndChallenges.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
@@ -1,7 +1,5 @@
package io.supertokens.cronjobs.cleanupOAuthRevokeListAndChallenges;

import java.util.List;

import io.supertokens.Main;
import io.supertokens.cronjobs.CronTask;
import io.supertokens.cronjobs.CronTaskTest;
Expand All @@ -11,19 +9,21 @@
import io.supertokens.pluginInterface.multitenancy.TenantIdentifier;
import io.supertokens.pluginInterface.oauth.OAuthStorage;

public class CleanupOAuthRevokeListAndChallenges extends CronTask {
import java.util.List;

public class CleanupOAuthSessionsAndChallenges extends CronTask {

public static final String RESOURCE_KEY = "io.supertokens.cronjobs.cleanupOAuthRevokeListAndChallenges" +
".CleanupOAuthRevokeListAndChallenges";

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

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

@Override
Expand All @@ -34,7 +34,7 @@ protected void doTaskPerStorage(Storage storage) throws Exception {

OAuthStorage oauthStorage = StorageUtils.getOAuthStorage(storage);
long monthAgo = System.currentTimeMillis() / 1000 - 31 * 24 * 3600;
oauthStorage.deleteExpiredRevokedOAuthTokens(monthAgo);
oauthStorage.deleteExpiredOAuthSessions(monthAgo);
oauthStorage.deleteExpiredOAuthM2MTokens(monthAgo);

oauthStorage.deleteOAuthLogoutChallengesBefore(System.currentTimeMillis() - 1000 * 60 * 60 * 48); // 48 hours
Expand Down
97 changes: 60 additions & 37 deletions src/main/java/io/supertokens/inmemorydb/Start.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,17 @@
import io.supertokens.pluginInterface.jwt.JWTSigningKeyInfo;
import io.supertokens.pluginInterface.jwt.exceptions.DuplicateKeyIdException;
import io.supertokens.pluginInterface.jwt.sqlstorage.JWTRecipeSQLStorage;
import io.supertokens.pluginInterface.multitenancy.*;
import io.supertokens.pluginInterface.multitenancy.AppIdentifier;
import io.supertokens.pluginInterface.multitenancy.MultitenancyStorage;
import io.supertokens.pluginInterface.multitenancy.TenantConfig;
import io.supertokens.pluginInterface.multitenancy.TenantIdentifier;
import io.supertokens.pluginInterface.multitenancy.exceptions.DuplicateClientTypeException;
import io.supertokens.pluginInterface.multitenancy.exceptions.DuplicateTenantException;
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.OAuthClient;
import io.supertokens.pluginInterface.oauth.OAuthLogoutChallenge;
import io.supertokens.pluginInterface.oauth.OAuthRevokeTargetType;
import io.supertokens.pluginInterface.oauth.OAuthStorage;
import io.supertokens.pluginInterface.oauth.exception.DuplicateOAuthLogoutChallengeException;
import io.supertokens.pluginInterface.oauth.exception.OAuthClientNotFoundException;
Expand Down Expand Up @@ -3070,42 +3072,39 @@ public List<OAuthClient> getOAuthClients(AppIdentifier appIdentifier, List<Strin
}

@Override
public void revokeOAuthTokensBasedOnTargetFields(AppIdentifier appIdentifier, OAuthRevokeTargetType targetType, String targetValue, long exp)
throws StorageQueryException, TenantOrAppNotFoundException {
public boolean revokeOAuthTokenByGID(AppIdentifier appIdentifier, String gid) throws StorageQueryException {
try {
OAuthQueries.revokeOAuthTokensBasedOnTargetFields(this, appIdentifier, targetType, targetValue, exp);
return OAuthQueries.deleteOAuthSessionByGID(this, appIdentifier, gid);
} catch (SQLException e) {
if (e instanceof SQLiteException) {
String errorMessage = e.getMessage();
SQLiteConfig config = Config.getConfig(this);
throw new StorageQueryException(e);
}
}
porcellus marked this conversation as resolved.
Show resolved Hide resolved

if (isForeignKeyConstraintError(
errorMessage,
config.getOAuthRevokeTable(),
new String[]{"app_id"},
new Object[]{appIdentifier.getAppId()})) {
throw new TenantOrAppNotFoundException(appIdentifier);
}
}
@Override
public boolean revokeOAuthTokenByClientId(AppIdentifier appIdentifier, String clientId)
throws StorageQueryException {
try {
return OAuthQueries.deleteOAuthSessionByClientId(this, appIdentifier, clientId);
} catch (SQLException e) {
throw new StorageQueryException(e);
}

}

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

@Override
public void deleteExpiredRevokedOAuthTokens(long exp) throws StorageQueryException {
public boolean revokeOAuthTokenBySessionHandle(AppIdentifier appIdentifier, String sessionHandle)
throws StorageQueryException {
try {
OAuthQueries.deleteExpiredRevokedOAuthTokens(this, exp);
return OAuthQueries.deleteOAuthSessionBySessionHandle(this, appIdentifier, sessionHandle);
} catch (SQLException e) {
throw new StorageQueryException(e);
}
Expand Down Expand Up @@ -3194,39 +3193,44 @@ public void deleteOAuthLogoutChallengesBefore(long time) throws StorageQueryExce
}

@Override
public void createOrUpdateRefreshTokenMapping(AppIdentifier appIdentifier, String superTokensRefreshToken,
String oauthProviderRefreshToken, long exp) throws StorageQueryException {
public void createOrUpdateOAuthSession(AppIdentifier appIdentifier, String gid, String clientId,
String externalRefreshToken, String internalRefreshToken,
String sessionHandle, List<String> jtis, long exp)
throws StorageQueryException, OAuthClientNotFoundException {
try {
OAuthQueries.createOrUpdateRefreshTokenMapping(this, appIdentifier, superTokensRefreshToken, oauthProviderRefreshToken, exp);
OAuthQueries.createOrUpdateOAuthSession(this, appIdentifier, gid, clientId, externalRefreshToken,
internalRefreshToken, sessionHandle, jtis, exp);
} catch (SQLException e) {
throw new StorageQueryException(e);
}
}
if (e instanceof SQLiteException) {
String errorMessage = e.getMessage();
SQLiteConfig config = Config.getConfig(this);

@Override
public String getRefreshTokenMapping(AppIdentifier appIdentifier, String superTokensRefreshToken)
throws StorageQueryException {
try {
return OAuthQueries.getRefreshTokenMapping(this, appIdentifier, superTokensRefreshToken);
} catch (SQLException e) {
if (isForeignKeyConstraintError(
errorMessage,
config.getOAuthClientsTable(),
new String[]{"app_id", "client_id"},
new Object[]{appIdentifier.getAppId(), clientId})) {
throw new OAuthClientNotFoundException();
}
}
throw new StorageQueryException(e);
}
}

@Override
public void deleteRefreshTokenMapping(AppIdentifier appIdentifier, String superTokensRefreshToken)
public String getRefreshTokenMapping(AppIdentifier appIdentifier, String externalRefreshToken)
throws StorageQueryException {
try {
OAuthQueries.deleteRefreshTokenMapping(this, appIdentifier, superTokensRefreshToken);
return OAuthQueries.getRefreshTokenMapping(this, appIdentifier, externalRefreshToken);
} catch (SQLException e) {
throw new StorageQueryException(e);
}
}

@Override
public void deleteExpiredRefreshTokenMappings(long exp) throws StorageQueryException {
public void deleteExpiredOAuthSessions(long exp) throws StorageQueryException {
try {
OAuthQueries.deleteExpiredRefreshTokenMappings(this, exp);
OAuthQueries.deleteExpiredOAuthSessions(this, exp);
} catch (SQLException e) {
throw new StorageQueryException(e);
}
Expand Down Expand Up @@ -3269,4 +3273,23 @@ public int countTotalNumberOfOAuthM2MTokensAlive(AppIdentifier appIdentifier) th
throw new StorageQueryException(e);
}
}

@Override
public boolean isOAuthTokenRevokedByGID(AppIdentifier appIdentifier, String gid) throws StorageQueryException {
try {
return !OAuthQueries.isOAuthSessionExistsByGID(this, appIdentifier, gid);
} catch (SQLException e) {
throw new StorageQueryException(e);
}
}
porcellus marked this conversation as resolved.
Show resolved Hide resolved

@Override
public boolean isOAuthTokenRevokedByJTI(AppIdentifier appIdentifier, String gid, String jti)
throws StorageQueryException {
try {
return !OAuthQueries.isOAuthSessionExistsByJTI(this, appIdentifier, gid, jti);
} catch (SQLException e) {
throw new StorageQueryException(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -173,14 +173,14 @@ public String getOAuthRefreshTokenMappingTable() {
return "oauth_refresh_token_mapping";
}

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

public String getOAuthM2MTokensTable() {
return "oauth_m2m_tokens";
}

public String getOAuthSessionsTable() {
return "oauth_sessions";
}

public String getOAuthLogoutChallengesTable() {
return "oauth_logout_challenges";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -432,21 +432,13 @@ public static void createTablesIfNotExists(Start start, Main main) throws SQLExc
update(start, OAuthQueries.getQueryToCreateOAuthClientTable(start), NO_OP_SETTER);
}

if (!doesTableExists(start, Config.getConfig(start).getOAuthRefreshTokenMappingTable())) {
if (!doesTableExists(start, Config.getConfig(start).getOAuthSessionsTable())) {
getInstance(main).addState(CREATING_NEW_TABLE, null);
update(start, OAuthQueries.getQueryToCreateOAuthRefreshTokenMappingTable(start), NO_OP_SETTER);
update(start, OAuthQueries.getQueryToCreateOAuthSessionsTable(start), NO_OP_SETTER);

// index
update(start, OAuthQueries.getQueryToCreateOAuthRefreshTokenMappingExpIndex(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);
update(start, OAuthQueries.getQueryToCreateOAuthRevokeExpIndex(start), NO_OP_SETTER);
update(start, OAuthQueries.getQueryToCreateOAuthSessionsExpIndex(start), NO_OP_SETTER);
update(start, OAuthQueries.getQueryToCreateOAuthSessionsExternalRefreshTokenIndex(start), NO_OP_SETTER);
}

if (!doesTableExists(start, Config.getConfig(start).getOAuthM2MTokensTable())) {
Expand Down
Loading
Loading