Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: login with google now use new SDK and flow #1108

Merged
merged 12 commits into from
Sep 13, 2023
34 changes: 15 additions & 19 deletions src/actions/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,26 +55,22 @@ export const loginWithFB = FB => (dispatch, getState, { api }) => {
};

/**
* Use `hooks/login/useGoogleLogin` as possible
* Google prevent user to customize login button
* Use `common/Auth/GoogleLoginButton` as possible
* GoogleLoginButton wraps the "Google button flow" with react to render
* "Sign in with Google" buttons
* https://developers.google.com/identity/gsi/web/guides/integrate#button_customization
*/
export const loginWithGoogle = googleAuth => (dispatch, getState, { api }) => {
return googleAuth
.signIn({
scope: 'profile email',
prompt: 'select_account',
})
.then(result => {
const { id_token } = result.getAuthResponse();
return api.auth
.postAuthGoogle({
idToken: id_token,
})
.then(({ token, user: { _id, google_id } }) => {
dispatch(loginWithToken(token));
})
.then(() => authStatus.CONNECTED);
})
.catch(err => authStatus.NOT_AUTHORIZED);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

不需要沿用之前的 autStatus了嗎

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

需要處理,列為之後 TODO 來解

export const loginWithGoogle = credentialResponse => async (
dispatch,
getState,
{ api },
) => {
const idToken = credentialResponse.credential;
const { token } = await api.auth.postAuthGoogle({
idToken,
});
await dispatch(loginWithToken(token));
};

const getMeInfo = token => (dispatch, getState, { api }) =>
Expand Down
34 changes: 34 additions & 0 deletions src/components/common/Auth/GoogleLoginButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React, { useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import { useContext } from 'react';
import GoogleContext from 'contexts/GoogleContext';
import { useIsLoggedIn } from 'hooks/auth';

const GoogleLoginButton = ({ onSuccess }) => {
const googleAuth = useContext(GoogleContext);
const ref = useRef(null);

// use ref in case onSuccess is mutable
const onSuccessRef = useRef(onSuccess);
const isLoggedIn = useIsLoggedIn();

useEffect(() => {
if (googleAuth && ref.current) {
googleAuth.renderButton(ref.current, {});
}
}, [googleAuth]);

useEffect(() => {
if (isLoggedIn) {
onSuccessRef.current();
}
}, [isLoggedIn]);

return <div ref={ref}>Google 登入</div>;
};

GoogleLoginButton.propTypes = {
onSuccess: PropTypes.func.isRequired,
};

export default GoogleLoginButton;
25 changes: 14 additions & 11 deletions src/components/common/GoogleContextProvider/index.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
import React, { useEffect, useState } from 'react';
import { useDispatch } from 'react-redux';
import GoogleContext from 'contexts/GoogleContext';
import { GOOGLE_APP_ID } from '../../../config';
import { loginWithGoogle } from '../../../actions/auth';

const GoogleContextProvider = ({ children }) => {
// initialize google sdk
// when login status change, google will call callback
const dispatch = useDispatch();
const [googleAuth, setGoogleAuth] = useState(null);

useEffect(() => {
if (!window || window.gapi) return;
window.initGoogle = () => {
const gapi = window.gapi;
gapi.load('auth2', () => {
gapi.auth2.init({ client_id: GOOGLE_APP_ID });
const googleAuth = gapi.auth2.getAuthInstance();
setGoogleAuth(googleAuth);
window.onload = function() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

需要檢查原本的 onload 嗎?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

需要...我想想怎麼改,或有建議怎麼做嗎?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

初步直覺想是這樣

let prevOnload = window.onload;
window.onload = function() {
  if (prevOnload) prevOnload();
  // ...
};

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

有 addEventListener 可以用,這樣就不用猜 on 的意思了

window.google.accounts.id.initialize({
client_id: GOOGLE_APP_ID,
callback: async response => {
await dispatch(loginWithGoogle(response));
},
});

setGoogleAuth(window.google.accounts.id);
};
const script = document.createElement('script');
script.src = 'https://apis.google.com/js/platform.js?onload=initGoogle';
document.body.appendChild(script);
}, []);
}, [dispatch]);

return (
<GoogleContext.Provider value={googleAuth}>
Expand Down
19 changes: 8 additions & 11 deletions src/components/common/LoginModal/index.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useFacebookLogin, useGoogleLogin } from 'hooks/login';
import { useFacebookLogin } from 'hooks/login';
import Modal from 'common/Modal.js';
import GoogleLoginButton from 'common/Auth/GoogleLoginButton';
import authStatus from '../../../constants/authStatus';
import styles from './LoginModal.module.css';

const LoginModal = ({ isOpen, close }) => {
const fbLogin = useFacebookLogin();
const googleLogin = useGoogleLogin();

return (
<Modal isOpen={isOpen} hasColose close={close} closableOnClickOutside>
Expand All @@ -24,16 +24,13 @@ const LoginModal = ({ isOpen, close }) => {
>
Facebook 登入
</button>
<button
className={styles['btn-google']}
onClick={async () => {
if (await googleLogin()) {
<div className={styles['btn-google']}>
<GoogleLoginButton
onSuccess={() => {
close();
}
}}
>
Google 登入
</button>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

發現有現成的 button 可用

}}
/>
</div>
<p className={styles['login-tips']}>
為了避免使用者大量輸入假資訊,我們會以你的帳戶做驗證。但別擔心!您的帳戶資訊不會以任何形式被揭露、顯示。
</p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import PropTypes from 'prop-types';
import cn from 'classnames';
import { Link } from 'react-router-dom';
import { useIsLoggedIn } from 'hooks/auth';
import { useFacebookLogin, useGoogleLogin } from 'hooks/login';
import { useFacebookLogin } from 'hooks/login';
import GoogleLoginButton from 'common/Auth/GoogleLoginButton';
import styles from './PermissionBlock.module.css';

const AuthenticatedButton = ({ to, onClick, children }) => (
Expand All @@ -24,7 +25,6 @@ AuthenticatedButton.propTypes = {

const UnauthenticatedButton = () => {
const fbLogin = useFacebookLogin();
const googleLogin = useGoogleLogin();

return (
<div className={styles.loginBtnContainer}>
Expand All @@ -38,11 +38,8 @@ const UnauthenticatedButton = () => {
</button>
<button
className={`${cn('buttonCircleM')} ${styles.btn} ${styles.btnGoogle}`}
onClick={async () => {
await googleLogin();
}}
>
<pre>Google 登入</pre>
<GoogleLoginButton onSuccess={() => {}} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

可以讓 onSuccess optional 或是 default to () => {},這樣這裡就不需要傳空的進去了

</button>
</div>
);
Expand Down
5 changes: 5 additions & 0 deletions src/helpers/Html.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ export default class Html extends Component {
async
src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"
></script>
<script
src="https://accounts.google.com/gsi/client"
async={true}
defer={true}
></script>
</head>
<body>
{/* install google tag manager */}
Expand Down
3 changes: 1 addition & 2 deletions src/hooks/login/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import useFacebookLogin from './useFacebookLogin';
import useGoogleLogin from './useGoogleLogin';
import useLogin from './useLogin';
import useLogout from './useLogout';

export { useFacebookLogin, useGoogleLogin, useLogin, useLogout };
export { useFacebookLogin, useLogin, useLogout };
18 changes: 0 additions & 18 deletions src/hooks/login/useGoogleLogin.js

This file was deleted.

11 changes: 11 additions & 0 deletions src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,17 @@ server.get(
res.status(context.status);
}

/**
* https://developers.google.com/identity/gsi/web/guides/get-google-api-clientid
* > The Referrer-Policy header must also be set to no-referrer-when-downgrade when using http and localhost.
*
* When we develop in localhost
* npm run start run in `development` environment
*/
if (process.env.NODE_ENV === 'development') {
res.set('Referrer-Policy', 'no-referrer-when-downgrade');
}

// TODO handle 301/302

res.send(`<!doctype html>\n${html}`);
Expand Down