Skip to content

Commit

Permalink
chore: merge master to feature branch
Browse files Browse the repository at this point in the history
  • Loading branch information
tamassoltesz committed Nov 5, 2024
2 parents 4e0e7a0 + f2482ea commit 519561e
Show file tree
Hide file tree
Showing 9 changed files with 833 additions and 3 deletions.
1 change: 1 addition & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ jobs:
test:
docker:
- image: rishabhpoddar/supertokens_core_testing
- image: rishabhpoddar/oauth-server-cicd
resource_class: large
steps:
- checkout
Expand Down
59 changes: 59 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,65 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- Adds queries for Bulk Import
- Adds support for multithreaded bulk import

## [7.2.0] - 2024-10-03

- Compatible with plugin interface version 6.3
- Adds support for OAuthStorage

### Migration

```sql
CREATE TABLE IF NOT EXISTS oauth_clients (
app_id VARCHAR(64),
client_id VARCHAR(255) NOT NULL,
is_client_credentials_only BOOLEAN NOT NULL,
PRIMARY KEY (app_id, client_id),
FOREIGN KEY(app_id) REFERENCES apps(app_id) ON DELETE CASCADE
);

CREATE TABLE IF NOT EXISTS oauth_sessions (
gid VARCHAR(255),
app_id VARCHAR(64) DEFAULT 'public',
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_session_exp_index ON oauth_sessions(exp DESC);
CREATE INDEX 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',
client_id VARCHAR(255) NOT NULL,
iat BIGINT UNSIGNED NOT NULL,
exp BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (app_id, client_id, iat),
FOREIGN KEY(app_id, client_id) REFERENCES oauth_clients(app_id, client_id) ON DELETE CASCADE
);

CREATE INDEX oauth_m2m_token_iat_index ON oauth_m2m_tokens(iat DESC, app_id DESC);
CREATE INDEX oauth_m2m_token_exp_index ON oauth_m2m_tokens(exp DESC);

CREATE TABLE IF NOT EXISTS oauth_logout_challenges (
app_id VARCHAR(64) DEFAULT 'public',
challenge VARCHAR(128) NOT NULL,
client_id VARCHAR(255) NOT NULL,
post_logout_redirect_uri VARCHAR(1024),
session_handle VARCHAR(128),
state VARCHAR(128),
time_created BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (app_id, challenge),
FOREIGN KEY(app_id, client_id) REFERENCES oauth_clients(app_id, client_id) ON DELETE CASCADE
);

CREATE INDEX oauth_logout_challenges_time_created_index ON oauth_logout_challenges(time_created ASC, app_id ASC);
```

## [7.1.3] - 2024-09-04

- Adds index on `last_active_time` for `user_last_active` table to improve the performance of MAU computation.
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ plugins {
id 'java-library'
}

version = "7.1.3"
version = "7.2.0"

