How to fix the "Malformed auth code" when trying to refreshToken on the second attempt?

Ausiàs Armesto

I'm developping an Android App with Angular and Cordova plugins and I want to integrate it with Google Authentication. I have installed the cordova-plugin-googleplus and I have successfully integrated into the application. When the user logs in, I get a response where I can get accessToken, profile user information and refreshToken.

Now I want to implement a feature to refresh the token without disturbing the user with a new prompt screen every hour.

I have managed to renew accessToken, but it only works the first time

I have used these two ways:

  1. Sending a curl request with the following data
curl -X POST \
  'https://oauth2.googleapis.com/token?code=XXXXXXXXXXXXXXXX&client_id=XXXXXXXXXXXXXXXX.apps.googleusercontent.com&client_secret=YYYYYYYYYYYY&grant_type=authorization_code' \
  -H 'Cache-Control: no-cache' \
  -H 'Content-Type: application/x-www-form-urlencoded'
  1. Implementing it on the server side using the Google API Client Library for Java and following mainly these code

The point is that when the user logs in for the first time (using the cordova-plugin-googleplus), I receive a refreshToken with this format

4/rgFU-hxw9QSbfdj3ppQ4sqDjK2Dr3m_YU_UMCqcveUgjIa3voawbN9TD6SVLShedTPveQeZWDdR-Sf1nFrss1hc

If after a while I try to refresh the token in any of the above ways I get a successful response with a new accessToken, and a new refreshToken. And that new refreshToken has this other format

1/FTSUyYTgU2AG8K-ZsgjVi6pExdmpZejXfoYIchp9KuhtdknEMd6uYCfqMOoX2f85J

In the second attempt to renew the token, I replace the token with the one returned in the first request

curl -X POST \
  'https://oauth2.googleapis.com/token?code=1/FTSUyYTgU2AG8K-ZsgjVi6pExdmpZejXfoYIchp9KuhtdknEMd6uYCfqMOoX2f85J&client_id=XXXXXXXXXXXXXXXX.apps.googleusercontent.com&client_secret=YYYYYYYYYYYY&grant_type=authorization_code' \
  -H 'Cache-Control: no-cache' \
  -H 'Content-Type: application/x-www-form-urlencoded'

But this time, both ways (Curl and Java) I am getting the same error.

{
  "error" : "invalid_grant",
  "error_description" : "Malformed auth code."
}

I read on this thread that was a problem to specify the clientId as an email, but I have not discovered how to solve it either because the first login it's done with the client id 'XXXXXXX.apps.googleusercontent.com' and if I set an email from the google accounts it says that is an "Unknown Oauth Client"

I hope any can help me with this, as I'm stuck for several days

Ausiàs Armesto

Finally I achieved refreshing the access token as times as needed. The problem was a misconception of how it works the Google Api.

The first time to update the token, it is needed to call this endpoint with these parameters and setting as {{refreshToken}} the value obtained from the response of the Consent Screen call (serverAuthCode)

https://oauth2.googleapis.com/token?code={{refreshToken}}&client_id={{googleClientId}}&client_secret={{googleClientSecret}}&grant_type=authorization_code

After the first refresh, any update to the token needs to be call to this other endpoint by setting as {{tokenUpdated}} the attribute {{refresh_token}} obtained from the response of the first call.

https://oauth2.googleapis.com/token?refresh_token={{tokenUpdated}}&client_id={{googleClientId}}&client_secret={{googleClientSecret}}&grant_type=refresh_token

Here I show you an example of my AuthenticationService

import { Injectable} from '@angular/core';
import { Router } from '@angular/router';
import { GooglePlus } from '@ionic-native/google-plus/ngx';

@Injectable({
  providedIn: 'root'
})
export class AuthenticationService {


static AUTH_INFO_GOOGLE = 'auth-info-google';
static CLIENT_ID = 'XXXXX-XXXX.apps.googleusercontent.com';
static CLIENT_SECRET = 'SecretPasswordClientId';


public authenticationState = new BehaviorSubject(false);

  constructor(
    private router: Router,
    private googlePlus: GooglePlus) {

  }

public isAuthenticated() {
    return this.authenticationState.value;
}

public logout(): Promise<void> {
    this.authenticationState.next(false);   
    return this.googlePlus.disconnect()
    .then(msg => {
      console.log('User logged out: ' + msg);
    }, err => {
      console.log('User already disconected');
    }); 
}

/**
* Performs the login
*/
public async login(): Promise<any> {
    return this.openGoogleConsentScreen().then(async (user) => {
      console.log(' ServerAuth Code: ' + user.serverAuthCode);
      user.updated = false;
      await this.setData(AuthenticationService.AUTH_INFO_GOOGLE, JSON.stringify(user));
      this.authenticationState.next(true);
      // Do more staff after successfully login
    }, err => {
        this.authenticationState.next(false);
        console.log('An error ocurred in the login process: ' + err);
        console.log(err);
    });

}

