I am using ClientCredentialsResourceDetails to setup my OAuth2RestTemplate object.
My OAuth2 enabled api is sending me back refresh_token in the response, which can be used to refresh access tokens.
However, Spring does not implement token refresh for ClientCredentialsResourceDetails object as it can be seen here
Is there a way to get token refresh working or am I doing anything incorrect ?
FYI, this is my piece of spring code for oauth beans
#Bean
#Primary
public OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails() {
final ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails();
resourceDetails.setClientId(clientKey);
resourceDetails.setClientSecret(clientSecret);
final URI accessTokenUri = UriComponentsBuilder.newInstance()
.host(host)
.path(tokenUrlPath)
.scheme(scheme)
.build()
.toUri();
resourceDetails.setAccessTokenUri(accessTokenUri.toString());
resourceDetails.setClientAuthenticationScheme(AuthenticationScheme.header);
return resourceDetails;
}
#Bean
public OAuth2RestTemplate oAuth2RestTemplate(
OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails
) {
final OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(oAuth2ProtectedResourceDetails);
oAuth2RestTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
return oAuth2RestTemplate;
}
It's because "4.4.3. Access Token Response" in RFC 6749 (The OAuth 2.0 Authorization Framework) says as follows:
If the access token request is valid and authorized, the authorization server issues an access token as described in Section 5.1. A refresh token SHOULD NOT be included. If the request failed client authentication or is invalid, the authorization server returns an error response as described in Section 5.2.
In short, Client Credentials Flow should not issue a refresh token. Spring complies with the requirement.
Related
I have an application that uses microservice architecture and services are protected by Spring OAuth2 Client Credentials Grant type. We use spring WebClient to call from one service to another along with Spring OAuth client library as it handles fetching OAuth credentials transparently.
As we know the Spring (OAuth client) makes a call to Authorization server to request a new token from Auth server when it doesn't have a token or the current token is expired. How can we log a statement on client side whenever Spring (OAuth client) makes a call to the OAuth server? We would like to log this statement to see how frequently the OAuth client library is making calls to the Authorization Server.
public WebClient webClient()
{
return WebClient.builder()
.apply(filter())
.baseUrl("http://localhost:8082/resource")
.build();
}
public Consumer<Builder> filter()
{
ServletOAuth2AuthorizedClientExchangeFilterFunction oAuth =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oAuth.setDefaultClientRegistrationId("my-client");
return oAuth.oauth2Configuration();
}
Define a custom token client that logs when sending a token request to the authorization server.
#Slf4j
public class LoggingClientCredentialsTokenResponseClient
implements OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> {
private DefaultClientCredentialsTokenResponseClient delegate =
new DefaultClientCredentialsTokenResponseClient();
#Override
public OAuth2AccessTokenResponse getTokenResponse(
OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest) {
log.debug("Sending request {}", clientCredentialsGrantRequest);
var response = delegate.getTokenResponse(clientCredentialsGrantRequest);
log.debug("Received response {}", response);
return response;
}
}
Create an authorized client manager that uses the custom token client.
#Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientService authorizedClientService) {
var authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials(builder ->
builder.accessTokenResponseClient(
new LoggingClientCredentialsTokenResponseClient()))
.build();
var authorizedClientManager =
new AuthorizedClientServiceOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientService);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
Create a WebClient that uses the authorized client manager.
#Bean
public WebClient oauth2WebClient(
WebClient.Builder webClientBuilder,
OAuth2AuthorizedClientManager authorizedClientManager) {
var oauth2Filter =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oauth2Filter.setDefaultClientRegistrationId("my-client");
return webClientBuilder.apply(oauth2Filter.oauth2Configuration())
.baseUrl("http://localhost:8082/resource")
.build();
}
I have a spring boot client app which uses OAuth2RestTemplate as OAuth2Client. I have configured the OAuth2RestTemplate to call authserver and add token obtained from it to header for accessing resource server.
The problem occured is that whenever i call the method in client app to access resource server using restTemplate, it used the token coming from header of the request of the client app instead of calling the auth server.
It uses that token and the token gets rejected by my resource server. And after it is rejected, it then only calls the auth server and puts correct token and again sends the request to my resource server.
Is there any way to make rest template not use the token from the header and call the auth server for the token before connecting resource server?
Thank u
My config class
#Configuration
#EnableOAuth2Client
public class OAuth2ClientConfig {
#Autowired
ConfigProperties configProperties;
#Bean("oauth2AuthServer")
public OAuth2RestOperations restTemplate(OAuth2ClientContext oauth2ClientContext) {
OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(resource(), oauth2ClientContext);
oAuth2RestTemplate.setAccessTokenProvider(new CustomResourceOwnerPasswordAccessTokenProvider());
return oAuth2RestTemplate;
}
#Bean
protected OAuth2ProtectedResourceDetails resource() {
ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();
resource.setId(configProperties.getClientId());
resource.setAccessTokenUri(configProperties.getAccessTokenUri());
resource.setClientId(configProperties.getClientId());
resource.setClientSecret(configProperties.getClientSecret());
resource.setGrantType(configProperties.getGrantType());
resource.setClientAuthenticationScheme(AuthenticationScheme.header);
resource.setAuthenticationScheme(AuthenticationScheme.header); //
resource.setUsername(configProperties.getUsername());
resource.setPassword(configProperties.getPassword());
return resource;
}
}
My serviceImpl method is
#Autowired
#Qualifier("oauth2AuthServer")
private OAuth2RestOperations oauth2RestOperations;
RequestResponse callResourceServer(ResourceRequest request) {
try {
RequestResponse response;
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<ReseourceRequest> entity = new HttpEntity<>(request, headers);
response = this.oauth2RestOperations.postForObject(
microServiceConfig.getUrl(),
entity,
RequestResponse.class
);
return response;
} catch (Exception ex) {
log.error(ex);
throw new exception("error");
}
}
I see BaseOAuth2ProtectedResourceDetails and Oauth2RestTemplate deprecated, Can we still use them ? or we should migrate to 5.x options
I have implemented JWT authorization within my Spring Boot REST API using Auth0.
It is generally working as expected, however I have noticed a strange issue when testing in POSTMAN.
When I successfully authenticate any one request, e.g. a GET request using the Bearer JWT token from Auth0, I then get the following Cookie populated in all my other requests:
Now, with this JESSIONID cookie I am able to perform my other REST requests with no JWT Token?
Why is this? It does not seem secure, I would expect a JWT to need to be passed for every REST request?
My Spring SecurityConfig for reference:
/**
* Configures our application with Spring Security to restrict access to our API endpoints.
*/
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${auth0.audience}")
private String audience;
#Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
private String issuer;
#Override
public void configure(HttpSecurity http) throws Exception {
/*
This is where we configure the security required for our endpoints and setup our app to serve as
an OAuth2 Resource Server, using JWT validation.
*/
http.cors().and().csrf().disable().authorizeRequests()
.mvcMatchers(HttpMethod.GET,"/users/**").authenticated()
.mvcMatchers(HttpMethod.POST,"/users/**").authenticated()
.mvcMatchers(HttpMethod.DELETE,"/users/**").authenticated()
.mvcMatchers(HttpMethod.PUT,"/users/**").authenticated()
.and()
.oauth2ResourceServer().jwt();
}
#Bean
JwtDecoder jwtDecoder() {
/*
By default, Spring Security does not validate the "aud" claim of the token, to ensure that this token is
indeed intended for our app. Adding our own validator is easy to do:
*/
NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder)
JwtDecoders.fromOidcIssuerLocation(issuer);
OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator(audience);
OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuer);
OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);
jwtDecoder.setJwtValidator(withAudience);
return jwtDecoder;
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("*"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
There are 4 ways to manage session in Spring Security,
always – a session will always be created if one doesn't already exist
ifRequired – a session will be created only if required (default)
never – the framework will never create a session itself but it will use one if it already exists
stateless – no session will be created or used by Spring Security
So looking at your configuration, it seems that your application is using 2nd option which is "ifRequired" and it creates the session and if request comes with existing sessionId, it allows the user to access the resource because that user is already authorized.
So if you want your application to be completely stateless and no session should be created, you should use the last option which is stateless.
So to make it stateless, change your configuration to,
http.cors().and().csrf().disable().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()
.mvcMatchers(HttpMethod.GET, "/users/**").authenticated()
.mvcMatchers(HttpMethod.POST, "/users/**").authenticated()
.mvcMatchers(HttpMethod.DELETE, "/users/**").authenticated()
.mvcMatchers(HttpMethod.PUT, "/users/**").authenticated().and().oauth2ResourceServer()
.jwt();
Hope this helps
I have a working api-gateway application built on spring-boot 2.2, which is an Oauth2 client supporting authorization-code grant flow. It is built using spring-boot #EnableOAuth2Sso, which will create a spring session and oauth2 context once the user is successfully logs in. Every request to the resource server will be intercepted by this api-gateway, and validates the user session also the oauth2 token from the session context. All the resource servers are Oauth2 protected.
I need to support SAML2 login also through this gateway. I have setup WSO2 as Identity provider, which provides the SAML response as a POST request to an endpoint in the api-gateway once a user is successfully logged in through an IDP initiated login flow. Right now the WSO2 IDP is able to provide me an Oauth2 token when I submit a token request with SAML assertion and SAML grant type. What I need is when a SAML POST request comes from the WSO2 IDP to the api-gateway, it should create an Oauth2 token from WSO2 using SAML assertion and SAML grant type, also should create a Spring session and Oauth2 context with this received Oauth2 token.
I have two possible options at this moment,
1) Define a custom spring security Oauth2 grant-type in the api-gateway Oauth2 client, and handle the SAML response to generate Oauth2 token using spring-security, also the Spring session and Oauth2 context.
2) Manually write code to generate Oauth2 token using SAML response, also manually create a new Spring session and Oauth2 context, which will be an ugly approach.
Given below the current security configuration class.
#Configuration
#EnableOAuth2Sso
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
private static final String ROOT_PATH = "/";
private static final String SUFFIX = "**";
private static final String ANY_OTHER = "/webjars";
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher(ROOT_PATH + SUFFIX)
.authorizeRequests()
.antMatchers(LOGIN_PATH.value() + SUFFIX, ERROR_PATH.value() + SUFFIX, ANY_OTHER + SUFFIX, "/saml**").permitAll()
.antMatchers(HttpMethod.POST, "/saml**").permitAll()
.anyRequest()
.authenticated()
.and().csrf().ignoringAntMatchers("/saml**")
.and().logout().logoutSuccessUrl(LOGOUT_SUCCESS_PATH.value()).permitAll();
}
#Bean
public OAuth2RestOperations restTemplate(OAuth2ClientContext clientContext, OAuth2ProtectedResourceDetails resourceDetails) {
return new OAuth2RestTemplate(resourceDetails, clientContext);
}
#SuppressWarnings({ "rawtypes", "unchecked" })
#Bean
public FilterRegistrationBean oauth2ClientFilterRedirectRegistration(OAuth2ClientContextFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(filter);
registration.setOrder(-100);
return registration;
}
}
The Yml configuration is given below
security:
basic:
enabled: false
oauth2:
client:
clientId: xxxx
clientSecret: xxxxx
accessTokenUri: http://localhost:9763/oauth2/token
userAuthorizationUri: http://localhost:9763/oauth2/authorize
scope: openid,email,name
resource:
userInfoUri: http://localhost:9763/oauth2/userinfo
jwk:
key-set-uri: http://localhost:9763/oauth2/jwks
Can someone suggest how we can define a custom spring security Oauth2 grant-type in a Oauth2 client? Is there any documentation available to configure the same? Does the spring-security support this requirement?
Also is there any other solutions to handle this scenario? Any suggestions please.
We created an authorization server with JDBC backend token store. A similar implementation is hosted on GitHub.
It is working perfectly fine in our environment using different grant types. Different web applications use this for SSO, and it issues tokens, which are then used to consume API as well.
We need a way to log a user in, and issue token if the user is returned as authenticated from external IDP, kind of simulating a user logging in manually from the login form.
We have to extend this server with external IDP authentication. So if a user is connected to their domain network, and has ADFS (as an example), expected flow is as follows:
User tries to access a client app
Redirected to authorization server
Instead of entering credentials user can click on a button to authenticate via ADFS (this can be automated too later on)
ADFS should return authentication ok, with user information
Trigger login of that user in the authorization server, so that an OAuth2 token is issued, and redirected back to the client app
We have tried multiple ways to achieve it, and have referred to multiple resources online, but no success yet. Please note that we do not have the need to connect to social media IDP, rather we have to consume response from enterprise-grade like ADFS, One-login etc.
Any initial pointers would be much appreciated.
To authenticate with GitHub and generate spring token which can be used downstream application we can change our codes like below.
In WebSecurityConfigurerAdapter add below code additional to configure(HttpSecurity http)
http.exceptionHandling()
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/")).and()
.addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class).addFilter(customBasicAuthFilter);
then in WebSecurityConfigurerAdapter again
#Bean
public FilterRegistrationBean oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(filter);
registration.setOrder(-100);
return registration;
}
#Bean
#ConfigurationProperties("github")
public ClientResources github() {
return new ClientResources();
}
private Filter ssoFilter() {
CompositeFilter filter = new CompositeFilter();
List<Filter> filters = new ArrayList<>();
filters.add(ssoFilter(github(), "/login/github"));
filter.setFilters(filters);
return filter;
}
private Filter ssoFilter(ClientResources client, String path) {
OAuth2ClientAuthenticationProcessingFilter oAuth2ClientAuthenticationFilter = new OAuth2ClientAuthenticationProcessingFilter(
path);
OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(client.getClient(), oauth2ClientContext);
oAuth2ClientAuthenticationFilter.setRestTemplate(oAuth2RestTemplate);
UserInfoTokenServices tokenServices = new UserInfoTokenServices(client.getResource().getUserInfoUri(),
client.getClient().getClientId());
tokenServices.setRestTemplate(oAuth2RestTemplate);
oAuth2ClientAuthenticationFilter.setTokenServices(tokenServices);
return oAuth2ClientAuthenticationFilter;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.parentAuthenticationManager(authenticationManager);
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
auth.jdbcAuthentication().dataSource(dataSource).passwordEncoder(passwordEncoder);
}
add one class ClientResources
class ClientResources {
#NestedConfigurationProperty
private AuthorizationCodeResourceDetails client = new AuthorizationCodeResourceDetails();
#NestedConfigurationProperty
private ResourceServerProperties resource = new ResourceServerProperties();
public AuthorizationCodeResourceDetails getClient() {
return client;
}
public ResourceServerProperties getResource() {
return resource;
}
}
additional to all we need to add GitHub setting in our application.
github.client.clientId = <<Clientid>>
github.client.clientSecret = <<clientSecret>>
github.client.accessTokenUri = https://github.com/login/oauth/access_token
github.client.userAuthorizationUri = https://github.com/login/oauth/authorize
github.client.clientAuthenticationScheme = form
github.resource.userInfoUri = https://api.github.com/user
logging.level.org.springframework.security = DEBUG
Similar way you can do it for other which supports OAuth. I am also exploring for working with ADFS authentication. Query posted on Stackoverflow for the same.