Let say you have a Spring-Boot
application and you need a token in order to authenticate/authorize a call made by your application targeting another app.
Since Spring-Boot 3
, and Spring 6
, the spring-security
packages handle the OIDC
mechanism with built-in code. Also their is a common part in registration of OIDC
provider used to authenticate/authorize a call coming in the application and registration of OIDC
provider to get a token to make some external call.
Here are the steps you’ll follow to get your calls with tokens.
Step zero you have to add those dependencies to your project.
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
First you choose a name for your OIDC
provider, here it will be “custom-client”, then you declare it in ten configurations.
Here are some properties for example, for the exhaustive list of properties look into the spring documentation : https://docs.spring.io/spring-security/reference/servlet/oauth2/login/core.html.
spring:
application:
name: sample-spring-boot-oidc
security:
oauth2:
client:
registration:
custom-client:
authorization-grant-type: client_credentials
client-authentication-method: client_secret_post
client-id: [Client-ID]
client-secret: [Secret]
provider: custom-client
provider:
custom-client:
token-uri: https://[IDP Host]/[....]/token
Second you have to build a token provider that will get your registered OIDC provider from the Registration Bean.
import static java.util.Objects.isNull;
import java.util.Optional;
import org.springframework.lang.Nullable;
import org.springframework.security.oauth2.client.AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.InMemoryReactiveOAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProviderBuilder;
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2Token;
public class Oauth2TokenSupplierImpl implements TokenSupplier {
private OAuth2Token oauth2token;
private ClientRegistration registration;
private ReactiveOAuth2AuthorizedClientManager authorizedClientManager;
// `ClientRegistration` is an autoconfigured @Bean provided by Spring, that mean YOU have to get it from the context during the startup sequence and inject it into this constructor.
public Oauth2TokenSupplierImpl(final ClientRegistration registration) {
this.registration = registration;
ReactiveClientRegistrationRepository clientRegistrationRepository = new InMemoryReactiveClientRegistrationRepository(registration);
ReactiveOAuth2AuthorizedClientService clientService = new InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrationRepository);
ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder().clientCredentials().build();
authorizedClientManager = new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(clientRegistrationRepository, clientService);
((AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager) authorizedClientManager).setAuthorizedClientProvider(authorizedClientProvider);
}
private void refeshToken() {
if (isNull(oauth2token) || TokenUtils.isValid(oauth2token)) {
oauth2token = getAccessToken(authorizedClientManager);
}
}
@Override
public @Nullable OAuth2Token get() {
refeshToken();
return oauth2token;
}
public OAuth2AccessToken getAccessToken(ReactiveOAuth2AuthorizedClientManager manager) {
// Since we connect with `client-id` and `client-secret`, using a `principal` is useless.
OAuth2AuthorizeRequest req = OAuth2AuthorizeRequest.withClientRegistrationId(registration.getRegistrationId()).principal("N/A").build();
OAuth2AuthorizedClient client = Optional.of(manager.authorize(req).block()).get();
return client.getAccessToken();
}
}
Third you’re ready to get a token supplier instance, using the ClientRegistrationRepository
and the provider you registered.
public class TokenSupplierFactory {
TokenSupplier tokenSupplierBuilder(String providerName, ClientRegistrationRepository clientRegistrationRepository) {
return new Oauth2TokenSupplierImpl(clientRegistrationRepository.findByRegistrationId(providerName));
}
}
B.N.: Here are the TokenSupplier
interface ans TokenUtils
class
import java.util.function.Supplier;
import org.springframework.security.oauth2.core.OAuth2Token;
public interface TokenSupplier extends Supplier<OAuth2Token> {}
import java.time.Instant;
import org.springframework.security.oauth2.core.OAuth2Token;
public class TokenUtils {
private TokenUtils() {}
public static boolean isValid(OAuth2Token oauth2token) {
return Instant.now().isBefore(oauth2token.getExpiresAt());
}
}
Leave a Reply