Open anmol-bst opened 2 years ago
This would be really good to know. Thank you 🙇
I'd also love to have support after March 31. 🙏 For reference https://developers.google.com/identity/oauth2/web/guides/migration-to-gis
+1 on this. Thanks you!
+1 We just got an email from Google asking us to migrate to Google Identity Services.
+1
+1 Just incase, Is there any alternative?
+1
@anthonyjgrove Do you have any estimates on this? Or is this project on ice? :D
I haven't had the time with work and having a baby recently.
Would be happy to help plan out the work and review a 6.x
release that migrates the hook to the new Google API though.
@anthonyjgrove Congratulations 🥳
Since this will stop working for existing users March 31 in 2023, we still have some time. Although new users of the google api have a shorter deadline...
I might have some time in the near future (after May) to spike something out. Maybe we can get this upgrade in as a group effort 😄
Whether this project supports new gis on time or not, it would be necessary to have a consent / disclaimer about this so new developers won't accidentally integrate using this. -> on README
I'm happy to contribute to the migration process, let me know once there are some tasks I can help with
+1
As of today I receive below message in the console.
react_devtools_backend.js:3973 Your client application uses libraries for user authentication or authorization that will soon be deprecated. See the [Migration Guide](https://developers.google.com/identity/gsi/web/guides/gis-migration) for more information.
I take it new users can still use react-google-login so long as their client id has a timestamp before April 30th, 2022, is that correct? That gives even new users a year before the March 31, 2023 date:
Beginning April 30th, 2022 new web applications must use the Google Identity Services library, existing web apps may continue using the Platform Library until the March 31, 2023 deprecation date.
Don't forget to create 2 client ids. One for dev and one for prod.
Yesterday I've implemented the new login on my site, it was easier than to use any library. Will share the code later, it's just several lines.
@talaikis can you share the code here?
Here is what I am using.
First I have a general hook called useScript
that can load any <script>
tag into the HTML head and has a callback function for when the script fully loads:
import { useEffect } from "react";
const useScript = (url, onload) => {
useEffect(() => {
const script = document.createElement("script");
script.src = url;
script.onload = onload;
document.head.appendChild(script);
return () => {
document.head.removeChild(script);
};
}, [url, onload]);
};
export default useScript;
Then I have created a GoogleLogin
component that loads Google's button.
import { useRef } from "react";
import useScript from "hooks/useScript";
export default function GoogleLogin({
onGoogleSignIn = () => {},
text = "signin_with",
// feel free to add more options here
}) {
const googleSignInButton = useRef(null);
useScript("https://accounts.google.com/gsi/client", () => {
window.google.accounts.id.initialize({
client_id: process.env.REACT_APP_GOOGLE_CLIENT_ID,
callback: onGoogleSignIn,
});
window.google.accounts.id.renderButton(
googleSignInButton.current,
{ theme: "outline", size: "large", text, width: "250" } // customization attributes
);
});
return <div className="test" ref={googleSignInButton}></div>;
}
Pretty straightforward!
Edit: I no longer suggest using my snippets above, as they can lead to unnecessary script fetches. I suggest using the @react-oauth package below as it's a better implementation.
@talaikis can you share the code here?
I've said, I'll share :) Here it is. It's for Next.js, but can be adjusted.
Step 1:
Include somewhere (_app.js)
import Script from 'next/script'
<Script src="https://accounts.google.com/gsi/client" />
Step 2:
const onResponse = async ({ credential }) => {
// send `credential` to backend
}
const onClick = () => {
window.google.accounts.id.initialize({
client_id: clientId, // here's your Google ID
callback: onResponse,
auto_select: false
})
window.google.accounts.id.prompt()
}
return <button onClick={onClick}>Sign in with Google</button>
Step 3: decode the token:
import { OAuth2Client } from 'google-auth-library'
async function verify (idToken) {
const ticket = await client.verifyIdToken({
idToken,
audience: clientId
})
return ticket.getPayload()
}
// user data, like payload.email, etc.
const payload = await verify(credential)
Here is what I am using.
First I have a general hook called
useScript
that can load any<script>
tag into the HTML head and has a callback function for when the script fully loads:import { useEffect } from "react"; const useScript = (url, onload) => { useEffect(() => { const script = document.createElement("script"); script.src = url; script.onload = onload; document.head.appendChild(script); return () => { document.head.removeChild(script); }; }, [url, onload]); }; export default useScript;
Then I have created a
GoogleLogin
component that loads Google's button.import { useRef } from "react"; import useScript from "hooks/useScript"; export default function GoogleLogin({ onGoogleSignIn = () => {}, text = "signin_with", // feel free to add more options here }) { const googleSignInButton = useRef(null); useScript("https://accounts.google.com/gsi/client", () => { window.google.accounts.id.initialize({ client_id: process.env.REACT_APP_GOOGLE_CLIENT_ID, callback: onGoogleSignIn, }); window.google.accounts.id.renderButton( googleSignInButton.current, { theme: "outline", size: "large", text, width: "250" } // customization attributes ); }); return <div className="test" ref={googleSignInButton}></div>; }
Pretty straightforward!
This works like a charm. In the callback function you will get CredentialResponse from google oauth. You have to decode the credential (which is jwt token). Here is a doc relating to that from Google https://developers.google.com/identity/gsi/web/guides/handle-credential-responses-js-functions#handle_credential_response
@thebrucecgit Thanks
@thebrucecgit @talaikis Thanks for the code snippets, but how do you handle authorization scopes?
@thebrucecgit @talaikis Thanks for the code snippets, but how do you handle authorization scopes?
An ID token has replaced OAuth2 access tokens and scopes.
how to get access_token on call back?
For anyone who is struggling, you can use the following lib instead. I used this for one my apps and it works effortlessly
how to get access_token on call back?
Here is what I am using. First I have a general hook called
useScript
that can load any<script>
tag into the HTML head and has a callback function for when the script fully loads:import { useEffect } from "react"; const useScript = (url, onload) => { useEffect(() => { const script = document.createElement("script"); script.src = url; script.onload = onload; document.head.appendChild(script); return () => { document.head.removeChild(script); }; }, [url, onload]); }; export default useScript;
Then I have created a
GoogleLogin
component that loads Google's button.import { useRef } from "react"; import useScript from "hooks/useScript"; export default function GoogleLogin({ onGoogleSignIn = () => {}, text = "signin_with", // feel free to add more options here }) { const googleSignInButton = useRef(null); useScript("https://accounts.google.com/gsi/client", () => { window.google.accounts.id.initialize({ client_id: process.env.REACT_APP_GOOGLE_CLIENT_ID, callback: onGoogleSignIn, }); window.google.accounts.id.renderButton( googleSignInButton.current, { theme: "outline", size: "large", text, width: "250" } // customization attributes ); }); return <div className="test" ref={googleSignInButton}></div>; }
Pretty straightforward!
This works like a charm. In the callback function you will get CredentialResponse from google oauth. You have to decode the credential (which is jwt token). Here is a doc relating to that from Google https://developers.google.com/identity/gsi/web/guides/handle-credential-responses-js-functions#handle_credential_response
@ArdiansDev refer to this doc by Google.
@thebrucecgit how could you deal with "TS2339: Property 'google' does not exist on type 'Window & typeof globalThis'" when using TypeScript?
@opalkonrad Typescript file :
import React, { useRef } from "react";
import useScript from "../../../utils/useScript";
import { googleClientId } from "../../../config";
interface Props {
text: String;
onGoogleSignIn: Function;
}
declare const window: Window &
typeof globalThis & {
google: any;
GoogleAuth:any;
}
const GoogleLogin: React.FunctionComponent<Props> = ({
onGoogleSignIn = () => {},
text = "signin_with",
// feel free to add more options here
}) => {
const googleSignInButton = useRef<HTMLDivElement>(null);
useScript("https://accounts.google.com/gsi/client", () => {
window.google.accounts.id.initialize({
client_id: googleClientId,
callback: onGoogleSignIn,
});
window.google.accounts.id.renderButton(
googleSignInButton.current,
{ theme: "outline", size: "large", text, width: "250" } // customization attributes
);
});
return (
<div className="test" ref={googleSignInButton}></div>
);
}
export default GoogleLogin;
I created a small wrapper around new Google identity service SDK
@react-oauth/google,
it covers the different ways google can handle login these days and it's strongly typed
As mentioned by @kasvith and @MomenSherif , we can use @react-oauth/google and get the userInfo as below
import { GoogleOAuthProvider } from '@react-oauth/google';
import { GoogleLogin } from '@react-oauth/google';
---------------------------------------------------
const googleSuccess = async (res) => {
console.log('auth.js-googlesuccess-res',res)
fetch(`https://oauth2.googleapis.com/tokeninfo?id_token=${res.credential}`)
.then(res => res.json())
.then(response => {
console.log('user Info=',response)
})
.catch(error => console.log(error));
};
const googleError = (error) => {
console.log('google signin failed-error',error)
}
--------------------------------------------------
<GoogleOAuthProvider clientId="CLIENT_ID">
<GoogleLogin
onSuccess={googleSuccess}
onFailure={googleError}
/>
</GoogleOAuthProvider>
@manojkmishra yup 🎉 and if you are using GoogleLogin you can decode res.credential (jwt) for user info without need for extra request
@MomenSherif Hi! Thanks for your sharing. When I try to get the userInfo by using the code @manojkmishra mentioned. It worked on
@MomenSherif Hi! Thanks for your sharing. When I try to get the userInfo by using the code @manojkmishra mentioned. It worked on but seems not work on custom login button. It showed error {error: 'invalid_token', error_description: 'Invalid Value'} and said res.credential is undefined. So how do I get userInfo on custom login button?
Hi @CharleneKwok, I think the comments in this issue should help you out
Hello @CharleneKwok
'access_token' provided by google is used to authorize us to communicate with google APIs
To get user info from custom button you can follow the issue mentioned by @Rec0iL99
@MomenSherif Hi! Thanks for your sharing. When I try to get the userInfo by using the code @manojkmishra mentioned. It worked on but seems not work on custom login button. It showed error {error: 'invalid_token', error_description: 'Invalid Value'} and said res.credential is undefined. So how do I get userInfo on custom login button?
Hi @CharleneKwok, I think the comments in this issue should help you out
Thank you so much! I can get userInfo now
What about token refresh? How can I do that?
@opalkonrad You need to follow google authorization code flow.
If you are using @react-oauth/google, It can be done using useGoogleLogin
with flow: 'auth-code'
will return code
that you will exchange with your backend to obtain
access_token
(to talk with google APIs)refresh_token
(to refresh user's token)id_token
(JWT contains all user's info)Client
const googleLogin = useGoogleLogin({
onSuccess: async ({ code }) => {
const tokens = await axios.post('http://localhost:3001/auth/google', { // http://localhost:3001/auth/google backend that will exchange the code
code,
});
console.log(tokens);
},
flow: 'auth-code',
});
Backend using express
require('dotenv').config();
const express = require('express');
const {
OAuth2Client,
} = require('google-auth-library');
const cors = require('cors');
const app = express();
app.use(cors());
app.use(express.json());
const oAuth2Client = new OAuth2Client(
process.env.CLIENT_ID,
process.env.CLIENT_SECRET,
'postmessage',
);
app.post('/auth/google', async (req, res) => {
const { tokens } = await oAuth2Client.getToken(req.body.code); // exchange code for tokens
console.log(tokens);
res.json(tokens);
});
app.post('/auth/google/refresh-token', async (req, res) => {
const user = new UserRefreshClient(
clientId,
clientSecret,
req.body.refreshToken,
);
const { credentials } = await user.refreshAccessToken(); // optain new tokens
res.json(credentials);
})
app.listen(3001, () => console.log(`server is running`));
So, I'm going to need to drop this package, or will it be fixable with a patch?
Here is what I am using.
First I have a general hook called
useScript
that can load any<script>
tag into the HTML head and has a callback function for when the script fully loads:import { useEffect } from "react"; const useScript = (url, onload) => { useEffect(() => { const script = document.createElement("script"); script.src = url; script.onload = onload; document.head.appendChild(script); return () => { document.head.removeChild(script); }; }, [url, onload]); }; export default useScript;
Then I have created a
GoogleLogin
component that loads Google's button.import { useRef } from "react"; import useScript from "hooks/useScript"; export default function GoogleLogin({ onGoogleSignIn = () => {}, text = "signin_with", // feel free to add more options here }) { const googleSignInButton = useRef(null); useScript("https://accounts.google.com/gsi/client", () => { window.google.accounts.id.initialize({ client_id: process.env.REACT_APP_GOOGLE_CLIENT_ID, callback: onGoogleSignIn, }); window.google.accounts.id.renderButton( googleSignInButton.current, { theme: "outline", size: "large", text, width: "250" } // customization attributes ); }); return <div className="test" ref={googleSignInButton}></div>; }
Pretty straightforward!
Edit: I no longer suggest using my snippets above, as they can lead to unnecessary script fetches. I suggest using the @react-oauth package below as it's a better implementation.
I found a easy solution for this, in GoogleLogin function use a useEffect hook useEffect({ return () => { window.google = undefined; } }, [])
and in the useScript hook
const useScript = (url, onload) => { useEffect(() => { if (typeof window.google === "undefined") { const script = document.createElement("script");
script.src = url;
script.onload = onload;
document.head.appendChild(script);
return () => {
document.head.removeChild(script);
};
} }, [url, onload]); };
@anthonyjgrove Do you have any estimates on this? Or is this project on ice? :D
@MomenSherif Will your library work after March 31st?
@Georgi-Filipov Yes it will
I'm curious, is the jury out on the best choices to make here? I have (of course) put it off until the last minute, but Im still wondering if it's worth doing manually or if there's a nice package already. People using @MomenSherif's package in production?
I have a question regarding the cutover to Google Identity Services. It is not specifically about the solution we are discussing here, but I plan to follow the great advice above once I figure out what I'm trying to fix :). I thought that the "old way" would no longer work if I created a new Google Client ID. I'm trying to confirm what does not work, so I can be sure that I've fixed it when I've implemented the "new way." I just tried https://github.com/LucasAndrad/gapi-script-live-example with a new client ID, and it seems to be working fine. That's my current confusion. I'm pretty sure that gapi-script library uses the "old way ."I hope somewhere here can help me learn what I am missing. Thank you for your time.
Will the plugin be providing the support and migration to the new standard google identity based login that returns a CredentialResponse rather than a userToken as google is "discontinuing the Google Sign-In JavaScript Platform Library for web".