Skip to content

Commit

Permalink
Add external property to configure token type for user info
Browse files Browse the repository at this point in the history
User can set spring.oauth2.resource.tokentype=foo (Bearer is
the default). This makes it easier to use SSO with Google
and Facebook.

Fixes spring-atticgh-63
  • Loading branch information
dsyer authored and Jannik Weichert committed Aug 4, 2017
1 parent 45b8792 commit 9435074
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 3 deletions.
8 changes: 8 additions & 0 deletions src/main/asciidoc/spring-cloud-security.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ Github for instance, your OAuth2 provider doesn't like header
authentication). The `spring.oauth2.client.*` properties are bound to an instance
of `AuthorizationCodeResourceDetails` so all its properties can be specified.

=== Token Type in User Info

Google (and certain other 3rd party identity providers) is more strict
about the token type name that is sent in the headers to the user info
endpoint. The default is "Bearer" which suits most providers and
matches the spec, but if you need to change it you can set
`spring.oauth2.resource.tokenType`.

=== Customizing the RestTemplate

The SSO (and Resource Server) features use an `OAuth2RestTemplate`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
Expand Down Expand Up @@ -65,6 +66,11 @@ public class ResourceServerProperties implements Validator {
*/
private boolean preferTokenInfo = true;

/**
* The token type to send when using the userInfoUri.
*/
private String tokenType = DefaultOAuth2AccessToken.BEARER_TYPE;

private Jwt jwt = new Jwt();

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ public SpringSocialTokenServices socialTokenServices() {
public UserInfoTokenServices userInfoTokenServices() {
UserInfoTokenServices services = new UserInfoTokenServices(
sso.getUserInfoUri(), sso.getClientId());
services.setTokenType(sso.getTokenType());
services.setRestTemplate(restTemplate);
return services;
}
Expand All @@ -229,6 +230,7 @@ public UserInfoTokenServices userInfoTokenServices() {
UserInfoTokenServices services = new UserInfoTokenServices(
sso.getUserInfoUri(), sso.getClientId());
services.setRestTemplate(restTemplate);
services.setTokenType(sso.getTokenType());
return services;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,16 @@ public class UserInfoTokenServices implements ResourceServerTokenServices {

private OAuth2RestOperations restTemplate;

private String tokenType = DefaultOAuth2AccessToken.BEARER_TYPE;

public UserInfoTokenServices(String userInfoEndpointUrl, String clientId) {
this.userInfoEndpointUrl = userInfoEndpointUrl;
this.clientId = clientId;
}

public void setTokenType(String tokenType) {
this.tokenType = tokenType;
}

public void setRestTemplate(OAuth2RestOperations restTemplate) {
this.restTemplate = restTemplate;
Expand Down Expand Up @@ -99,8 +105,9 @@ private Map<String, Object> getMap(String path, String accessToken) {
resource.setClientId(clientId);
restTemplate = new OAuth2RestTemplate(resource);
}
restTemplate.getOAuth2ClientContext().setAccessToken(
new DefaultOAuth2AccessToken(accessToken));
DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(accessToken);
token.setTokenType(tokenType);
restTemplate.getOAuth2ClientContext().setAccessToken(token);
@SuppressWarnings("rawtypes")
Map map = restTemplate.getForEntity(path, Map.class).getBody();
@SuppressWarnings("unchecked")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
Expand All @@ -31,6 +32,7 @@
import org.springframework.security.oauth2.client.resource.BaseOAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.client.resource.UserRedirectRequiredException;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;

/**
* @author Dave Syer
Expand All @@ -42,6 +44,7 @@ public class UserInfoTokenServicesTests {
"http://example.com", "foo");
private BaseOAuth2ProtectedResourceDetails resource = new BaseOAuth2ProtectedResourceDetails();
private OAuth2RestOperations template = Mockito.mock(OAuth2RestOperations.class);
private OAuth2ClientContext clientContext = Mockito.mock(OAuth2ClientContext.class);
private Map<String, Object> map = new LinkedHashMap<String, Object>();

@Before
Expand All @@ -55,7 +58,7 @@ public void init() {
new DefaultOAuth2AccessToken("FOO"));
Mockito.when(template.getResource()).thenReturn(resource);
Mockito.when(template.getOAuth2ClientContext()).thenReturn(
Mockito.mock(OAuth2ClientContext.class));
clientContext);
}

@Test
Expand All @@ -64,6 +67,16 @@ public void sunnyDay() {
assertEquals("unknown", services.loadAuthentication("FOO").getName());
}

@Test
public void tokenType() {
services.setRestTemplate(template);
services.setTokenType("access_token");
assertEquals("unknown", services.loadAuthentication("FOO").getName());
ArgumentCaptor<OAuth2AccessToken> argument = ArgumentCaptor.forClass(OAuth2AccessToken.class);
Mockito.verify(clientContext).setAccessToken(argument.capture());
assertEquals("access_token", argument.getValue().getTokenType());
}

@Test
public void noClientId() {
services = new UserInfoTokenServices("http://example.com", null);
Expand Down

0 comments on commit 9435074

Please sign in to comment.