Skip to content

Commit

Permalink
feat: hydra integration for auth, token and few more endpoints (#1032)
Browse files Browse the repository at this point in the history
* fix: auth and token api

* fix: cookie and code transformations

* fix: token re-signing

* fix: token endpoint

* fix: license check and jwks caching

* fix: exceptions

* fix: refactor

* fix: refactor

* fix: refactor and client crud APIs

* fix: token type enum

* fix: process ext only on access token

* fix: refactor

* fix: bugs and refactor

* fix: oauth clients list api

* fix: consent get accept and reject

* fix: login request

* fix: query param transformation

* fix: logout request

* fix: refactor

* fix: remove error debug and hint

* fix: introspect api

* fix: pr comments

* fix: pr comments

* fix: pr comment

* fix: pr comment

* fix: pr comments and refactor

* fix: pr comments

* fix: pr comments

* fix: revert original http request

* fix: pr comment refactor

* fix: pr comment

* fix: pr comment

* fix: pr comment

* fix: pr comments

* fix: owner and pagination

* fix: pr comment

* fix: client id check

* fix: ext related

* fix: pr comment

* fix: revoke APIs (#1041)

* fix: revoke consent sessions

* fix: revoke token

* fix: revoke impl

* fix: revoke session

* fix: introspect impl after revoke

* fix: revoke by client_id

* fix: refresh token check in token exchange

* fix: at hash

* fix: sqlite impl

* fix: client props whitelist

* fix: status and query null check

* fix: plugin interface update

* fix: logout api

* fix: ext

* fix: accept consent

* fix: accept consent

* fix: introspect in token api

* fix: keep fragment while updating query params

* fix: count creds and pr comment

* fix: oauth stats

* fix: oauth cleanup cron task

* fix: gid in refresh token

* fix: inememory impl

* feat: add initial payload fields to accept consent

* fix: revoke cleanup

* fix: stats

* fix: client credentials basic

* fix: authorization header

* fix: authorizaion header in revoke

* fix: missing table

---------

Co-authored-by: Mihaly Lengyel <[email protected]>

---------

Co-authored-by: Mihaly Lengyel <[email protected]>
  • Loading branch information
sattvikc and porcellus authored Sep 25, 2024
1 parent 6b403c6 commit 2ea8d10
Show file tree
Hide file tree
Showing 48 changed files with 4,025 additions and 1,336 deletions.
2 changes: 1 addition & 1 deletion config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ core_config_version: 0
# service.
# oauth_provider_admin_service_url:

# (OPTIONAL | Default: http://localhost:3000) string value. If specified, the core uses this URL replace the default
# (OPTIONAL | Default: null) string value. If specified, the core uses this URL replace the default
# consent and login URLs to {apiDomain}.
# oauth_provider_consent_login_base_url:

Expand Down
11 changes: 5 additions & 6 deletions devConfig.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -154,17 +154,16 @@ disable_telemetry: true

# (OPTIONAL | Default: null) string value. If specified, the core uses this URL to connect to the OAuth provider
# public service.
# oauth_provider_public_service_url:
oauth_provider_public_service_url: http://localhost:4444

# (OPTIONAL | Default: null) string value. If specified, the core uses this URL to connect to the OAuth provider admin
# service.
# oauth_provider_admin_service_url:
oauth_provider_admin_service_url: http://localhost:4445


# (OPTIONAL | Default: http://localhost:3000) string value. If specified, the core uses this URL replace the default
# (OPTIONAL | Default: null) string value. If specified, the core uses this URL replace the default
# consent and login URLs to {apiDomain}.
# oauth_provider_consent_login_base_url:
oauth_provider_consent_login_base_url: http://localhost:4001/auth

# (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_hydra:
# oauth_provider_url_configured_in_hydra:
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
1 change: 0 additions & 1 deletion src/main/java/io/supertokens/config/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import io.supertokens.Main;
Expand Down
8 changes: 3 additions & 5 deletions src/main/java/io/supertokens/config/CoreConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -297,17 +297,15 @@ public class CoreConfig {
@JsonProperty
@HideFromDashboard
@ConfigDescription(
"If specified, the core uses this URL replace the default consent and login URLs to {apiDomain}. Defaults to 'http://localhost:3000'")
private String oauth_provider_consent_login_base_url = "http://localhost:3000";
"If specified, the core uses this URL replace the default consent and login URLs to {apiDomain}. Defaults to 'null'")
private String oauth_provider_consent_login_base_url = null;

@NotConflictingInApp
@JsonProperty
@HideFromDashboard
@ConfigDescription(
"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. Defaults to the oauth_provider_public_service_url")
private String oauth_provider_url_configured_in_hydra;


private String oauth_provider_url_configured_in_hydra = null;

@ConfigYamlOnly
@JsonProperty
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;
}
}
}
53 changes: 17 additions & 36 deletions src/main/java/io/supertokens/httpRequest/HttpRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,22 @@

