Skip to content

Commit

Permalink
Merge pull request #1065 from supertokens/feat/oauth/remaining-changes
Browse files Browse the repository at this point in the history
feat(oauth): allow setting, store&return clientId/clientSecret for clients and add enableRefreshTokenRotation
  • Loading branch information
porcellus authored Oct 27, 2024
2 parents e1f462c + 2abfb68 commit be09a1b
Show file tree
Hide file tree
Showing 45 changed files with 4,279 additions and 476 deletions.
53 changes: 30 additions & 23 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,28 +47,31 @@ If using PostgreSQL, run the following SQL script:
```sql
CREATE TABLE IF NOT EXISTS oauth_clients (
app_id VARCHAR(64),
client_id VARCHAR(128) NOT NULL,
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_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',
client_id VARCHAR(128) NOT NULL,
client_id VARCHAR(255) NOT NULL,
iat BIGINT NOT NULL,
exp BIGINT NOT NULL,
PRIMARY KEY (app_id, client_id, iat),
Expand All @@ -81,7 +84,7 @@ CREATE INDEX IF NOT EXISTS oauth_m2m_token_exp_index ON oauth_m2m_tokens(exp DES
CREATE TABLE IF NOT EXISTS oauth_logout_challenges (
app_id VARCHAR(64) DEFAULT 'public',
challenge VARCHAR(128) NOT NULL,
client_id VARCHAR(128) NOT NULL,
client_id VARCHAR(255) NOT NULL,
post_logout_redirect_uri VARCHAR(1024),
session_handle VARCHAR(128),
state VARCHAR(128),
Expand All @@ -98,28 +101,32 @@ If using MySQL, run the following SQL script:
```sql
CREATE TABLE IF NOT EXISTS oauth_clients (
app_id VARCHAR(64),
client_id VARCHAR(128) NOT NULL,
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_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',
client_id VARCHAR(128) NOT NULL,
client_id VARCHAR(255) NOT NULL,
iat BIGINT UNSIGNED NOT NULL,
exp BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (app_id, client_id, iat),
Expand All @@ -132,7 +139,7 @@ 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(128) NOT NULL,
client_id VARCHAR(255) NOT NULL,
post_logout_redirect_uri VARCHAR(1024),
session_handle VARCHAR(128),
state VARCHAR(128),
Expand Down
3 changes: 3 additions & 0 deletions config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,6 @@ core_config_version: 0
# (OPTIONAL | Default: oauth_provider_public_service_url) If specified, the core uses this URL to parse responses from
# the oauth provider when the oauth provider's internal address differs from the known public provider address.
# oauth_provider_url_configured_in_oauth_provider:

# (Optional | Default: null) string value. The encryption key used for saving OAuth client secret on the database.
# oauth_client_secret_encryption_key:
3 changes: 3 additions & 0 deletions devConfig.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,6 @@ disable_telemetry: true
# (OPTIONAL | Default: oauth_provider_public_service_url) If specified, the core uses this URL to parse responses from
# the oauth provider when the oauth provider's internal address differs from the known public provider address.
# oauth_provider_url_configured_in_oauth_provider:

# (Optional | Default: null) string value. The encryption key used for saving OAuth client secret on the database.
# oauth_client_secret_encryption_key:
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
13 changes: 13 additions & 0 deletions src/main/java/io/supertokens/config/CoreConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,12 @@ public class CoreConfig {
"If specified, the core uses this URL to parse responses from the oauth provider when the oauth provider's internal address differs from the known public provider address.")
private String oauth_provider_url_configured_in_oauth_provider = null;

@ConfigYamlOnly
@JsonProperty
@HideFromDashboard
@ConfigDescription("The encryption key used for saving OAuth client secret on the database.")
private String oauth_client_secret_encryption_key = null;

@ConfigYamlOnly
@JsonProperty
@ConfigDescription(
Expand Down Expand Up @@ -391,6 +397,13 @@ public String getOAuthProviderUrlConfiguredInOAuthProvider() throws InvalidConfi
return oauth_provider_url_configured_in_oauth_provider;
}

public String getOAuthClientSecretEncryptionKey() throws InvalidConfigException {
if(oauth_client_secret_encryption_key == null) {
throw new InvalidConfigException("oauth_client_secret_encryption_key is not set");
}
return oauth_client_secret_encryption_key;
}

public String getIpAllowRegex() {
return ip_allow_regex;
}
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 @@ -33,8 +33,11 @@ protected void doTaskPerStorage(Storage storage) throws Exception {
}

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

oauthStorage.deleteOAuthLogoutChallengesBefore(System.currentTimeMillis() - 1000 * 60 * 60 * 48); // 48 hours
}

@Override
Expand Down
Loading

0 comments on commit be09a1b

Please sign in to comment.