spring-projects / spring-boot

Spring Boot helps you to create Spring-powered, production-grade applications and services with absolute minimum fuss.
https://spring.io/projects/spring-boot
Apache License 2.0
75.22k stars 40.7k forks source link

How resolve 401 unauthorized nobody in springboot #26884

Closed shanmukhavarma11 closed 3 years ago

shanmukhavarma11 commented 3 years ago

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

wilkinsona commented 3 years ago

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.

RaminHeidari commented 3 years ago

I had the same issue. It fixed by adding client-authentication-method in application.yml

Chamithfernando commented 2 years ago

I also have the same issue

shanmukhavarma11 commented 2 years ago

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.

OmexIT commented 2 years ago

@shanmukhavarma11 do you mind sharing

danielkv commented 11 months ago

@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

shanmukhavarma11 commented 11 months ago

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: @.***>

danielkv commented 11 months ago

@shanmukhavarma11 Did you send the code somewhere? I'm still having the issue

TejashwiniBusnur commented 3 months ago

@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.