package io.supertokens.httpRequest;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Map;

import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import io.supertokens.Main;

import java.io.*;
import java.net.*;
import java.net.http.HttpClient;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import io.supertokens.Main;

public class HttpRequest {

Expand Down Expand Up @@ -126,7 +131,7 @@ public static <T> T sendGETRequest(Main main, String requestID, String url, Map<
public static <T> T sendGETRequestWithResponseHeaders(Main main, String requestID, String url,
Map<String, String> params,
int connectionTimeoutMS, int readTimeoutMS, Integer version,
Map<String, List<String>> responseHeaders, boolean followRedirects)
Map<String, String> responseHeaders)
throws IOException, HttpResponseException {
StringBuilder paramBuilder = new StringBuilder();

Expand All @@ -152,12 +157,12 @@ public static <T> T sendGETRequestWithResponseHeaders(Main main, String requestI
if (version != null) {
con.setRequestProperty("api-version", version + "");
}
con.setInstanceFollowRedirects(followRedirects);

int responseCode = con.getResponseCode();

con.getHeaderFields().forEach((key, value) -> {
if (key != null) {
responseHeaders.put(key, value);
responseHeaders.put(key, value.get(0));
}
});

Expand Down Expand Up @@ -262,35 +267,11 @@ public static <T> T sendJsonPUTRequest(Main main, String requestID, String url,
return sendJsonRequest(main, requestID, url, requestBody, connectionTimeoutMS, readTimeoutMS, version, "PUT");
}

public static <T> T sendJsonPATCHRequest(Main main, String url, JsonElement requestBody)
throws IOException, HttpResponseException, InterruptedException {

HttpClient client = null;

String body = requestBody.toString();
java.net.http.HttpRequest rawRequest = java.net.http.HttpRequest.newBuilder()
.uri(URI.create(url))
.method("PATCH", java.net.http.HttpRequest.BodyPublishers.ofString(body))
.build();
client = HttpClient.newHttpClient();
HttpResponse<String> response = client.send(rawRequest, HttpResponse.BodyHandlers.ofString());

int responseCode = response.statusCode();

if (responseCode < STATUS_CODE_ERROR_THRESHOLD) {
if (!isJsonValid(response.body().toString())) {
return (T) response.body().toString();
}
return (T) (new JsonParser().parse(response.body().toString()));
}
throw new HttpResponseException(responseCode, response.body().toString());
}

public static <T> T sendJsonDELETERequest(Main main, String requestID, String url, JsonElement requestBody,
int connectionTimeoutMS, int readTimeoutMS, Integer version)
throws IOException, HttpResponseException {
return sendJsonRequest(main, requestID, url, requestBody, connectionTimeoutMS, readTimeoutMS, version,
"DELETE");
}

}
}
Loading

0 comments on commit 2ea8d10

Please sign in to comment.