Skip to content

Commit 50ce1fe

Browse files
authored
feat: SAML (#183)
* fix: saml interfaces * fix: relay state info * fix: interface updates * fix: client json * fix: allow idp initiated login * fix: remove saml client * fix: idp flow * fix: add client secret and metadata url * fix: cleanup query * fix: version update * fix: enable request signing * fix: add enable request signing * fix: remove sp entity id from client * fix: unique idp entity id * fix: changelog * fix: SAML client count * fix: validity for claims and relay state
1 parent fef2ec7 commit 50ce1fe

File tree

8 files changed

+185
-1
lines changed

8 files changed

+185
-1
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

88
## [Unreleased]
99

10+
## [8.3.0]
11+
12+
- Adds SAML support
13+
1014
## [8.2.0]
1115

1216
- Adds OpenTelemetry javaagent support

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ plugins {
22
id 'java-library'
33
}
44

5-
version = "8.2.0"
5+
version = "8.3.0"
66

77
repositories {
88
mavenCentral()

src/main/java/io/supertokens/pluginInterface/StorageUtils.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import io.supertokens.pluginInterface.multitenancy.MultitenancyStorage;
2525
import io.supertokens.pluginInterface.oauth.OAuthStorage;
2626
import io.supertokens.pluginInterface.passwordless.sqlStorage.PasswordlessSQLStorage;
27+
import io.supertokens.pluginInterface.saml.SAMLStorage;
2728
import io.supertokens.pluginInterface.session.SessionStorage;
2829
import io.supertokens.pluginInterface.thirdparty.sqlStorage.ThirdPartySQLStorage;
2930
import io.supertokens.pluginInterface.totp.sqlStorage.TOTPSQLStorage;
@@ -159,4 +160,11 @@ public static WebAuthNSQLStorage getWebAuthNStorage(Storage storage) {
159160
}
160161
return (WebAuthNSQLStorage) storage;
161162
}
163+
164+
public static SAMLStorage getSAMLStorage(Storage storage) {
165+
if (storage.getType() != STORAGE_TYPE.SQL) {
166+
throw new UnsupportedOperationException("");
167+
}
168+
return (SAMLStorage) storage;
169+
}
162170
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright (c) 2025, VRAI Labs and/or its affiliates. All rights reserved.
3+
*
4+
* This software is licensed under the Apache License, Version 2.0 (the
5+
* "License") as published by the Apache Software Foundation.
6+
*
7+
* You may not use this file except in compliance with the License. You may
8+
* obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
17+
package io.supertokens.pluginInterface.saml;
18+
19+
import com.google.gson.JsonObject;
20+
21+
public class SAMLClaimsInfo {
22+
public final String clientId;
23+
public final JsonObject claims;
24+
25+
public SAMLClaimsInfo(String clientId, JsonObject claims) {
26+
this.clientId = clientId;
27+
this.claims = claims;
28+
}
29+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright (c) 2025, VRAI Labs and/or its affiliates. All rights reserved.
3+
*
4+
* This software is licensed under the Apache License, Version 2.0 (the
5+
* "License") as published by the Apache Software Foundation.
6+
*
7+
* You may not use this file except in compliance with the License. You may
8+
* obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*
16+
*/
17+
18+
package io.supertokens.pluginInterface.saml;
19+
20+
import com.google.gson.JsonArray;
21+
import com.google.gson.JsonObject;
22+
23+
public class SAMLClient {
24+
public final String clientId;
25+
public final String clientSecret;
26+
public final String ssoLoginURL;
27+
public final JsonArray redirectURIs;
28+
public final String defaultRedirectURI;
29+
public final String idpEntityId;
30+
public final String idpSigningCertificate;
31+
public final boolean allowIDPInitiatedLogin;
32+
public final boolean enableRequestSigning;
33+
34+
public SAMLClient(String clientId, String clientSecret, String ssoLoginURL, JsonArray redirectURIs, String defaultRedirectURI, String idpEntityId, String idpSigningCertificate, boolean allowIDPInitiatedLogin, boolean enableRequestSigning) {
35+
this.clientId = clientId;
36+
this.clientSecret = clientSecret;
37+
this.ssoLoginURL = ssoLoginURL;
38+
this.redirectURIs = redirectURIs;
39+
this.defaultRedirectURI = defaultRedirectURI;
40+
this.idpEntityId = idpEntityId;
41+
this.idpSigningCertificate = idpSigningCertificate;
42+
this.allowIDPInitiatedLogin = allowIDPInitiatedLogin;
43+
this.enableRequestSigning = enableRequestSigning;
44+
}
45+
46+
public JsonObject toJson() {
47+
JsonObject res = new JsonObject();
48+
49+
res.addProperty("clientId", this.clientId);
50+
if (this.clientSecret != null) {
51+
res.addProperty("clientSecret", this.clientSecret);
52+
}
53+
res.addProperty("defaultRedirectURI", this.defaultRedirectURI);
54+
res.add("redirectURIs", redirectURIs);
55+
res.addProperty("idpEntityId", this.idpEntityId);
56+
if (this.idpSigningCertificate != null) {
57+
res.addProperty("idpSigningCertificate", this.idpSigningCertificate);
58+
}
59+
res.addProperty("allowIDPInitiatedLogin", this.allowIDPInitiatedLogin);
60+
res.addProperty("enableRequestSigning", this.enableRequestSigning);
61+
62+
return res;
63+
}
64+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright (c) 2025, VRAI Labs and/or its affiliates. All rights reserved.
3+
*
4+
* This software is licensed under the Apache License, Version 2.0 (the
5+
* "License") as published by the Apache Software Foundation.
6+
*
7+
* You may not use this file except in compliance with the License. You may
8+
* obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
17+
package io.supertokens.pluginInterface.saml;
18+
19+
public class SAMLRelayStateInfo {
20+
public final String relayState;
21+
public final String clientId;
22+
public final String state;
23+
public final String redirectURI;
24+
25+
public SAMLRelayStateInfo(String relayState, String clientId, String state, String redirectURI) {
26+
this.relayState = relayState;
27+
this.clientId = clientId;
28+
this.state = state;
29+
this.redirectURI = redirectURI;
30+
}
31+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright (c) 2025, VRAI Labs and/or its affiliates. All rights reserved.
3+
*
4+
* This software is licensed under the Apache License, Version 2.0 (the
5+
* "License") as published by the Apache Software Foundation.
6+
*
7+
* You may not use this file except in compliance with the License. You may
8+
* obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*
16+
*/
17+
18+
package io.supertokens.pluginInterface.saml;
19+
20+
import java.util.List;
21+
22+
import com.google.gson.JsonObject;
23+
24+
import io.supertokens.pluginInterface.exceptions.StorageQueryException;
25+
import io.supertokens.pluginInterface.multitenancy.TenantIdentifier;
26+
import io.supertokens.pluginInterface.nonAuthRecipe.NonAuthRecipeStorage;
27+
import io.supertokens.pluginInterface.saml.exception.DuplicateEntityIdException;
28+
29+
public interface SAMLStorage extends NonAuthRecipeStorage {
30+
public SAMLClient createOrUpdateSAMLClient(TenantIdentifier tenantIdentifier, SAMLClient samlClient) throws StorageQueryException, DuplicateEntityIdException;
31+
public boolean removeSAMLClient(TenantIdentifier tenantIdentifier, String clientId) throws StorageQueryException;
32+
public SAMLClient getSAMLClient(TenantIdentifier tenantIdentifier, String clientId) throws StorageQueryException;
33+
public SAMLClient getSAMLClientByIDPEntityId(TenantIdentifier tenantIdentifier, String idpEntityId) throws StorageQueryException;
34+
public List<SAMLClient> getSAMLClients(TenantIdentifier tenantIdentifier) throws StorageQueryException;
35+
36+
public void saveRelayStateInfo(TenantIdentifier tenantIdentifier, SAMLRelayStateInfo relayStateInfo, long relayStateValidity) throws StorageQueryException;
37+
public SAMLRelayStateInfo getRelayStateInfo(TenantIdentifier tenantIdentifier, String relayState) throws StorageQueryException;
38+
39+
public void saveSAMLClaims(TenantIdentifier tenantIdentifier, String clientId, String code, JsonObject claims, long claimsValidity) throws StorageQueryException;
40+
public SAMLClaimsInfo getSAMLClaimsAndRemoveCode(TenantIdentifier tenantIdentifier, String code) throws StorageQueryException;
41+
42+
public void removeExpiredSAMLCodesAndRelayStates() throws StorageQueryException;
43+
public int countSAMLClients(TenantIdentifier tenantIdentifier) throws StorageQueryException;
44+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package io.supertokens.pluginInterface.saml.exception;
2+
3+
public class DuplicateEntityIdException extends Exception {
4+
}

0 commit comments

Comments
 (0)