passport-google-id-token icon indicating copy to clipboard operation
passport-google-id-token copied to clipboard

Google plus oauth and node js. Is there a flaw? Or am I Wrong?

Open rakesh1988 opened this issue 8 years ago • 6 comments

I was doing a POC on oauth using Android and nodejs and I below are my observations:

  • According to http://android-developers.blogspot.sg/2013/01/verifying-back-end-calls-from-android.html , first step is to generate a JWT.
  • I successfully generated JWT via playstore's auth API using "GoogleSignInOptions.requestIdToken(serverClientId)". Here serverClientId is OAuth 2.0 Web application ID.
  • Next step is to verify the given JWT from my server. For this step I used https://www.npmjs.com/package/passport-google-id-token .
  • According to Google's blog (mentioned above), we need to decode and verify if the "aud" is same as our client ID.
  • If yes, then we are sure that the JWT was generated for our application.
  • For doing this all I wanted was the client ID of my Web application . I NEVER USED CLIENT SECRET

Below are my doubts

  • Hacker can reverse engineer my android app and get my client ID
  • He can use the same client ID and create a new JWT and route it to a different server and decode the JWT and get user details.
  • End user agrees to share his details having faith on my application as my application will be shown to client's on the acceptance page.
  • Why is Google allowing to decode JWT without the client secret?
  • Have I understood the process wrong? Is there something that I am missing?

I have also asked this question on SO. I felt its more appropriate to ask here as I am using this node package.

rakesh1988 avatar Mar 21 '16 05:03 rakesh1988

You have to take into account that the only piece of user information that your server (or a malicious one) can get by default is the "sub" field, which is just the Google ID. Take a look at the example response here. Of course you can add the rest of scopes to get e-mail, name, picture and so on, but if your communications are secured with HTTPS (which really should) that shouldn't be an issue, and the user has to grant explicit approval for this information to be shared with you.

The thing with Google ID tokens is that they can't be used to perform actions (Google API calls) from your backend on behalf of the user. In case you wanted that functionality you would in fact need some kind of client secret to authorize your backend, and that brings us to OAuth 2.0 or equivalent solutions that use full-featured tokens (such scenario is documented here).

It's also worth mentioning that a malicious app can't simply show the acceptance page of your legitimate app on a user's device, since the Client IDs you generated are tied to your app's cert signature, as shown in the link you provide.

Hope everything is clearer now, otherwise just let me know!

jmreyes avatar Mar 25 '16 16:03 jmreyes

Does that mean the id token is worthless without the client ID?

I had a problem a few months ago, whereby my passport.authenticate(google-id-token) route only accepted the token in a querystring, instead of the JSON POST data mentioned in the documentation.

This has been working fine for me, except that I worry that the querystring is not a secure place to have sensitive data.

Has anyone else had that problem (of having to use a querystring instead of POST data)? This is getting off topic, but if I'd been sending the JSON incorrectly, I'd like to know how it should be done. And maybe the docs could use a clarification.

ki9us avatar Apr 10 '16 20:04 ki9us

Hi Keith, sorry to hear that you're having trouble with this. After some testing I've confirmed that sending the token via POST with JSON body is working correctly. Keep in mind that you probably need to use some kind of middleware to parse the request body, such as body-parser.

var bodyParser = require('body-parser');
app.use(bodyParser.json());

After adding this, Passport should be able to access body contents (you can use id_token or access_token as attribute).

jmreyes avatar Apr 11 '16 21:04 jmreyes

Oh! Maybe that was it. I do have body-parser, but might have added it after setting up google-id-token.

To be honest, if the query string is secure, I'll just keep using that. Unless google-id-token plans to discontinue it someday.

ki9us avatar Apr 11 '16 22:04 ki9us

@keith24 The query string is not a secure means of communicating information that needs to remain secure. Over HTTPS request headers and body are secure but the URL is not. It must be sent in the clear for routing purposes (DNS etc.). However, I wouldn't put Google ID Token into category of things which need to remain secure. Take for example Google's own service for validating these tokens takes the token via a query string. https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=XYZ123

There are a few risk mitigation factors of ID tokens to consider:

  • Getting one requires your client ID and a user manually clicking login. The op said: "He can use the same client ID and create a new JWT" which just isn't realistic. You can't generate a new token without the users direct involvement. If you're interested in the security model, you would need to read up on oAuth2 implicit grant which is the serverless model where a secret is not exchanged.
  • After being generated they are short lived. An oAuth refresh token can be used to generate a new one, but you're not passing refresh tokens around.
  • The tokens are only useful for validating identity, not making API calls.

Even if someone did successfully decompile your app to get your client id then subsequently man-in-the-middle users so that they could see the request between their phone/computer and your servers, the only thing an ID token can be used to gather is that users profile details. While this is likely to be a privacy policy violation, which can be very serious for a business, the fact that an attacker was able to do all of this to begin with probably indicates some more serious breach.

wwalser avatar Jun 15 '16 23:06 wwalser

is fix yet ? ?

ghost avatar Aug 25 '17 12:08 ghost