react-use-googlelogin
react-use-googlelogin copied to clipboard
Could `refreshUser` return the `tokenId` too
refreshUser is a really useful addition but I think it should return the tokenId too ? That or the function just returns the response object. In the project I'm using this great lib in I use the tokenId (JWT token) to authenticate requests to my API as per Google's instructions: https://developers.google.com/identity/sign-in/web/backend-auth#send-the-id-token-to-your-server.
These is the code in question: https://github.com/asyarb/react-use-googlelogin/blob/master/src/index.tsx#L170-L176
I'd be happy to create a PR, I just wanted to ask the question first in case there was good reason etc.
I would like this functionality as well. I am about to try using the HookReturnValue.googleUser.tokenId property after resolution of the refreshUser() promise, in the hopes that it is up-to-date at that point.
My apologies for the delay on responding to this issue, but to answser @whomwah, there isn't any technical reason the tokenId couldn't be returned in the return of refreshUser().
I've been a bit swamped with work and other things lately, but I'd be more than happy to take a PR if that's alright on your guys end.
Just noting that using the existing googleUser.tokenId property seemed to work just fine for me, so this particular issue isn't blocking. Additionally, since the refreshUser()call is updating the user state all on it's own, and the tokenId along with it. I didn't even have a need to use the refreshUser() result at all in my application, other than to ensure that any code that depends on the updated user state isn't executed until after the Promise resolves.
@NightWatchman how googleUser.tokenId updated for you?
I have code such as:
const fetchWithRefresh = async (
uri,
options,
) => {
if (!googleUser) return;
const timeToExpire = googleUser.expiresAt - 300 * 1000 - Date.now()
const shouldRefreshToken = timeToExpire <= 0
// const shouldRefreshToken = true;
console.log("Refreshing token? ", timeToExpire, googleUser.expiresAt)
if (shouldRefreshToken) {
await refreshUser()
}
let tokenId = googleUser.tokenId
return fetch(uri, {
...options,
headers: {
...options?.headers,
authorization: `Bearer ${tokenId}`,
},
})
}
but googleUser.tokenId never get updated (I guess it needs rerender) even though I see in refreshUser function that new googleuser is returned from google library.
@pszafer In my use-case, I am using a React context to hold the user state and easily access it from anywhere in my React application. I set JavaScript interval in a React useEffect() hook, to periodically refresh the user state every 10 minutes (since the google tokens last for 15 minutes).
React itself takes care of triggering an refresh on any of my components that reference the AuthenticationProvider context. When those components perform their updates, the googleProvider.googleUser.tokenId has already been updated by the refreshUser() call, so those components pick up the new tokenId without me having to do anything.
I used to manage my own tokenId state in this construct, which I manually maintained off of the googleProvider.googleUser object directly, but since it's now updated internally by react-use-googlelogin I don't have to do that anymore and I simply do tokenId: googleProvider.googleUser?.tokenId to set my user context.
export const AuthenticationProvider: React.FC<{clientId: string}> = ({clientId, children}) => {
const googleProvider = useGoogleLogin({clientId});
useEffect(() => {
if (googleProvider.googleUser) {
// Google token only works for 15 minutes so it needs to be periodically
// refreshed.
const iter = setInterval(async () => {
// typescript optional chaining suddenly stopped working.
// possibly this issue https://github.com/facebook/create-react-app/issues/8107
if (googleProvider.googleUser)
{
await googleProvider.refreshUser()
}
}, 10 * 60 * 1000);
return (): void => {
clearInterval(iter);
}
}
}, [googleProvider.googleUser]);
const context: AuthenticationContext = {
signIn: googleProvider.signIn as () => Promise<User|undefined>,
// intentionally discards unused google implementation detail.
signOut: googleProvider.signOut as unknown as () => Promise<void>,
user: {
tokenId: googleProvider.googleUser?.tokenId,
profile: {
userKey: googleProvider.googleUser?.profileObj?.googleId,
imageUrl: googleProvider.googleUser?.profileObj?.imageUrl,
emailAddress: googleProvider.googleUser?.profileObj?.email,
name: googleProvider.googleUser?.profileObj?.name,
givenName: googleProvider.googleUser?.profileObj?.givenName,
familyName: googleProvider.googleUser?.profileObj?.familyName
}
},
isSignedIn: googleProvider.isSignedIn,
isInitialized: googleProvider.isInitialized
};
return (
<AuthenticationContext.Provider value={context}>
{children}
</AuthenticationContext.Provider>
);
@NightWatchman thanks for you code.
I am surprised that it is working for you, I have almost same code, but I need also fetchWithRefresh with Bearer token inside and at least in this function googleProvided.googleUser values never get updated after initial signIn...
one more question though, how is it possible that your expiration time from google of token is only 15 minutes? default is 1 hour I think, and 15 minutes it would be great for me. because right now I can test each change every hour (force refresh while old token is still valid works great).
@pszafer To be honest, it's entirely possible that I'm wrong about how long it takes for the google provided token to expire, and it's an hour instead of 15 minutes. As far as I know we don't have the ability to set how long the token is valid for, and we're stuck with Google's decision on that front.
Thanks @asyarb I too have switched and am working on a different project so the change is not as important to me. If I get any spare time I'll try to come back to it if it's still an issue.
@asyarb is there an updated ETA for this? 🙏