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 #131

Merged
merged 1 commit 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
21 changes: 12 additions & 9 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,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 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
140 changes: 108 additions & 32 deletions src/main/java/io/supertokens/storage/mysql/Start.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@
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 @@ -3042,26 +3042,30 @@ public int countUsersThatHaveMoreThanOneLoginMethodOrTOTPEnabledAndActiveSince(A
}

@Override
public boolean doesOAuthClientIdExist(AppIdentifier appIdentifier, String clientId)
throws StorageQueryException {
public OAuthClient getOAuthClientById(AppIdentifier appIdentifier, String clientId)
throws StorageQueryException, OAuthClientNotFoundException {
try {
return OAuthQueries.doesOAuthClientIdExist(this, clientId, appIdentifier);
OAuthClient client = OAuthQueries.getOAuthClientById(this, clientId, appIdentifier);
if (client == null) {
throw new OAuthClientNotFoundException();
}
return client;
} catch (SQLException e) {
throw new StorageQueryException(e);
}
}

@Override
public void addOrUpdateOauthClient(AppIdentifier appIdentifier, String clientId, boolean isClientCredentialsOnly)
public void addOrUpdateOauthClient(AppIdentifier appIdentifier, String clientId, String clientSecret, boolean isClientCredentialsOnly, boolean enableRefreshTokenRotation)
throws StorageQueryException, TenantOrAppNotFoundException {
try {
OAuthQueries.addOrUpdateOauthClient(this, appIdentifier, clientId, isClientCredentialsOnly);
OAuthQueries.addOrUpdateOauthClient(this, appIdentifier, clientId, clientSecret, isClientCredentialsOnly, enableRefreshTokenRotation);
} catch (SQLException e) {
if (e instanceof SQLIntegrityConstraintViolationException) {
String errorMessage = e.getMessage();
MySQLConfig config = Config.getConfig(this);
String serverMessage = e.getMessage();

if (isForeignKeyConstraintError(serverMessage, config.getAppsTable(), "app_id")) {
if (isForeignKeyConstraintError(errorMessage, config.getOAuthClientsTable(), "app_id")) {
throw new TenantOrAppNotFoundException(appIdentifier);
}
}
Expand All @@ -3079,38 +3083,48 @@ public boolean deleteOAuthClient(AppIdentifier appIdentifier, String clientId) t
}

@Override
public List<String> listOAuthClients(AppIdentifier appIdentifier) throws StorageQueryException {
public List<OAuthClient> getOAuthClients(AppIdentifier appIdentifier, List<String> clientIds) throws StorageQueryException {
try {
return OAuthQueries.listOAuthClients(this, appIdentifier);
return OAuthQueries.getOAuthClients(this, appIdentifier, clientIds);
} catch (SQLException e) {
throw new StorageQueryException(e);
}
}

@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 SQLIntegrityConstraintViolationException) {
MySQLConfig config = Config.getConfig(this);
String serverMessage = e.getMessage();
throw new StorageQueryException(e);
}
}

if (isForeignKeyConstraintError(serverMessage, config.getAppsTable(), "app_id")) {
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 revokeOAuthTokenByJTI(AppIdentifier appIdentifier, String gid, String jti)
throws StorageQueryException {
try {
return OAuthQueries.deleteJTIFromOAuthSession(this, appIdentifier, gid, jti);
} catch (SQLException e) {
throw new StorageQueryException(e);
}

}

@Override
public boolean isOAuthTokenRevokedBasedOnTargetFields(AppIdentifier appIdentifier, OAuthRevokeTargetType[] targetTypes, String[] targetValues, long issuedAt)
public boolean revokeOAuthTokenBySessionHandle(AppIdentifier appIdentifier, String sessionHandle)
throws StorageQueryException {
try {
return OAuthQueries.isOAuthTokenRevokedBasedOnTargetFields(this, appIdentifier, targetTypes, targetValues, issuedAt);
return OAuthQueries.deleteOAuthSessionBySessionHandle(this, appIdentifier, sessionHandle);
} catch (SQLException e) {
throw new StorageQueryException(e);
}
Expand All @@ -3123,10 +3137,10 @@ public void addOAuthM2MTokenForStats(AppIdentifier appIdentifier, String clientI
OAuthQueries.addOAuthM2MTokenForStats(this, appIdentifier, clientId, iat, exp);
} catch (SQLException e) {
if (e instanceof SQLIntegrityConstraintViolationException) {
String errorMessage = e.getMessage();
MySQLConfig config = Config.getConfig(this);
String serverMessage = e.getMessage();

if (isForeignKeyConstraintError(serverMessage, config.getOAuthClientsTable(), "client_id")) {
if (isForeignKeyConstraintError(errorMessage, config.getOAuthM2MTokensTable(), "client_id")) {
throw new OAuthClientNotFoundException();
}
}
Expand All @@ -3135,28 +3149,30 @@ public void addOAuthM2MTokenForStats(AppIdentifier appIdentifier, String clientI
}

@Override
public void cleanUpExpiredAndRevokedOAuthTokensList() throws StorageQueryException {
public void deleteExpiredOAuthM2MTokens(long exp) throws StorageQueryException {
try {
OAuthQueries.cleanUpExpiredAndRevokedOAuthTokensList(this);
OAuthQueries.deleteExpiredOAuthM2MTokens(this, exp);
} catch (SQLException e) {
throw new StorageQueryException(e);
}
}

@Override
public void addOAuthLogoutChallenge(AppIdentifier appIdentifier, String challenge, String clientId,
String postLogoutRedirectionUri, String sessionHandle, String state, long timeCreated)
String postLogoutRedirectionUri, String sessionHandle, String state, long timeCreated)
throws StorageQueryException, DuplicateOAuthLogoutChallengeException, OAuthClientNotFoundException {
try {
OAuthQueries.addOAuthLogoutChallenge(this, appIdentifier, challenge, clientId, postLogoutRedirectionUri, sessionHandle, state, timeCreated);
} catch (SQLException e) {
if (e instanceof SQLIntegrityConstraintViolationException) {
String errorMessage = e.getMessage();
MySQLConfig config = Config.getConfig(this);
String serverMessage = e.getMessage();
if (isPrimaryKeyError(serverMessage, config.getOAuthLogoutChallengesTable())) {


if (isPrimaryKeyError(errorMessage, config.getOAuthLogoutChallengesTable(), "challenge")) {
throw new DuplicateOAuthLogoutChallengeException();
}
else if (isForeignKeyConstraintError(serverMessage, config.getOAuthClientsTable(), "client_id")) {
} else if (isForeignKeyConstraintError(errorMessage, config.getOAuthLogoutChallengesTable(),
"client_id")) {
throw new OAuthClientNotFoundException();
}
}
Expand Down Expand Up @@ -3191,6 +3207,47 @@ public void deleteOAuthLogoutChallengesBefore(long time) throws StorageQueryExce
}
}

@Override
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.createOrUpdateOAuthSession(this, appIdentifier, gid, clientId, externalRefreshToken,
internalRefreshToken, sessionHandle, jtis, exp);
} catch (SQLException e) {
if (e instanceof SQLIntegrityConstraintViolationException) {
String errorMessage = e.getMessage();
MySQLConfig config = Config.getConfig(this);

if (isForeignKeyConstraintError(errorMessage, config.getOAuthSessionsTable(),
"client_id")) {
throw new OAuthClientNotFoundException();
}
}
throw new StorageQueryException(e);
}
}

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

@Override
public void deleteExpiredOAuthSessions(long exp) throws StorageQueryException {
try {
OAuthQueries.deleteExpiredOAuthSessions(this, exp);
} catch (SQLException e) {
throw new StorageQueryException(e);
}
}

@Override
public int countTotalNumberOfOAuthClients(AppIdentifier appIdentifier) throws StorageQueryException {
try {
Expand Down Expand Up @@ -3226,7 +3283,26 @@ public int countTotalNumberOfOAuthM2MTokensAlive(AppIdentifier appIdentifier) th
return OAuthQueries.countTotalNumberOfOAuthM2MTokensAlive(this, appIdentifier);
} catch (SQLException e) {
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);
}
}

@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);
}
}

public static boolean isEnabledForDeadlockTesting() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -377,14 +377,14 @@ public String getOAuthClientsTable() {
return addPrefixToTableName("oauth_clients");
}

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

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

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

public String getOAuthLogoutChallengesTable() {
return addPrefixToTableName("oauth_logout_challenges");
}
Expand Down Expand Up @@ -714,4 +714,5 @@ public String getConnectionPoolId() {
public String getTablePrefix() {
return mysql_table_names_prefix;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -422,13 +422,13 @@ public static void createTablesIfNotExists(Start start, Connection con) throws S
update(con, OAuthQueries.getQueryToCreateOAuthClientTable(start), NO_OP_SETTER);
}

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

// index
update(con, OAuthQueries.getQueryToCreateOAuthRevokeTimestampIndex(start), NO_OP_SETTER);
update(con, OAuthQueries.getQueryToCreateOAuthRevokeExpIndex(start), NO_OP_SETTER);
update(con, OAuthQueries.getQueryToCreateOAuthSessionsExpIndex(start), NO_OP_SETTER);
update(con, OAuthQueries.getQueryToCreateOAuthSessionsExternalRefreshTokenIndex(start), NO_OP_SETTER);
}

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