I am in a very weird problem, most probably caused by linkedin API. I have configured linkedin as a provider, as normally as anyone can. PFA Snippets.
Problem is very weird:
For me to get user's information from endpoint, I need openid
in scope. Now, even though scope is openid
, linkedin returns an opaque token and doesn't provide JWKs URI.
By looking openid
in scope, spring's OAuth2LoginAuthenticationProvider rejects it and asks OidcAuthorizationCodeAuthenticationProvider (See line where it calls createOidcToken
which internally looks for JWKs URI) Problem is, Linkedin doesn't return a JWT in the first place.
Important Note: I have tried keeping openid scope initially and removing in debug point so that OAuth2LoginAuthenticationProvider accepts it, and it works.
I am trying to create my CustomAuthenticationProvider, but Spring is failing to inject dependencies.
FILES*
application.yml
spring:
security:
oauth2:
client:
registration:
linkedin:
provider: linkedin
client-id: ${LINKEDIN_CLIENT_ID}
client-secret: ${LINKEDIN_CLIENT_SECRET}
client-authentication-method: client_secret_post
authorization-grant-type: 'authorization_code'
redirect-uri: '{baseUrl}/login/oauth2/code/linkedin'
scope:
- profile
- email
- openid
provider:
linkedin:
authorization-uri: https://www.linkedin.com/oauth/v2/authorization
token-uri: https://www.linkedin.com/oauth/v2/accessToken
user-info-uri: https://api.linkedin.com/v2/userinfo
user-name-attribute: sub
Config File
@Bean
@Order(2)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/login", "/resources/**", "/logout")
.permitAll()
.anyRequest().authenticated()
)
.oauth2Login(oauthLoginConfig -> oauthLoginConfig
.tokenEndpoint(tokenEndpointConfig -> {
tokenEndpointConfig.accessTokenResponseClient(linkedinTokenResponseClient());
}));
return httpSecurity.build();
}
private static DefaultAuthorizationCodeTokenResponseClient linkedinTokenResponseClient() {
var defaultMapConverter = new DefaultMapOAuth2AccessTokenResponseConverter();
Converter<Map<String, Object>, OAuth2AccessTokenResponse> linkedinMapConverter = tokenResponse -> {
var withTokenType = new HashMap<>(tokenResponse);
withTokenType.put(OAuth2ParameterNames.TOKEN_TYPE, OAuth2AccessToken.TokenType.BEARER.getValue());
return defaultMapConverter.convert(withTokenType);
};
var httpConverter = new OAuth2AccessTokenResponseHttpMessageConverter();
httpConverter.setAccessTokenResponseConverter(linkedinMapConverter);
var restOperations = new RestTemplate(List.of(new FormHttpMessageConverter(), httpConverter));
restOperations.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
var client = new DefaultAuthorizationCodeTokenResponseClient();
client.setRestOperations(restOperations);
return client;
}
CustomAuthenticationProvider
Copied as it is from here Except, deleted line 98 to 105.
Actually linkedin DOES SEND id_token (open id token) as JWT.
I was getting error because once it parses JWT, next thing it does is verifies Nonce which LinkedIn does not support. That's where error was coming from.
So to prevent that, remove Nonce from request and spring will understand that you don't want nonce verification.
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/login", "/resources/**", "/logout")
.permitAll()
.anyRequest().authenticated()
)
.oauth2Login(oauthLoginConfig -> oauthLoginConfig
.authorizationEndpoint((authorizationEndpointConfig ->
authorizationEndpointConfig.authorizationRequestResolver(
requestResolver(this.clientRegistrationRepository) // See this.
))
)
.tokenEndpoint(tokenEndpointConfig -> tokenEndpointConfig
.accessTokenResponseClient(linkedinTokenResponseClient())
)
);
return httpSecurity.build();
}
private static DefaultOAuth2AuthorizationRequestResolver requestResolver
(ClientRegistrationRepository clientRegistrationRepository) {
DefaultOAuth2AuthorizationRequestResolver requestResolver =
new DefaultOAuth2AuthorizationRequestResolver(clientRegistrationRepository,
"/oauth2/authorization");
requestResolver.setAuthorizationRequestCustomizer(c ->
c.attributes(stringObjectMap -> stringObjectMap.remove(OidcParameterNames.NONCE))
.parameters(params -> params.remove(OidcParameterNames.NONCE))
);
return requestResolver;
}