Skip to content
This repository was archived by the owner on Dec 12, 2018. It is now read-only.

Commit b9c0c15

Browse files
committed
Using JwtSigningKeyResolver component in CookieAuthenticationResultSaver instead of duplicate code
1 parent 7b6564a commit b9c0c15

File tree

5 files changed

+71
-30
lines changed

5 files changed

+71
-30
lines changed

extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/account/CookieAuthenticationResultSaver.java

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
import io.jsonwebtoken.JwsHeader;
4040
import io.jsonwebtoken.Jwts;
4141
import io.jsonwebtoken.SignatureAlgorithm;
42+
import io.jsonwebtoken.SigningKeyResolver;
43+
import io.jsonwebtoken.SigningKeyResolverAdapter;
4244
import org.joda.time.DateTime;
4345
import org.joda.time.Seconds;
4446
import org.slf4j.Logger;
@@ -47,6 +49,7 @@
4749
import javax.servlet.http.HttpServletRequest;
4850
import javax.servlet.http.HttpServletResponse;
4951
import java.io.UnsupportedEncodingException;
52+
import java.security.Key;
5053
import java.util.Date;
5154

5255
/**
@@ -65,15 +68,19 @@ public class CookieAuthenticationResultSaver implements Saver<AuthenticationResu
6568
private final CookieConfig accessTokenCookieConfig;
6669
private final CookieConfig refreshTokenCookieConfig;
6770

71+
private final JwtSigningKeyResolver signingKeyResolver;
72+
6873
public CookieAuthenticationResultSaver(CookieConfig accessTokenCookieConfig,
6974
CookieConfig refreshTokenCookieConfig,
70-
Resolver<Boolean> secureCookieRequired) {
75+
Resolver<Boolean> secureCookieRequired,
76+
JwtSigningKeyResolver signingKeyResolver) {
7177
Assert.notNull(accessTokenCookieConfig, "accessTokenCookieConfig cannot be null.");
7278
Assert.notNull(refreshTokenCookieConfig, "refreshTokenCookieConfig cannot be null.");
7379
Assert.notNull(secureCookieRequired, "secureCookieRequired cannot be null.");
7480
this.accessTokenCookieConfig = accessTokenCookieConfig;
7581
this.refreshTokenCookieConfig = refreshTokenCookieConfig;
7682
this.secureCookieRequired = secureCookieRequired;
83+
this.signingKeyResolver = signingKeyResolver;
7784
}
7885

7986
@Override
@@ -93,10 +100,10 @@ public void set(HttpServletRequest request, HttpServletResponse response, Authen
93100
String accessToken = accessTokenResult.getTokenResponse().getAccessToken();
94101
String refreshToken = accessTokenResult.getTokenResponse().getRefreshToken();
95102

96-
getAccessTokenCookieSaver(request, getMaxAge(accessToken, clientSecret, accessTokenCookieConfig)).set(request, response, accessToken);
103+
getAccessTokenCookieSaver(request, getMaxAge(accessToken, clientSecret, accessTokenCookieConfig, request, response)).set(request, response, accessToken);
97104
//Client Credentials auth workflow doesn't contains a refresh token
98105
if (Strings.hasText(refreshToken)) {
99-
getRefreshTokenCookieSaver(request, getMaxAge(refreshToken, clientSecret, refreshTokenCookieConfig)).set(request, response, refreshToken);
106+
getRefreshTokenCookieSaver(request, getMaxAge(refreshToken, clientSecret, refreshTokenCookieConfig, request, response)).set(request, response, refreshToken);
100107
}
101108
}
102109
if (value instanceof TransientAuthenticationResult) {
@@ -121,8 +128,8 @@ public void set(HttpServletRequest request, HttpServletResponse response, Authen
121128
String accessToken = authenticationResult.getAccessTokenString();
122129
String refreshToken = authenticationResult.getRefreshTokenString();
123130

124-
getAccessTokenCookieSaver(request, getMaxAge(accessToken, clientSecret, accessTokenCookieConfig)).set(request, response, accessToken);
125-
getRefreshTokenCookieSaver(request, getMaxAge(refreshToken, clientSecret, refreshTokenCookieConfig)).set(request, response, refreshToken);
131+
getAccessTokenCookieSaver(request, getMaxAge(accessToken, clientSecret, accessTokenCookieConfig, request, response)).set(request, response, accessToken);
132+
getRefreshTokenCookieSaver(request, getMaxAge(refreshToken, clientSecret, refreshTokenCookieConfig, request, response)).set(request, response, refreshToken);
126133
} catch (UnsupportedEncodingException e) {
127134
//Should not happen since UTF-8 should always be a supported encoding, but we logged just in case
128135
log.error("Error get the client API Secret", e);
@@ -234,7 +241,7 @@ public String getDomain() {
234241

235242
@Override
236243
public int getMaxAge() {
237-
return 1;
244+
return maxAge;
238245
}
239246

240247
@Override
@@ -254,18 +261,28 @@ public boolean isHttpOnly() {
254261
});
255262
}
256263

257-
private int getMaxAge(String token, byte[] clientSecret, CookieConfig cookieConfig) {
264+
private int getMaxAge(String token, byte[] clientSecret, CookieConfig cookieConfig, HttpServletRequest request, HttpServletResponse response) {
258265
// non-zero indicates override from cookie config
259-
// if (cookieConfig.getMaxAge() != 0) {
266+
if (cookieConfig.getMaxAge() != 0 || token.split("\\.").length != 3) {
260267
return cookieConfig.getMaxAge();
261-
// }
262-
263-
// // otherwise, use the claims in the JWT to determine maxAge
264-
// Jws<Claims> claimsJws = Jwts.parser().setSigningKey(clientSecret).parseClaimsJws(token);
265-
// DateTime issueAt = new DateTime(claimsJws.getBody().getIssuedAt());
266-
// DateTime expiration = new DateTime(claimsJws.getBody().getExpiration());
267-
//
268-
//
269-
// return Seconds.secondsBetween(issueAt, expiration).getSeconds() - Seconds.secondsBetween(issueAt, DateTime.now()).getSeconds();
268+
}
269+
270+
// otherwise, use the claims in the JWT to determine maxAge
271+
Jws<Claims> claimsJws = Jwts.parser().setSigningKeyResolver(createKeyResolver(request, response)).parseClaimsJws(token);
272+
DateTime issueAt = new DateTime(claimsJws.getBody().getIssuedAt());
273+
DateTime expiration = new DateTime(claimsJws.getBody().getExpiration());
274+
275+
276+
return Seconds.secondsBetween(issueAt, expiration).getSeconds() - Seconds.secondsBetween(issueAt, DateTime.now()).getSeconds();
277+
}
278+
279+
private SigningKeyResolver createKeyResolver(final HttpServletRequest request, final HttpServletResponse response) {
280+
return new SigningKeyResolverAdapter() {
281+
282+
@Override
283+
public Key resolveSigningKey(JwsHeader header, Claims claims) {
284+
return signingKeyResolver.getSigningKey(request, response, header, claims);
285+
}
286+
};
270287
}
271288
}

extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/account/config/CookieAuthenticationResultSaverFactory.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.stormpath.sdk.servlet.config.ConfigSingletonFactory;
2222
import com.stormpath.sdk.servlet.config.CookieConfig;
2323
import com.stormpath.sdk.servlet.filter.account.CookieAuthenticationResultSaver;
24+
import com.stormpath.sdk.servlet.filter.account.JwtSigningKeyResolver;
2425
import com.stormpath.sdk.servlet.http.Resolver;
2526
import com.stormpath.sdk.servlet.http.Saver;
2627

@@ -39,10 +40,12 @@ protected Saver<AuthenticationResult> createInstance(ServletContext servletConte
3940
CookieConfig accessTokenCookieConfig = config.getAccessTokenCookieConfig();
4041
CookieConfig refreshTokenCookieConfig = config.getRefreshTokenCookieConfig();
4142
Resolver<Boolean> secureCookieRequired = config.getInstance(COOKIE_SECURE_RESOLVER);
43+
JwtSigningKeyResolver signingKeyResolver = config.getInstance(JwtSigningKeyResolver.class.getName()); // TODO: does this work?
4244
return new CookieAuthenticationResultSaver(
4345
accessTokenCookieConfig,
4446
refreshTokenCookieConfig,
45-
secureCookieRequired
47+
secureCookieRequired,
48+
signingKeyResolver
4649
);
4750
}
4851
}

extensions/servlet/src/test/groovy/com/stormpath/sdk/servlet/filter/account/CookieAuthenticationResultSaverHttpsWarningTest.groovy

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import org.testng.annotations.Test
1111
import javax.servlet.http.HttpServletRequest
1212
import javax.servlet.http.HttpServletResponse
1313

14+
import static org.easymock.EasyMock.createMock
1415
import static org.easymock.EasyMock.expect
1516
import static org.easymock.EasyMock.isA
1617
import static org.easymock.EasyMock.isNull
@@ -41,7 +42,7 @@ class CookieAuthenticationResultSaverHttpsWarningTest extends PowerMockTestCase
4142
HttpServletRequest request = createMock(HttpServletRequest.class)
4243
CookieConfig accessTokenConfig = createMock(CookieConfig.class)
4344
CookieConfig refreshTokenConfig = createMock(CookieConfig.class)
44-
45+
JwtSigningKeyResolver signingKeyResolver = createMock(JwtSigningKeyResolver.class)
4546
def resolver = createMock(Resolver.class)
4647

4748
expect(accessTokenConfig.isSecure()).andReturn(true)
@@ -50,13 +51,13 @@ class CookieAuthenticationResultSaverHttpsWarningTest extends PowerMockTestCase
5051
expect(log.warn(isA(String)))
5152

5253
replay LoggerFactory.class
53-
replay log, request, accessTokenConfig, refreshTokenConfig, resolver
54+
replay log, request, accessTokenConfig, refreshTokenConfig, resolver, signingKeyResolver
5455

55-
def saver = new CookieAuthenticationResultSaver(accessTokenConfig, refreshTokenConfig, resolver)
56+
def saver = new CookieAuthenticationResultSaver(accessTokenConfig, refreshTokenConfig, resolver, signingKeyResolver)
5657
assertFalse saver.isCookieSecure(request, accessTokenConfig)
5758

5859
verify LoggerFactory.class
59-
verify log, request, accessTokenConfig, refreshTokenConfig, resolver
60+
verify log, request, accessTokenConfig, refreshTokenConfig, resolver, signingKeyResolver
6061
reset LoggerFactory.class
6162
}
6263
}

extensions/servlet/src/test/groovy/com/stormpath/sdk/servlet/filter/account/CookieAuthenticationResultSaverTest.groovy

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class CookieAuthenticationResultSaverTest extends PowerMockTestCase {
4343
HttpServletRequest request = createMock(HttpServletRequest.class)
4444
CookieConfig accessTokenConfig = createMock(CookieConfig.class)
4545
CookieConfig refreshTokenConfig = createMock(CookieConfig.class)
46+
JwtSigningKeyResolver signingKeyResolver = createMock(JwtSigningKeyResolver.class)
4647

4748
//Let's make the IsRequestSecureResolver return true
4849
def resolver = new Resolver<Boolean>() {
@@ -55,13 +56,13 @@ class CookieAuthenticationResultSaverTest extends PowerMockTestCase {
5556
expect(accessTokenConfig.isSecure()).andReturn(true)
5657
expect(request.getScheme()).andReturn "http"
5758

58-
replay request, accessTokenConfig, refreshTokenConfig
59+
replay request, accessTokenConfig, refreshTokenConfig, signingKeyResolver
5960

60-
def saver = new CookieAuthenticationResultSaver(accessTokenConfig, refreshTokenConfig, resolver)
61+
def saver = new CookieAuthenticationResultSaver(accessTokenConfig, refreshTokenConfig, resolver, signingKeyResolver)
6162

6263
assertFalse saver.isCookieSecure(request, accessTokenConfig)
6364

64-
verify request, refreshTokenConfig, refreshTokenConfig
65+
verify request, refreshTokenConfig, refreshTokenConfig, signingKeyResolver
6566
}
6667

6768
/**
@@ -73,6 +74,7 @@ class CookieAuthenticationResultSaverTest extends PowerMockTestCase {
7374
HttpServletRequest request = createMock(HttpServletRequest.class)
7475
CookieConfig accessTokenConfig = createMock(CookieConfig.class)
7576
CookieConfig refreshTokenConfig = createMock(CookieConfig.class)
77+
JwtSigningKeyResolver signingKeyResolver = createMock(JwtSigningKeyResolver.class)
7678
def localhost = createMock(Resolver.class)
7779

7880
def resolver = new SecureRequiredExceptForLocalhostResolver(localhost) {
@@ -85,12 +87,12 @@ class CookieAuthenticationResultSaverTest extends PowerMockTestCase {
8587
expect(accessTokenConfig.isSecure()).andReturn(true)
8688
expect(request.getScheme()).andReturn "https"
8789

88-
replay request, accessTokenConfig, refreshTokenConfig, localhost
90+
replay request, accessTokenConfig, refreshTokenConfig, localhost, signingKeyResolver
8991

90-
def saver = new CookieAuthenticationResultSaver(accessTokenConfig, refreshTokenConfig, resolver)
92+
def saver = new CookieAuthenticationResultSaver(accessTokenConfig, refreshTokenConfig, resolver, signingKeyResolver)
9193

9294
assertFalse saver.isCookieSecure(request, accessTokenConfig)
9395

94-
verify request, refreshTokenConfig, refreshTokenConfig, localhost
96+
verify request, refreshTokenConfig, refreshTokenConfig, localhost, signingKeyResolver
9597
}
9698
}

extensions/spring/stormpath-spring-webmvc/src/main/java/com/stormpath/spring/config/AbstractStormpathWebMvcConfiguration.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@
169169
import com.stormpath.spring.mvc.TemplateLayoutInterceptor;
170170
import com.stormpath.spring.mvc.VerifyControllerConfig;
171171
import com.stormpath.spring.util.SpringPatternMatcher;
172+
import io.jsonwebtoken.Claims;
173+
import io.jsonwebtoken.JwsHeader;
172174
import io.jsonwebtoken.SignatureAlgorithm;
173175
import org.slf4j.Logger;
174176
import org.slf4j.LoggerFactory;
@@ -212,6 +214,7 @@
212214
import javax.servlet.http.HttpServletRequest;
213215
import javax.servlet.http.HttpServletResponse;
214216
import java.io.IOException;
217+
import java.security.Key;
215218
import java.util.ArrayList;
216219
import java.util.Arrays;
217220
import java.util.HashMap;
@@ -755,7 +758,8 @@ public Saver<AuthenticationResult> stormpathCookieAuthenticationResultSaver() {
755758
return new CookieAuthenticationResultSaver(
756759
stormpathAccessTokenCookieConfig(),
757760
stormpathRefreshTokenCookieConfig(),
758-
stormpathSecureResolver()
761+
stormpathSecureResolver(),
762+
stormpathJwtSigningKeyResolver()
759763
);
760764
}
761765

@@ -789,7 +793,21 @@ public Saver<AuthenticationResult> stormpathAuthenticationResultSaver() {
789793
}
790794

791795
public JwtSigningKeyResolver stormpathJwtSigningKeyResolver() {
792-
return new JwtTokenSigningKeyResolver();
796+
if (oktaEnabled) {
797+
return new JwtSigningKeyResolver() {
798+
@Override
799+
public Key getSigningKey(HttpServletRequest request, HttpServletResponse response, AuthenticationResult result, SignatureAlgorithm alg) {
800+
throw new UnsupportedOperationException("getSigningKey() is not supported");
801+
}
802+
803+
@Override
804+
public Key getSigningKey(HttpServletRequest request, HttpServletResponse response, JwsHeader jwsHeader, Claims claims) {
805+
return client.instantiate(OktaSigningKeyResolver.class).resolveSigningKey(jwsHeader, claims);
806+
}
807+
};
808+
} else {
809+
return new JwtTokenSigningKeyResolver();
810+
}
793811
}
794812

795813
public RequestEventListener stormpathRequestEventListener() {

0 commit comments

Comments
 (0)