undefined## More Detail {#more-detail}

Single Sign On

An app will activate @EnableOAuth2Sso if you bind provide the following properties in the Environment:

  • spring.oauth2.client.* with * equal to clientId, clientSecret, accessTokenUri, userAuthorizationUri and one of:

    If you specify both the userInfoUri and the tokenInfoUri then you can set a flag to say that one is preferred over the other (preferTokenInfo=true is the default). Or

  • spring.oauth2.resource.jwt.keyValue to decode a JWT token locally, where the key is a verification key. The verification key value is either a symmetric secret or PEM-encoded RSA public key. If you don’t have the key and it’s public you can provide a URI where it can be downloaded (as a JSON object with a "value" field) with spring.oauth2.resource.jwt.keyUri. E.g. on PWS:

    $ curl https://uaa.run.pivotal.io/token_key
    {"alg":"SHA256withRSA","value":"-----BEGIN PUBLIC KEY-----\nMIIBI...\n-----END PUBLIC KEY-----\n"}
Warning If you use the spring.oauth2.resource.jwt.keyUri the authorization server needs to be running when your application starts up. It will log a warning if it can’t find the key, and tell you what to do to fix it.

You can set the preferred scope (as a comma-separated list or YAML array) in spring.oauth2.client.scope. It defaults to empty, in which case most Authorization Servers will ask the user for approval for the maximum allowed scope for the client.

There is also a setting for spring.oauth2.client.clientAuthenticationScheme which defaults to "header" (but you might need to set it to "form" if, like 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 internally to fetch user details for authentication. This is provided as a qualified @Bean with id "userInfoRestTemplate", but you shouldn’t need to know that to just use it. The default should be fine for most providers, but occasionally you might need to add additional interceptors, or change the request authenticator (which is how the token gets attached to outgoing requests). To add a customization just create a bean of type UserInfoRestTemplateCustomizer - it has a single method that will be called after the bean is created but before it is initialized. The rest template that is being customized here is only used internally to carry out authentication (in the SSO or Resource Server use cases).

A second [email protected]Illegal HTML tag removed : / / OAuth2RestTemplate} is available for autowiring if you want to use it for back channel calls, and if there is a token-authenticated user (in a web application) it will have the token injected for you.

Tip To set an RSA key value in YAML use the "pipe" continuation marker to split it over multiple lines (" ") and remember to indent the key value (it’s a standard YAML language feature). Example:

Access Decision Rules

By default the whole application will be secured with OAuth2 with the same access rule ("authenticated"). This includes the Actuator endpoints, which you might prefer to be secured differently, so Spring Cloud Security provides a configurer callback that lets you change the matching and access rules for OAuth2 authentication. Any bean of type OAuth2SsoConfigurer (there is a convenient empty base class) will get 2 callbacks, one to set the request matchers for the OAuth2 filter, and one with the full HttpSecurity builder (so you can set up all sorts of behaviour, but the main application is to control access rules).

The default login path, i.e. the one that triggers the redirect to the OAuth2 Authorization Server, is "/login". It will always be added to the matching patterns for the OAuth2 SSO, even if you have OAuth2SsoConfigurer beans as well. The default logout path is "/logout" and it gets similar treatment, as does the "home" page (which is the logout success page, defaults to "/"). Those paths can be overriden by setting spring.oauth2.sso.*' (loginPath,logoutPathandhome.path`).

For example if you want the resources under "/ui/**" to be protected with OAuth2:

@Configuration
@EnableOAuth2Sso
@EnableAutoConfiguration
protected static class TestConfiguration extends OAuth2SsoConfigurerAdapter {
    @Override
    public void match(RequestMatchers matchers) {
        matchers.antMatchers("/ui/**");
    }
}

In this case the rest of the application will default to the normal Spring Boot access control (Basic authentication, or whatever custom filters you put in place).

Integrating with the Actuator Endpoints

