I am doing a health project where i need to implement fitbit login using oauth2.
fitbit authorization and ,token url are below
https://www.fitbit.com/oauth2/authorize?response_type=code&client_id=XXXXX&redirect_uri=
http://localhost:XXXX/oauth2/code/fitbit&scope=activity
to get token from fitbit url is we use
POST https://api.fitbit.com/oauth2/token
Authorization: Basic Y2xpZW50X2lkOmNsaWVudCBzZWNyZXQ=
Content-Type: application/x-www-form-urlencoded
client_id=XXXXX&grant_type=auhorization_code&redirect_uri=
http://localhost:8080/oauth2/code/fitbit&code=XXXXX
Above are url for getting authorization code and access_token
I my problem I could able to implement the oauth2 flow using springboot but where I hit the url
http://localhost:8080/login or
http://localhost:8080/oauth2/authorize/fitbit
I am getting error like
[invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0
Access Token Response: 401 Unauthorized: [no body]
here is the application.yml file
spring:
security:
oauth2:
client:
registration:
fitbit:
clientId: XXXXXX
clientSecret: XXXXXXXXXXXXXXX
clientAuthenticationMethod: post
authorizationGrantType: authorization_code
redirectUri: http://localhost:8080/oauth2/code/fitbit
scope: activity
provider:
fitbit:
authorizationUri: https://www.fitbit.com/oauth2/authorize
tokenUri: https://api.fitbit.com/oauth2/token
userInfoUri: https://api.fitbit.com/1/user/-/profile.json
Spring securitycongif class code
@EnableWebSecurity
public class SecurityFor extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers("/home","/login","/oauth2/code/fitbit" ,"/test/login/**","/callback/", "/webjars/**", "/error**", "**/oauth2/**")
.permitAll()
.anyRequest().authenticated()
.and()
.oauth2Login().authorizationEndpoint()
.baseUri("/oauth2/authorize/*")
.and()
.redirectionEndpoint()
.baseUri("/oauth2/code/fitbit")
.and()
.tokenEndpoint()
.accessTokenResponseClient(authorizationCodeTokenResponseClient());
}
private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeTokenResponseClient() {
OAuth2AccessTokenResponseHttpMessageConverter tokenResponseHttpMessageConverter =
new OAuth2AccessTokenResponseHttpMessageConverter();
tokenResponseHttpMessageConverter.setTokenResponseConverter(new OAuth2AccessTokenResponseConverterWithDefaults());
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
new FormHttpMessageConverter(), tokenResponseHttpMessageConverter));
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
DefaultAuthorizationCodeTokenResponseClient tokenResponseClient = new DefaultAuthorizationCodeTokenResponseClient();
tokenResponseClient.setRestOperations(restTemplate);
return tokenResponseClient;
}
OAuth2AccessTokenResponseConverterWithDefaults class
public class OAuth2AccessTokenResponseConverterWithDefaults implements Converter<Map<String, String>, OAuth2AccessTokenResponse> {
private static final Set<String> TOKEN_RESPONSE_PARAMETER_NAMES = Stream.of(
OAuth2ParameterNames.ACCESS_TOKEN,
OAuth2ParameterNames.TOKEN_TYPE,
OAuth2ParameterNames.EXPIRES_IN,
OAuth2ParameterNames.REFRESH_TOKEN,
OAuth2ParameterNames.SCOPE).collect(Collectors.toSet());
private OAuth2AccessToken.TokenType defaultAccessTokenType = OAuth2AccessToken.TokenType.BEARER;
@Override
public OAuth2AccessTokenResponse convert(Map<String, String> tokenResponseParameters) {
System.out.println(OAuth2ParameterNames.ACCESS_TOKEN);
String accessToken = tokenResponseParameters.get(OAuth2ParameterNames.ACCESS_TOKEN);
System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%"+accessToken);
OAuth2AccessToken.TokenType accessTokenType = this.defaultAccessTokenType;
if (OAuth2AccessToken.TokenType.BEARER.getValue().equalsIgnoreCase(
tokenResponseParameters.get(OAuth2ParameterNames.TOKEN_TYPE))) {
accessTokenType = OAuth2AccessToken.TokenType.BEARER;
}
long expiresIn = 0;
if (tokenResponseParameters.containsKey(OAuth2ParameterNames.EXPIRES_IN)) {
try {
expiresIn = Long.valueOf(tokenResponseParameters.get(OAuth2ParameterNames.EXPIRES_IN));
} catch (NumberFormatException ex) { }
}
Set<String> scopes = Collections.emptySet();
if (tokenResponseParameters.containsKey(OAuth2ParameterNames.SCOPE)) {
String scope = tokenResponseParameters.get(OAuth2ParameterNames.SCOPE);
scopes = Arrays.stream(StringUtils.delimitedListToStringArray(scope, " ")).collect(Collectors.toSet());
}
Map<String, Object> additionalParameters = new LinkedHashMap<>();
tokenResponseParameters.entrySet().stream()
.filter(e -> !TOKEN_RESPONSE_PARAMETER_NAMES.contains(e.getKey()))
.forEach(e -> additionalParameters.put(e.getKey(), e.getValue()));
return OAuth2AccessTokenResponse.withToken(accessToken)
.tokenType(accessTokenType)
.expiresIn(expiresIn)
.scopes(scopes)
.additionalParameters(additionalParameters)
.build();
}
public final void setDefaultAccessTokenType(OAuth2AccessToken.TokenType defaultAccessTokenType) {
Assert.notNull(defaultAccessTokenType, "defaultAccessTokenType cannot be null");
this.defaultAccessTokenType = defaultAccessTokenType;
}
}
websecurityconfig class
@Configuration
public class WebClientConfig {
@Bean
public WebClient webClient(ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
System.out.println("&&&&&&&&&&&");
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2 = new ServletOAuth2AuthorizedClientExchangeFilterFunction(
clientRegistrationRepository, authorizedClientRepository);
System.out.println(oauth2.oauth2Configuration());
return WebClient.builder()
.apply(oauth2.oauth2Configuration())
.build();
}
}
I will be repeating my problem again
when ever I give hit to the url localhost:8080/oauth2/authorize/fitbit or localhost:8080/login I am getting the error
[invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: 401 Unauthorized: [no body]
here is the image for the error enter image description here
Thank you
Comment From: wilkinsona
Fitbit's token endpoint is rejecting your request for an access token credential as the request isn't authorized. That suggests that the correct credentials aren't being set when making the request. This happens in DefaultAuthorizationCodeTokenResponseClient. You could try debugging your application with a breakpoint in this class to check the credentials that are being included in the request.
As an aside, while I'm not familiar with Fitbit's API, you seem to have quite a bit more code than I would expect to be necessary to integrate with its OAuth 2 support. I don't think you should need to do much more than is described in the documentation.
If you have any further questions, please follow up on Stack Overflow or Gitter. As mentioned in the guidelines for contributing, we prefer to use GitHub issues only for bugs and enhancements. If you do follow up elsewhere, I'd recommend taking a bit of time to explain what your custom configuration is doing beyond what Spring Boot and Spring Security provide out of the box so that it's easier for those trying to help you to understand its purpose.
Comment From: RaminHeidari
I had the same issue. It fixed by adding client-authentication-method in application.yml
Comment From: Chamithfernando
I also have the same issue
Comment From: shanmukhavarma11
I have solved the issue I will share my code
On Sat, 11 Dec 2021, 6:49 am Chamithfernando, @.***> wrote:
I also have the same issue
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/spring-projects/spring-boot/issues/26884#issuecomment-991401620, or unsubscribe https://github.com/notifications/unsubscribe-auth/AKQ3AF2NVLQF5ZDUPVR3QWLUQKRI5ANCNFSM46UPVFWA . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.
Comment From: OmexIT
@shanmukhavarma11 do you mind sharing
Comment From: danielkv
@shanmukhavarma11 How did you solve this problem? I know the issue is old, but I'm having the same problem here after upgrading to springboot 3.1.5
Comment From: shanmukhavarma11
I will send the code with in one hour
On Fri, Dec 1, 2023, 8:00 PM Daniel K. Guolo @.***> wrote:
@shanmukhavarma11 https://github.com/shanmukhavarma11 How did you solve this problem? I know the issue is old, but I'm having the same problem here after upgrading to springboot 3.1.5
— Reply to this email directly, view it on GitHub https://github.com/spring-projects/spring-boot/issues/26884#issuecomment-1836212005, or unsubscribe https://github.com/notifications/unsubscribe-auth/AKQ3AF2D37EB4C7CBBRDGGDYHHSY3AVCNFSM46UPVFWKU5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TCOBTGYZDCMRQGA2Q . You are receiving this because you were mentioned.Message ID: @.***>
Comment From: danielkv
@shanmukhavarma11 Did you send the code somewhere? I'm still having the issue
Comment From: TejashwiniBusnur
@danielkv @shanmukhavarma11 I am facing the same issue after upgrading to 3.0.13. Could you please share your knowledge on how you resolved this.