diff --git a/.github/workflows/dev-pull-request.yaml b/.github/workflows/dev-pull-request.yaml index 7388bdd0..ae416f8d 100644 --- a/.github/workflows/dev-pull-request.yaml +++ b/.github/workflows/dev-pull-request.yaml @@ -39,6 +39,7 @@ jobs: --build-arg NEXT_PUBLIC_DEBUG_LOGGING=false \ --build-arg CLOUD_ENV=DEV \ --build-arg NEXT_PUBLIC_GOOGLE_APP_ID=688095955960-t0fpaj4ec3gh5vsr9lhg8govapk2oeo9.apps.googleusercontent.com \ + --build-arg GOOGLE_APP_SECRET=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 \ --build-arg NEXT_PUBLIC_CONTACT_FORM_POST_URL=https://api-dev.rubinobs.com/actions/contact-form/send \ --build-arg NEXT_PUBLIC_PLAUSIBLE_DOMAIN= \ --build-arg NEXT_PREVIEW_SLUG=preview-in-craft-cms \ diff --git a/.github/workflows/dev-push.yaml b/.github/workflows/dev-push.yaml index dca62ef0..d782dc72 100644 --- a/.github/workflows/dev-push.yaml +++ b/.github/workflows/dev-push.yaml @@ -33,6 +33,7 @@ jobs: --build-arg CLOUD_ENV=DEV \ --build-arg NEXT_PUBLIC_BASE_URL=https://dev.rubinobs.com \ --build-arg NEXT_PUBLIC_GOOGLE_APP_ID=688095955960-t0fpaj4ec3gh5vsr9lhg8govapk2oeo9.apps.googleusercontent.com \ + --build-arg GOOGLE_APP_SECRET=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 \ --build-arg NEXT_PUBLIC_CONTACT_FORM_POST_URL=https://api-dev.rubinobs.com/actions/contact-form/send \ --build-arg NEXT_PUBLIC_PLAUSIBLE_DOMAIN= \ --build-arg NEXT_PREVIEW_SLUG=preview-in-craft-cms \ diff --git a/.github/workflows/master-pr.yaml b/.github/workflows/master-pr.yaml index dc3777cb..917994a8 100644 --- a/.github/workflows/master-pr.yaml +++ b/.github/workflows/master-pr.yaml @@ -38,7 +38,8 @@ jobs: --build-arg NEXT_PUBLIC_DEBUG_LOGGING=false \ --build-arg CLOUD_ENV=INT \ --build-arg NEXT_PUBLIC_BASE_URL=https://int.rubinobs.dev \ - --build-arg NEXT_PUBLIC_GOOGLE_APP_ID=688095955960-t0fpaj4ec3gh5vsr9lhg8govapk2oeo9.apps.googleusercontent.com \ + --build-arg NEXT_PUBLIC_GOOGLE_APP_ID=422617727806-q7opitpsisom3fekpplsh8b77s0oe6n2.apps.googleusercontent.com \ + --build-arg GOOGLE_APP_SECRET=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 \ --build-arg NEXT_PUBLIC_CONTACT_FORM_POST_URL=https://api-int.rubinobs.dev/actions/contact-form/send \ --build-arg NEXT_PUBLIC_PLAUSIBLE_DOMAIN= \ --build-arg NEXT_PREVIEW_SLUG=preview-in-craft-cms \ diff --git a/.github/workflows/master-push.yaml b/.github/workflows/master-push.yaml index f9839403..ccec1b63 100644 --- a/.github/workflows/master-push.yaml +++ b/.github/workflows/master-push.yaml @@ -32,7 +32,8 @@ jobs: --build-arg EDC_LOGGER_API_URL=https://us-central1-skyviewer.cloudfunctions.net/edc-logger \ --build-arg NEXT_PUBLIC_DEBUG_LOGGING=false \ --build-arg CLOUD_ENV=INT \ - --build-arg NEXT_PUBLIC_GOOGLE_APP_ID=688095955960-t0fpaj4ec3gh5vsr9lhg8govapk2oeo9.apps.googleusercontent.com \ + --build-arg NEXT_PUBLIC_GOOGLE_APP_ID=422617727806-q7opitpsisom3fekpplsh8b77s0oe6n2.apps.googleusercontent.com \ + --build-arg GOOGLE_APP_SECRET=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 \ --build-arg NEXT_PUBLIC_CONTACT_FORM_POST_URL=https://api-int.rubinobs.dev/actions/contact-form/send \ --build-arg NEXT_PUBLIC_PLAUSIBLE_DOMAIN= \ --build-arg NEXT_PREVIEW_SLUG=preview-in-craft-cms \ diff --git a/.github/workflows/prod-push.yaml b/.github/workflows/prod-push.yaml index f1eaa181..f7209e06 100644 --- a/.github/workflows/prod-push.yaml +++ b/.github/workflows/prod-push.yaml @@ -39,6 +39,7 @@ jobs: --build-arg NEXT_PUBLIC_DEBUG_LOGGING=false \ --build-arg CLOUD_ENV=PROD \ --build-arg NEXT_PUBLIC_GOOGLE_APP_ID=596747551410-vcqlrp7erg5c5gm0dkvc0k21mgi0ilg0.apps.googleusercontent.com \ + --build-arg GOOGLE_APP_SECRET=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 \ --build-arg NEXT_PUBLIC_CONTACT_FORM_POST_URL=https://api.rubinobs.org/actions/contact-form/send \ --build-arg NEXT_PUBLIC_PLAUSIBLE_DOMAIN=rubinobservatory.org,all.epo.sites \ --build-arg NEXT_PREVIEW_SLUG=preview-in-craft-cms \ diff --git a/components/atomic/Button/patterns/GoogleSSOButton.js b/components/atomic/Button/patterns/GoogleSSOButton.js new file mode 100644 index 00000000..2cf2df02 --- /dev/null +++ b/components/atomic/Button/patterns/GoogleSSOButton.js @@ -0,0 +1,52 @@ +import PropTypes from "prop-types"; +import { useGoogleLogin } from "@react-oauth/google"; +import { useRouter } from "next/router"; +import { useAuthenticationContext } from "@/contexts/Authentication"; +import SSOButton from "./SSOButton"; + +export default function GoogleSSOButton({ children, ...buttonProps }) { + const { authenticateWithGoogle } = useAuthenticationContext(); + const { query, push, asPath, pathname } = useRouter(); + const goToGoogleSignIn = useGoogleLogin({ + state: asPath, + onSuccess: (response) => { + push( + { pathname: asPath.split("?")[0], query: { sso: true } }, + undefined, + { + shallow: true, + } + ); + fetch("/api/authGoogle", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ code: response.code }), + }) + .then((res) => res.json()) + .then((data) => { + authenticateWithGoogle(data); + }) + .catch(console.error); + }, + onError: (error) => { + console.error(error); + }, + flow: "auth-code", + }); + + return ( + + {children} + + ); +} + +GoogleSSOButton.propTypes = { + children: PropTypes.node, + service: PropTypes.oneOf(["google", "facebook", "email"]), +}; diff --git a/components/atomic/index.js b/components/atomic/index.js index fc309792..181f6fd7 100644 --- a/components/atomic/index.js +++ b/components/atomic/index.js @@ -1,4 +1,5 @@ export { default as SSOButton } from "./Button/patterns/SSOButton"; +export { default as GoogleSSOButton } from "./Button/patterns/GoogleSSOButton"; export { default as EarlyAccess } from "./Flag/patterns/EarlyAccess"; export { default as Tile } from "./Tile"; export { default as InvestigationTile } from "./Tile/patterns/InvestigationTile"; diff --git a/components/auth/AuthorizePage/index.js b/components/auth/AuthorizePage/index.js index cc0b3358..8c6295ba 100644 --- a/components/auth/AuthorizePage/index.js +++ b/components/auth/AuthorizePage/index.js @@ -15,7 +15,8 @@ const AUTHORIZED_TYPES = { }; function isAuthorized(typeHandle, user, status) { - if (typeHandle === "educatorPages") return user?.group === "educators"; + if (typeHandle === "educatorPages") + return user?.group === "educators" && status === "active"; if (typeHandle === "userProfilePage") return !!user && status === "active"; return false; } diff --git a/components/modal/RegisterModal/JoinForm/index.js b/components/modal/RegisterModal/JoinForm/index.js index 18600b1e..2c49a780 100644 --- a/components/modal/RegisterModal/JoinForm/index.js +++ b/components/modal/RegisterModal/JoinForm/index.js @@ -2,7 +2,7 @@ import PropTypes from "prop-types"; import { useTranslation } from "react-i18next"; import Link from "next/link"; import { Link as BaseLink } from "@rubin-epo/epo-react-lib"; -import { SSOButton } from "@/components/atomic"; +import { GoogleSSOButton, SSOButton } from "@/components/atomic"; import useAuthModal from "@/hooks/useAuthModal"; import { useAuthenticationContext } from "@/contexts/Authentication"; import AuthModal from "../../AuthModal"; @@ -14,7 +14,7 @@ export default function JoinForm({ onEmailSignup }) { const { pendingGroup, setPendingGroup, - goToGoogleSignIn, + // goToGoogleSignIn, goToFacebookSignIn, } = useAuthenticationContext(); @@ -47,9 +47,9 @@ export default function JoinForm({ onEmailSignup }) { */} - + {t("join.continue_with_google")} - + {/* */} - - {t("sign_in.continue_with_google")} - + + {t("join.continue_with_google")} + {/* {t("sign_in.continue_with_facebook")} */} diff --git a/hooks/useAuthentication.js b/hooks/useAuthentication.js index 29f4790c..665e9b29 100644 --- a/hooks/useAuthentication.js +++ b/hooks/useAuthentication.js @@ -1,6 +1,5 @@ import { useState, useEffect } from "react"; import { useRouter } from "next/router"; -import { useGoogleLogin } from "react-google-login"; import jwtDecode from "jwt-decode"; import { authenticate, @@ -17,7 +16,6 @@ import { requestDeletion, } from "@/lib/api/auth"; -const GOOGLE_APP_ID = process.env.NEXT_PUBLIC_GOOGLE_APP_ID; const SESSION_STORAGE_KEYS = [ "jwt", "jwtExpiresAt", @@ -83,24 +81,6 @@ export default function useAuthentication(data) { const [loading, setLoading] = useState(false); const [error, setError] = useState(false); - const { signIn: goToGoogleSignIn } = useGoogleLogin({ - clientId: GOOGLE_APP_ID, - onSuccess: (response) => { - const ssoModalUrl = { pathname: "/", query: { sso: true } }; - push(ssoModalUrl, undefined, { - shallow: true, - }); - // eslint-disable-next-line no-console - console.log("onSuccess", response, "onSuccess"); - authenticateWithGoogle({ idToken: response.tokenId }); - }, - onFailure: (error) => { - // eslint-disable-next-line no-console - console.log("onFailure", error, "onFailure"); - console.error(error); - }, - }); - useEffect(() => { // TODO: cancel promise if component unmounts first (async () => await maybeRefreshToken())(); @@ -442,8 +422,8 @@ export default function useAuthentication(data) { forgotPassword, setPassword, activateUser, - goToGoogleSignIn, goToFacebookSignIn, + authenticateWithGoogle, fetchUserData, requestAccountDeletion, }; diff --git a/package.json b/package.json index 9a686b8b..75f7166a 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "@headlessui/react": "^1.6.6", "@influxdata/influxdb-client": "^1.33.2", "@popperjs/core": "^2.11.5", + "@react-oauth/google": "^0.12.1", "@rubin-epo/epo-react-lib": "^1.2.9", "add": "^2.0.6", "classnames": "^2.3.1", @@ -68,6 +69,7 @@ "feed": "^4.2.2", "focus-trap": "^7.0.0", "focus-visible": "^5.1.0", + "google-auth-library": "^9.6.3", "graphql": "^16.5.0", "graphql-request": "^5.0.0", "hoist-non-react-statics": "^3.3.2", @@ -79,7 +81,6 @@ "npm-run-all": "^4.1.5", "react": "18.2.0", "react-dom": "18.2.0", - "react-google-login": "^5.2.2", "react-hook-form": "^7.33.1", "react-i18next": "^12.0.0", "react-is": "^18.2.0", diff --git a/pages/[[...uriSegments]].js b/pages/[[...uriSegments]].js index e49badeb..a23ce0c6 100644 --- a/pages/[[...uriSegments]].js +++ b/pages/[[...uriSegments]].js @@ -1,4 +1,5 @@ import PropTypes from "prop-types"; +import { GoogleOAuthProvider } from "@react-oauth/google"; import { getGlobalData } from "@/api/global"; import { getAllEntries } from "@/api/entries"; import { getEntryDataByUri, getEntrySectionTypeByUri } from "@/api/entry"; @@ -57,6 +58,7 @@ function logNextDir() { }); } +const GOOGLE_APP_ID = process.env.NEXT_PUBLIC_GOOGLE_APP_ID; export default function Page({ section, globalData, ...entryProps }) { globalData.localeInfo.locale === "es" ? updateI18n("es") : updateI18n("en"); @@ -75,9 +77,11 @@ export default function Page({ section, globalData, ...entryProps }) { const Template = sectionMap[section] || PageTemplate; return ( - -