Skip to content

Commit aa6adde

Browse files
Merge pull request #104 from KomanRudden/bugfix/issue-103-refresh-token
Bugfix/issue 103 refresh token
2 parents c3efa12 + 7b648d3 commit aa6adde

File tree

12 files changed

+263
-103
lines changed

12 files changed

+263
-103
lines changed

kinde-core/src/main/java/com/kinde/authorization/AuthorizationType.java

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,19 @@
11
package com.kinde.authorization;
22

3-
import com.kinde.config.KindeParameters;
3+
import lombok.Getter;
44

55
import java.util.ArrayList;
6-
import java.util.Arrays;
76
import java.util.List;
8-
import java.util.function.Function;
97

108
public enum AuthorizationType {
119
CODE("CODE",List.of("authorization_code")),
1210
IMPLICIT("IMPLICIT"),
1311
CUSTOM("CUSTOM");
1412

15-
// Field to store the string value
13+
@Getter
1614
private final String value;
1715
private final List<String> values = new ArrayList<>();
1816

19-
// Private constructor to initialize the enum constants
2017
AuthorizationType(String value) {
2118
this.value = value;
2219
}
@@ -26,12 +23,6 @@ public enum AuthorizationType {
2623
this.values.addAll(values);
2724
}
2825

29-
// Getter method to retrieve the string value
30-
public String getValue() {
31-
return value;
32-
}
33-
34-
// Method to get the enum constant by string value
3526
public static AuthorizationType fromValue(Object value) {
3627
if (value instanceof AuthorizationType) {
3728
return (AuthorizationType)value;
@@ -40,7 +31,7 @@ public static AuthorizationType fromValue(Object value) {
4031
if (constant.value.equals(value)) {
4132
return constant;
4233
}
43-
if (constant.values.stream().filter(entry->entry.equals(value)).findAny().isPresent()) {
34+
if (constant.values.stream().anyMatch(entry->entry.equals(value))) {
4435
return constant;
4536
}
4637
}

kinde-core/src/main/java/com/kinde/token/BaseToken.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,32 @@
66
import java.util.List;
77
import java.util.Map;
88

9+
import static com.kinde.token.JwtValidator.isJwt;
10+
911
public class BaseToken implements KindeToken {
1012

11-
private String token;
12-
private boolean valid;
13-
private SignedJWT signedJWT;
13+
private final String token;
14+
private final boolean valid;
15+
private final SignedJWT signedJWT;
1416

1517
@SneakyThrows
1618
protected BaseToken(String token, boolean valid) {
1719
this.token = token;
1820
this.valid = valid;
19-
signedJWT = SignedJWT.parse(this.token);
21+
SignedJWT parsedJwt = JwtValidator.isJwt(this.token);
22+
if (parsedJwt != null) {
23+
signedJWT = parsedJwt;
24+
} else {
25+
signedJWT = null;
26+
}
2027
}
2128

2229
@Override
2330
public boolean valid() {
2431
return valid;
2532
}
2633

34+
@Override
2735
public String token() {
2836
return this.token;
2937
}
@@ -34,16 +42,20 @@ public String getUser() {
3442
return this.signedJWT.getJWTClaimsSet().getSubject();
3543
}
3644

45+
@SuppressWarnings("unchecked")
3746
@Override
3847
public List<String> getOrganisations() {
3948
return (List<String>)this.getClaim("org_codes");
4049
}
4150

4251
@SneakyThrows
52+
@Override
4353
public Object getClaim(String key) {
4454
return this.signedJWT.getJWTClaimsSet().getClaim(key);
4555
}
4656

57+
@SuppressWarnings("unchecked")
58+
@Override
4759
public List<String> getPermissions() {
4860
return (List<String>) getClaim("permissions");
4961
}
@@ -63,10 +75,12 @@ public Boolean getBooleanFlag(String key) {
6375
return (Boolean) getFlagClaims().get(key);
6476
}
6577

78+
@SuppressWarnings("unchecked")
6679
private Map<String,Object> getFlagClaims() {
6780
return ((Map<String,Object>)getClaim("feature_flags"));
6881
}
6982

83+
@SuppressWarnings("unchecked")
7084
public Map<String,Object> getFlags() {
7185
return (Map<String,Object>) getClaim("feature_flags");
7286
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.kinde.token;
2+
3+
import com.nimbusds.jwt.SignedJWT;
4+
5+
import java.text.ParseException;
6+
7+
public class JwtValidator {
8+
public static SignedJWT isJwt(String token) {
9+
if (token == null || token.trim().isEmpty()) {
10+
return null;
11+
}
12+
13+
try {
14+
return SignedJWT.parse(token);
15+
} catch (ParseException e) {
16+
return null;
17+
}
18+
}
19+
}

kinde-core/src/main/java/com/kinde/token/KindeTokenFactoryImpl.java

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import com.kinde.KindeTokenFactory;
55
import com.kinde.client.OidcMetaData;
66
import com.kinde.token.jwk.KindeJwkStore;
7-
import com.nimbusds.jose.JWSVerifier;
87
import com.nimbusds.jose.crypto.RSASSAVerifier;
98
import com.nimbusds.jose.jwk.RSAKey;
109
import com.nimbusds.jose.jwk.JWK;
@@ -15,50 +14,55 @@
1514
import com.nimbusds.jose.proc.SecurityContext;
1615
import com.nimbusds.jwt.JWTClaimsSet;
1716
import com.nimbusds.jwt.SignedJWT;
17+
1818
import lombok.SneakyThrows;
1919
import lombok.extern.slf4j.Slf4j;
2020

2121
import java.util.List;
2222

23+
import static com.kinde.token.JwtValidator.isJwt;
24+
2325
@Slf4j
2426
public class KindeTokenFactoryImpl implements KindeTokenFactory {
2527

26-
private OidcMetaData oidcMetaData;
27-
private KindeJwkStore kindeJwkStore;
28+
private final KindeJwkStore kindeJwkStore;
2829

2930
@Inject
30-
public KindeTokenFactoryImpl(OidcMetaData oidcMetaData,KindeJwkStore kindeJwkStore) {
31-
this.oidcMetaData = oidcMetaData;
31+
public KindeTokenFactoryImpl(KindeJwkStore kindeJwkStore) {
3232
this.kindeJwkStore = kindeJwkStore;
3333
}
3434

35-
3635
@SneakyThrows
3736
public KindeToken parse(String token) {
37+
SignedJWT parsedJwt = JwtValidator.isJwt(token);
38+
if (parsedJwt == null) {
39+
log.debug("Token is not a JWT, returning as is");
40+
return RefreshToken.init(token, true);
41+
}
3842
SignedJWT signedJWT = SignedJWT.parse(token);
3943

40-
JWKSelector jwkSelector = new JWKSelector(new JWKMatcher.Builder().keyID(signedJWT.getHeader().getKeyID()).build());
44+
JWKSelector jwkSelector = new JWKSelector(
45+
new JWKMatcher.Builder().keyID(signedJWT.getHeader().getKeyID()).build());
4146
JWKSource<SecurityContext> jwkSource = new ImmutableJWKSet<>(this.kindeJwkStore.publicKeys());
4247
List<JWK> jwks = jwkSource.get(jwkSelector, null);
4348
if (jwks.isEmpty()) {
4449
throw new IllegalStateException("No matching JWK found");
4550
}
4651
JWK jwk = jwks.get(0);
4752
boolean valid = false;
48-
if (jwk instanceof RSAKey) {
49-
RSAKey rsaKey = (RSAKey) jwk;
53+
if (jwk instanceof RSAKey rsaKey) {
5054
RSASSAVerifier verifier = new RSASSAVerifier(rsaKey);
5155
valid = signedJWT.verify(verifier);
5256
}
5357

5458
JWTClaimsSet claimsSet = signedJWT.getJWTClaimsSet();
5559

5660
if (claimsSet.getClaim("idp") != null || claimsSet.getStringClaim("nonce") != null) {
57-
return IDToken.init(token,valid);
61+
return IDToken.init(token, valid);
5862
} else if (claimsSet.getStringClaim("refresh_token") != null) {
59-
return RefreshToken.init(token,valid);
63+
return RefreshToken.init(token, valid);
6064
} else {
61-
return AccessToken.init(token,valid);
65+
return AccessToken.init(token, valid);
6266
}
6367
}
6468

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
package com.kinde.token;
22

3-
import com.nimbusds.jwt.SignedJWT;
4-
import lombok.SneakyThrows;
5-
6-
import java.util.List;
7-
83
public class RefreshToken extends BaseToken {
94

105
private RefreshToken(String token, boolean valid) {
@@ -14,6 +9,4 @@ private RefreshToken(String token, boolean valid) {
149
public static RefreshToken init(String token,boolean valid) {
1510
return new RefreshToken(token,valid);
1611
}
17-
18-
1912
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.kinde.token;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import static org.junit.jupiter.api.Assertions.*;
6+
7+
class JwtValidatorTest {
8+
9+
@Test
10+
public void isJwtReturnsTrueForValidJwt() {
11+
String validJwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
12+
assertNotNull(JwtValidator.isJwt(validJwt));
13+
}
14+
15+
@Test
16+
public void isJwtReturnsFalseForNullToken() {
17+
assertNull(JwtValidator.isJwt(null));
18+
}
19+
20+
@Test
21+
public void isJwtReturnsFalseForEmptyToken() {
22+
assertNull(JwtValidator.isJwt(""));
23+
}
24+
25+
@Test
26+
public void isJwtReturnsFalseForTokenWithLessThanThreeParts() {
27+
String invalidJwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ";
28+
assertNull(JwtValidator.isJwt(invalidJwt));
29+
}
30+
31+
@Test
32+
public void isJwtReturnsFalseForTokenWithMoreThanThreeParts() {
33+
String invalidJwt = "part1.part2.part3.part4";
34+
assertNull(JwtValidator.isJwt(invalidJwt));
35+
}
36+
37+
}

playground/kinde-core-example/src/test/java/com/kinde/KindeCoreExampleTest.java

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,23 @@
11
package com.kinde;
22

3-
import com.kinde.token.KindeToken;
43
import com.kinde.token.KindeTokens;
54
import junit.framework.Test;
65
import junit.framework.TestCase;
76
import junit.framework.TestSuite;
87
import org.junit.Ignore;
98

10-
import java.util.List;
9+
public class KindeCoreExampleTest extends TestCase {
1110

12-
13-
/**
14-
* Unit test for simple App.
15-
*/
16-
public class KindeCoreExampleTest
17-
extends TestCase
18-
{
19-
/**
20-
* Create the test case
21-
*
22-
* @param testName name of the test case
23-
*/
24-
public KindeCoreExampleTest(String testName )
25-
{
26-
super( testName );
11+
public KindeCoreExampleTest(String testName) {
12+
super(testName);
2713
}
2814

29-
/**
30-
* @return the suite of tests being tested
31-
*/
32-
public static Test suite()
33-
{
34-
return new TestSuite( KindeCoreExampleTest.class );
15+
public static Test suite() {
16+
return new TestSuite(KindeCoreExampleTest.class);
3517
}
3618

37-
/**
38-
* Rigourous Test :-)
39-
*/
4019
@Ignore
41-
public void testApp() throws Exception {
20+
public void testApp() {
4221
System.out.println("Test the kinde builder");
4322
KindeClient kindeClient = KindeClientBuilder
4423
.builder()

playground/springboot-thymeleaf-full-example/src/main/java/com/kinde/oauth/controller/KindeController.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.kinde.oauth.controller;
22

33
import com.kinde.oauth.service.KindeService;
4+
import jakarta.servlet.http.HttpSession;
45
import lombok.extern.slf4j.Slf4j;
56
import org.springframework.security.access.prepost.PreAuthorize;
67
import org.springframework.stereotype.Controller;
@@ -38,16 +39,25 @@ public String home() {
3839
return "home";
3940
}
4041

42+
/**
43+
* Handles requests to the logout page.
44+
*
45+
* @return the name of the "logout" view.
46+
*/
47+
@RequestMapping(path = {"/sdkLogout"}, method = RequestMethod.GET)
48+
public String logout() throws Exception {
49+
return kindeService.logout();
50+
}
51+
4152
/**
4253
* Handles requests to the dashboard page, loading the authenticated user's Kinde profile.
4354
*
4455
* @param model the {@link Model} used to pass attributes to the view.
4556
* @return the name of the "dashboard" view.
4657
*/
4758
@GetMapping(path = "/dashboard")
48-
public String dashboard(Model model) {
49-
model.addAttribute("kindeUser", kindeService.loadDashboard());
50-
return "dashboard";
59+
public String dashboard(HttpSession session, Model model) {
60+
return kindeService.loadDashboard(session, model);
5161
}
5262

5363
/**
@@ -82,4 +92,9 @@ public String readEndpoint() {
8292
public String writeEndpoint() {
8393
return "write";
8494
}
95+
96+
@GetMapping("/callSdkApi")
97+
public String callSdkApi(HttpSession session, Model model) {
98+
return kindeService.callSdkApi(session, model);
99+
}
85100
}

playground/springboot-thymeleaf-full-example/src/main/java/com/kinde/oauth/model/KindeProfile.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
public class KindeProfile {
1212
private String idToken;
1313
private String accessToken;
14+
private String refreshToken;
1415
private String fullName;
1516
private Set<GrantedAuthority> roles;
1617
private String userProfile;

0 commit comments

Comments
 (0)