It is not as simple as I would expect to authenticate using Twitter / X v2 Log In APIs. We should simplify this process. This is inspired by the question at https://devcommunity.x.com/t/oauth2-in-spring-boot-3-implementation/233316
- [ ] #16377
- [x] #16379
- [x] #16380
- [x] #16382
- [x] #16383
- [ ] #16390
- [ ] #16391
Comment From: rwinch
NOTE: I added this here instead of https://devcommunity.x.com/t/oauth2-in-spring-boot-3-implementation/233316 because X was rejecting my POST due to the links in it. I spent too long trying to figure out what was wrong, so I'm posting it here instead.
Let's go over the changes:
1) X uses PKCE but also requires the client id / secret to be provided as basic authentication. This means that client-authentication-method=none is incorrect. You can change the value to client_secret_basic
or remove the property since this is the default. Unfortunately, there is not currently a declarative way to use PKCE + Basic authentication in Spring Security. Instead, you will need to add some code:
@Bean
SecurityFilterChain springSecurity(HttpSecurity http, OAuth2AuthorizationRequestResolver resolver) throws Exception {
http
.authorizeHttpRequests( r -> r
.anyRequest().authenticated()
)
.oauth2Login(l -> l
.authorizationEndpoint(a -> a
.authorizationRequestResolver(resolver)
)
);
return http.build();
}
@Bean
OAuth2AuthorizationRequestResolver pkceResolver(ClientRegistrationRepository clientRegistrationRepository) {
DefaultOAuth2AuthorizationRequestResolver resolver = new DefaultOAuth2AuthorizationRequestResolver(clientRegistrationRepository,"/oauth2/authorization");
resolver.setAuthorizationRequestCustomizer(OAuth2AuthorizationRequestCustomizers.withPkce());
return resolver;
}
This should be improved when gh-16382 is resolved.
2) In the sample you provided, it has the scope of users.read
, but X's /users/me
endpoint requires both the users.read
and tweet.read
scopes.
3) The redirect-uri
is required and the value provided in the sample is correct. However, it can be improved to work with any registration id by changing the value to {baseUrl}/login/oauth2/code/{registrationId}
. This should be defaulted so the property can be omitted and will be fixed in gh-16377.
4) The user-name-attribute=name
is incorrect. Twitter data.username
to represent the username. This can be fixed by using the following:
@Bean
DefaultOAuth2UserService userService() {
DefaultOAuth2UserService service = new DefaultOAuth2UserService();
// unwrap the response from the user to be the contents of the `data` JSON propert
service.setAttributesConverter((
request) -> (attributes) -> (java.util.Map<String, Object>) attributes.get("data"));
return service;
}
# Since the code above unwrapped the attributes, we can now access the username from the username attribute
spring.security.oauth2.client.provider.x.user-name-attribute=username
We plan to improve this with gh-16390.