Search code examples
angularjsspring-bootspring-securityspring-security-oauth2

Spring OAuth 2.0 Client returns 401 Unauthorized after entering correct credentials


Here is my problem.

I am trying to integrate dexcom rest api using springboot to get user health data.

by the dexcom api use oauth2 flow.

when I click on link /login application redirects to dexcom api login page and ask user for username and password if authentication is successfull the dexcom login page is redirected to my application redirect uri by sending authorization code with this authorization I need to get access token from the dexcom and later I have fetch the data using dexcom api.This is my project

My problem is, after successfully entered username and password in dexcom login page it is validated and redirected to my application by giving authorization code like this,

http://localhost:8080/login?code=c956626ef691a1abe46bcc827a68ddfb&state=lJCK1p but in the same page I am getting below error enter image description here Whitelabel Error Page This application has no explicit mapping for /error, so you are seeing this as a fallback.

Tue Jun 08 20:52:24 IST 2021 There was an unexpected error (type=Unauthorized, status=401).

My code

   package com.example.mystorageapp;


 @RestController
 @EnableWebSecurity
 @EnableOAuth2Sso
public class Dexomapi extends WebSecurityConfigurerAdapter {

//@SuppressWarnings("deprecation")
 
@GetMapping(value="/login")
public Response get_auth(@RequestParam(value="code",required =false) String code, 
@RequestParam(value="state") String state ) throws IOException  {
RestTemplate ss=new RestTemplate();
    OkHttpClient client = new OkHttpClient();
    
    String client1= (String)"client_secret=t2sI8N7eY3dW50GK&
     client_id=XXXXXXXX&code="+code+"&grant_type=
      authorization_code&redirect_uri=http://localhost:8080/login";
        System.out.println(code);
        String data23="https://api.dexcom.com/v2/oauth2/token?"+client1;
            MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
            ResponseEntity<String> response1
              = ss.getForEntity(data23  , String.class);
            ObjectMapper mapper3 = new ObjectMapper();
            JsonNode root = mapper3.readTree(response1.getBody());
            System.out.println(root);
            // Manually converting the response body InputStream to APOD using Jackson
            ObjectMapper mapper = new ObjectMapper();
            
            
            // Finally we have the response
            //System.out.println(apod.title);
            RequestBody body = RequestBody.create(mediaType,client1 );
            Request request = new Request.Builder()
              .url("https://api.dexcom.com/v2/oauth2/token")
              .post(body)
              .addHeader("content-type:", "application/x-www-form-urlencoded")
              .addHeader("cache-control", "no-cache")
              .build();
            ObjectMapper mapper1 = new ObjectMapper();
            Response response = client.newCall(request).execute();
            
             Map<String, Object> studentMap1 = mapper.convertValue(response, Map.class);
            System.out.println(studentMap1);
            return response;
    }
   @GetMapping("/")
     public String wow() {
    return "nice";
   }
   @Override
   public void configure(HttpSecurity http) throws Exception { 
    http.csrf().disable()
        .authorizeRequests()
        .antMatchers(
          "/index.html","/error").permitAll().anyRequest().authenticated();
  }
            
  }

By redirect uri is /login

my dependecy

 <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven- 
 4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.4.5</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>googleauth</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>dexcomapi</name>
<description>Demo project for Spring Boot</description>
<properties>
    <java.version>1.8</java.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-oauth2-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.webjars</groupId>
        <artifactId>angularjs</artifactId>
        <version>1.4.3</version>
    </dependency>
    <dependency>
        <groupId>org.webjars</groupId>
        <artifactId>bootstrap</artifactId>
        <version>3.2.0</version>
    </dependency>
    <dependency>
        <groupId>org.webjars</groupId>
        <artifactId>jquery</artifactId>
        <version>2.1.1</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.webjars/webjars-locator -->
   <dependency>
   <groupId>org.webjars</groupId>
   <artifactId>webjars-locator</artifactId>
   <version>0.40</version>
  </dependency>
  <dependency>
  <groupId>org.springframework.security.oauth</groupId>
  <artifactId>spring-security-oauth2</artifactId>
  <version>2.5.1.RELEASE</version>
</dependency>
 <dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.8.RELEASE</version>
</dependency>
    
<dependency>
  <groupId>com.squareup.okhttp3</groupId>
  <artifactId>okhttp</artifactId>
 
</dependency>


    
</dependencies>


<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

my application.yml

    security:
        oauth2:
           client:
              clientId: Ncv8kRTLDnWM1oVeAuQRFFQugcBwIME2
              clientSecret: 2sI8N7eY3dW50GK
              accessTokenUri: https://api.dexcom.com/v2/oauth2/token
              userAuthorizationUri: https://api.dexcom.com/v2/oauth2/login
              tokenName: oauth_token
              authenticationScheme: header
              clientAuthenticationScheme: header
              scope: offline_access
          resource:
             userInfoUri: https://api.dexcom.com/v2/users/self/dataRange
 

my fron end code with angular js

                  <body ng-app="app" ng-controller="home as home">
   <h1>Login</h1>
  <div class="container" ng-show="!home.authenticated">
    With Fitbit: <a href="/login">click here</a>
   </div>
   <div class="container" ng-show="home.authenticated">
    Logged in as: <span ng-bind="home.user"></span><br />
    Lifetime Steps: <span ng-bind="home.lifetimeSteps"></span><br />
    Lifetime Distance: <span ng-bind="home.lifetimeDistance"></span><br />
    Lifetime Floors: <span ng-bind="home.lifetimeFloors"></span><br />
   </div>
  <script type="text/javascript" src="/webjars/angularjs/angular.min.js"></script>
  <script type="text/javascript">
    angular.module("app", []).controller("home", function($http) {
        var self = this;

        $http.get("/login").success(function(data) {
            self.user = data.userAuthentication.details.user.fullName;
            self.authenticated = true;
        }).error(function() {
            self.user = "N/A";
            self.authenticated = false;
        });

        $http.get("/loginDexcom").success(function(data) {
            self.lifetimeSteps = data.steps.toLocaleString();
            self.lifetimeFloors = data.floors.toLocaleString();
            self.lifetimeDistance = data.distance.toLocaleString();
        }).error(function() {
            self.lifetimeSteps = "N/A";
        });
      });
    </script>
     </body>

Thankyou


Solution

  • By explicitly setting the redirect URI to http://localhost:8080/login, you override the default redirect URI, which is /login/oauth2/callback/{registrationId}.

    This URI is special because it prompts the OAuth2LoginAuthenticationFilter to process the request, attempt to authenticate the user and create the OAuth2AuthenticationToken.

    When you set the redirect URI to /login, the OAuth2LoginAuthenticationFilter is not invoked and the application does not know if the user is authenticated, causing a 401.