  /**
   * Gets the Authentication Token
   */
public async getAuthenticationToken(): Promise<string> {
      return this.getAuthInfoGoogle()
        .then(auth => {
          if (this.isTokenExpired(auth)) {
            return this.refreshToken(auth);
          } else {
            return 'Bearer ' + auth.accessToken;
          }
        });
}



private async openGoogleConsentScreen(): Promise<any> {
  return this.googlePlus.login({
    // optional, space-separated list of scopes, If not included or empty, defaults to `profile` and `email`.
    'scopes': 'profile email openid',
    'webClientId': AuthenticationService.CLIENT_ID,
    'offline': true
  });
}

private isTokenExpired(auth: any): Boolean {
    const expiresIn = auth.expires - (Date.now() / 1000);
     const extraSeconds = 60 * 59 + 1;
    // const extraSeconds = 0;
    const newExpiration = expiresIn - extraSeconds;
     console.log('Token expires in ' + newExpiration + ' seconds. Added ' + extraSeconds + ' seconds for debugging purpouses');
    return newExpiration < 0;
}

private async refreshToken(auth: any): Promise<any> {
      console.log('The authentication token has expired. Calling for renewing');
      if (auth.updated) {
        auth = await this.requestGoogleRefreshToken(auth.serverAuthCode, auth.userId, auth.email);
      } else {
        auth = await this.requestGoogleAuthorizationCode(auth.serverAuthCode, auth.userId, auth.email);
      }
      await this.setData(AuthenticationService.AUTH_INFO_GOOGLE, JSON.stringify(auth));
      return 'Bearer ' + auth.accessToken;
}


private getAuthInfoGoogle(): Promise<any> {
    return this.getData(AuthenticationService.AUTH_INFO_GOOGLE)
    .then(oauthInfo => {
      return JSON.parse(oauthInfo);
    }, err => {
      this.clearStorage();
      throw err;
    });
}

private async requestGoogleAuthorizationCode(serverAuthCode: string, userId: string, email: string): Promise<any> {
    let headers = new HttpHeaders();
    headers = headers.set('Content-Type', 'application/x-www-form-urlencoded');
    let params: HttpParams = new HttpParams();
    params = params.set('code', serverAuthCode);
    params = params.set('client_id', AuthenticationService.CLIENT_ID);
    params = params.set('client_secret', AuthenticationService.CLIENT_SECRET);
    params = params.set('grant_type', 'authorization_code');
    const options = {
      headers: headers,
      params: params
    };
    const url = 'https://oauth2.googleapis.com/token';
    const renewalTokenRequestPromise: Promise<any> = this.http.post(url, {}, options).toPromise()
      .then((response: any) => {
        const auth: any = {};
        auth.accessToken = response.access_token;
        console.log('RefreshToken: ' + response.refresh_token);
        auth.serverAuthCode = response.refresh_token;
        auth.expires = Date.now() / 1000 + response.expires_in;
        auth.userId = userId;
        auth.email = email;
        auth.updated = true;
        return auth;
      }, (error) => {
        console.error('Error renewing the authorization code: ' + JSON.stringify(error));
        return {};
      });
    return await renewalTokenRequestPromise;
}

private async requestGoogleRefreshToken(serverAuthCode: string, userId: string, email: string): Promise<any> {
    let headers = new HttpHeaders();
    headers = headers.set('Content-Type', 'application/x-www-form-urlencoded');
    let params: HttpParams = new HttpParams();
    params = params.set('refresh_token', serverAuthCode);
    params = params.set('client_id', AuthenticationService.CLIENT_ID);
    params = params.set('client_secret', AuthenticationService.CLIENT_SECRET);
    params = params.set('grant_type', 'refresh_token');
    const options = {
      headers: headers,
      params: params
    };
    const url = 'https://oauth2.googleapis.com/token';
    const renewalTokenRequestPromise: Promise<any> = this.http.post(url, {}, options).toPromise()
      .then((response: any) => {
        const auth: any = {};
        auth.accessToken = response.access_token;
        console.log('RefreshToken: ' + serverAuthCode);
        auth.serverAuthCode = serverAuthCode;
        auth.expires = Date.now() / 1000 + response.expires_in;
        auth.userId = userId;
        auth.email = email;
        auth.updated = true;
        return auth;
      }, (error) => {
        console.error('Error renewing refresh token: ' + JSON.stringify(error));
        return {};
      });
    return await renewalTokenRequestPromise;
}

private setData(key: string, value: any): Promise<any> {
    console.log('Store the value at key entry in the DDBB, Cookies, LocalStorage, etc')
}

private getData(key: string): Promise<string> {
    console.log('Retrieve the value from the key entry from DDBB, Cookies, LocalStorage, etc')
}

private clearStorage(): Promise<string> {
    console.log('Remove entries from DDBB, Cookies, LocalStorage, etc related to authentication')
}



}

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

