I am running Spring Framework with Spring Security 5.7. This is a relatively old project, written prior to spring boot, so I am not using any spring boot libraries or configuration.
I already have multiple methods for a user to authenticate with my server, via username/password, SSO via google, Microsoft, etc.
I am attempting to use the Spring Security OAuth2 Client to authenticate an already logged in user with a 3rd party service, so I can in turn make API calls to that service on behalf of the user.
The application is also old enough that I am using XML configuration for my spring security configuration, and I have multiple realm tags set up for different security/authentication/authorization purposes. However, we are slowly moving our configuration over from xml to java, and would prefer to configure as much of this in java if possible.
At this point, here is my java configuration:
@Configuration
public class OAuthConfig {
@Bean
public ClientRegistrationRepository clientRegistrationRepository() {
List<ClientRegistration> registrations = new ArrayList<>(1);
ClientRegistration client = ClientRegistration.withRegistrationId("blarg")
/* omitted client details here */
.redirectUri("https://example.com/oauth/blarg")
.build();
registrations.add(client);
return new InMemoryClientRegistrationRepository(registrations);
}
@Bean
public OAuth2AuthorizedClientService authorizedClientService(ClientRegistrationRepository repository) {
return new InMemoryOAuth2AuthorizedClientService(repository);
}
@Bean
public OAuth2AuthorizedClientRepository authorizedClientRepository(OAuth2AuthorizedClientService service) {
return new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(service);
}
@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
OAuth2AuthorizedClientProvider provider = OAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.build();
DefaultOAuth2AuthorizedClientManager manager = new DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);
manager.setAuthorizedClientProvider(provider);
return manager;
}
@Bean
public AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository() {
return new HttpSessionOAuth2AuthorizationRequestRepository();
}
In addition, I have added a filter to my main security realm in xml:
applicationContext-web-security.xml:
...
<bean id="oAuth2AuthorizationRequestRedirectFilter"
class="org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter">
<constructor-arg ref="clientRegistrationRepository"/>
<constructor-arg value="/oauth/authorization"/>
</bean>
<security:http auto-config="false" use-expressions="true" disable-url-rewriting="true">
...
<security:custom-filter ref="oAuth2AuthorizationRequestRedirectFilter" position="OAUTH2_AUTHORIZATION_REQUEST_FILTER"/>
...
</security:http>
I am obviously leaving out a lot of my configuration, but this is the new stuff related specifically to the oauth2 client.
At this point, I am redirecting my client to /oauth/authorization/blarg to start the OAuth 2 flow. This is correctly being handled by the RedirectFilter to redirect the 3rd party for login and authorization. This issue I have is how do I handle the response which will be sent to https://example.com:9000/oauth/blarg? It should contain an authorization code, which I should be able to convert to an accessToken, and store the result in the authorizedClientRepository. I know how to use the repository from that point on, but I don't know how to go from getting the callback with the authorization code, to getting the access token. The documentation references OAuth2AccessTokenResponseClient, but doesn't really say how to use it.
I am not even sure if my manually redirecting to the /oauth/authorization/blarg is the correct way of starting the flow.
So much of what I am finding on the internet is related to the old Spring Security OAuth stuff, or it is related to using oauth to login the user via SSO.
Please don't just give me a link to the Spring Security OAuth2 Client documentation or javadocs, as I have been going over them over and over for quite some time.
Thanks for any help you can provide!
This is what I finally came up with. Still using the Java config from above, this is how my XML configuration looks. I know I can move some of these beans to the java config, but the parts still work.
I basically create both the RedirectFilter and the AuthorizationCodeGrantFilter, but the GrantFilter needed an authentication manager with a provider that knows how to exchange the authorization code for a access token, which is what the Authentication provider does.
<bean id="authorizationCodeTokenResponseClient"
class="org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient"/>
<bean id="oAuth2AuthorizationCodeAuthenticationProvider"
class="org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationProvider">
<constructor-arg ref="authorizationCodeTokenResponseClient"/>
</bean>
<bean id="oAuthAuthenticationManager" class="org.springframework.security.authentication.ProviderManager">
<constructor-arg name="providers">
<list>
<ref bean="oAuth2AuthorizationCodeAuthenticationProvider"/>
</list>
</constructor-arg>
</bean>
<bean id="oAuth2AuthorizationRequestRedirectFilter"
class="org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter">
<constructor-arg ref="clientRegistrationRepository"/>
<constructor-arg value="/oauth/authorization"/>
</bean>
<bean id="oAuth2AuthorizationCodeGrantFilter"
class="org.springframework.security.oauth2.client.web.BulbGrantFilter">
<constructor-arg ref="clientRegistrationRepository"/>
<constructor-arg ref="authorizedClientRepository"/>
<constructor-arg ref="oAuthAuthenticationManager"/>
</bean>
Then in my http tag, I add both filters to the standard chain:
<secuirty:http ...>
...
<security:custom-filter ref="oAuth2AuthorizationRequestRedirectFilter"
position="OAUTH2_AUTHORIZATION_REQUEST_FILTER"/>
<security:custom-filter ref="oAuth2AuthorizationCodeGrantFilter"
position="OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER"/>
...
</security:http>
When I want to have a user authorize the 3rd party service, I redirect them to /oauth/authorization/blarg, which gets intercepted by the requestFilter, which used the configured blarg client information to redirect the user to the 3rd party to login/authorize my app. The 3rd party redirects back to my app, and the GrantFilter is smart enough to tell it is an auth code response. That filter takes the auth code and uses the authentication provider to validate it and exchange it for an access token and refresh token, then saves those in the authorizedClientRepository.
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