generated from kinde-oss/kinde-oss-repo-template
-
Notifications
You must be signed in to change notification settings - Fork 10
feat: Add Connection ID support across all frameworks #212
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
BrandtKruger
wants to merge
4
commits into
main
Choose a base branch
from
feature/connection-id-support
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
703c1b9
feat: Add Connection ID support across all frameworks
BrandtKruger 7275c40
fix: Update ConnectionIdFilterTest to use Mockito.mockStatic for Kind…
BrandtKruger ee669bc
fix: Address CodeRabbit review comments
BrandtKruger db618f3
fix: Update testGetConnectionIdWithExtProviderButNoConnectionId to pr…
BrandtKruger File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
167 changes: 167 additions & 0 deletions
167
kinde-core/src/test/java/com/kinde/session/ConnectionIdTest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,167 @@ | ||
| package com.kinde.session; | ||
|
|
||
| import com.kinde.KindeClient; | ||
| import com.kinde.KindeClientBuilder; | ||
| import com.kinde.KindeClientSession; | ||
| import com.kinde.authorization.AuthorizationType; | ||
| import com.kinde.authorization.AuthorizationUrl; | ||
| import com.kinde.client.KindeCoreGuiceTestModule; | ||
| import com.kinde.guice.KindeEnvironmentSingleton; | ||
| import com.kinde.guice.KindeGuiceSingleton; | ||
| import com.kinde.token.KindeTokenGuiceTestModule; | ||
| import org.junit.jupiter.api.BeforeEach; | ||
| import org.junit.jupiter.api.DisplayName; | ||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.*; | ||
|
|
||
| public class ConnectionIdTest { | ||
|
|
||
| @BeforeEach | ||
| public void setUp() { | ||
| KindeGuiceSingleton.fin(); | ||
| KindeEnvironmentSingleton.fin(); | ||
| KindeEnvironmentSingleton.init(KindeEnvironmentSingleton.State.TEST); | ||
|
|
||
| KindeGuiceSingleton.init( | ||
| new KindeCoreGuiceTestModule(), | ||
| new KindeTokenGuiceTestModule()); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("authorizationUrlWithParameters should include connection_id when provided") | ||
| public void testAuthorizationUrlWithConnectionId() { | ||
| KindeClient kindeClient = KindeClientBuilder.builder() | ||
| .domain("http://localhost:8089") | ||
| .clientId("test") | ||
| .clientSecret("test") | ||
| .redirectUri("http://localhost:8080/") | ||
| .build(); | ||
|
|
||
| KindeClientSession kindeClientSession = kindeClient.initClientSession("test", null); | ||
|
|
||
| Map<String, String> parameters = new HashMap<>(); | ||
| parameters.put(KindeRequestParameters.CONNECTION_ID, "conn_123456789"); | ||
|
|
||
| AuthorizationUrl authorizationUrl = kindeClientSession.authorizationUrlWithParameters(parameters); | ||
|
|
||
| assertNotNull(authorizationUrl); | ||
| assertNotNull(authorizationUrl.getUrl()); | ||
| String urlString = authorizationUrl.getUrl().toString(); | ||
| assertTrue(urlString.contains("connection_id=conn_123456789"), | ||
| "URL should contain connection_id parameter. URL: " + urlString); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("login should support connection_id via authorizationUrlWithParameters") | ||
| public void testLoginWithConnectionId() { | ||
| KindeClient kindeClient = KindeClientBuilder.builder() | ||
| .domain("http://localhost:8089") | ||
| .clientId("test") | ||
| .clientSecret("test") | ||
| .redirectUri("http://localhost:8080/") | ||
| .build(); | ||
|
|
||
| KindeClientSession kindeClientSession = kindeClient.initClientSession("test", null); | ||
|
|
||
| Map<String, String> parameters = new HashMap<>(); | ||
| parameters.put("supports_reauth", "true"); | ||
| parameters.put(KindeRequestParameters.CONNECTION_ID, "conn_social_google"); | ||
|
|
||
| AuthorizationUrl authorizationUrl = kindeClientSession.authorizationUrlWithParameters(parameters); | ||
|
|
||
| assertNotNull(authorizationUrl); | ||
| assertNotNull(authorizationUrl.getUrl()); | ||
| String urlString = authorizationUrl.getUrl().toString(); | ||
| assertTrue(urlString.contains("connection_id=conn_social_google"), | ||
| "URL should contain connection_id parameter. URL: " + urlString); | ||
| assertTrue(urlString.contains("supports_reauth=true"), | ||
| "URL should contain supports_reauth parameter. URL: " + urlString); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("register should support connection_id via authorizationUrlWithParameters") | ||
| public void testRegisterWithConnectionId() { | ||
| KindeClient kindeClient = KindeClientBuilder.builder() | ||
| .domain("http://localhost:8089") | ||
| .clientId("test") | ||
| .clientSecret("test") | ||
| .redirectUri("http://localhost:8080/") | ||
| .build(); | ||
|
|
||
| KindeClientSession kindeClientSession = kindeClient.initClientSession("test", null); | ||
|
|
||
| Map<String, String> parameters = new HashMap<>(); | ||
| parameters.put("prompt", "create"); | ||
| parameters.put(KindeRequestParameters.CONNECTION_ID, "conn_enterprise_saml"); | ||
|
|
||
| AuthorizationUrl authorizationUrl = kindeClientSession.authorizationUrlWithParameters(parameters); | ||
|
|
||
| assertNotNull(authorizationUrl); | ||
| assertNotNull(authorizationUrl.getUrl()); | ||
| String urlString = authorizationUrl.getUrl().toString(); | ||
| assertTrue(urlString.contains("connection_id=conn_enterprise_saml"), | ||
| "URL should contain connection_id parameter. URL: " + urlString); | ||
| assertTrue(urlString.contains("prompt=create"), | ||
| "URL should contain prompt parameter. URL: " + urlString); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("connection_id should work with CODE grant type") | ||
| public void testConnectionIdWithCodeGrant() { | ||
| KindeClient kindeClient = KindeClientBuilder.builder() | ||
| .domain("http://localhost:8089") | ||
| .clientId("test") | ||
| .clientSecret("test") | ||
| .redirectUri("http://localhost:8080/") | ||
| .grantType(AuthorizationType.CODE) | ||
| .build(); | ||
|
|
||
| KindeClientSession kindeClientSession = kindeClient.initClientSession("test", null); | ||
|
|
||
| Map<String, String> parameters = new HashMap<>(); | ||
| parameters.put(KindeRequestParameters.CONNECTION_ID, "conn_123456789"); | ||
|
|
||
| AuthorizationUrl authorizationUrl = kindeClientSession.authorizationUrlWithParameters(parameters); | ||
|
|
||
| assertNotNull(authorizationUrl); | ||
| assertNotNull(authorizationUrl.getUrl()); | ||
| assertNotNull(authorizationUrl.getCodeVerifier(), "Code verifier should be present for CODE grant type"); | ||
| String urlString = authorizationUrl.getUrl().toString(); | ||
| assertTrue(urlString.contains("connection_id=conn_123456789"), | ||
| "URL should contain connection_id parameter. URL: " + urlString); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("connection_id should work with other parameters like org_code and lang") | ||
| public void testConnectionIdWithOtherParameters() { | ||
| KindeClient kindeClient = KindeClientBuilder.builder() | ||
| .domain("http://localhost:8089") | ||
| .clientId("test") | ||
| .clientSecret("test") | ||
| .redirectUri("http://localhost:8080/") | ||
| .orgCode("ORG123") | ||
| .lang("en") | ||
| .build(); | ||
|
|
||
| KindeClientSession kindeClientSession = kindeClient.initClientSession("test", null); | ||
|
|
||
| Map<String, String> parameters = new HashMap<>(); | ||
| parameters.put(KindeRequestParameters.CONNECTION_ID, "conn_123456789"); | ||
|
|
||
| AuthorizationUrl authorizationUrl = kindeClientSession.authorizationUrlWithParameters(parameters); | ||
|
|
||
| assertNotNull(authorizationUrl); | ||
| assertNotNull(authorizationUrl.getUrl()); | ||
| String urlString = authorizationUrl.getUrl().toString(); | ||
| assertTrue(urlString.contains("connection_id=conn_123456789"), | ||
| "URL should contain connection_id parameter. URL: " + urlString); | ||
| assertTrue(urlString.contains("org_code=ORG123"), | ||
| "URL should contain org_code parameter. URL: " + urlString); | ||
| assertTrue(urlString.contains("lang=en"), | ||
| "URL should contain lang parameter. URL: " + urlString); | ||
| } | ||
| } |
124 changes: 124 additions & 0 deletions
124
kinde-core/src/test/java/com/kinde/token/ConnectionIdTokenTest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| package com.kinde.token; | ||
|
|
||
| import com.kinde.token.jwt.JwtGenerator; | ||
| import org.junit.jupiter.api.DisplayName; | ||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.*; | ||
|
|
||
| public class ConnectionIdTokenTest { | ||
|
|
||
| @Test | ||
| @DisplayName("getConnectionId should return connection_id from token when present as direct claim") | ||
| public void testGetConnectionIdDirectClaim() throws Exception { | ||
| String connectionId = "conn_123456789"; | ||
| String tokenString = JwtGenerator.generateIDTokenWithConnectionId(connectionId); | ||
|
|
||
| KindeToken kindeToken = IDToken.init(tokenString, true); | ||
|
|
||
| assertNotNull(kindeToken); | ||
| assertTrue(kindeToken.valid()); | ||
| assertEquals(connectionId, kindeToken.getConnectionId(), | ||
| "getConnectionId() should return the connection_id from the token"); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("getConnectionId should return connection_id from ext_provider nested structure") | ||
| public void testGetConnectionIdFromExtProvider() throws Exception { | ||
| String connectionId = "conn_enterprise_saml_789"; | ||
| String tokenString = JwtGenerator.generateIDTokenWithExtProviderConnectionId(connectionId); | ||
|
|
||
| KindeToken kindeToken = IDToken.init(tokenString, true); | ||
|
|
||
| assertNotNull(kindeToken); | ||
| assertTrue(kindeToken.valid()); | ||
| assertEquals(connectionId, kindeToken.getConnectionId(), | ||
| "getConnectionId() should return the connection_id from ext_provider.connection_id"); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("getConnectionId should return null when connection_id is not present") | ||
| public void testGetConnectionIdWhenNotPresent() throws Exception { | ||
| String tokenString = JwtGenerator.generateIDToken(); | ||
|
|
||
| KindeToken kindeToken = IDToken.init(tokenString, true); | ||
|
|
||
| assertNotNull(kindeToken); | ||
| assertTrue(kindeToken.valid()); | ||
| assertNull(kindeToken.getConnectionId(), | ||
| "getConnectionId() should return null when connection_id is not in the token"); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("getConnectionId should prefer direct connection_id over nested ext_provider.connection_id") | ||
| public void testGetConnectionIdPreferDirectOverNested() throws Exception { | ||
| // Create a token with both direct and nested connection_id to test preference | ||
| String directConnectionId = "conn_direct_123"; | ||
| String nestedConnectionId = "conn_nested_456"; | ||
|
|
||
| String tokenString = JwtGenerator.generateIDTokenWithBothConnectionIds(directConnectionId, nestedConnectionId); | ||
|
|
||
| KindeToken kindeToken = IDToken.init(tokenString, true); | ||
|
|
||
| assertNotNull(kindeToken); | ||
| assertTrue(kindeToken.valid()); | ||
| assertEquals(directConnectionId, kindeToken.getConnectionId(), | ||
| "getConnectionId() should prefer direct connection_id over ext_provider.connection_id"); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("getConnectionId should work with AccessToken") | ||
| public void testGetConnectionIdWithAccessToken() throws Exception { | ||
| String connectionId = "conn_access_token_123"; | ||
| String tokenString = JwtGenerator.generateIDTokenWithConnectionId(connectionId); | ||
|
|
||
| // AccessToken uses the same BaseToken implementation | ||
| KindeToken kindeToken = AccessToken.init(tokenString, true); | ||
|
|
||
| assertNotNull(kindeToken); | ||
| assertTrue(kindeToken.valid()); | ||
| assertEquals(connectionId, kindeToken.getConnectionId(), | ||
| "getConnectionId() should work with AccessToken"); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("getConnectionId should return null for invalid token") | ||
| public void testGetConnectionIdWithInvalidToken() throws Exception { | ||
| String tokenString = "invalid.token.string"; | ||
|
|
||
| KindeToken kindeToken = IDToken.init(tokenString, false); | ||
|
|
||
| assertNotNull(kindeToken); | ||
| assertFalse(kindeToken.valid()); | ||
| assertNull(kindeToken.getConnectionId(), | ||
| "getConnectionId() should return null for invalid tokens"); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("getConnectionId should handle null ext_provider gracefully") | ||
| public void testGetConnectionIdWithNullExtProvider() throws Exception { | ||
| // Token without ext_provider should work fine | ||
| String tokenString = JwtGenerator.generateIDToken(); | ||
|
|
||
| KindeToken kindeToken = IDToken.init(tokenString, true); | ||
|
|
||
| assertNotNull(kindeToken); | ||
| assertTrue(kindeToken.valid()); | ||
| assertNull(kindeToken.getConnectionId(), | ||
| "getConnectionId() should handle missing ext_provider gracefully"); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("getConnectionId should handle ext_provider without connection_id gracefully") | ||
| public void testGetConnectionIdWithExtProviderButNoConnectionId() throws Exception { | ||
| // This test verifies that if ext_provider exists but doesn't have connection_id, it returns null | ||
| String tokenString = JwtGenerator.generateIDTokenWithExtProviderButNoConnectionId(); | ||
|
|
||
| KindeToken kindeToken = IDToken.init(tokenString, true); | ||
|
|
||
| assertNotNull(kindeToken); | ||
| assertTrue(kindeToken.valid()); | ||
| assertNull(kindeToken.getConnectionId(), | ||
| "getConnectionId() should return null when ext_provider exists but has no connection_id"); | ||
| } | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.