nextauthjs / next-auth

Authentication for the Web.
https://authjs.dev
ISC License
24.75k stars 3.48k forks source link

How to get Google provider ID token and not access token #393

Closed vladfulgeanu closed 3 years ago

vladfulgeanu commented 4 years ago

When using the Google provider I want to be able to authenticate with a backend server and for this I need to send the ID token to that API and get back the access token. Further info about this flow can be found here.

This extra request I plan to do in the JWT callback, but in there I cannot access ID token. Only accessToken is given back in token.account.

This is what I get in the JWT callback: token (first argument):

{
  user: {
    name: <name>,
    email: <email>,
    image: <image>
  },
  account: {
    provider: 'google',
    type: 'oauth',
    id: <id>,
    refreshToken: undefined,
    accessToken: <accessToken>,
    accessTokenExpires: null
  },
  isNewUser: undefined
}

profile (second argument):

{
  id: <id>,
  email: <email>,
  verified_email: true,
  name: <name>,
  given_name: <given_name>,
  family_name: <family_name>,
  picture: <picture>,
  locale: 'en'
}
iaincollins commented 4 years ago

Yeah Google is unusual and this has come up before, we should really cover this in the docs.

tl;dr Google only returns a RefreshToken on first sign in and should to be used with a database. You can force it to issue a new one every time using access_type=offline and prompt=consent but that is intended for mobile and desktop applications and changes the sort of prompt a user sees when they sign in (and isn't as seamless).

More info in #269

vladfulgeanu commented 4 years ago

If I were to use react-google-login library, _in the responseGoogle(response) {...} callback function, I should get back a standard JWT located at response.tokenId or res.getAuthResponse().idtoken (from here) That is the only thing that I need in order to send the request to the backend server. I'm not sure how RefreshToken can help me in this case.

iaincollins commented 4 years ago

You can try setting idToken: true as a provider option - it works on quite a few providers, but I'm not sure about Google - and then using the JWT callback (assuming you are using JSON Web Tokens) to handle saving data from the profile in your JWT to use in subsequent calls.

vladfulgeanu commented 4 years ago

I did try exactly this as I saw it possible on the Okta provider, but 2 things happen:

iaincollins commented 4 years ago

Thanks for trying that and reporting what you saw.

I think at the moment the answer is this is not possible, but it seems like a reasonable feature request.

Dreched commented 3 years ago

Any update on this?

The Google provider isn't really complete without it since most front ends will be calling services that need to verify a user's id_token.

MaryJJ commented 3 years ago

I need this too, thanks

theapplefolks commented 3 years ago

Yeah, any updates? I also really need this.

balazsorban44 commented 3 years ago

So if I understand correctly, #837 would help here, right? Can anyone confirm this? (@vladfulgeanu @Dreched @MaryJJ @theapplefolks)

It basically sends the id_token to the jwt callback if it is provided by the IdP at sign-in.

Dreched commented 3 years ago

So if I understand correctly, #837 would help here, right? Can anyone confirm this? (@vladfulgeanu @Dreched @MaryJJ @theapplefolks)

It basically sends the id_token to the jwt callback if it is provided by the IdP at sign-in.

I think #837 will help. To clarify, /api/auth/session will also get id_token set from signIn?

balazsorban44 commented 3 years ago

What ends up in your session is up to you. If you want the id_token available in your session, you can look at the jwt and session callbacks: https://next-auth.js.org/configuration/callbacks

fedeberon commented 3 years ago

const getIdToken = async (refreshToken) => { var requestOptions = { method: 'POST', redirect: 'follow' };

const response = await fetch(`https://oauth2.googleapis.com/token?refresh_token=${refreshToken}&client_id=955818486406-snr4kuu25v16keu169sc1kbm6ofv7lfj.apps.googleusercontent.com&client_secret=yBPnCGw81u_OEHCRHLX8tbO4&grant_type=refresh_token&Content-Type=application/x-www-form-urlencoded`, requestOptions);
let result= await response.json();

return result.id_token;

}

balazsorban44 commented 3 years ago

@fedeberon so when using a refresh token, you will actually receive a new ID token as well? It never occurred to me, I'll check this out, thanks!

Update:

Hmm a quick search in the OIDC spec: https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokenResponse

It says

Upon successful validation of the Refresh Token, the response body is the Token Response of Section 3.1.3.3 except that it might not contain an id_token.

So getting an id_token is not given. 😔

github-actions[bot] commented 3 years ago

:tada: This issue has been resolved in version 3.2.0-canary.10 :tada:

The release is available on:

Your semantic-release bot :package::rocket:

github-actions[bot] commented 3 years ago

:tada: This issue has been resolved in version 3.3.0-canary.1 :tada:

The release is available on:

Your semantic-release bot :package::rocket: