diff --git a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/OAuth2AccessTokenImpl.java b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/OAuth2AccessTokenImpl.java index 166747953..fc2528847 100644 --- a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/OAuth2AccessTokenImpl.java +++ b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/OAuth2AccessTokenImpl.java @@ -16,16 +16,11 @@ ******************************************************************************/ package org.mitre.oauth2.introspectingfilter; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.common.OAuth2RefreshToken; @@ -50,13 +45,8 @@ public class OAuth2AccessTokenImpl implements OAuth2AccessToken { scopes = Sets.newHashSet(Splitter.on(" ").split(token.get("scope").getAsString())); } - DateFormat dateFormater = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); if (token.get("exp") != null) { - try { - expireDate = dateFormater.parse(token.get("exp").getAsString()); - } catch (ParseException ex) { - Logger.getLogger(IntrospectingTokenService.class.getName()).log(Level.SEVERE, null, ex); - } + expireDate = new Date(token.get("exp").getAsLong() * 1000L); } } diff --git a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/TestOAuth2AccessTokenImpl.java b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/TestOAuth2AccessTokenImpl.java new file mode 100644 index 000000000..ac38255f1 --- /dev/null +++ b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/TestOAuth2AccessTokenImpl.java @@ -0,0 +1,90 @@ +package org.mitre.oauth2.introspectingfilter; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.util.Collections; +import java.util.Date; +import java.util.Set; + +import org.junit.Test; + +import com.google.common.collect.ImmutableSet; +import com.google.gson.JsonObject; + +public class TestOAuth2AccessTokenImpl { + + private static String tokenString = "thisisatokenstring"; + + private static Set scopes = ImmutableSet.of("bar", "foo"); + private static String scopeString = "foo bar"; + + private static Date exp = new Date(123 * 1000L); + private static Long expVal = 123L; + + @Test + public void testFullToken() { + + + JsonObject tokenObj = new JsonObject(); + tokenObj.addProperty("active", true); + tokenObj.addProperty("scope", scopeString); + tokenObj.addProperty("exp", expVal); + tokenObj.addProperty("sub", "subject"); + tokenObj.addProperty("client_id", "123-456-789"); + + OAuth2AccessTokenImpl tok = new OAuth2AccessTokenImpl(tokenObj, tokenString); + + assertThat(tok.getScope(), is(equalTo(scopes))); + assertThat(tok.getExpiration(), is(equalTo(exp))); + } + + @Test + public void testNullExp() { + + + JsonObject tokenObj = new JsonObject(); + tokenObj.addProperty("active", true); + tokenObj.addProperty("scope", scopeString); + tokenObj.addProperty("sub", "subject"); + tokenObj.addProperty("client_id", "123-456-789"); + + OAuth2AccessTokenImpl tok = new OAuth2AccessTokenImpl(tokenObj, tokenString); + + assertThat(tok.getScope(), is(equalTo(scopes))); + assertThat(tok.getExpiration(), is(equalTo(null))); + } + + @Test + public void testNullScopes() { + + + JsonObject tokenObj = new JsonObject(); + tokenObj.addProperty("active", true); + tokenObj.addProperty("exp", expVal); + tokenObj.addProperty("sub", "subject"); + tokenObj.addProperty("client_id", "123-456-789"); + + OAuth2AccessTokenImpl tok = new OAuth2AccessTokenImpl(tokenObj, tokenString); + + assertThat(tok.getScope(), is(equalTo(Collections.EMPTY_SET))); + assertThat(tok.getExpiration(), is(equalTo(exp))); + } + + @Test + public void testNullScopesNullExp() { + + + JsonObject tokenObj = new JsonObject(); + tokenObj.addProperty("active", true); + tokenObj.addProperty("sub", "subject"); + tokenObj.addProperty("client_id", "123-456-789"); + + OAuth2AccessTokenImpl tok = new OAuth2AccessTokenImpl(tokenObj, tokenString); + + assertThat(tok.getScope(), is(equalTo(Collections.EMPTY_SET))); + assertThat(tok.getExpiration(), is(equalTo(null))); + } + +} diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultIntrospectionResultAssembler.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultIntrospectionResultAssembler.java index abf8c126c..3e0424a25 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultIntrospectionResultAssembler.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultIntrospectionResultAssembler.java @@ -16,17 +16,24 @@ ******************************************************************************/ package org.mitre.oauth2.service.impl; -import com.google.common.base.Joiner; +import static com.google.common.collect.Maps.newLinkedHashMap; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Map; + +import javax.swing.text.DateFormatter; + import org.mitre.oauth2.model.OAuth2AccessTokenEntity; import org.mitre.oauth2.model.OAuth2RefreshTokenEntity; import org.mitre.oauth2.service.IntrospectionResultAssembler; import org.mitre.openid.connect.model.UserInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.stereotype.Service; -import java.util.Map; - -import static com.google.common.collect.Maps.newLinkedHashMap; +import com.google.common.base.Joiner; /** * Default implementation of the {@link IntrospectionResultAssembler} interface. @@ -34,6 +41,10 @@ import static com.google.common.collect.Maps.newLinkedHashMap; @Service public class DefaultIntrospectionResultAssembler implements IntrospectionResultAssembler { + private static Logger log = LoggerFactory.getLogger(DefaultIntrospectionResultAssembler.class); + + private static DateFormatter dateFormat = new DateFormatter(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")); + @Override public Map assembleFrom(OAuth2AccessTokenEntity accessToken, UserInfo userInfo) { @@ -45,7 +56,12 @@ public class DefaultIntrospectionResultAssembler implements IntrospectionResultA result.put("scope", Joiner.on(" ").join(accessToken.getScope())); if (accessToken.getExpiration() != null) { - result.put("exp", accessToken.getExpiration()); + try { + result.put("expires_at", dateFormat.valueToString(accessToken.getExpiration())); + result.put("exp", accessToken.getExpiration().getTime() / 1000L); + } catch (ParseException e) { + log.error("Parse exception in token introspection", e); + } } if (userInfo != null) { @@ -76,9 +92,15 @@ public class DefaultIntrospectionResultAssembler implements IntrospectionResultA result.put("scope", Joiner.on(" ").join(authentication.getOAuth2Request().getScope())); if (refreshToken.getExpiration() != null) { - result.put("exp", refreshToken.getExpiration()); + try { + result.put("expires_at", dateFormat.valueToString(refreshToken.getExpiration())); + result.put("exp", refreshToken.getExpiration().getTime() / 1000L); + } catch (ParseException e) { + log.error("Parse exception in token introspection", e); + } } + if (userInfo != null) { // if we have a UserInfo, use that for the subject result.put("sub", userInfo.getSub()); diff --git a/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultIntrospectionResultAssembler.java b/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultIntrospectionResultAssembler.java index cff392105..77ce84bb5 100644 --- a/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultIntrospectionResultAssembler.java +++ b/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultIntrospectionResultAssembler.java @@ -24,10 +24,14 @@ import org.mitre.openid.connect.model.UserInfo; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.OAuth2Request; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; import java.util.Set; +import javax.swing.text.DateFormatter; + import static com.google.common.collect.Sets.newHashSet; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; @@ -40,11 +44,13 @@ public class TestDefaultIntrospectionResultAssembler { private DefaultIntrospectionResultAssembler assembler = new DefaultIntrospectionResultAssembler(); + private static DateFormatter dateFormat = new DateFormatter(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")); + @Test - public void shouldAssembleExpectedResultForAccessToken() { + public void shouldAssembleExpectedResultForAccessToken() throws ParseException { // given - OAuth2AccessTokenEntity accessToken = accessToken(new Date(123), scopes("foo", "bar"), "Bearer", + OAuth2AccessTokenEntity accessToken = accessToken(new Date(123 * 1000L), scopes("foo", "bar"), "Bearer", authentication("name", request("clientId"))); UserInfo userInfo = userInfo("sub"); @@ -56,7 +62,8 @@ public class TestDefaultIntrospectionResultAssembler { // then Map expected = new ImmutableMap.Builder() .put("sub", "sub") - .put("exp", new Date(123)) + .put("exp", 123L) + .put("expires_at", dateFormat.valueToString(new Date(123 * 1000L))) .put("scope", "bar foo") .put("active", Boolean.TRUE) .put("user_id", "name") @@ -67,10 +74,10 @@ public class TestDefaultIntrospectionResultAssembler { } @Test - public void shouldAssembleExpectedResultForAccessTokenWithoutUserInfo() { + public void shouldAssembleExpectedResultForAccessTokenWithoutUserInfo() throws ParseException { // given - OAuth2AccessTokenEntity accessToken = accessToken(new Date(123), scopes("foo", "bar"), "Bearer", + OAuth2AccessTokenEntity accessToken = accessToken(new Date(123 * 1000L), scopes("foo", "bar"), "Bearer", authentication("name", request("clientId"))); // when @@ -80,7 +87,8 @@ public class TestDefaultIntrospectionResultAssembler { // then Map expected = new ImmutableMap.Builder() .put("sub", "name") - .put("exp", new Date(123)) + .put("exp", 123L) + .put("expires_at", dateFormat.valueToString(new Date(123 * 1000L))) .put("scope", "bar foo") .put("active", Boolean.TRUE) .put("user_id", "name") @@ -116,10 +124,10 @@ public class TestDefaultIntrospectionResultAssembler { } @Test - public void shouldAssembleExpectedResultForRefreshToken() { + public void shouldAssembleExpectedResultForRefreshToken() throws ParseException { // given - OAuth2RefreshTokenEntity refreshToken = refreshToken(new Date(123), + OAuth2RefreshTokenEntity refreshToken = refreshToken(new Date(123 * 1000L), authentication("name", request("clientId", scopes("foo", "bar")))); UserInfo userInfo = userInfo("sub"); @@ -131,7 +139,8 @@ public class TestDefaultIntrospectionResultAssembler { // then Map expected = new ImmutableMap.Builder() .put("sub", "sub") - .put("exp", new Date(123)) + .put("exp", 123L) + .put("expires_at", dateFormat.valueToString(new Date(123 * 1000L))) .put("scope", "bar foo") .put("active", Boolean.TRUE) .put("user_id", "name") @@ -141,10 +150,10 @@ public class TestDefaultIntrospectionResultAssembler { } @Test - public void shouldAssembleExpectedResultForRefreshTokenWithoutUserInfo() { + public void shouldAssembleExpectedResultForRefreshTokenWithoutUserInfo() throws ParseException { // given - OAuth2RefreshTokenEntity refreshToken = refreshToken(new Date(123), + OAuth2RefreshTokenEntity refreshToken = refreshToken(new Date(123 * 1000L), authentication("name", request("clientId", scopes("foo", "bar")))); // when @@ -154,7 +163,8 @@ public class TestDefaultIntrospectionResultAssembler { // then Map expected = new ImmutableMap.Builder() .put("sub", "name") - .put("exp", new Date(123)) + .put("exp", 123L) + .put("expires_at", dateFormat.valueToString(new Date(123 * 1000L))) .put("scope", "bar foo") .put("active", Boolean.TRUE) .put("user_id", "name")