18

My front end application is authenticated using gmail account.

I retrieve id_token after the authentication is successful and send it as Authorization Header as bearer token.

E.g. http://localhost:4000/api

Authorization Bearer token_id

At nodejs server side, I call the following method to verify the token.

exports.verifyUser = function(req, res, next) {
    var GoogleAuth = require('google-auth-library');
    var auth = new GoogleAuth();
    var client = new auth.OAuth2(config.passport.google.clientID, config.passport.google.clientSecret, config.passport.google.callbackURL);
    // check header or url parameters or post parameters for token
    var token = "";
    var tokenHeader = req.headers["authorization"];
    var items = tokenHeader.split(/[ ]+/);
    if (items.length > 1 && items[0].trim().toLowerCase() == "bearer") {
        token = items[1];
    }
    if (token) {
        var verifyToken = new Promise(function(resolve, reject) {
            client.verifyIdToken(
                token,
                config.passport.google.clientID,
                function(e, login) {
                    console.log(e);
                    if (login) {
                        var payload = login.getPayload();
                        var googleId = payload['sub'];
                        resolve(googleId);
                        next();
                    } else {
                        reject("invalid token");
                    }
                }
            )
        }).then(function(googleId) {
            res.send(googleId);
        }).catch(function(err) {
            res.send(err);
        })
    } else {
        res.send("Please pass token");
    }
}

When I call the above method, I always get Invalid token response with following error.