The Spring Boot Actuator endpoints ("/env", "/metrics", etc.) if present will, by default, be protected by the standard Spring Boot basic authentication. The SSO authentication filter is added in a position directly behind the filter that intercepts requests to the Actuator endpoints by default (i.e. ManagementProperties.BASIC_AUTH_ORDER + 1 which is Ordered.LOWEST_PRECEDENCE-9 or 2147483636). If you want to change the order you can set spring.oauth2.sso.filterOrder. If you do that and the value is less than the default, then you will need to consider setting the access rules for the Actuator, since they will become accessible to all authenticated users who sign on with the external provider. One way to do that would be to set management.contextPath=/admin (for instance) and use an OAuth2SsoConfigurer to set the access rules, e.g.

    @Configuration
    @EnableOAuth2Sso
    @EnableAutoConfiguration
    protected static class TestConfiguration extends OAuth2SsoConfigurerAdapter {
        @Override
        public void configure(HttpSecurity http) {
             http.authorizeRequests()
                 .antMatchers("/admin/**").role("ADMIN")
                 .anyRequest().authenticated();
        }
    }

Resource Server

The @EnableOAuth2Resource annotation will protect your API endpoints if you have the same environment settings as the SSO client, except that it doesn’t need a tokenUri or authorizationUri, and it also doesn’t need a clientId and clientSecret if it isn’t using the tokenInfoUri (i.e. if it has jwt.* or userInfoUri).

By default all your endpoints are protected (i.e. "/") but you can pick and choose by adding a ResourceServerConfigurerAdapter (standard Spring OAuth feature), e.g. to protect only the "/api/" resources

Application.java

@RestController
@EnableOAuth2Resource
class Application extends ResourceServerConfigurerAdapter {

  @Override
  public void configure(HttpSecurity http) throws Exception {
    http.requestMatchers()
      .antMatchers("/api/**")
   .and()
     .authorizeRequests()
       .anyRequest().authenticated();
  }

  @RequestMapping("/api")
  public String home() {
    return "Hello World";
  }

}

Customizing the JWT Token Converter

When a resource server accepts an access token as a JWT, it has to convert it to an Authentication so that Spring Security can do its access decisions. Different token providers might support JWT tokens with different contents, so Spring OAuth2 has an abstraction for converting the token into security domain objects (AccessTokenConverter). You can modify the default behaviour easily by providing a @Bean of type JwtAccessTokenConverterConfigurer, e.g.

@Component
public class JwtCustomization extends DefaultAccessTokenConverter implements
        JwtAccessTokenConverterConfigurer {

    @Override
    public void configure(JwtAccessTokenConverter converter) {
        converter.setAccessTokenConverter(this);
    }

    ... // implement custom AccessTokenConverter here

}

Token Relay

A Token Relay is where an OAuth2 consumer acts as a Client and forwards the incoming token to outgoing resource requests. The consumer can be a pure Client (like an SSO application) or a Resource Server.

Client Token Relay

If your app has a Spring Cloud Zuul embedded reverse proxy (using @EnableZuulProxy) then you can ask it to forward OAuth2 access tokens downstream to the services it is proxying. Thus the SSO app above can be enhanced simply like this:

app.groovy

@Controller
@EnableOAuth2Sso
@EnableZuulProxy
class Application {

}

and it will (in addition to loggin the user in and grabbing a token) pass the authentication token downstream to the /proxy/* services. If those services are implemented with @EnableOAuth2Resource then they will get a valid token in the correct header.

How does it work? The @EnableOAuth2Sso annotation pulls in spring-cloud-starter-security (which you could do manually in a traditional app), and that in turn triggers some autoconfiguration for a ZuulFilter, which itself is activated because Zuul is on the classpath (via @EnableZuulProxy). The {github}/tree/master/src/main/java/org/springframework/cloud/security/oauth2/proxy/OAuth2TokenRelayFilter.java[filter] just extracts an access token from the currently authenticated user, and puts it in a request header for the downstream requests.

Resource Server Token Relay

If your app has @EnableOAuth2Resource and also is a Client (i.e. it has a spring.oauth2.client.clientId, even if it doesn’t use it), then the OAuth2RestOperations that is provided for @Autowired users by Spring Cloud (it is declared as @Primary) will also forward tokens. If you don’t want to forward tokens (and that is a valid choice, since you might want to act as yourself, rather than the client that sent you the token), then you only need to create your own OAuth2RestOperations instead of autowiring the default one. Here’s a basic example showing the use of the autowired rest template ("foo.com" is a Resource Server accepting the same tokens as the surrounding app):

MyController.java

@Autowired
private OAuth2RestOperations restTemplate;

@RequestMapping("/relay")
public String relay() {
    ResponseEntity<String> response =
      restTemplate.getForEntity("https://foo.com/bar", String.class);
    return "Success! (" + response.getBody() + ")";
}