2

The react-google-login from the client react app sends the response back to the Nodejs server with a post request-
client code -

import axios from 'axios';
import React, { Component } from 'react';
import GoogleLogin from 'react-google-login';
import refreshTokenSetup from '../../utils/refreshToken';

const clientId =
 'xxxxxx-xfdgsdjg3gfxxxxxxxxxxx.apps.googleusercontent.com';


function Login() {
 const onSuccess = (res) => {
   console.log('Login Success: currentUser:', res.profileObj);
   alert(
     `Logged in successfully welcome ${res.profileObj.name} . \n See console for full profile object.`
   );
   axios
     .post('http://localhost:5000/auth/checkToken', { body: res.tokenId })
     .then()
     .catch((err) => {
       console.log(err);
     });
 };

 const onFailure = (res) => {
   console.log('Login failed: res:', res);
   alert(
     `Failed to login.  Please ping this to repo owner twitter.com/sivanesh_fiz`
   );
 };

 return (
   <div>
     <GoogleLogin
       clientId={clientId}
       buttonText='Login'
       onSuccess={onSuccess}
       onFailure={onFailure}
       cookiePolicy={'single_host_origin'}
       style={{ marginTop: '100px' }}
       isSignedIn={true}
     />
   </div>
 );
}

export default Login;

the backend route-

const { OAuth2Client } = require('google-auth-library');
const key = require('../config/key');
module.exports = {
  checkToken: (req, res, next) => {
    console.log('checking begins...', req.body);

    const client = new OAuth2Client(key.GOOGLE_CLIENT_ID);
    async function verify() {
      const ticket = await client.verifyIdToken({
        idToken: req.body,
        audience: key.GOOGLE_CLIENT_ID, // Specify the CLIENT_ID of the app that accesses the backend
        // Or, if multiple clients access the backend:
        //[CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3]
      });
      const payload = ticket.getPayload();
      const userid = payload['sub'];
      // If request specified a G Suite domain:
      // const domain = payload['hd'];
    }
    verify().catch(console.error);
  },
};

The above code is in reference to official Google Documentation available at- https://developers.google.com/identity/sign-in/web/backend-auth

Now everything works fine, user is signed in in the client side, the tokenId is sent back to the server and can be verified by console logging it, even on https://jwt.io/ but the following error is shown-

TypeError: jwt.split is not a function
    at OAuth2Client.verifySignedJwtWithCertsAsync (E:\Projects\EAbackend\node_modules\google-auth-library\build\src\auth\oauth2client.js:528:30)
    at OAuth2Client.verifyIdTokenAsync (E:\Projects\EAbackend\node_modules\google-auth-library\build\src\auth\oauth2client.js:394:34)
    at processTicksAndRejections (internal/process/task_queues.js:93:5)
    at async verify (E:\Projects\EAbackend\middleware\auth.js:9:22)
Saheel Sapovadia
  • 2,755
  • 3
  • 11
  • 22

2 Answers2

1

The main issue is that the example from Google doesn't really tell us what is expected as input to the verifyIdToken({options}) function.

This is what Google stated:

After Google returns an ID token, it's submitted by an HTTP POST method request, with the parameter name credential, to your login endpoint.

Which to me, is a little unclear of what is actually sent to the server to be verified. So here is a working example, copied/pasted from https://developers.google.com/identity/gsi/web/guides/verify-google-id-token, but with proper definitions for token and CLIENT_ID that are not mentioned on the Google site.

Server side Node JS:

exports.googleTokenChecker = (request, response) => {
    const CLIENT_ID = request.body.clientId;
    const token = request.body.credential;
    
    // copied from Google example
    const {OAuth2Client} = require('google-auth-library');
    const client = new OAuth2Client(CLIENT_ID);
    async function verify() {
      const ticket = await client.verifyIdToken({
          idToken: token, 
          audience: CLIENT_ID,  // Specify the CLIENT_ID of the app that accesses the backend
          // Or, if multiple clients access the backend:
          //[CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3]
      });
      const payload = ticket.getPayload();
      const userid = payload['sub'];
      // If request specified a G Suite domain:
      // const domain = payload['hd'];
    }
    verify().catch(console.error);

}

Client side HTML to show what is sent to the backend:

<div id="g_id_onload"
  data-client_id="CLIENT_ID.apps.googleusercontent.com"
  data-callback="handleCredentialResponse"
  data-auto_prompt="false">
</div>
<script>
  function handleCredentialResponse(response) {
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "http://localhost:3000/api/google_token_checker", true);
    xhr.setRequestHeader('Content-Type', 'application/json');
    xhr.send(JSON.stringify(response));
  }
</script>
// contents of response parameter
// {
//  clientId: 'CLIENT_ID.apps.googleusercontent.com',
//  credential: 'JWT_HEADER.JWT_PAYLOAD.JWT_SIGNATURE',
//  select_by: 'btn'
// }
Will
  • 156
  • 2
  • 13
0

Problem is in the idToken: req.body,
req.body has a body object in which the token was present, simply changing it to req.body.body solved the error.
The problem might be very begginner level but took a lot of my time and no online resourse was available which could point me in any direction.
Check the POST Request you will find the error.

Saheel Sapovadia
  • 2,755
  • 3
  • 11
  • 22