Error: No pem found for envelope:     {"alg":"RS256","kid":"c1ab5857066442ea01a01601
850770676460a712"}
    at OAuth2Client.verifySignedJwtWithCerts (\node_modules\google-auth-libr
ary\lib\auth\oauth2client.js:518:13)
  • Is this the right approach to verify token?
  • Do I send the id_token as Authorization bearer? Or is it for authorization only?
  • How do I send the id_token to the sever side? Thru url, header?
  • What am I doing wrong?

Any help is highly appreciated.

Bertrand Martel
  • 42,756
  • 16
  • 135
  • 159
ANewGuyInTown
  • 5,957
  • 5
  • 33
  • 45

4 Answers4

12

OAuth2Client.verifyIdToken take an idToken in arguments, from the library source :

/**
 * Verify id token is token by checking the certs and audience
 * @param {string} idToken ID Token.
 * @param {(string|Array.<string>)} audience The audience to verify against the ID Token
 * @param {function=} callback Callback supplying GoogleLogin if successful
 */
OAuth2Client.prototype.verifyIdToken = function(idToken, audience, callback)

You have passed the whole header value bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImMxYWI1OD U3MDY2NDQyZWEwMWEwMTYwMTg1MDc3MDY3NjQ2MGE3MTIifQ so you will have to split the headers value as :

var authorization = req.headers["authorization"];
var items = authorization.split(/[ ]+/);

if (items.length > 1 && items[0].trim() == "Bearer") {
    var token = items[1];
    console.log(token);
    // verify token
}

Is this the right approach to verify token ?

Yes, this is the right way to verify token. For debugging, you can also verify token with the tokeninfo endpoint if you have any doubt or for quick testing :

https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=XYZ123
  • Do I send the id_token as Authorization bearer? Or is it for authorization only?
  • How do I send the id_token to the sever side? Thru url, header?

You can send JWT token in Authorization header but it could lead to usecase where you have multiple Authorization headers. It's best to URL encode or embed the token in the body. You can check Google example here

Moreover, the following are required by Google :

  • the token must be sent via HTTPS POST
  • the token integrity must be verified

To optimize your code, you could also move your Google auth object to your app.js at the root of your app instead of redefining it each time the token should be verified. In app.js :

var app = express();

var GoogleAuth = require('google-auth-library');
var auth = new GoogleAuth();
app.authClient = new auth.OAuth2(config.passport.google.clientID, config.passport.google.clientSecret, config.passport.google.callbackURL);

and in verifyUser call it from req.app.authClient :

req.app.authClient.verifyIdToken(...)
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
Bertrand Martel
  • 42,756
  • 16
  • 135
  • 159
  • Please can you answer my other question in the post? – ANewGuyInTown Feb 23 '17 at 02:26
  • "You can send JWT token in Authorization header, no problem with that" -> But this is not authorization header, it's for authentication, I thought the approach is different. What if someone has to send both(Authentication and authorization) at the same time? – ANewGuyInTown Feb 23 '17 at 03:22
  • You are right, as [multiple authorization header](http://stackoverflow.com/questions/29282578/multiple-http-authorization-headers) is controversial, even though it is possible I will update for using URL encoded or embedded in the body. Thanks for pointing that – Bertrand Martel Feb 23 '17 at 03:28
  • I do not think you can send `embedded in the body` for `get request`. – ANewGuyInTown Feb 23 '17 at 03:34
  • You can't use a GET request, it must be a POST [check this](https://developers.google.com/identity/sign-in/web/backend-auth) – Bertrand Martel Feb 23 '17 at 03:35
  • I do not think so. What if you are calling a `protected api with get request`. How are you doing to send the authentication token then? It's should be either in the URL or Headers. – ANewGuyInTown Feb 23 '17 at 03:36
  • POST method are preferred because of CORS policy – Bertrand Martel Feb 23 '17 at 03:46
  • So, how do you send the `protected api GET request`? that means only authenticated users are able to GET the request. I think what you are referring to is while sending the token from client to the server, use POST and then start consuming other services (api). But, this would need the server to cache the token and use it for other api request, which is not REST. – ANewGuyInTown Feb 23 '17 at 03:48
  • I don't know what you mean by protected api, I would just follow [Google recommendation](https://developers.google.com/identity/sign-in/web/backend-auth). And your signin endpoint is for unauthenticated users – Bertrand Martel Feb 23 '17 at 03:56
  • id_token is not for consuming API, maybe you confuse with authorization_code that is needed to get an access_token that is used to consume Google API. This [Google cross identity doc](https://developers.google.com/identity/protocols/CrossClientAuth) is excellent to understand the flow (it is focused on Android but the same apply for web client) – Bertrand Martel Feb 23 '17 at 04:11
  • Okay, so I followed your code but now I receive the following error message:` Error: No pem found for envelope: {"alg":"RS256","kid":"c1ab5857066442ea01a01601 850770676460a712"}` – ANewGuyInTown Feb 23 '17 at 04:19
  • if you check the id_token with the tokeninfo endpoint, what does it give ? – Bertrand Martel Feb 23 '17 at 04:22
  • Thanks. I got this working. :) The token I was using was expired. Using token info end point I got this sorted out. Good for debugging. – ANewGuyInTown Feb 23 '17 at 06:17
  • Since header allows key-value pair, instead of keep adding `authorization` for `Bearer` idToken, add something like `googleauthorization` will address the multiple authorization issue. – Jeb50 Jun 10 '21 at 04:34
1

First of all, do not use Id_Token for authorization. It is only for authentication. Use access token for authorization. Use link below to verify token.

https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=${access_token}
0

I finally found the answer today. The Firebase tool will connect the native Google to the third-party login token, and then encapsulate another layer. The token obtained at this time is no longer the original token given to us by Google.

  • A1:
    • Original Token: GoogleDesignInAccount Account = Task.getResult(ApiException.class);
    • Account.getidToken () // This is the original token
  • B1:
    • Firebase token: FireBaseUser currentUser = Mauth.getCurrentUser ();
    • String token = currentUser.getIdToken(false).getResult().getToken();
  • A2:
    • Google officially provides a method to verify the token
  • B2:
    • Firebase officially provides the authentication token method

We use code names for the four data points above. If you need to verify the validity of tokens in the background, they must correspond to each other, A1 to A2 and B1 to B2. If you use A2 to validate the B1, it will fail

shaedrich
  • 5,457
  • 3
  • 26
  • 42
  • Thanks for your answer but could you please post the content in the picture as text in your answer to make sure, the answer is still useful even if the picture is deleted? – shaedrich May 11 '21 at 16:14
0

You can now able to validate google auth token simply using firebase.

first of all create file named firebase-config and store your firebase configurations (which is got from firebase settings).

imports these:

import { initializeApp } from 'firebase-admin/app';
import { getAuth  } from 'firebase-admin/auth';
import { firebaseConfig } from 'PATH_TO_CONFIG_FILE';  //(importing of your config file that you had created)

initialisation:

const defaultApp = initializeApp(firebaseConfig);

google token verify function:

async verifyGoogleId(token) {
      const auth = getAuth(defaultApp);
      const firebaseUser = await auth.verifyIdToken(token);

      if (!firebaseUser) {
         throw new error();
      }
}