diff --git a/CHANGELOG.md b/CHANGELOG.md index 931d87df..c1c36a1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [8.3.0] + +- Adds SAML support + ## [8.2.0] - Adds OpenTelemetry javaagent support diff --git a/build.gradle b/build.gradle index 18746ad6..402ba394 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java-library' } -version = "8.2.0" +version = "8.3.0" repositories { mavenCentral() diff --git a/src/main/java/io/supertokens/pluginInterface/StorageUtils.java b/src/main/java/io/supertokens/pluginInterface/StorageUtils.java index 0ec0ebdc..3a488c93 100644 --- a/src/main/java/io/supertokens/pluginInterface/StorageUtils.java +++ b/src/main/java/io/supertokens/pluginInterface/StorageUtils.java @@ -24,6 +24,7 @@ import io.supertokens.pluginInterface.multitenancy.MultitenancyStorage; import io.supertokens.pluginInterface.oauth.OAuthStorage; import io.supertokens.pluginInterface.passwordless.sqlStorage.PasswordlessSQLStorage; +import io.supertokens.pluginInterface.saml.SAMLStorage; import io.supertokens.pluginInterface.session.SessionStorage; import io.supertokens.pluginInterface.thirdparty.sqlStorage.ThirdPartySQLStorage; import io.supertokens.pluginInterface.totp.sqlStorage.TOTPSQLStorage; @@ -159,4 +160,11 @@ public static WebAuthNSQLStorage getWebAuthNStorage(Storage storage) { } return (WebAuthNSQLStorage) storage; } + + public static SAMLStorage getSAMLStorage(Storage storage) { + if (storage.getType() != STORAGE_TYPE.SQL) { + throw new UnsupportedOperationException(""); + } + return (SAMLStorage) storage; + } } diff --git a/src/main/java/io/supertokens/pluginInterface/saml/SAMLClaimsInfo.java b/src/main/java/io/supertokens/pluginInterface/saml/SAMLClaimsInfo.java new file mode 100644 index 00000000..0e489a09 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/saml/SAMLClaimsInfo.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2025, 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.pluginInterface.saml; + +import com.google.gson.JsonObject; + +public class SAMLClaimsInfo { + public final String clientId; + public final JsonObject claims; + + public SAMLClaimsInfo(String clientId, JsonObject claims) { + this.clientId = clientId; + this.claims = claims; + } +} diff --git a/src/main/java/io/supertokens/pluginInterface/saml/SAMLClient.java b/src/main/java/io/supertokens/pluginInterface/saml/SAMLClient.java new file mode 100644 index 00000000..9180fd13 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/saml/SAMLClient.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2025, 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.pluginInterface.saml; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +public class SAMLClient { + public final String clientId; + public final String clientSecret; + public final String ssoLoginURL; + public final JsonArray redirectURIs; + public final String defaultRedirectURI; + public final String idpEntityId; + public final String idpSigningCertificate; + public final boolean allowIDPInitiatedLogin; + public final boolean enableRequestSigning; + + public SAMLClient(String clientId, String clientSecret, String ssoLoginURL, JsonArray redirectURIs, String defaultRedirectURI, String idpEntityId, String idpSigningCertificate, boolean allowIDPInitiatedLogin, boolean enableRequestSigning) { + this.clientId = clientId; + this.clientSecret = clientSecret; + this.ssoLoginURL = ssoLoginURL; + this.redirectURIs = redirectURIs; + this.defaultRedirectURI = defaultRedirectURI; + this.idpEntityId = idpEntityId; + this.idpSigningCertificate = idpSigningCertificate; + this.allowIDPInitiatedLogin = allowIDPInitiatedLogin; + this.enableRequestSigning = enableRequestSigning; + } + + public JsonObject toJson() { + JsonObject res = new JsonObject(); + + res.addProperty("clientId", this.clientId); + if (this.clientSecret != null) { + res.addProperty("clientSecret", this.clientSecret); + } + res.addProperty("defaultRedirectURI", this.defaultRedirectURI); + res.add("redirectURIs", redirectURIs); + res.addProperty("idpEntityId", this.idpEntityId); + if (this.idpSigningCertificate != null) { + res.addProperty("idpSigningCertificate", this.idpSigningCertificate); + } + res.addProperty("allowIDPInitiatedLogin", this.allowIDPInitiatedLogin); + res.addProperty("enableRequestSigning", this.enableRequestSigning); + + return res; + } +} diff --git a/src/main/java/io/supertokens/pluginInterface/saml/SAMLRelayStateInfo.java b/src/main/java/io/supertokens/pluginInterface/saml/SAMLRelayStateInfo.java new file mode 100644 index 00000000..2d90928e --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/saml/SAMLRelayStateInfo.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2025, 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.pluginInterface.saml; + +public class SAMLRelayStateInfo { + public final String relayState; + public final String clientId; + public final String state; + public final String redirectURI; + + public SAMLRelayStateInfo(String relayState, String clientId, String state, String redirectURI) { + this.relayState = relayState; + this.clientId = clientId; + this.state = state; + this.redirectURI = redirectURI; + } +} diff --git a/src/main/java/io/supertokens/pluginInterface/saml/SAMLStorage.java b/src/main/java/io/supertokens/pluginInterface/saml/SAMLStorage.java new file mode 100644 index 00000000..eef6d5a6 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/saml/SAMLStorage.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2025, 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.pluginInterface.saml; + +import java.util.List; + +import com.google.gson.JsonObject; + +import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; +import io.supertokens.pluginInterface.nonAuthRecipe.NonAuthRecipeStorage; +import io.supertokens.pluginInterface.saml.exception.DuplicateEntityIdException; + +public interface SAMLStorage extends NonAuthRecipeStorage { + public SAMLClient createOrUpdateSAMLClient(TenantIdentifier tenantIdentifier, SAMLClient samlClient) throws StorageQueryException, DuplicateEntityIdException; + public boolean removeSAMLClient(TenantIdentifier tenantIdentifier, String clientId) throws StorageQueryException; + public SAMLClient getSAMLClient(TenantIdentifier tenantIdentifier, String clientId) throws StorageQueryException; + public SAMLClient getSAMLClientByIDPEntityId(TenantIdentifier tenantIdentifier, String idpEntityId) throws StorageQueryException; + public List getSAMLClients(TenantIdentifier tenantIdentifier) throws StorageQueryException; + + public void saveRelayStateInfo(TenantIdentifier tenantIdentifier, SAMLRelayStateInfo relayStateInfo) throws StorageQueryException; + public SAMLRelayStateInfo getRelayStateInfo(TenantIdentifier tenantIdentifier, String relayState) throws StorageQueryException; + + public void saveSAMLClaims(TenantIdentifier tenantIdentifier, String clientId, String code, JsonObject claims) throws StorageQueryException; + public SAMLClaimsInfo getSAMLClaimsAndRemoveCode(TenantIdentifier tenantIdentifier, String code) throws StorageQueryException; + + public void removeExpiredSAMLCodesAndRelayStates() throws StorageQueryException; + public int countSAMLClients(TenantIdentifier tenantIdentifier) throws StorageQueryException; +} diff --git a/src/main/java/io/supertokens/pluginInterface/saml/exception/DuplicateEntityIdException.java b/src/main/java/io/supertokens/pluginInterface/saml/exception/DuplicateEntityIdException.java new file mode 100644 index 00000000..ec1843c0 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/saml/exception/DuplicateEntityIdException.java @@ -0,0 +1,4 @@ +package io.supertokens.pluginInterface.saml.exception; + +public class DuplicateEntityIdException extends Exception { +}