/** * Cloud Foundry 2012.02.03 Beta Copyright (c) [2009-2012] VMware, Inc. All Rights Reserved. * * This product is licensed to you under the Apache License, Version 2.0 (the "License"). You may not use this product * except in compliance with the License. * * This product includes a number of subcomponents with separate copyright notices and license terms. Your use of these * subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. */ package org.springframework.security.oauth2.provider.token.store; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.security.KeyPair; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.Map; import java.util.Set; import org.junit.Before; import org.junit.Test; import org.springframework.core.io.ClassPathResource; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.jwt.JwtHelper; import org.springframework.security.jwt.crypto.sign.RsaVerifier; import org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken; import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; import org.springframework.security.oauth2.common.DefaultOAuth2RefreshToken; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.common.util.JsonParser; import org.springframework.security.oauth2.common.util.JsonParserFactory; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.OAuth2Request; import org.springframework.security.oauth2.provider.token.AccessTokenConverter; import org.springframework.security.oauth2.provider.token.UserAuthenticationConverter; /** * @author Dave Syer * @author Luke Taylor */ public class JwtAccessTokenConverterTests { private JwtAccessTokenConverter tokenEnhancer; private Authentication userAuthentication; @Before public void setUp() throws Exception { tokenEnhancer = new JwtAccessTokenConverter(); userAuthentication = new TestAuthentication("test2", true); } @Test public void testEnhanceAccessToken() { OAuth2Authentication authentication = new OAuth2Authentication( createOAuth2Request("foo", null), userAuthentication); OAuth2AccessToken token = tokenEnhancer.enhance(new DefaultOAuth2AccessToken( "FOO"), authentication); assertNotNull(token.getValue()); assertEquals("FOO", token.getAdditionalInformation() .get(AccessTokenConverter.JTI)); String claims = JwtHelper.decode(token.getValue()).getClaims(); assertTrue("Wrong claims: " + claims, claims.contains("\"" + AccessTokenConverter.JTI + "\":\"FOO\"")); assertTrue("Wrong claims: " + claims, claims.contains("\"" + UserAuthenticationConverter.USERNAME + "\"")); } @Test public void testScopePreserved() { OAuth2Authentication authentication = new OAuth2Authentication( createOAuth2Request("foo", Collections.singleton("read")), userAuthentication); DefaultOAuth2AccessToken original = new DefaultOAuth2AccessToken("FOO"); original.setScope(authentication.getOAuth2Request().getScope()); OAuth2AccessToken token = tokenEnhancer.enhance(original, authentication); assertNotNull(token.getValue()); assertEquals(Collections.singleton("read"), token.getScope()); } @Test public void testRefreshTokenAdded() throws Exception { OAuth2Authentication authentication = new OAuth2Authentication( createOAuth2Request("foo", Collections.singleton("read")), userAuthentication); DefaultOAuth2AccessToken original = new DefaultOAuth2AccessToken("FOO"); original.setScope(authentication.getOAuth2Request().getScope()); original.setRefreshToken(new DefaultOAuth2RefreshToken("BAR")); original.setExpiration(new Date()); OAuth2AccessToken token = tokenEnhancer.enhance(original, authentication); assertNotNull(token.getValue()); assertNotNull(token.getRefreshToken()); JsonParser parser = JsonParserFactory.create(); Map<String, Object> claims = parser.parseMap(JwtHelper.decode( token.getRefreshToken().getValue()).getClaims()); assertEquals(Arrays.asList("read"), claims.get(AccessTokenConverter.SCOPE)); assertEquals("FOO", claims.get(AccessTokenConverter.ATI)); assertEquals("BAR", claims.get(AccessTokenConverter.JTI)); assertNull(claims.get(AccessTokenConverter.EXP)); tokenEnhancer.afterPropertiesSet(); assertTrue(tokenEnhancer.isRefreshToken(tokenEnhancer.extractAccessToken(token .getRefreshToken().getValue(), tokenEnhancer.decode(token .getRefreshToken().getValue())))); } @Test public void testExpiringRefreshTokenAdded() throws Exception { OAuth2Authentication authentication = new OAuth2Authentication( createOAuth2Request("foo", Collections.singleton("read")), userAuthentication); DefaultOAuth2AccessToken original = new DefaultOAuth2AccessToken("FOO"); original.setScope(authentication.getOAuth2Request().getScope()); original.setRefreshToken(new DefaultExpiringOAuth2RefreshToken("BAR", new Date(0))); original.setExpiration(new Date()); OAuth2AccessToken token = tokenEnhancer.enhance(original, authentication); assertNotNull(token.getValue()); assertNotNull(token.getRefreshToken()); JsonParser parser = JsonParserFactory.create(); Map<String, Object> claims = parser.parseMap(JwtHelper.decode( token.getRefreshToken().getValue()).getClaims()); assertEquals(Arrays.asList("read"), claims.get(AccessTokenConverter.SCOPE)); assertEquals("FOO", claims.get(AccessTokenConverter.ATI)); assertEquals("BAR", claims.get(AccessTokenConverter.JTI)); assertEquals(0, claims.get(AccessTokenConverter.EXP)); tokenEnhancer.afterPropertiesSet(); assertTrue(tokenEnhancer.isRefreshToken(tokenEnhancer.extractAccessToken(token .getRefreshToken().getValue(), tokenEnhancer.decode(token .getRefreshToken().getValue())))); } @Test public void testRefreshTokenAccessTokenIdWhenDoubleEnhanced() throws Exception { OAuth2Authentication authentication = new OAuth2Authentication( createOAuth2Request("foo", Collections.singleton("read")), userAuthentication); DefaultOAuth2AccessToken original = new DefaultOAuth2AccessToken("FOO"); original.setScope(authentication.getOAuth2Request().getScope()); original.setRefreshToken(new DefaultOAuth2RefreshToken("BAR")); OAuth2AccessToken token = tokenEnhancer.enhance(original, authentication); token = tokenEnhancer.enhance(token, authentication); assertNotNull(token.getValue()); assertNotNull(token.getRefreshToken()); JsonParser parser = JsonParserFactory.create(); Map<String, Object> claims = parser.parseMap(JwtHelper.decode( token.getRefreshToken().getValue()).getClaims()); assertEquals(Arrays.asList("read"), claims.get(AccessTokenConverter.SCOPE)); assertEquals("FOO", claims.get(AccessTokenConverter.ATI)); assertEquals("Wrong claims: " + claims, "BAR", claims.get(AccessTokenConverter.JTI)); } @Test public void rsaKeyCreatesValidRsaSignedTokens() throws Exception { String rsaKey = "-----BEGIN RSA PRIVATE KEY----- \n" + "MIIBywIBAAJhAOTeb4AZ+NwOtPh+ynIgGqa6UWNVe6JyJi+loPmPZdpHtzoqubnC \n" + "wEs6JSiSZ3rButEAw8ymgLV6iBY02hdjsl3h5Z0NWaxx8dzMZfXe4EpfB04ISoqq\n" + "hZCxchvuSDP4eQIDAQABAmEAqUuYsuuDWFRQrZgsbGsvC7G6zn3HLIy/jnM4NiJK\n" + "t0JhWNeN9skGsR7bqb1Sak2uWqW8ZqnqgAC32gxFRYHTavJEk6LTaHWovwDEhPqc\n" + "Zs+vXd6tZojJQ35chR/slUEBAjEA/sAd1oFLWb6PHkaz7r2NllwUBTvXL4VcMWTS\n" + "pN+5cU41i9fsZcHw6yZEl+ZCicDxAjEA5f3R+Bj42htNI7eylebew1+sUnFv1xT8\n" + "jlzxSzwVkoZo+vef7OD6OcFLeInAHzAJAjEAs6izolK+3ETa1CRSwz0lPHQlnmdM\n" + "Y/QuR5tuPt6U/saEVuJpkn4LNRtg5qt6I4JRAjAgFRYTG7irBB/wmZFp47izXEc3\n" + "gOdvA1hvq3tlWU5REDrYt24xpviA0fvrJpwMPbECMAKDKdiDi6Q4/iBkkzNMefA8\n" + "7HX27b9LR33don/1u/yvzMUo+lrRdKAFJ+9GPE9XFA== \n" + "-----END RSA PRIVATE KEY----- "; tokenEnhancer.setSigningKey(rsaKey); OAuth2Authentication authentication = new OAuth2Authentication( createOAuth2Request("foo", null), userAuthentication); OAuth2AccessToken token = tokenEnhancer.enhance(new DefaultOAuth2AccessToken( "FOO"), authentication); JwtHelper.decodeAndVerify(token.getValue(), new RsaVerifier(rsaKey)); } @Test public void publicKeyStringIsReturnedFromTokenKeyEndpoint() throws Exception { tokenEnhancer.setVerifierKey("-----BEGIN RSA PUBLIC KEY-----\n" + "MGgCYQDk3m+AGfjcDrT4fspyIBqmulFjVXuiciYvpaD5j2XaR7c6Krm5wsBLOiUo\n" + "kmd6wbrRAMPMpoC1eogWNNoXY7Jd4eWdDVmscfHczGX13uBKXwdOCEqKqoWQsXIb\n" + "7kgz+HkCAwEAAQ==\n" + "-----END RSA PUBLIC KEY-----"); tokenEnhancer.afterPropertiesSet(); Map<String, String> key = tokenEnhancer.getKey(); assertTrue("Wrong key: " + key, key.get("value").contains("-----BEGIN")); } @Test public void publicKeyStringIsReturnedFromTokenKeyEndpointWithNullPrincipal() throws Exception { tokenEnhancer.setVerifierKey("-----BEGIN RSA PUBLIC KEY-----\n" + "MGgCYQDk3m+AGfjcDrT4fspyIBqmulFjVXuiciYvpaD5j2XaR7c6Krm5wsBLOiUo\n" + "kmd6wbrRAMPMpoC1eogWNNoXY7Jd4eWdDVmscfHczGX13uBKXwdOCEqKqoWQsXIb\n" + "7kgz+HkCAwEAAQ==\n" + "-----END RSA PUBLIC KEY-----"); Map<String, String> key = tokenEnhancer.getKey(); assertTrue("Wrong key: " + key, key.get("value").contains("-----BEGIN")); } @Test public void sharedSecretIsReturnedFromTokenKeyEndpoint() throws Exception { tokenEnhancer.setVerifierKey("someKey"); assertEquals("{alg=HMACSHA256, value=someKey}", tokenEnhancer.getKey().toString()); } @Test(expected = IllegalStateException.class) public void keysNotMatchingWithMacSigner() throws Exception { tokenEnhancer.setSigningKey("aKey"); tokenEnhancer.setVerifierKey("someKey"); tokenEnhancer.afterPropertiesSet(); } @Test public void rsaKeyPair() throws Exception { KeyStoreKeyFactory factory = new KeyStoreKeyFactory(new ClassPathResource( "keystore.jks"), "foobar".toCharArray()); KeyPair keys = factory.getKeyPair("test"); tokenEnhancer.setKeyPair(keys); tokenEnhancer.afterPropertiesSet(); assertTrue(tokenEnhancer.getKey().get("value").contains("BEGIN PUBLIC")); } @Test public void publicKeyOnlyAllowedForVerification() throws Exception { tokenEnhancer.setVerifierKey("-----BEGIN RSA PUBLIC KEY-----\n" + "MGgCYQDk3m+AGfjcDrT4fspyIBqmulFjVXuiciYvpaD5j2XaR7c6Krm5wsBLOiUo\n" + "kmd6wbrRAMPMpoC1eogWNNoXY7Jd4eWdDVmscfHczGX13uBKXwdOCEqKqoWQsXIb\n" + "7kgz+HkCAwEAAQ==\n" + "-----END RSA PUBLIC KEY-----"); tokenEnhancer.afterPropertiesSet(); tokenEnhancer .decode("eyJhbGciOiJSUzI1NiJ9.eyJ1c2VyX25hbWUiOiJ0ZXN0MiIsImp0aSI6IkZPTyIsImNsaWVudF9pZCI6ImZvbyJ9.b43ob1ALSIwr_J2oEnfMhsXvYkr1qVBNhigNH2zlaE1OQLhLfT-DMlFtHcyUlyap0C2n0q61SPaGE_z715TV0uTAv2YKDN4fKZz2bMR7eHLsvaaCuvs7KCOi_aSROaUG"); Map<String, String> key = tokenEnhancer.getKey(); assertTrue("Wrong key: " + key, key.get("value").contains("-----BEGIN")); } private OAuth2Request createOAuth2Request(String clientId, Set<String> scope) { return new OAuth2Request(Collections.<String, String> emptyMap(), clientId, null, true, scope, null, null, null, null); } protected static class TestAuthentication extends AbstractAuthenticationToken { private static final long serialVersionUID = 1L; private String principal; public TestAuthentication(String name, boolean authenticated) { super(null); setAuthenticated(authenticated); this.principal = name; } public Object getCredentials() { return null; } public Object getPrincipal() { return this.principal; } } }