Refresh token not available after initial login.
Refresh token not available after initial login.
Description
When using Google Login, the refesh token is not always available in the Access Token Response. It is available the first time the user logs into the application but not in subsequent logins. This was discovered in this this community thread.
Affects versions
Currently tested with 1.48.1
Steps to reproduce
Steps to reproduce the behavior:
-
Create an Application and enable debugging. I used fusionauth-quickstart-dotnet-web as a base.
-
Set up the application in Google
-
Create and Open ID Connect Identity Provider and enable if for the application
- Need to do this so you can supply the access_type=offline to the Authorization endpoint.
- Need to do this so you can supply the access_type=offline to the Authorization endpoint.
-
Login to the application using the Open ID Connect button.
-
Choose your Google account.
-
Consent to access.
-
View the debug log and you can view the refresh token
-
Logout of Application.
-
Log back into Application using the Open ID Connect button.
-
Look at the logs and see there is now no refresh token.
-
Log out.
-
Go into the Google account you are using to test with and remove all connections with the test application.
-
Go to web application and login again using Open Id Connect Button.
-
View the logs and see the refresh token again.
Expected behavior
I expect the refresh token to be available every time the user logs in.
Platform
(Please complete the following information)
- Device: Desktop
- OS: macOS
- Browser + version Chrome 119.0.6045.159
- Database PostgresSQL with image.
Community guidelines
All issues filed in this repository must abide by the FusionAuth community guidelines.
Additional context
N/A
The current design is that we take whatever the IdP returns and store it. So that means this is working how I expect.
We could optionally keep the previous value of the current login response doesn't return a refresh token.
The potential issue here is that we would just have to guess that the reason the IdP doesn't return a refresh token is because it thinks the prior one is still valid (assuming that is why Google is not retuning it on the second login - because the current session is still active).
Maybe this is a safe assumption? It just means we may hold onto old tokens unknowingly.
@robotdan @mark-robustelli I am confused. As a user of FusionAuth, I supposed to call Google to get the access token using the refresh token that is initially returned? I know that's how OAuth works but I was under the impression that fusionauth was handling much of the heavy lifting. It does not say this in the docs that this is a refresh token.
To be clear all I need is the access token so I can call Google API's and I need that to be refreshed somehow. The token that comes back from retrieveUserLinksByUserId the typescript sdk is some times blank. This is my problem. @robotdan is right that we wouldn't expect the refresh token every time. I just need access to that access token so I can call Google APIs.
@jschatz1 From https://fusionauth.io/docs/apis/identity-providers/openid-connect
FusionAuth will also store the refresh_token returned from the external OpenID Connect provider, if such a token is provided, in the identityProviderLink object, in the identityProviderLink.token field. This object is accessible using the Link API.
It varies based on the identity provider, but is documented in the API.
We should continue the support questions on the forum post, because this issue is for the bug that you uncovered (thank you!), not for support.
@robotdan I agree that this is "working as designed" but it seems to me that nulling out an existing refresh token is not exactly behavior that the user will expect or desire. I think it is reasonable to think of the token field as remaining present. I think there are two options:
- document that the refresh token may be lost (on a provider by provider basis, depending if they send back the refresh token for subsequent logins)
- if the refresh token is not present, don't update the value.
the latter feels more in keeping with the documentation:
FusionAuth will also store the refresh_token returned from the external OpenID Connect provider, if such a token is provided, in the identityProviderLink object
Since the refresh token is not provided in the second login above, as Mark's screenshots show, we shouldn't update it. Basically, we should test if we get back a refresh token.
Honestly @mooreds if it was clearer that the token is a refresh token I might expect it to disappear. In many flows a refresh token is only provided once.
The API for the Link API does not re mention that this is a refresh token and instead it says it is an opaque token.
Thanks for the feedback @jschatz1 . I'll take a look at the documentation and see if it can be made clearer.
Pinging to see if there has been any further movement on this issue. Has anything been prioritized or investigated further, or is just a matter of updating the docs for clarity?
@Noblebrown at this point, this is not in plan. The documentation may be clarified, but I don't expect any changes to the OIDC behavior.
Hi, trying to write an article about Google login with FA and I'm stuck here.
In my web app, I send the user to Google to log in with https://accounts.google.com/o/oauth2/v2/auth?client_id=535042435541-eridnie9d09pa69dsq1hh066ushvp4e0.apps.googleusercontent.com&redirect_uri=http://localhost:3000/fago&response_type=code&scope=openid%20email%20profile%20https://www.googleapis.com/auth/contacts.readonly&state=STATE.
Page returns successfully to my callback function with the data below:
{
state: 'STATE',
code: '4/0AVG7fiTY7ScSkdA9uVG_BT_uB8e',
scope: 'email profile https://www.googleapis.com/auth/contacts.readonly openid https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email',
authuser: '1',
hd: 'ritza.co',
prompt: 'consent'
}
As I understand this post, I send the code to FusionAuth to complete the login and create a pending link between the FA user and the Google user, http://localhost:9011/api/identity-provider/login?idp_hint=26481189-e3f7-433f-804b-9643a025806a.
I then POST here to complete the pending link, http://localhost:9011/api/identity-provider/link.
I then GET here to get the user details, including access token or refresh token: http://localhost:9011/api/identity-provider/link?identityProviderId=${linkResponse.data.identityProviderLink.identityProviderId}&identityProviderUserId=${linkResponse.data.identityProviderLink.identityProviderUserId}&userId=${linkResponse.data.identityProviderLink.userId}.
But no token is returned, which lead me to search and find this issue.
So if the token isn't returned from that last call, where do I get it please? This bug says it's supposed to be available on initial login, but all I get is an auth code from Google, shown in that first JSON.
And if my app has to store and manage all the tokens anyway, what is the point of telling readers to use FA to manage Oauth stuff? They could more easily complete the full login to Google with an Oauth library like Passport in code, since they have to store the user and her Google token details in a database anyway.
Thanks.