repositories {
mavenCentral()
Expand Down
Binary file not shown.
2 changes: 1 addition & 1 deletion pluginInterfaceSupported.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"_comment": "contains a list of plugin interfaces branch names that this core supports",
"versions": [
"6.2"
"6.3"
]
}
273 changes: 272 additions & 1 deletion src/main/java/io/supertokens/storage/mysql/Start.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@
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.OAuthStorage;
import io.supertokens.pluginInterface.oauth.exception.DuplicateOAuthLogoutChallengeException;
import io.supertokens.pluginInterface.oauth.exception.OAuthClientNotFoundException;
import io.supertokens.pluginInterface.passwordless.PasswordlessCode;
import io.supertokens.pluginInterface.passwordless.PasswordlessDevice;
import io.supertokens.pluginInterface.passwordless.exception.*;
Expand Down Expand Up @@ -115,7 +120,7 @@ public class Start
implements SessionSQLStorage, EmailPasswordSQLStorage, EmailVerificationSQLStorage, ThirdPartySQLStorage,
JWTRecipeSQLStorage, PasswordlessSQLStorage, UserMetadataSQLStorage, UserRolesSQLStorage, UserIdMappingStorage,
UserIdMappingSQLStorage, MultitenancyStorage, MultitenancySQLStorage, DashboardSQLStorage, TOTPSQLStorage,
ActiveUsersStorage, ActiveUsersSQLStorage, AuthRecipeSQLStorage, BulkImportSQLStorage {
ActiveUsersStorage, ActiveUsersSQLStorage, AuthRecipeSQLStorage, OAuthStorage, BulkImportSQLStorage {

// these configs are protected from being modified / viewed by the dev using the SuperTokens
// SaaS. If the core is not running in SuperTokens SaaS, this array has no effect.
Expand Down Expand Up @@ -871,6 +876,8 @@ public void addInfoToNonAuthRecipesBasedOnUserId(TenantIdentifier tenantIdentifi
}
} else if (className.equals(JWTRecipeStorage.class.getName())) {
/* Since JWT recipe tables do not store userId we do not add any data to them */
} else if (className.equals(OAuthStorage.class.getName())) {
/* Since OAuth recipe tables do not store userId we do not add any data to them */
} else if (className.equals(ActiveUsersStorage.class.getName())) {
try {
ActiveUsersQueries.updateUserLastActive(this, tenantIdentifier.toAppIdentifier(), userId);
Expand Down Expand Up @@ -3068,6 +3075,270 @@ public int countUsersThatHaveMoreThanOneLoginMethodOrTOTPEnabledAndActiveSince(A
}
}

@Override
public OAuthClient getOAuthClientById(AppIdentifier appIdentifier, String clientId)
throws StorageQueryException, OAuthClientNotFoundException {
try {
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, String clientSecret, boolean isClientCredentialsOnly, boolean enableRefreshTokenRotation)
throws StorageQueryException, TenantOrAppNotFoundException {
try {
OAuthQueries.addOrUpdateOauthClient(this, appIdentifier, clientId, clientSecret, isClientCredentialsOnly, enableRefreshTokenRotation);
} catch (SQLException e) {
if (e instanceof SQLIntegrityConstraintViolationException) {
String errorMessage = e.getMessage();
MySQLConfig config = Config.getConfig(this);

if (isForeignKeyConstraintError(errorMessage, config.getOAuthClientsTable(), "app_id")) {
throw new TenantOrAppNotFoundException(appIdentifier);
}
}
throw new StorageQueryException(e);
}
}

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

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

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

@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 revokeOAuthTokenBySessionHandle(AppIdentifier appIdentifier, String sessionHandle)
throws StorageQueryException {
try {
return OAuthQueries.deleteOAuthSessionBySessionHandle(this, appIdentifier, sessionHandle);
} catch (SQLException e) {
throw new StorageQueryException(e);
}
}

@Override
public void addOAuthM2MTokenForStats(AppIdentifier appIdentifier, String clientId, long iat, long exp)
throws StorageQueryException, OAuthClientNotFoundException {
try {
OAuthQueries.addOAuthM2MTokenForStats(this, appIdentifier, clientId, iat, exp);
} catch (SQLException e) {
if (e instanceof SQLIntegrityConstraintViolationException) {
String errorMessage = e.getMessage();
MySQLConfig config = Config.getConfig(this);

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

@Override
public void deleteExpiredOAuthM2MTokens(long exp) throws StorageQueryException {
try {
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)
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);


if (isPrimaryKeyError(errorMessage, config.getOAuthLogoutChallengesTable(), "challenge")) {
throw new DuplicateOAuthLogoutChallengeException();
} else if (isForeignKeyConstraintError(errorMessage, config.getOAuthLogoutChallengesTable(),
"client_id")) {
throw new OAuthClientNotFoundException();
}
}
throw new StorageQueryException(e);
}
}

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

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

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

@Override
public void createOrUpdateOAuthSession(AppIdentifier appIdentifier, String gid, String clientId,
String externalRefreshToken, String internalRefreshToken,
String sessionHandle, String jti, long exp)
throws StorageQueryException, OAuthClientNotFoundException {
try {
OAuthQueries.createOrUpdateOAuthSession(this, appIdentifier, gid, clientId, externalRefreshToken,
internalRefreshToken, sessionHandle, jti, 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 {
return OAuthQueries.countTotalNumberOfClients(this, appIdentifier, false);
} catch (SQLException e) {
throw new StorageQueryException(e);
}
}

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

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

@Override
public int countTotalNumberOfOAuthM2MTokensAlive(AppIdentifier appIdentifier) throws StorageQueryException {
try {
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() {
return enableForDeadlockTesting;
}
Expand Down
Loading

0 comments on commit 519561e

Please sign in to comment.