diff --git a/.changeset/gentle-rabbits-relax.md b/.changeset/gentle-rabbits-relax.md new file mode 100644 index 0000000000..e6b32b685a --- /dev/null +++ b/.changeset/gentle-rabbits-relax.md @@ -0,0 +1,6 @@ +--- +'@graphcommerce/googlerecaptcha': patch +'@graphcommerce/graphql': patch +--- + +ReCaptcha now using the `recaptchaV3Config` query and add a fallback for Magento 2.4.6 and earlier to still use the configuration. Magento 2.4.7 doesn't need that configuration anymore. diff --git a/packages/googlerecaptcha/README.md b/packages/googlerecaptcha/README.md index 12c7f390d9..d12d44256e 100644 --- a/packages/googlerecaptcha/README.md +++ b/packages/googlerecaptcha/README.md @@ -7,8 +7,8 @@ page so it isn't initialized on all pages. ## Configuration -1. Configure the following ([configuration values](./Config.graphqls)) in your - graphcommerce.config.js +1. Only Magento 2.4.6 and earlier, 2.4.7: Configure the following + ([configuration values](./Config.graphqls)) in your graphcommerce.config.js 2. Add `X-Recaptcha` header to your `.meshrc.yml` if it isn't there. [example](../../examples/magento-graphcms/.meshrc.yml) diff --git a/packages/googlerecaptcha/graphql/RecaptchaV3Config.graphql b/packages/googlerecaptcha/graphql/RecaptchaV3Config.graphql new file mode 100644 index 0000000000..85c357a831 --- /dev/null +++ b/packages/googlerecaptcha/graphql/RecaptchaV3Config.graphql @@ -0,0 +1,5 @@ +query RecaptchaV3Config { + recaptchaV3Config { + website_key + } +} diff --git a/packages/googlerecaptcha/hooks/useGoogleRecaptchaSiteKey.ts b/packages/googlerecaptcha/hooks/useGoogleRecaptchaSiteKey.ts deleted file mode 100644 index 491aa36a15..0000000000 --- a/packages/googlerecaptcha/hooks/useGoogleRecaptchaSiteKey.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { useStorefrontConfig } from '@graphcommerce/next-ui' - -export function useGoogleRecaptchaSiteKey() { - const key = - useStorefrontConfig().googleRecaptchaKey ?? import.meta.graphCommerce.googleRecaptchaKey - if (!key) throw new Error('[@graphcommerce/googlerecaptcha]: googleRecaptchaKey not configured') - return key -} diff --git a/packages/googlerecaptcha/link/recaptchaLink.ts b/packages/googlerecaptcha/link/recaptchaLink.ts index 2a3455c171..6b35c63209 100644 --- a/packages/googlerecaptcha/link/recaptchaLink.ts +++ b/packages/googlerecaptcha/link/recaptchaLink.ts @@ -1,6 +1,7 @@ import type { GraphQLRequest } from '@graphcommerce/graphql' import { setContext } from '@graphcommerce/graphql/apollo' import { Kind, OperationTypeNode } from 'graphql' +import { RecaptchaV3ConfigDocument } from '../graphql/RecaptchaV3Config.gql' const isMutation = (operation: GraphQLRequest) => operation.query.definitions.some( @@ -11,8 +12,10 @@ const isMutation = (operation: GraphQLRequest) => /** Apollo link that adds the Google reCAPTCHA token to the request context. */ export const recaptchaLink = setContext(async (operation, context) => { - const recaptchaKey = import.meta.graphCommerce.googleRecaptchaKey - if (!recaptchaKey || !globalThis.grecaptcha || !isMutation(operation)) return context + const siteKey = context.cache?.readQuery({ query: RecaptchaV3ConfigDocument })?.recaptchaV3Config + ?.website_key + + if (!siteKey || !globalThis.grecaptcha || !isMutation(operation)) return context await new Promise((resolve) => { globalThis.grecaptcha?.ready(resolve) @@ -23,7 +26,7 @@ export const recaptchaLink = setContext(async (operation, context) => { while (failure < 5) { try { // eslint-disable-next-line no-await-in-loop - token = await globalThis.grecaptcha.execute(recaptchaKey, { action: 'submit' }) + token = await globalThis.grecaptcha.execute(siteKey, { action: 'submit' }) break } catch { failure++ diff --git a/packages/googlerecaptcha/mesh/resolvers.ts b/packages/googlerecaptcha/mesh/resolvers.ts new file mode 100644 index 0000000000..26d16ff7db --- /dev/null +++ b/packages/googlerecaptcha/mesh/resolvers.ts @@ -0,0 +1,31 @@ +import { storefrontFromContext, type Resolvers } from '@graphcommerce/graphql-mesh' + +export const resolvers: Resolvers = { + Query: { + recaptchaV3Config: (root, args, context, info) => { + const config = storefrontFromContext(context) + const key = config?.googleRecaptchaKey ?? import.meta.graphCommerce.googleRecaptchaKey + + if (!key) return null + return { + website_key: key, + minimum_score: 0, + badge_position: '', + failure_message: '', + is_enabled: !!key, + forms: [ + 'PLACE_ORDER', + 'CONTACT', + 'CUSTOMER_LOGIN', + 'CUSTOMER_FORGOT_PASSWORD', + 'CUSTOMER_CREATE', + 'CUSTOMER_EDIT', + 'NEWSLETTER', + 'PRODUCT_REVIEW', + 'SENDFRIEND', + 'BRAINTREE', + ], + } + }, + }, +} diff --git a/packages/googlerecaptcha/package.json b/packages/googlerecaptcha/package.json index f0553746ea..0164eaf3e5 100644 --- a/packages/googlerecaptcha/package.json +++ b/packages/googlerecaptcha/package.json @@ -17,6 +17,7 @@ "peerDependencies": { "@graphcommerce/eslint-config-pwa": "^9.1.0-canary.16", "@graphcommerce/graphql": "^9.1.0-canary.16", + "@graphcommerce/graphql-mesh": "^9.1.0-canary.16", "@graphcommerce/next-ui": "^9.1.0-canary.16", "@graphcommerce/prettier-config-pwa": "^9.1.0-canary.16", "@graphcommerce/typescript-config-pwa": "^9.1.0-canary.16", @@ -25,5 +26,8 @@ "next": "*", "react": "^18.2.0", "react-dom": "^18.2.0" + }, + "dependencies": { + "@types/react-recaptcha-v3": "^1.1.5" } } diff --git a/packages/googlerecaptcha/plugins/GrecaptchaApolloErrorAlert.tsx b/packages/googlerecaptcha/plugins/GrecaptchaApolloErrorAlert.tsx index cbd74123b1..d671430ad2 100644 --- a/packages/googlerecaptcha/plugins/GrecaptchaApolloErrorAlert.tsx +++ b/packages/googlerecaptcha/plugins/GrecaptchaApolloErrorAlert.tsx @@ -5,7 +5,6 @@ import { useGoogleRecaptcha } from '../hooks/useGoogleRecaptcha' export const config: PluginConfig = { type: 'component', module: '@graphcommerce/ecommerce-ui', - ifConfig: 'googleRecaptchaKey', } export function ApolloErrorAlert(props: PluginProps) { diff --git a/packages/googlerecaptcha/plugins/GrecaptchaApolloErrorFullPage.tsx b/packages/googlerecaptcha/plugins/GrecaptchaApolloErrorFullPage.tsx index dfb7db8b0a..9f3a1d1f2f 100644 --- a/packages/googlerecaptcha/plugins/GrecaptchaApolloErrorFullPage.tsx +++ b/packages/googlerecaptcha/plugins/GrecaptchaApolloErrorFullPage.tsx @@ -5,7 +5,6 @@ import { useGoogleRecaptcha } from '../hooks/useGoogleRecaptcha' export const config: PluginConfig = { type: 'component', module: '@graphcommerce/ecommerce-ui', - ifConfig: 'googleRecaptchaKey', } export function ApolloErrorFullPage(props: PluginProps) { diff --git a/packages/googlerecaptcha/plugins/GrecaptchaApolloErrorSnackbar.tsx b/packages/googlerecaptcha/plugins/GrecaptchaApolloErrorSnackbar.tsx index d772bda3ec..4d7fc7491a 100644 --- a/packages/googlerecaptcha/plugins/GrecaptchaApolloErrorSnackbar.tsx +++ b/packages/googlerecaptcha/plugins/GrecaptchaApolloErrorSnackbar.tsx @@ -5,7 +5,6 @@ import { useGoogleRecaptcha } from '../hooks/useGoogleRecaptcha' export const config: PluginConfig = { type: 'component', module: '@graphcommerce/ecommerce-ui', - ifConfig: 'googleRecaptchaKey', } export function ApolloErrorSnackbar(props: PluginProps) { diff --git a/packages/googlerecaptcha/plugins/GrecaptchaGraphQLProvider.tsx b/packages/googlerecaptcha/plugins/GrecaptchaGraphQLProvider.tsx index c3495edbb4..da922105ae 100644 --- a/packages/googlerecaptcha/plugins/GrecaptchaGraphQLProvider.tsx +++ b/packages/googlerecaptcha/plugins/GrecaptchaGraphQLProvider.tsx @@ -1,24 +1,37 @@ -import type { GraphQLProviderProps } from '@graphcommerce/graphql' +import { useQuery, type GraphQLProviderProps } from '@graphcommerce/graphql' import type { PluginConfig, PluginProps } from '@graphcommerce/next-config' import { GlobalStyles } from '@mui/material' import Script from 'next/script' import { useMemo, useState } from 'react' import type { RecaptchaContext } from '../context/recaptchaContext' import { recaptchaContext } from '../context/recaptchaContext' -import { useGoogleRecaptchaSiteKey } from '../hooks/useGoogleRecaptchaSiteKey' +import { RecaptchaV3ConfigDocument } from '../graphql/RecaptchaV3Config.gql' import { recaptchaLink } from '../link/recaptchaLink' export const config: PluginConfig = { type: 'component', module: '@graphcommerce/graphql', - ifConfig: 'googleRecaptchaKey', +} + +function ReCaptchaScript() { + const siteKey = useQuery(RecaptchaV3ConfigDocument).data?.recaptchaV3Config?.website_key + + console.log(siteKey) + + if (!siteKey) return null + return ( +