diff --git a/client-lib/src/main/java/org/cloudfoundry/identity/client/UaaContextFactory.java b/client-lib/src/main/java/org/cloudfoundry/identity/client/UaaContextFactory.java index 8064a86646..de0d82f657 100644 --- a/client-lib/src/main/java/org/cloudfoundry/identity/client/UaaContextFactory.java +++ b/client-lib/src/main/java/org/cloudfoundry/identity/client/UaaContextFactory.java @@ -17,15 +17,30 @@ import org.cloudfoundry.identity.client.token.TokenRequest; import org.cloudfoundry.identity.uaa.oauth.token.CompositeAccessToken; +import org.springframework.http.HttpHeaders; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext; import org.springframework.security.oauth2.client.OAuth2RestTemplate; +import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; +import org.springframework.security.oauth2.client.token.AccessTokenRequest; +import org.springframework.security.oauth2.client.token.RequestEnhancer; import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; -import org.springframework.security.oauth2.common.AuthenticationScheme; +import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordAccessTokenProvider; +import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.common.exceptions.UnsupportedGrantTypeException; +import org.springframework.security.oauth2.common.util.OAuth2Utils; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.HttpMessageConverterExtractor; +import org.springframework.web.client.ResponseExtractor; import org.springframework.web.util.UriComponentsBuilder; import java.net.URI; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.Objects; + +import static org.springframework.security.oauth2.common.AuthenticationScheme.header; public class UaaContextFactory { @@ -70,22 +85,72 @@ public UaaContext authenticate(TokenRequest request) { if (request == null) { throw new NullPointerException(TokenRequest.class.getName() + " cannot be null."); } + if (!request.isValid()) { + throw new IllegalArgumentException("Invalid token request."); + } switch (request.getGrantType()) { case CLIENT_CREDENTIALS: return authenticateClientCredentials(request); + case PASSWORD: return authenticatePassword(request); default: throw new UnsupportedGrantTypeException("Not implemented:"+request.getGrantType()); } } + protected UaaContext authenticatePassword(TokenRequest request) { + ResourceOwnerPasswordAccessTokenProvider provider = new ResourceOwnerPasswordAccessTokenProvider() { + @Override + protected ResponseExtractor getResponseExtractor() { + getRestTemplate(); // force initialization + MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); + return new HttpMessageConverterExtractor(CompositeAccessToken.class, Arrays.asList(converter)); + } + }; + provider.setTokenRequestEnhancer(new PasswordTokenRequestEnhancer(request)); + ResourceOwnerPasswordResourceDetails details = new ResourceOwnerPasswordResourceDetails(); + details.setUsername(request.getUsername()); + details.setPassword(request.getPassword()); + details.setClientId(request.getClientId()); + details.setClientSecret(request.getClientSecret()); + if (!Objects.isNull(request.getScopes())) { + details.setScope(new LinkedList(request.getScopes())); + } + details.setClientAuthenticationScheme(header); + details.setAccessTokenUri(request.getTokenEndpoint().toString()); + OAuth2RestTemplate template = new OAuth2RestTemplate(details,new DefaultOAuth2ClientContext()); + template.setAccessTokenProvider(provider); + OAuth2AccessToken token = template.getAccessToken(); + return new UaaContextImpl(request, template, (CompositeAccessToken) token); + } + protected UaaContext authenticateClientCredentials(TokenRequest request) { + if (!request.isValid()) { + + } ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails(); details.setClientId(request.getClientId()); details.setClientSecret(request.getClientSecret()); details.setAccessTokenUri(request.getTokenEndpoint().toString()); - details.setClientAuthenticationScheme(AuthenticationScheme.header); + details.setClientAuthenticationScheme(header); OAuth2RestTemplate template = new OAuth2RestTemplate(details,new DefaultOAuth2ClientContext()); OAuth2AccessToken token = template.getAccessToken(); CompositeAccessToken result = new CompositeAccessToken(token); return new UaaContextImpl(request, template, result); } + public static class PasswordTokenRequestEnhancer implements RequestEnhancer { + private final TokenRequest request; + + public PasswordTokenRequestEnhancer(TokenRequest request) { + this.request = request; + } + + @Override + public void enhance(AccessTokenRequest request, OAuth2ProtectedResourceDetails resource, MultiValueMap form, HttpHeaders headers) { + if (this.request.wantsIdToken()) { + form.put(OAuth2Utils.RESPONSE_TYPE, Arrays.asList("id_token token")); + } + } + + + } + } diff --git a/client-lib/src/main/java/org/cloudfoundry/identity/client/token/GrantType.java b/client-lib/src/main/java/org/cloudfoundry/identity/client/token/GrantType.java index 75f0ca84fb..6d0f2b54c5 100644 --- a/client-lib/src/main/java/org/cloudfoundry/identity/client/token/GrantType.java +++ b/client-lib/src/main/java/org/cloudfoundry/identity/client/token/GrantType.java @@ -19,5 +19,5 @@ public enum GrantType { PASSWORD, IMPLICIT, AUTHORIZATION_CODE, - REFRESH + REFRESH_TOKEN } diff --git a/client-lib/src/main/java/org/cloudfoundry/identity/client/token/TokenRequest.java b/client-lib/src/main/java/org/cloudfoundry/identity/client/token/TokenRequest.java index e274a95942..6488d2c8b2 100644 --- a/client-lib/src/main/java/org/cloudfoundry/identity/client/token/TokenRequest.java +++ b/client-lib/src/main/java/org/cloudfoundry/identity/client/token/TokenRequest.java @@ -15,7 +15,17 @@ package org.cloudfoundry.identity.client.token; import java.net.URI; - +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +/** + * A token request contains all the information needed to retrieve a token from the UAA. + * + */ public class TokenRequest { private GrantType grantType; @@ -23,15 +33,40 @@ public class TokenRequest { private String clientSecret; private String username; private String password; + private Set scopes; private URI tokenEndpoint; private URI authorizationEndpoint; + private boolean idToken = false; public TokenRequest(URI tokenEndpoint, URI authorizationEndpoint) { this.tokenEndpoint = tokenEndpoint; } public boolean isValid() { - return false; + if (grantType==null) { + return false; + } + switch (grantType) { + case CLIENT_CREDENTIALS: + return !isNull( + Arrays.asList( + tokenEndpoint, + clientId, + clientSecret + ) + ); + case PASSWORD: + return !isNull( + Arrays.asList( + tokenEndpoint, + clientId, + clientSecret, + username, + password + ) + ); + default: return false; + } } public URI getTokenEndpoint() { @@ -96,4 +131,29 @@ public TokenRequest setAuthorizationEndpoint(URI authorizationEndpoint) { this.authorizationEndpoint = authorizationEndpoint; return this; } + + public TokenRequest withIdToken() { + idToken = true; + return this; + } + + public boolean wantsIdToken() { + return idToken; + } + + public TokenRequest setScopes(Collection scopes) { + this.scopes = scopes==null ? null : new HashSet<>(scopes); + return this; + } + + public Set getScopes() { + return scopes; + } + + protected boolean isNull(List objects) { + if (Objects.isNull(objects)) { + return true; + } + return objects.stream().filter(o -> Objects.isNull(o)).count() > 0; + } } diff --git a/client-lib/src/test/java/org/cloudfoundry/identity/client/integration/ClientCredentialsTokenIntegrationTest.java b/client-lib/src/test/java/org/cloudfoundry/identity/client/integration/ClientAPITokenIntegrationTest.java similarity index 54% rename from client-lib/src/test/java/org/cloudfoundry/identity/client/integration/ClientCredentialsTokenIntegrationTest.java rename to client-lib/src/test/java/org/cloudfoundry/identity/client/integration/ClientAPITokenIntegrationTest.java index 22c697761b..af7e192a4b 100644 --- a/client-lib/src/test/java/org/cloudfoundry/identity/client/integration/ClientCredentialsTokenIntegrationTest.java +++ b/client-lib/src/test/java/org/cloudfoundry/identity/client/integration/ClientAPITokenIntegrationTest.java @@ -19,6 +19,7 @@ import org.cloudfoundry.identity.client.UaaContextFactory; import org.cloudfoundry.identity.client.token.GrantType; import org.cloudfoundry.identity.client.token.TokenRequest; +import org.junit.Before; import org.junit.Test; import java.net.URI; @@ -27,17 +28,22 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -public class ClientCredentialsTokenIntegrationTest { +public class ClientAPITokenIntegrationTest { public static String uaaURI = "http://localhost:8080/uaa"; - @Test - public void test_admin_client_token() throws Exception { - UaaContextFactory factory = + private UaaContextFactory factory; + + @Before + public void setUp() throws Exception { + factory = UaaContextFactory.factory(new URI(uaaURI)) - .authorizePath("/oauth/authorize") - .tokenPath("/oauth/token"); + .authorizePath("/oauth/authorize") + .tokenPath("/oauth/token"); + } + @Test + public void test_admin_client_token() throws Exception { TokenRequest request = factory.tokenRequest() .setClientId("admin") .setClientSecret("adminsecret") @@ -51,4 +57,40 @@ public void test_admin_client_token() throws Exception { assertTrue(context.getToken().getScope().contains("uaa.admin")); } + @Test + public void test_password_token_no_id_token() throws Exception { + TokenRequest request = factory.tokenRequest() + .setClientId("cf") + .setClientSecret("") + .setGrantType(GrantType.PASSWORD) + .setUsername("marissa") + .setPassword("koala"); + + UaaContext context = factory.authenticate(request); + assertNotNull(context); + assertTrue(context.hasAccessToken()); + assertFalse(context.hasIdToken()); + assertTrue(context.hasRefreshToken()); + assertTrue(context.getToken().getScope().contains("openid")); + } + + @Test + public void test_password_token_with_id_token() throws Exception { + TokenRequest request = factory.tokenRequest() + .withIdToken() + .setClientId("cf") + .setClientSecret("") + .setGrantType(GrantType.PASSWORD) + .setUsername("marissa") + .setPassword("koala"); + + UaaContext context = factory.authenticate(request); + assertNotNull(context); + assertTrue(context.hasAccessToken()); + assertTrue(context.hasIdToken()); + assertTrue(context.hasRefreshToken()); + assertTrue(context.getToken().getScope().contains("openid")); + } + + } diff --git a/client-lib/src/test/java/org/cloudfoundry/identity/client/token/TokenRequestTest.java b/client-lib/src/test/java/org/cloudfoundry/identity/client/token/TokenRequestTest.java index 64f54aed35..67a760b914 100644 --- a/client-lib/src/test/java/org/cloudfoundry/identity/client/token/TokenRequestTest.java +++ b/client-lib/src/test/java/org/cloudfoundry/identity/client/token/TokenRequestTest.java @@ -17,16 +17,51 @@ import org.junit.Before; import org.junit.Test; +import java.net.URI; +import java.util.Arrays; + +import static java.util.Collections.EMPTY_LIST; +import static org.cloudfoundry.identity.client.token.GrantType.CLIENT_CREDENTIALS; +import static org.cloudfoundry.identity.client.token.GrantType.PASSWORD; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + public class TokenRequestTest { + private TokenRequest request; + @Before public void setUp() throws Exception { + URI turi = new URI("http://localhost:8080/uaa/oauth/token"); + URI auri = new URI("http://localhost:8080/uaa/oauth/authorize"); + request = new TokenRequest(turi, auri); + } + @Test + public void test_is_client_credentials_grant_valid() throws Exception { + assertFalse(request.isValid()); + assertFalse(request.setGrantType(CLIENT_CREDENTIALS).isValid()); + assertFalse(request.setClientId("client_id").isValid()); + assertTrue(request.setClientSecret("client_secret").isValid()); } @Test - public void testIsValid() throws Exception { + public void test_is_password_grant_valid() throws Exception { + assertFalse(request.isValid()); + assertFalse(request.setGrantType(PASSWORD).isValid()); + assertFalse(request.setClientId("client_id").isValid()); + assertFalse(request.setClientSecret("client_secret").isValid()); + assertFalse(request.setUsername("username").isValid()); + assertTrue(request.setPassword("password").isValid()); + } + + @Test + public void test_is_null_function() { + assertTrue(request.isNull(null)); + assertFalse(request.isNull(EMPTY_LIST)); + assertTrue(request.isNull(Arrays.asList("1",null,"2"))); + assertFalse(request.isNull(Arrays.asList("1","2","3"))); } } \ No newline at end of file diff --git a/models/src/main/java/org/cloudfoundry/identity/uaa/oauth/token/CompositeAccessToken.java b/models/src/main/java/org/cloudfoundry/identity/uaa/oauth/token/CompositeAccessToken.java index 2be5471df2..4b2b56c038 100644 --- a/models/src/main/java/org/cloudfoundry/identity/uaa/oauth/token/CompositeAccessToken.java +++ b/models/src/main/java/org/cloudfoundry/identity/uaa/oauth/token/CompositeAccessToken.java @@ -13,31 +13,13 @@ */ package org.cloudfoundry.identity.uaa.oauth.token; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; 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.OAuth2RefreshToken; -import org.springframework.security.oauth2.common.util.OAuth2Utils; -import org.springframework.util.Assert; -import java.io.IOException; -import java.util.Date; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; - -@JsonSerialize(using = CompositeAccessToken.CompositeAccessTokenSerializer.class) -@JsonDeserialize(using = CompositeAccessToken.CompositeAccessTokenDeserializer.class) +@JsonSerialize(using = CompositeAccessTokenSerializer.class) +@JsonDeserialize(using = CompositeAccessTokenDeserializer.class) public class CompositeAccessToken extends DefaultOAuth2AccessToken { public static String ID_TOKEN = "id_token"; @@ -60,106 +42,4 @@ public CompositeAccessToken(OAuth2AccessToken accessToken) { super(accessToken); } - public static final class CompositeAccessTokenSerializer extends StdSerializer { - - public CompositeAccessTokenSerializer() { - super(OAuth2AccessToken.class); - } - - @Override - public void serialize(OAuth2AccessToken token, JsonGenerator jgen, SerializerProvider provider) throws IOException { - - jgen.writeStartObject(); - jgen.writeStringField(OAuth2AccessToken.ACCESS_TOKEN, token.getValue()); - jgen.writeStringField(OAuth2AccessToken.TOKEN_TYPE, token.getTokenType()); - if (token instanceof CompositeAccessToken && ((CompositeAccessToken)token).getIdTokenValue()!=null) { - jgen.writeStringField(ID_TOKEN, ((CompositeAccessToken) token).getIdTokenValue()); - } - OAuth2RefreshToken refreshToken = token.getRefreshToken(); - if (refreshToken != null) { - jgen.writeStringField(OAuth2AccessToken.REFRESH_TOKEN, refreshToken.getValue()); - } - Date expiration = token.getExpiration(); - if (expiration != null) { - long now = System.currentTimeMillis(); - jgen.writeNumberField(OAuth2AccessToken.EXPIRES_IN, (expiration.getTime() - now) / 1000); - } - Set scope = token.getScope(); - if (scope != null && !scope.isEmpty()) { - StringBuffer scopes = new StringBuffer(); - for (String s : scope) { - Assert.hasLength(s, "Scopes cannot be null or empty. Got " + scope + ""); - scopes.append(s); - scopes.append(" "); - } - jgen.writeStringField(OAuth2AccessToken.SCOPE, scopes.substring(0, scopes.length() - 1)); - } - Map additionalInformation = token.getAdditionalInformation(); - for (String key : additionalInformation.keySet()) { - jgen.writeObjectField(key, additionalInformation.get(key)); - } - jgen.writeEndObject(); - } - } - - public final class CompositeAccessTokenDeserializer extends StdDeserializer { - - public CompositeAccessTokenDeserializer() { - super(OAuth2AccessToken.class); - } - - @Override - public OAuth2AccessToken deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { - - String idTokenValue = null; - String tokenValue = null; - String tokenType = null; - String refreshToken = null; - Long expiresIn = null; - Set scope = null; - Map additionalInformation = new LinkedHashMap(); - - // TODO What should occur if a parameter exists twice - while (jp.nextToken() != JsonToken.END_OBJECT) { - String name = jp.getCurrentName(); - jp.nextToken(); - if (OAuth2AccessToken.ACCESS_TOKEN.equals(name)) { - tokenValue = jp.getText(); - } else if (ID_TOKEN.equals(name)) { - idTokenValue = jp.getText(); - } else if (OAuth2AccessToken.TOKEN_TYPE.equals(name)) { - tokenType = jp.getText(); - } else if (OAuth2AccessToken.REFRESH_TOKEN.equals(name)) { - refreshToken = jp.getText(); - } else if (OAuth2AccessToken.EXPIRES_IN.equals(name)) { - try { - expiresIn = jp.getLongValue(); - } catch (JsonParseException e) { - expiresIn = Long.valueOf(jp.getText()); - } - } else if (OAuth2AccessToken.SCOPE.equals(name)) { - String text = jp.getText(); - scope = OAuth2Utils.parseParameterList(text); - } else { - additionalInformation.put(name, jp.readValueAs(Object.class)); - } - } - - // TODO What should occur if a required parameter (tokenValue or tokenType) is missing? - - CompositeAccessToken accessToken = new CompositeAccessToken(tokenValue); - accessToken.setIdTokenValue(idTokenValue); - accessToken.setTokenType(tokenType); - if (expiresIn != null) { - accessToken.setExpiration(new Date(System.currentTimeMillis() + (expiresIn * 1000))); - } - if (refreshToken != null) { - accessToken.setRefreshToken(new DefaultOAuth2RefreshToken(refreshToken)); - } - accessToken.setScope(scope); - accessToken.setAdditionalInformation(additionalInformation); - - return accessToken; - } - } } diff --git a/models/src/main/java/org/cloudfoundry/identity/uaa/oauth/token/CompositeAccessTokenDeserializer.java b/models/src/main/java/org/cloudfoundry/identity/uaa/oauth/token/CompositeAccessTokenDeserializer.java new file mode 100644 index 0000000000..e9bfc9ddc2 --- /dev/null +++ b/models/src/main/java/org/cloudfoundry/identity/uaa/oauth/token/CompositeAccessTokenDeserializer.java @@ -0,0 +1,91 @@ +/* + * ***************************************************************************** + * Cloud Foundry + * Copyright (c) [2009-2015] Pivotal Software, 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.cloudfoundry.identity.uaa.oauth.token; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import org.springframework.security.oauth2.common.DefaultOAuth2RefreshToken; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.common.util.OAuth2Utils; + +import java.io.IOException; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +public final class CompositeAccessTokenDeserializer extends StdDeserializer { + + public CompositeAccessTokenDeserializer() { + super(CompositeAccessToken.class); + } + + @Override + public CompositeAccessToken deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { + + String idTokenValue = null; + String tokenValue = null; + String tokenType = null; + String refreshToken = null; + Long expiresIn = null; + Set scope = null; + Map additionalInformation = new LinkedHashMap(); + + // TODO What should occur if a parameter exists twice + while (jp.nextToken() != JsonToken.END_OBJECT) { + String name = jp.getCurrentName(); + jp.nextToken(); + if (OAuth2AccessToken.ACCESS_TOKEN.equals(name)) { + tokenValue = jp.getText(); + } else if (CompositeAccessToken.ID_TOKEN.equals(name)) { + idTokenValue = jp.getText(); + } else if (OAuth2AccessToken.TOKEN_TYPE.equals(name)) { + tokenType = jp.getText(); + } else if (OAuth2AccessToken.REFRESH_TOKEN.equals(name)) { + refreshToken = jp.getText(); + } else if (OAuth2AccessToken.EXPIRES_IN.equals(name)) { + try { + expiresIn = jp.getLongValue(); + } catch (JsonParseException e) { + expiresIn = Long.valueOf(jp.getText()); + } + } else if (OAuth2AccessToken.SCOPE.equals(name)) { + String text = jp.getText(); + scope = OAuth2Utils.parseParameterList(text); + } else { + additionalInformation.put(name, jp.readValueAs(Object.class)); + } + } + + // TODO What should occur if a required parameter (tokenValue or tokenType) is missing? + + CompositeAccessToken accessToken = new CompositeAccessToken(tokenValue); + accessToken.setIdTokenValue(idTokenValue); + accessToken.setTokenType(tokenType); + if (expiresIn != null) { + accessToken.setExpiration(new Date(System.currentTimeMillis() + (expiresIn * 1000))); + } + if (refreshToken != null) { + accessToken.setRefreshToken(new DefaultOAuth2RefreshToken(refreshToken)); + } + accessToken.setScope(scope); + accessToken.setAdditionalInformation(additionalInformation); + + return accessToken; + } +} diff --git a/models/src/main/java/org/cloudfoundry/identity/uaa/oauth/token/CompositeAccessTokenSerializer.java b/models/src/main/java/org/cloudfoundry/identity/uaa/oauth/token/CompositeAccessTokenSerializer.java new file mode 100644 index 0000000000..5d0e9c90b2 --- /dev/null +++ b/models/src/main/java/org/cloudfoundry/identity/uaa/oauth/token/CompositeAccessTokenSerializer.java @@ -0,0 +1,70 @@ +/* + * ***************************************************************************** + * Cloud Foundry + * Copyright (c) [2009-2015] Pivotal Software, 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.cloudfoundry.identity.uaa.oauth.token; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.common.OAuth2RefreshToken; +import org.springframework.util.Assert; + +import java.io.IOException; +import java.util.Date; +import java.util.Map; +import java.util.Set; + + +public final class CompositeAccessTokenSerializer extends StdSerializer { + + public CompositeAccessTokenSerializer() { + super(CompositeAccessToken.class); + } + + @Override + public void serialize(CompositeAccessToken token, JsonGenerator jgen, SerializerProvider provider) throws IOException { + + jgen.writeStartObject(); + jgen.writeStringField(OAuth2AccessToken.ACCESS_TOKEN, token.getValue()); + jgen.writeStringField(OAuth2AccessToken.TOKEN_TYPE, token.getTokenType()); + if (token instanceof CompositeAccessToken && ((CompositeAccessToken) token).getIdTokenValue() != null) { + jgen.writeStringField(CompositeAccessToken.ID_TOKEN, ((CompositeAccessToken) token).getIdTokenValue()); + } + OAuth2RefreshToken refreshToken = token.getRefreshToken(); + if (refreshToken != null) { + jgen.writeStringField(OAuth2AccessToken.REFRESH_TOKEN, refreshToken.getValue()); + } + Date expiration = token.getExpiration(); + if (expiration != null) { + long now = System.currentTimeMillis(); + jgen.writeNumberField(OAuth2AccessToken.EXPIRES_IN, (expiration.getTime() - now) / 1000); + } + Set scope = token.getScope(); + if (scope != null && !scope.isEmpty()) { + StringBuffer scopes = new StringBuffer(); + for (String s : scope) { + Assert.hasLength(s, "Scopes cannot be null or empty. Got " + scope + ""); + scopes.append(s); + scopes.append(" "); + } + jgen.writeStringField(OAuth2AccessToken.SCOPE, scopes.substring(0, scopes.length() - 1)); + } + Map additionalInformation = token.getAdditionalInformation(); + for (String key : additionalInformation.keySet()) { + jgen.writeObjectField(key, additionalInformation.get(key)); + } + jgen.writeEndObject(); + } +}