diff --git a/build.gradle b/build.gradle index c565f1bdc..1a462ca04 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,7 @@ compileTestJava { options.encoding = "UTF-8" } // } //} -version = "9.2.2" +version = "9.3.0" repositories { diff --git a/coreDriverInterfaceSupported.json b/coreDriverInterfaceSupported.json index aa87aab03..130c4b2c1 100644 --- a/coreDriverInterfaceSupported.json +++ b/coreDriverInterfaceSupported.json @@ -20,6 +20,7 @@ "3.1", "4.0", "5.0", - "5.1" + "5.1", + "5.2" ] } diff --git a/pluginInterfaceSupported.json b/pluginInterfaceSupported.json index 0dedee885..25f823814 100644 --- a/pluginInterfaceSupported.json +++ b/pluginInterfaceSupported.json @@ -1,6 +1,6 @@ { "_comment": "contains a list of plugin interfaces branch names that this core supports", "versions": [ - "6.2" + "6.3" ] } \ No newline at end of file diff --git a/src/main/java/io/supertokens/inmemorydb/Start.java b/src/main/java/io/supertokens/inmemorydb/Start.java index 93715c091..e53363268 100644 --- a/src/main/java/io/supertokens/inmemorydb/Start.java +++ b/src/main/java/io/supertokens/inmemorydb/Start.java @@ -56,7 +56,8 @@ import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; import io.supertokens.pluginInterface.multitenancy.sqlStorage.MultitenancySQLStorage; import io.supertokens.pluginInterface.oauth.OAuthLogoutChallenge; -import io.supertokens.pluginInterface.oauth.sqlStorage.OAuthSQLStorage; +import io.supertokens.pluginInterface.oauth.OAuthStorage; +import io.supertokens.pluginInterface.oauth.exception.DuplicateOAuthLogoutChallengeException; import io.supertokens.pluginInterface.passwordless.PasswordlessCode; import io.supertokens.pluginInterface.passwordless.PasswordlessDevice; import io.supertokens.pluginInterface.passwordless.exception.*; @@ -104,7 +105,7 @@ public class Start implements SessionSQLStorage, EmailPasswordSQLStorage, EmailVerificationSQLStorage, ThirdPartySQLStorage, JWTRecipeSQLStorage, PasswordlessSQLStorage, UserMetadataSQLStorage, UserRolesSQLStorage, UserIdMappingStorage, UserIdMappingSQLStorage, MultitenancyStorage, MultitenancySQLStorage, TOTPSQLStorage, ActiveUsersStorage, - ActiveUsersSQLStorage, DashboardSQLStorage, AuthRecipeSQLStorage, OAuthSQLStorage { + ActiveUsersSQLStorage, DashboardSQLStorage, AuthRecipeSQLStorage, OAuthStorage { private static final Object appenderLock = new Object(); private static final String ACCESS_TOKEN_SIGNING_KEY_NAME = "access_token_signing_key"; @@ -3023,7 +3024,7 @@ public boolean doesClientIdExistForApp(AppIdentifier appIdentifier, String clien public void addOrUpdateClientForApp(AppIdentifier appIdentifier, String clientId, boolean isClientCredentialsOnly) throws StorageQueryException { try { - OAuthQueries.insertClientIdForAppId(this, appIdentifier, clientId, isClientCredentialsOnly); + OAuthQueries.insertOrUpdateClient(this, appIdentifier, clientId, isClientCredentialsOnly); } catch (SQLException e) { throw new StorageQueryException(e); } @@ -3032,7 +3033,7 @@ public void addOrUpdateClientForApp(AppIdentifier appIdentifier, String clientId @Override public boolean removeAppClientAssociation(AppIdentifier appIdentifier, String clientId) throws StorageQueryException { try { - return OAuthQueries.deleteClientIdForAppId(this, clientId, appIdentifier); + return OAuthQueries.deleteClient(this, clientId, appIdentifier); } catch (SQLException e) { throw new StorageQueryException(e); } @@ -3080,10 +3081,18 @@ public void addM2MToken(AppIdentifier appIdentifier, String clientId, long iat, @Override public void addLogoutChallenge(AppIdentifier appIdentifier, String challenge, String clientId, - String postLogoutRedirectionUri, String sessionHandle, String state, long timeCreated) throws StorageQueryException { + String postLogoutRedirectionUri, String sessionHandle, String state, long timeCreated) + throws StorageQueryException, DuplicateOAuthLogoutChallengeException { try { OAuthQueries.addLogoutChallenge(this, appIdentifier, challenge, clientId, postLogoutRedirectionUri, sessionHandle, state, timeCreated); } catch (SQLException e) { + SQLiteConfig config = Config.getConfig(this); + String serverMessage = e.getMessage(); + + if (isPrimaryKeyError(serverMessage, config.getOAuthLogoutChallengesTable(), + new String[]{"app_id", "challenge"})) { + throw new DuplicateOAuthLogoutChallengeException(); + } throw new StorageQueryException(e); } } diff --git a/src/main/java/io/supertokens/inmemorydb/queries/OAuthQueries.java b/src/main/java/io/supertokens/inmemorydb/queries/OAuthQueries.java index 6aec76e02..9fa6e3d46 100644 --- a/src/main/java/io/supertokens/inmemorydb/queries/OAuthQueries.java +++ b/src/main/java/io/supertokens/inmemorydb/queries/OAuthQueries.java @@ -145,8 +145,8 @@ public static List listClientsForApp(Start start, AppIdentifier appIdent }); } - public static void insertClientIdForAppId(Start start, AppIdentifier appIdentifier, String clientId, - boolean isClientCredentialsOnly) + public static void insertOrUpdateClient(Start start, AppIdentifier appIdentifier, String clientId, + boolean isClientCredentialsOnly) throws SQLException, StorageQueryException { String INSERT = "INSERT INTO " + Config.getConfig(start).getOAuthClientsTable() + "(app_id, client_id, is_client_credentials_only) VALUES(?, ?, ?) " @@ -159,7 +159,7 @@ public static void insertClientIdForAppId(Start start, AppIdentifier appIdentifi }); } - public static boolean deleteClientIdForAppId(Start start, String clientId, AppIdentifier appIdentifier) + public static boolean deleteClient(Start start, String clientId, AppIdentifier appIdentifier) throws SQLException, StorageQueryException { String DELETE = "DELETE FROM " + Config.getConfig(start).getOAuthClientsTable() + " WHERE app_id = ? AND client_id = ?"; diff --git a/src/main/java/io/supertokens/oauth/OAuth.java b/src/main/java/io/supertokens/oauth/OAuth.java index 3cff18da7..b58a98318 100644 --- a/src/main/java/io/supertokens/oauth/OAuth.java +++ b/src/main/java/io/supertokens/oauth/OAuth.java @@ -38,6 +38,7 @@ import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; import io.supertokens.pluginInterface.oauth.OAuthLogoutChallenge; import io.supertokens.pluginInterface.oauth.OAuthStorage; +import io.supertokens.pluginInterface.oauth.exception.DuplicateOAuthLogoutChallengeException; import io.supertokens.session.jwt.JWT.JWTException; import io.supertokens.utils.Utils; @@ -575,10 +576,16 @@ public static String createLogoutRequestAndReturnRedirectUri(Main main, AppIdent OAuthStorage oauthStorage = StorageUtils.getOAuthStorage(storage); - String logoutChallenge = UUID.randomUUID().toString(); - oauthStorage.addLogoutChallenge(appIdentifier, logoutChallenge, clientId, postLogoutRedirectionUri, sessionHandle, state, System.currentTimeMillis()); + while (true) { + try { + String logoutChallenge = UUID.randomUUID().toString(); + oauthStorage.addLogoutChallenge(appIdentifier, logoutChallenge, clientId, postLogoutRedirectionUri, sessionHandle, state, System.currentTimeMillis()); - return "{apiDomain}/oauth/logout?logout_challenge=" + logoutChallenge; + return "{apiDomain}/oauth/logout?logout_challenge=" + logoutChallenge; + } catch (DuplicateOAuthLogoutChallengeException e) { + // retry + } + } } public static String consumeLogoutChallengeAndGetRedirectUri(Main main, AppIdentifier appIdentifier, Storage storage, String challenge) throws StorageQueryException, OAuthAPIException { diff --git a/src/main/java/io/supertokens/utils/SemVer.java b/src/main/java/io/supertokens/utils/SemVer.java index 6c94518c3..ef43014af 100644 --- a/src/main/java/io/supertokens/utils/SemVer.java +++ b/src/main/java/io/supertokens/utils/SemVer.java @@ -37,6 +37,7 @@ public class SemVer implements Comparable { public static final SemVer v4_0 = new SemVer("4.0"); public static final SemVer v5_0 = new SemVer("5.0"); public static final SemVer v5_1 = new SemVer("5.1"); + public static final SemVer v5_2 = new SemVer("5.2"); final private String version; diff --git a/src/main/java/io/supertokens/webserver/WebserverAPI.java b/src/main/java/io/supertokens/webserver/WebserverAPI.java index 558f91c26..28bb2b080 100644 --- a/src/main/java/io/supertokens/webserver/WebserverAPI.java +++ b/src/main/java/io/supertokens/webserver/WebserverAPI.java @@ -76,10 +76,11 @@ public abstract class WebserverAPI extends HttpServlet { supportedVersions.add(SemVer.v4_0); supportedVersions.add(SemVer.v5_0); supportedVersions.add(SemVer.v5_1); + supportedVersions.add(SemVer.v5_2); } public static SemVer getLatestCDIVersion() { - return SemVer.v5_1; + return SemVer.v5_2; } public SemVer getLatestCDIVersionForRequest(HttpServletRequest req) diff --git a/src/test/java/io/supertokens/test/oauth/api/OAuthAuthAPITest.java b/src/test/java/io/supertokens/test/oauth/api/OAuthAuthAPITest.java deleted file mode 100644 index bc27456e3..000000000 --- a/src/test/java/io/supertokens/test/oauth/api/OAuthAuthAPITest.java +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. - * - * This software is licensed under the Apache License, Version 2.0 (the - * "License") as published by the Apache Software Foundation. - * - * You may not use this file except in compliance with the License. You may - * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.supertokens.test.oauth.api; - -import com.google.gson.JsonObject; -import io.supertokens.ProcessState; -import io.supertokens.httpRequest.HttpResponseException; -import io.supertokens.oauth.OAuth; -import io.supertokens.oauth.exceptions.OAuthAPIException; -import io.supertokens.pluginInterface.RECIPE_ID; -import io.supertokens.pluginInterface.exceptions.InvalidConfigException; -import io.supertokens.pluginInterface.exceptions.StorageQueryException; -import io.supertokens.pluginInterface.multitenancy.AppIdentifier; -import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; -import io.supertokens.oauth.OAuthAuthResponse; -import io.supertokens.pluginInterface.oauth.sqlStorage.OAuthSQLStorage; -import io.supertokens.storageLayer.StorageLayer; -import io.supertokens.test.TestingProcessManager; -import io.supertokens.test.Utils; -import io.supertokens.test.httpRequest.HttpRequestForTesting; -import org.junit.*; -import org.junit.rules.TestRule; - -import java.io.IOException; - -import static org.junit.Assert.*; - -public class OAuthAuthAPITest { -// TestingProcessManager.TestingProcess process; -// -// @Rule -// public TestRule watchman = Utils.getOnFailure(); -// -// @AfterClass -// public static void afterTesting() { -// Utils.afterTesting(); -// } -// -// @Before -// public void beforeEach() throws InterruptedException { -// Utils.reset(); -// } -// -// -// @Test -// public void testLocalhostChangedToApiDomain() -// throws StorageQueryException, OAuthAPIException, HttpResponseException, TenantOrAppNotFoundException, -// InvalidConfigException, IOException, OAuth2ClientAlreadyExistsForAppException, -// io.supertokens.test.httpRequest.HttpResponseException, InterruptedException { -// -// String[] args = {"../"}; -// -// this.process = TestingProcessManager.start(args); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); -// -// String clientId = "6030f07e-c8ef-4289-80c9-c18e0bf4f679"; -// String redirectUri = "http://localhost.com:3031/auth/callback/ory"; -// String responseType = "code"; -// String scope = "profile"; -// String state = "%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BDv%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD"; -// -// OAuthSQLStorage oAuthStorage = (OAuthSQLStorage) StorageLayer.getStorage(process.getProcess()); -// -// AppIdentifier testApp = new AppIdentifier("", ""); -// oAuthStorage.addClientForApp(testApp, clientId); -// -// JsonObject requestBody = new JsonObject(); -// requestBody.addProperty("clientId", clientId); -// requestBody.addProperty("redirectUri", redirectUri); -// requestBody.addProperty("responseType", responseType); -// requestBody.addProperty("scope", scope); -// requestBody.addProperty("state", state); -// -// OAuthAuthResponse response = OAuth.getAuthorizationUrl(process.getProcess(), new AppIdentifier("", ""), -// oAuthStorage, requestBody); -// -// assertNotNull(response); -// assertNotNull(response.redirectTo); -// assertNotNull(response.cookies); -// -// assertTrue(response.redirectTo.startsWith("{apiDomain}/login?login_challenge=")); -// assertTrue(response.cookies.get(0).startsWith("ory_hydra_login_csrf_dev_134972871=")); -// -// -// -// { -// JsonObject actualResponse = HttpRequestForTesting.sendJsonPOSTRequest(process.getProcess(), "", -// "http://localhost:3567/recipe/oauth/auth", requestBody, 1000, 1000, null, -// null, RECIPE_ID.OAUTH.toString()); -// -// assertEquals("OK", actualResponse.get("status").getAsString()); -// assertTrue(actualResponse.has("redirectTo")); -// assertTrue(actualResponse.has("cookies")); -// assertTrue(actualResponse.get("redirectTo").getAsString().startsWith("{apiDomain}/login?login_challenge=")); -// assertEquals(1, actualResponse.getAsJsonArray("cookies").size()); -// assertTrue(actualResponse.getAsJsonArray("cookies").get(0).getAsString().startsWith("ory_hydra_login_csrf_dev_134972871=")); -// } -// -// process.kill(); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); -// } -// -// @Test -// public void testCalledWithWrongClientIdNotInST_exceptionThrown() -// throws StorageQueryException, OAuth2ClientAlreadyExistsForAppException, IOException, -// io.supertokens.test.httpRequest.HttpResponseException, InterruptedException { -// -// -// String[] args = {"../"}; -// -// this.process = TestingProcessManager.start(args); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); -// -// String clientId = "Not-Existing-In-Client-App-Table"; -// String redirectUri = "http://localhost.com:3031/auth/callback/ory"; -// String responseType = "code"; -// String scope = "profile"; -// String state = "%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BDv%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD"; -// -// JsonObject requestBody = new JsonObject(); -// requestBody.addProperty("clientId", clientId); -// requestBody.addProperty("redirectUri", redirectUri); -// requestBody.addProperty("responseType", responseType); -// requestBody.addProperty("scope", scope); -// requestBody.addProperty("state", state); -// -// OAuthSQLStorage oAuthStorage = (OAuthSQLStorage) StorageLayer.getStorage(process.getProcess()); -// -// AppIdentifier testApp = new AppIdentifier("", ""); -// oAuthStorage.addClientForApp(testApp, clientId); -// -// OAuthAPIException thrown = assertThrows(OAuthAPIException.class, () -> { -// -// OAuthAuthResponse response = OAuth.getAuthorizationUrl(process.getProcess(), new AppIdentifier("", ""), -// oAuthStorage, requestBody); -// }); -// -// String expectedError = "invalid_client"; -// String expectedDescription = "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The requested OAuth 2.0 Client does not exist."; -// -// assertEquals(expectedError, thrown.error); -// assertEquals(expectedDescription, thrown.errorDescription); -// -// { -// JsonObject actualResponse = HttpRequestForTesting.sendJsonPOSTRequest(process.getProcess(), "", -// "http://localhost:3567/recipe/oauth/auth", requestBody, 1000, 1000, null, -// null, RECIPE_ID.OAUTH.toString()); -// -// assertEquals("OAUTH2_AUTH_ERROR", actualResponse.get("status").getAsString()); -// assertTrue(actualResponse.has("error")); -// assertTrue(actualResponse.has("errorDescription")); -// assertEquals(expectedError,actualResponse.get("error").getAsString()); -// assertEquals(expectedDescription, actualResponse.get("errorDescription").getAsString()); -// } -// -// process.kill(); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); -// } -// -// @Test -// public void testCalledWithWrongClientIdNotInHydraButInST_exceptionThrown() -// throws StorageQueryException, OAuth2ClientAlreadyExistsForAppException, -// io.supertokens.test.httpRequest.HttpResponseException, IOException, InterruptedException { -// -// -// String[] args = {"../"}; -// -// this.process = TestingProcessManager.start(args); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); -// -// String clientId = "6030f07e-c8ef-4289-80c9-c18e0bf4f679NotInHydra"; -// String redirectUri = "http://localhost.com:3031/auth/callback/ory"; -// String responseType = "code"; -// String scope = "profile"; -// String state = "%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BDv%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD"; -// -// OAuthSQLStorage oAuthStorage = (OAuthSQLStorage) StorageLayer.getStorage(process.getProcess()); -// -// JsonObject requestBody = new JsonObject(); -// requestBody.addProperty("clientId", clientId); -// requestBody.addProperty("redirectUri", redirectUri); -// requestBody.addProperty("responseType", responseType); -// requestBody.addProperty("scope", scope); -// requestBody.addProperty("state", state); -// -// AppIdentifier testApp = new AppIdentifier("", ""); -// oAuthStorage.addClientForApp(testApp, clientId); -// -// OAuthAPIException thrown = assertThrows(OAuthAPIException.class, () -> { -// -// OAuthAuthResponse response = OAuth.getAuthorizationUrl(process.getProcess(), new AppIdentifier("", ""), -// oAuthStorage, requestBody); -// }); -// -// String expectedError = "invalid_client"; -// String expectedDescription = "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The requested OAuth 2.0 Client does not exist."; -// -// assertEquals(expectedError, thrown.error); -// assertEquals(expectedDescription, thrown.errorDescription); -// -// { -// JsonObject actualResponse = HttpRequestForTesting.sendJsonPOSTRequest(process.getProcess(), "", -// "http://localhost:3567/recipe/oauth/auth", requestBody, 1000, 1000, null, -// null, RECIPE_ID.OAUTH.toString()); -// -// assertEquals("OAUTH2_AUTH_ERROR", actualResponse.get("status").getAsString()); -// assertTrue(actualResponse.has("error")); -// assertTrue(actualResponse.has("errorDescription")); -// assertEquals(expectedError,actualResponse.get("error").getAsString()); -// assertEquals(expectedDescription, actualResponse.get("errorDescription").getAsString()); -// -// } -// -// process.kill(); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); -// } -// -// @Test -// public void testCalledWithWrongRedirectUrl_exceptionThrown() -// throws StorageQueryException, OAuth2ClientAlreadyExistsForAppException, -// io.supertokens.test.httpRequest.HttpResponseException, IOException, InterruptedException { -// -// -// String[] args = {"../"}; -// -// this.process = TestingProcessManager.start(args); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); -// -// String clientId = "6030f07e-c8ef-4289-80c9-c18e0bf4f679"; -// String redirectUri = "http://localhost.com:3031/auth/callback/ory_not_the_registered_one"; -// String responseType = "code"; -// String scope = "profile"; -// String state = "%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BDv%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD"; -// -// JsonObject requestBody = new JsonObject(); -// requestBody.addProperty("clientId", clientId); -// requestBody.addProperty("redirectUri", redirectUri); -// requestBody.addProperty("responseType", responseType); -// requestBody.addProperty("scope", scope); -// requestBody.addProperty("state", state); -// -// OAuthSQLStorage oAuthStorage = (OAuthSQLStorage) StorageLayer.getStorage(process.getProcess()); -// -// AppIdentifier testApp = new AppIdentifier("", ""); -// oAuthStorage.addClientForApp(testApp, clientId); -// -// OAuthAPIException thrown = assertThrows(OAuthAPIException.class, () -> { -// -// OAuthAuthResponse response = OAuth.getAuthorizationUrl(process.getProcess(), new AppIdentifier("", ""), -// oAuthStorage, requestBody); -// }); -// -// String expectedError = "invalid_request"; -// String expectedDescription = "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. The 'redirect_uri' parameter does not match any of the OAuth 2.0 Client's pre-registered redirect urls."; -// -// assertEquals(expectedError, thrown.error); -// assertEquals(expectedDescription, thrown.errorDescription); -// -// { -// -// JsonObject actualResponse = HttpRequestForTesting.sendJsonPOSTRequest(process.getProcess(), "", -// "http://localhost:3567/recipe/oauth/auth", requestBody, 1000, 1000, null, -// null, RECIPE_ID.OAUTH.toString()); -// -// assertEquals("OAUTH2_AUTH_ERROR", actualResponse.get("status").getAsString()); -// assertTrue(actualResponse.has("error")); -// assertTrue(actualResponse.has("errorDescription")); -// assertEquals(expectedError, actualResponse.get("error").getAsString()); -// assertEquals(expectedDescription, actualResponse.get("errorDescription").getAsString()); -// -// } -// process.kill(); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); -// } -} diff --git a/src/test/java/io/supertokens/test/oauth/api/OAuthClientsAPITest.java b/src/test/java/io/supertokens/test/oauth/api/OAuthClientsAPITest.java deleted file mode 100644 index 8b1169853..000000000 --- a/src/test/java/io/supertokens/test/oauth/api/OAuthClientsAPITest.java +++ /dev/null @@ -1,470 +0,0 @@ -/* - * Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. - * - * This software is licensed under the Apache License, Version 2.0 (the - * "License") as published by the Apache Software Foundation. - * - * You may not use this file except in compliance with the License. You may - * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.supertokens.test.oauth.api; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; -import io.supertokens.ProcessState; -import io.supertokens.httpRequest.HttpRequest; -import io.supertokens.pluginInterface.RECIPE_ID; -import io.supertokens.pluginInterface.exceptions.StorageQueryException; -import io.supertokens.pluginInterface.multitenancy.AppIdentifier; -import io.supertokens.pluginInterface.oauth.sqlStorage.OAuthSQLStorage; -import io.supertokens.storageLayer.StorageLayer; -import io.supertokens.test.TestingProcessManager; -import io.supertokens.test.Utils; -import io.supertokens.test.httpRequest.HttpRequestForTesting; -import io.supertokens.test.httpRequest.HttpResponseException; -import jakarta.servlet.ServletException; -import org.junit.*; -import org.junit.rules.TestRule; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import static org.junit.Assert.*; -import static org.junit.Assert.assertTrue; - -public class OAuthClientsAPITest { -// TestingProcessManager.TestingProcess process; -// -// @Rule -// public TestRule watchman = Utils.getOnFailure(); -// -// @AfterClass -// public static void afterTesting() { -// Utils.afterTesting(); -// } -// -// @Before -// public void beforeEach() throws InterruptedException { -// Utils.reset(); -// } -// -// @Test -// public void testClientRegisteredForApp() -// throws HttpResponseException, IOException, InterruptedException, -// io.supertokens.httpRequest.HttpResponseException { -// -// String[] args = {"../"}; -// this.process = TestingProcessManager.start(args); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); -// -// String clientName = "jozef"; -// String scope = "profile"; -// -// OAuthSQLStorage oAuthStorage = (OAuthSQLStorage) StorageLayer.getStorage(process.getProcess()); -// -// -// { -// JsonObject requestBody = new JsonObject(); -// requestBody.addProperty("clientName", clientName); -// requestBody.addProperty("scope", scope); -// -// JsonArray grantTypes = new JsonArray(); -// grantTypes.add(new JsonPrimitive("refresh_token")); -// grantTypes.add(new JsonPrimitive("authorization_code")); -// requestBody.add("grantTypes", grantTypes); -// -// JsonArray responseTypes = new JsonArray(); -// responseTypes.add(new JsonPrimitive("code")); -// responseTypes.add(new JsonPrimitive("id_token")); -// requestBody.add("responseTypes", responseTypes); -// -// JsonObject actualResponse = HttpRequestForTesting.sendJsonPOSTRequest(process.getProcess(), "", -// "http://localhost:3567/recipe/oauth/clients", requestBody, 1000, 1000, null, -// null, RECIPE_ID.OAUTH.toString()); -// -// assertTrue(actualResponse.has("client")); -// JsonObject client = actualResponse.get("client").getAsJsonObject(); -// -// assertTrue(client.has("clientSecret")); -// assertTrue(client.has("clientId")); -// -// String clientId = client.get("clientId").getAsString(); -// -// Map queryParams = new HashMap<>(); -// queryParams.put("clientId", client.get("clientId").getAsString()); -// JsonObject loadedClient = HttpRequest.sendGETRequest(process.getProcess(), "", -// "http://localhost:3567/recipe/oauth/clients", queryParams,10000,10000, null); -// -// assertTrue(loadedClient.has("client")); -// JsonObject loadedClientJson = loadedClient.get("client").getAsJsonObject(); -// assertFalse(loadedClientJson.has("clientSecret")); //this should only be sent when registering -// assertEquals(clientId, loadedClientJson.get("clientId").getAsString()); -// -// {//delete client -// JsonObject deleteRequestBody = new JsonObject(); -// deleteRequestBody.addProperty("clientId", clientId); -// JsonObject deleteResponse = HttpRequestForTesting.sendJsonDELETERequest(process.getProcess(), "", -// "http://localhost:3567/recipe/oauth/clients", deleteRequestBody, 1000, 1000, null, -// null, RECIPE_ID.OAUTH.toString()); -// -// assertTrue(deleteResponse.isJsonObject()); -// assertEquals("OK", deleteResponse.get("status").getAsString()); //empty response -// -// } -// -// process.kill(); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); -// } -// } -// -// @Test -// public void testMissingRequiredField_throwsException() throws InterruptedException { -// -// String[] args = {"../"}; -// this.process = TestingProcessManager.start(args); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); -// -// String clientName = "jozef"; -// //notice missing 'scope' field! -// -// { -// JsonObject requestBody = new JsonObject(); -// requestBody.addProperty("clientName", clientName); -// //notice missing 'scope' field -// -// JsonArray grantTypes = new JsonArray(); -// grantTypes.add(new JsonPrimitive("refresh_token")); -// grantTypes.add(new JsonPrimitive("authorization_code")); -// requestBody.add("grantTypes", grantTypes); -// -// JsonArray responseTypes = new JsonArray(); -// responseTypes.add(new JsonPrimitive("code")); -// responseTypes.add(new JsonPrimitive("id_token")); -// requestBody.add("responseTypes", responseTypes); -// -// io.supertokens.test.httpRequest.HttpResponseException expected = assertThrows(io.supertokens.test.httpRequest.HttpResponseException.class, () -> { -// HttpRequestForTesting.sendJsonPOSTRequest(process.getProcess(), "", -// "http://localhost:3567/recipe/oauth/clients", requestBody, 1000, 1000, null, -// null, RECIPE_ID.OAUTH.toString()); -// }); -// -// assertEquals(400, expected.statusCode); -// assertEquals("Http error. Status Code: 400. Message: Field name `scope` is missing in JSON input", expected.getMessage()); -// -// process.kill(); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); -// } -// } -// -// @Test -// public void testMoreFieldAreIgnored() -// throws InterruptedException, HttpResponseException, IOException { -// -// String[] args = {"../"}; -// this.process = TestingProcessManager.start(args); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); -// -// String clientName = "jozef"; -// String scope = "scope"; -// String maliciousAttempt = "giveMeAllYourBelongings!"; //here! -// -// { -// JsonObject requestBody = new JsonObject(); -// requestBody.addProperty("clientName", clientName); -// requestBody.addProperty("scope", scope); -// requestBody.addProperty("dontMindMe", maliciousAttempt); //here! -// -// JsonArray grantTypes = new JsonArray(); -// grantTypes.add(new JsonPrimitive("refresh_token")); -// grantTypes.add(new JsonPrimitive("authorization_code")); -// requestBody.add("grantTypes", grantTypes); -// -// JsonArray responseTypes = new JsonArray(); -// responseTypes.add(new JsonPrimitive("code")); -// responseTypes.add(new JsonPrimitive("id_token")); -// requestBody.add("responseTypes", responseTypes); -// -// JsonObject response = HttpRequestForTesting.sendJsonPOSTRequest(process.getProcess(), "", -// "http://localhost:3567/recipe/oauth/clients", requestBody, 1000, 1000, null, -// null, RECIPE_ID.OAUTH.toString()); -// -// assertEquals("OK", response.get("status").getAsString()); -// assertTrue(response.has("client")); -// process.kill(); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); -// } -// } -// -// -// @Test -// public void testGETClientNotExisting_returnsError() -// throws InterruptedException, io.supertokens.httpRequest.HttpResponseException, -// IOException { -// -// String[] args = {"../"}; -// this.process = TestingProcessManager.start(args); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); -// -// String clientId = "not-an-existing-one"; -// -// Map queryParams = new HashMap<>(); -// queryParams.put("clientId", clientId); -// JsonObject response = HttpRequest.sendGETRequest(process.getProcess(), "", -// "http://localhost:3567/recipe/oauth/clients", queryParams, 10000, 10000, null); -// -// assertEquals("OAUTH2_CLIENT_NOT_FOUND_ERROR", response.get("status").getAsString()); -// assertEquals("Unable to locate the resource", response.get("error").getAsString()); -// -// process.kill(); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); -// } -// -// @Test -// public void testClientUpdatePatch() -// throws StorageQueryException, IOException, OAuth2ClientAlreadyExistsForAppException, -// io.supertokens.test.httpRequest.HttpResponseException, InterruptedException { -// -// String[] args = {"../"}; -// -// this.process = TestingProcessManager.start(args); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); -// -// String clientId = "6030f07e-c8ef-4289-80c9-c18e0bf4f679"; -// String propToChangeKey = "clientName"; -// String newValue = "Jozef"; -// -// OAuthSQLStorage oAuthStorage = (OAuthSQLStorage) StorageLayer.getStorage(process.getProcess()); -// -// AppIdentifier testApp = new AppIdentifier("", ""); -// oAuthStorage.addClientForApp(testApp, clientId); -// -// JsonObject requestBody = new JsonObject(); -// requestBody.addProperty("clientId", clientId); -// requestBody.addProperty(propToChangeKey, newValue); -// -// JsonObject actualResponse = HttpRequestForTesting.sendJsonPATCHRequest(process.getProcess(), -// "http://localhost:3567/recipe/oauth/clients", requestBody); -// -// assertEquals("OK", actualResponse.get("status").getAsString()); -// assertTrue(actualResponse.has("client")); -// -// JsonObject updatedClient = actualResponse.get("client").getAsJsonObject(); -// -// assertTrue(updatedClient.has(propToChangeKey)); -// assertEquals(newValue, updatedClient.get(propToChangeKey).getAsString()); -// -// process.kill(); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); -// } -// -// @Test -// public void testClientUpdatePatch_multipleFields() -// throws StorageQueryException, IOException, OAuth2ClientAlreadyExistsForAppException, -// io.supertokens.test.httpRequest.HttpResponseException, InterruptedException { -// -// String[] args = {"../"}; -// -// this.process = TestingProcessManager.start(args); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); -// -// String clientId = "6030f07e-c8ef-4289-80c9-c18e0bf4f679"; -// String propToChangeKey = "clientName"; -// String newValue = "Jozef2"; -// -// String listPropToChange = "grantTypes"; -// JsonArray newListValue = new JsonArray(); -// newListValue.add(new JsonPrimitive("test1")); -// newListValue.add(new JsonPrimitive("test2")); -// -// OAuthSQLStorage oAuthStorage = (OAuthSQLStorage) StorageLayer.getStorage(process.getProcess()); -// -// AppIdentifier testApp = new AppIdentifier("", ""); -// oAuthStorage.addClientForApp(testApp, clientId); -// -// JsonObject requestBody = new JsonObject(); -// requestBody.addProperty("clientId", clientId); -// requestBody.addProperty(propToChangeKey, newValue); -// requestBody.add(listPropToChange, newListValue); -// -// JsonObject actualResponse = HttpRequestForTesting.sendJsonPATCHRequest(process.getProcess(), -// "http://localhost:3567/recipe/oauth/clients", requestBody); -// -// assertEquals("OK", actualResponse.get("status").getAsString()); -// assertTrue(actualResponse.has("client")); -// -// JsonObject updatedClient = actualResponse.get("client").getAsJsonObject(); -// -// assertTrue(updatedClient.has(propToChangeKey)); -// assertEquals(newValue, updatedClient.get(propToChangeKey).getAsString()); -// -// assertTrue(updatedClient.has(listPropToChange)); -// assertEquals(newListValue.getAsJsonArray(), updatedClient.get(listPropToChange).getAsJsonArray()); -// -// process.kill(); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); -// } -// -// @Test -// public void testClientUpdatePatch_missingClientIdResultsInError() -// throws StorageQueryException, IOException, OAuth2ClientAlreadyExistsForAppException, -// io.supertokens.test.httpRequest.HttpResponseException, InterruptedException { -// -// String[] args = {"../"}; -// -// this.process = TestingProcessManager.start(args); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); -// -// String clientId = "6030f07e-c8ef-4289-80c9-c18e0bf4f679"; -// String propToChangeKey = "clientName"; -// String newValue = "Jozef2"; -// -// String listPropToChange = "grantTypes"; -// JsonArray newListValue = new JsonArray(); -// newListValue.add(new JsonPrimitive("test1")); -// newListValue.add(new JsonPrimitive("test2")); -// -// OAuthSQLStorage oAuthStorage = (OAuthSQLStorage) StorageLayer.getStorage(process.getProcess()); -// -// AppIdentifier testApp = new AppIdentifier("", ""); -// oAuthStorage.addClientForApp(testApp, clientId); -// -// JsonObject requestBody = new JsonObject(); -// //note the missing client Id -// requestBody.addProperty(propToChangeKey, newValue); -// requestBody.add(listPropToChange, newListValue); -// -// io.supertokens.test.httpRequest.HttpResponseException expected = assertThrows(io.supertokens.test.httpRequest.HttpResponseException.class, -// () -> { HttpRequestForTesting.sendJsonPATCHRequest(process.getProcess(), -// "http://localhost:3567/recipe/oauth/clients", requestBody); -// }); -// -// assertEquals(400, expected.statusCode); -// assertEquals("Http error. Status Code: 400. Message: Field name `clientId` is missing in JSON input\n", expected.getMessage()); -// -// process.kill(); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); -// } -// -// @Test -// public void testClientUpdatePatch_hydraErrortResultsInError() -// throws StorageQueryException, IOException, OAuth2ClientAlreadyExistsForAppException, -// io.supertokens.test.httpRequest.HttpResponseException, InterruptedException { -// -// String[] args = {"../"}; -// -// this.process = TestingProcessManager.start(args); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); -// -// String clientId = "6030f07e-c8ef-4289-80c9-c18e0bf4f679"; -// String propToChangeKey = "clientName"; -// String newValue = "Jozef2"; -// -// String notAlistPropToChange = "scope"; -// JsonArray newListValue = new JsonArray(); -// newListValue.add(new JsonPrimitive("test1")); -// newListValue.add(new JsonPrimitive("test2")); -// -// OAuthSQLStorage oAuthStorage = (OAuthSQLStorage) StorageLayer.getStorage(process.getProcess()); -// -// AppIdentifier testApp = new AppIdentifier("", ""); -// oAuthStorage.addClientForApp(testApp, clientId); -// -// JsonObject requestBody = new JsonObject(); -// requestBody.addProperty("clientId", clientId); -// requestBody.addProperty(propToChangeKey, newValue); -// requestBody.add(notAlistPropToChange, newListValue); -// -// HttpResponseException expected = assertThrows(HttpResponseException.class, () -> { -// HttpRequestForTesting.sendJsonPATCHRequest(process.getProcess(), -// "http://localhost:3567/recipe/oauth/clients", requestBody); -// }); -// -// assertEquals("Http error. Status Code: 500. Message: Internal Error\n", expected.getMessage()); -// assertEquals(500, expected.statusCode); -// -// process.kill(); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); -// } -// -// @Test -// public void testClientUpdatePatch_invalidInputDataResultsInError() -// throws StorageQueryException, IOException, OAuth2ClientAlreadyExistsForAppException, -// io.supertokens.test.httpRequest.HttpResponseException, InterruptedException { -// -// String[] args = {"../"}; -// -// this.process = TestingProcessManager.start(args); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); -// -// String clientId = "6030f07e-c8ef-4289-80c9-c18e0bf4f679"; -// String propToChangeKey = "clientName"; -// String newValue = "Jozef2"; -// -// String notAlistPropToChange = "allowed_cors_origins"; -// JsonArray newListValue = new JsonArray(); -// newListValue.add(new JsonPrimitive("*")); -// newListValue.add(new JsonPrimitive("appleTree")); -// -// OAuthSQLStorage oAuthStorage = (OAuthSQLStorage) StorageLayer.getStorage(process.getProcess()); -// -// AppIdentifier testApp = new AppIdentifier("", ""); -// oAuthStorage.addClientForApp(testApp, clientId); -// -// JsonObject requestBody = new JsonObject(); -// requestBody.addProperty("clientId", clientId); -// requestBody.addProperty(propToChangeKey, newValue); -// requestBody.add(notAlistPropToChange, newListValue); -// -// io.supertokens.test.httpRequest.HttpResponseException expected = assertThrows(io.supertokens.test.httpRequest.HttpResponseException.class, -// () -> { HttpRequestForTesting.sendJsonPATCHRequest(process.getProcess(), -// "http://localhost:3567/recipe/oauth/clients", requestBody); -// }); -// -// assertEquals(400, expected.statusCode); -// -// process.kill(); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); -// } -// -// @Test -// public void testClientUpdatePatch_notExistingClientResultsInNotFound() -// throws StorageQueryException, IOException, OAuth2ClientAlreadyExistsForAppException, -// io.supertokens.test.httpRequest.HttpResponseException, InterruptedException { -// -// String[] args = {"../"}; -// -// this.process = TestingProcessManager.start(args); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); -// -// String clientId = "6030f07e-c8ef-4289-80c9-c18e0bf4f679-Not_Existing"; -// String propToChangeKey = "clientName"; -// String newValue = "Jozef2"; -// -// OAuthSQLStorage oAuthStorage = (OAuthSQLStorage) StorageLayer.getStorage(process.getProcess()); -// -// AppIdentifier testApp = new AppIdentifier("", ""); -// oAuthStorage.addClientForApp(testApp, clientId); // exists at our end, not exists in hydra -// -// JsonObject requestBody = new JsonObject(); -// requestBody.addProperty("clientId", clientId); -// requestBody.addProperty(propToChangeKey, newValue); -// -// JsonObject response = HttpRequestForTesting.sendJsonPATCHRequest(process.getProcess(), -// "http://localhost:3567/recipe/oauth/clients", requestBody); -// -// assertEquals("OAUTH2_CLIENT_NOT_FOUND_ERROR", response.get("status").getAsString()); -// -// process.kill(); -// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); -// } -// -}