I am trying to authenticate with facebook but getting error TokenError: Malformed auth code

How to get refreshToken when using GoogleAuthUtil

Error when trying to fix

How to fix end of statement when trying to insert multiple values in SQL?

How to fix ESLint Error when trying to remove service worker

How to fix "} expected" when trying to create an enum in a console application?

How to fix an error when trying to display an error from the React server?

How to fix “Permission denied” when trying to edit a file on aws server?

How to fix? Triggers exception when trying to free dynamic allocated memory

Musixml parsing exception when trying to parse with JFugue, how to fix this?

How to fix Laravel throwing FatalThrowableError when trying to broadcast a new event

How to fix error when trying to upload file to backend?

How to fix the "Nullpointer Exception", when trying to get a IGuildUser from a IGuild

How to fix "Broken Pipe" error when trying to write a named pipe?

how do you fix this error when trying to initialize an arraylist?

How to fix "attempt to index ? (a number value)" ? LUA

How to fix this error: "attempt to index ? (a number value)"

Error checking attempt, how would I fix this?

RxSwift refreshToken use retry(when:)

How to fix the disappearing word from the Jlabel when I implement a different class for the second level of my game?

How to fix Widget is disposed errer when tryng to open Shell for the second time?

In Moodle How do I allow a single student a second attempt on a quiz?

How to fix "ViewDestroyedError: Attempt to use a destroyed view" error in Angular tests?

How to fix Attempt to invoke interface method on a null object reference

How to fix 'attempt to call method 'addMoney' (a nil value)' error?

How to fix Attempt to invoke virtual method on a null object reference error

How to fix 'Attempt to invoke virtual method' error in recycleview project?

How to use the Firebase refreshToken to reauthenticate?

How to test Firebase messaging refreshToken

TOP Ranking

  1. 1

    Failed to listen on localhost:8000 (reason: Cannot assign requested address)

  2. 2

    Loopback Error: connect ECONNREFUSED 127.0.0.1:3306 (MAMP)

  3. 3

    How to import an asset in swift using Bundle.main.path() in a react-native native module

  4. 4

    pump.io port in URL

  5. 5

    Compiler error CS0246 (type or namespace not found) on using Ninject in ASP.NET vNext

  6. 6

    BigQuery - concatenate ignoring NULL

  7. 7

    ngClass error (Can't bind ngClass since it isn't a known property of div) in Angular 11.0.3

  8. 8

    ggplotly no applicable method for 'plotly_build' applied to an object of class "NULL" if statements

  9. 9

    Spring Boot JPA PostgreSQL Web App - Internal Authentication Error

  10. 10

    How to remove the extra space from right in a webview?

  11. 11

    java.lang.NullPointerException: Cannot read the array length because "<local3>" is null

  12. 12

    Jquery different data trapped from direct mousedown event and simulation via $(this).trigger('mousedown');

  13. 13

    flutter: dropdown item programmatically unselect problem

  14. 14

    How to use merge windows unallocated space into Ubuntu using GParted?

  15. 15

    Change dd-mm-yyyy date format of dataframe date column to yyyy-mm-dd

  16. 16

    Nuget add packages gives access denied errors

  17. 17

    Svchost high CPU from Microsoft.BingWeather app errors

  18. 18

    Can't pre-populate phone number and message body in SMS link on iPhones when SMS app is not running in the background

  19. 19

    12.04.3--- Dconf Editor won't show com>canonical>unity option

  20. 20

    Any way to remove trailing whitespace *FOR EDITED* lines in Eclipse [for Java]?

  21. 21

    maven-jaxb2-plugin cannot generate classes due to two declarations cause a collision in ObjectFactory class

HotTag

Archive