I am trying to protect my microservices on Spring Boot using Oath2 with Client Credentials flow.
By the way, those microservices will only talk each other over the middleware layer, I mean no user credentials are needed to allow the authorization (user login process as Facebook).
I have looked for samples on the Internet showing how to create an authorization and resource server to manage this communication. However I just found examples explaining how to do it using user credentials (three legs).
Does anyone have any sample how to do it in Spring Boot and Oauth2? If it is possible give further details about the scopes used, token exchanging would be grateful.
The OAuth 2.0 Client features provide support for the Client role as defined in the OAuth 2.0 Authorization Framework. At a high-level, the core features available are: Authorization Grant support. Authorization Code. Refresh Token.
The OAuth 2.0 client credentials grant flow permits a web service (confidential client) to use its own credentials, instead of impersonating a user, to authenticate when calling another web service.
Spring Security OAuth2 − Implements the OAUTH2 structure to enable the Authorization Server and Resource Server. Spring Security JWT − Generates the JWT Token for Web security. Spring Boot Starter JDBC − Accesses the database to ensure the user is available or not. Spring Boot Starter Web − Writes HTTP endpoints.
We have REST services protected with Oauth2 Client credentials scheme. The Resource and authorization service are running in the same app, but can be split into different apps.
@Configuration public class SecurityConfig { @Configuration @EnableResourceServer protected static class ResourceServer extends ResourceServerConfigurerAdapter { // Identifies this resource server. Usefull if the AuthorisationServer authorises multiple Resource servers private static final String RESOURCE_ID = "*****"; @Resource(name = "OAuth") @Autowired DataSource dataSource; @Override public void configure(HttpSecurity http) throws Exception { // @formatter:off http .authorizeRequests().anyRequest().authenticated(); // @formatter:on } @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources.resourceId(RESOURCE_ID); resources.tokenStore(tokenStore()); } @Bean public TokenStore tokenStore() { return new JdbcTokenStore(dataSource); } } @Configuration @EnableAuthorizationServer protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter { @Resource(name = "OAuth") @Autowired DataSource dataSource; @Bean public TokenStore tokenStore() { return new JdbcTokenStore(dataSource); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(tokenStore()); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.jdbc(dataSource); } } }
Datasource config for the Oauth2 tables:
@Bean(name = "OAuth") @ConfigurationProperties(prefix="datasource.oauth") public DataSource secondaryDataSource() { return DataSourceBuilder.create().build(); }
Communicating with authentication & resource server goes as followed
curl -H "Accept: application/json" user:password@localhost:8080/oauth/token -d grant_type=client_credentials curl -H "Authorization: Bearer token" localhost:8080/...
The following record is present in the Oauth2 Database:
client_id resource_ids client_secret scope authorized_grant_types web_server_redirect_uri authorities access_token_validity refresh_token_validity additional_information autoapprove user **** password NULL client_credentials NULL X NULL NULL NULL NULL
Resttemplate configuration in client application
@Configuration @EnableOAuth2Client public class OAuthConfig { @Value("${OAuth2ClientId}") private String oAuth2ClientId; @Value("${OAuth2ClientSecret}") private String oAuth2ClientSecret; @Value("${Oauth2AccesTokenUri}") private String accessTokenUri; @Bean public RestTemplate oAuthRestTemplate() { ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails(); resourceDetails.setId("1"); resourceDetails.setClientId(oAuth2ClientId); resourceDetails.setClientSecret(oAuth2ClientSecret); resourceDetails.setAccessTokenUri(accessTokenUri); /* When using @EnableOAuth2Client spring creates a OAuth2ClientContext for us: "The OAuth2ClientContext is placed (for you) in session scope to keep the state for different users separate. Without that you would have to manage the equivalent data structure yourself on the server, mapping incoming requests to users, and associating each user with a separate instance of the OAuth2ClientContext." (http://projects.spring.io/spring-security-oauth/docs/oauth2.html#client-configuration) Internally the SessionScope works with a threadlocal to store variables, hence a new thread cannot access those. Therefore we can not use @Async Solution: create a new OAuth2ClientContext that has no scope. *Note: this is only safe when using client_credentials as OAuth grant type! */ // OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, oauth2ClientContext); OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, new DefaultOAuth2ClientContext()); return restTemplate; } }
You can inject the restTemplate to talk (Asynchronously) to the Oauth2 secured service. We do not use scope at the moment.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With