diff --git a/.changeset/shiny-yaks-wave.md b/.changeset/shiny-yaks-wave.md new file mode 100644 index 0000000000..7d5810ee13 --- /dev/null +++ b/.changeset/shiny-yaks-wave.md @@ -0,0 +1,53 @@ +--- +'@clerk/clerk-js': minor +'@clerk/nextjs': minor +'@clerk/clerk-react': minor +'@clerk/types': minor +--- + +Add support for GoogleOneTap. New APIs listed: +### React component +- `` + +Customize the UX of the prompt + +```tsx + +``` + +### Use the component from with Vanilla JS +- `Clerk.openGoogleOneTap(props: GoogleOneTapProps)` +- `Clerk.closeGoogleOneTap()` +### Low level APIs for custom flows +- `await Clerk.authenticateWithGoogleOneTap({ token: 'xxxx'})` +- `await Clerk.handleGoogleOneTapCallback()` + +We recommend using this two methods together in order and let Clerk to perform the correct redirections. +```tsx +google.accounts.id.initialize({ + callback: async response => { + const signInOrUp = await Clerk.authenticateWithGoogleOneTap({ token: response.credential}) + await Clerk.handleGoogleOneTapCallback(signInOrUp, { + signInForceRedirectUrl: window.location.href, + }) + }, +}); +``` + +In case you want to handle the redirection and session management yourself you can do so like this +```tsx +google.accounts.id.initialize({ + callback: async response => { + const signInOrUp = await Clerk.authenticateWithGoogleOneTap({ token: response.credential}) + if(signInOrUp.status === 'complete') { + await Clerk.setActive({ + session: signInOrUp.createdSessionId + }) + } + }, +}); +``` diff --git a/packages/chrome-extension/src/__tests__/__snapshots__/exports.test.ts.snap b/packages/chrome-extension/src/__tests__/__snapshots__/exports.test.ts.snap index 07232140a1..5926a52cb7 100644 --- a/packages/chrome-extension/src/__tests__/__snapshots__/exports.test.ts.snap +++ b/packages/chrome-extension/src/__tests__/__snapshots__/exports.test.ts.snap @@ -7,6 +7,7 @@ exports[`public exports should not include a breaking change 1`] = ` "ClerkLoading", "ClerkProvider", "CreateOrganization", + "GoogleOneTap", "OrganizationList", "OrganizationProfile", "OrganizationSwitcher", @@ -26,7 +27,6 @@ exports[`public exports should not include a breaking change 1`] = ` "SignedOut", "UserButton", "UserProfile", - "__experimental_GoogleOneTap", "useAuth", "useClerk", "useEmailLink", diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index ecbe51f7a0..2982f17faf 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -16,8 +16,8 @@ import { } from '@clerk/shared'; import { eventPrebuiltComponentMounted, TelemetryCollector } from '@clerk/shared/telemetry'; import type { - __experimental_AuthenticateWithGoogleOneTapParams, ActiveSessionResource, + AuthenticateWithGoogleOneTapParams, AuthenticateWithMetamaskParams, Clerk as ClerkInterface, ClerkAPIError, @@ -28,12 +28,12 @@ import type { DomainOrProxyUrl, EnvironmentJSON, EnvironmentResource, + GoogleOneTapProps, HandleEmailLinkVerificationParams, HandleOAuthCallbackParams, InstanceType, ListenerCallback, NavigateOptions, - OneTapProps, OrganizationListProps, OrganizationProfileProps, OrganizationResource, @@ -330,14 +330,14 @@ export class Clerk implements ClerkInterface { } }; - public __experimental_openGoogleOneTap = (props?: OneTapProps): void => { + public openGoogleOneTap = (props?: GoogleOneTapProps): void => { this.assertComponentsReady(this.#componentControls); void this.#componentControls - .ensureMounted({ preloadHint: 'OneTap' }) + .ensureMounted({ preloadHint: 'GoogleOneTap' }) .then(controls => controls.openModal('googleOneTap', props || {})); }; - public __experimental_closeGoogleOneTap = (): void => { + public closeGoogleOneTap = (): void => { this.assertComponentsReady(this.#componentControls); void this.#componentControls.ensureMounted().then(controls => controls.closeModal('googleOneTap')); }; @@ -1003,7 +1003,7 @@ export class Clerk implements ClerkInterface { return null; }; - public __experimental_handleGoogleOneTapCallback = async ( + public handleGoogleOneTapCallback = async ( signInOrUp: SignInResource | SignUpResource, params: HandleOAuthCallbackParams, customNavigate?: (to: string) => Promise, @@ -1263,23 +1263,19 @@ export class Clerk implements ClerkInterface { return this.setActive({ session: null }); }; - public __experimental_authenticateWithGoogleOneTap = async ( - params: __experimental_AuthenticateWithGoogleOneTapParams, + public authenticateWithGoogleOneTap = async ( + params: AuthenticateWithGoogleOneTapParams, ): Promise => { return this.client?.signIn .create({ - // TODO-ONETAP: Add new types when feature is ready for public beta - // @ts-expect-error strategy: 'google_one_tap', - googleOneTapToken: params.token, + token: params.token, }) .catch(err => { if (isClerkAPIResponseError(err) && err.errors[0].code === 'external_account_not_found') { return this.client?.signUp.create({ - // TODO-ONETAP: Add new types when feature is ready for public beta - // @ts-expect-error strategy: 'google_one_tap', - googleOneTapToken: params.token, + token: params.token, }); } throw err; diff --git a/packages/clerk-js/src/core/resources/SignIn.ts b/packages/clerk-js/src/core/resources/SignIn.ts index c989de0ac1..62d099bdfd 100644 --- a/packages/clerk-js/src/core/resources/SignIn.ts +++ b/packages/clerk-js/src/core/resources/SignIn.ts @@ -1,6 +1,5 @@ -import { deepSnakeToCamel, isClerkAPIResponseError, Poller } from '@clerk/shared'; +import { deepSnakeToCamel, Poller } from '@clerk/shared'; import type { - __experimental_AuthenticateWithGoogleOneTapParams, AttemptFirstFactorParams, AttemptSecondFactorParams, AuthenticateWithPasskeyParams, @@ -26,7 +25,6 @@ import type { SignInSecondFactor, SignInStartEmailLinkFlowParams, SignInStatus, - SignUpResource, VerificationResource, Web3SignatureConfig, Web3SignatureFactor, @@ -225,27 +223,6 @@ export class SignIn extends BaseResource implements SignInResource { } }; - public __experimental_authenticateWithGoogleOneTap = async ( - params: __experimental_AuthenticateWithGoogleOneTapParams, - ): Promise => { - return this.create({ - // TODO-ONETAP: Add new types when feature is ready for public beta - // @ts-expect-error - strategy: 'google_one_tap', - googleOneTapToken: params.token, - }).catch(err => { - if (isClerkAPIResponseError(err) && err.errors[0].code === 'external_account_not_found') { - return SignIn.clerk.client?.signUp.create({ - // TODO-ONETAP: Add new types when feature is ready for public beta - // @ts-expect-error - strategy: 'google_one_tap', - googleOneTapToken: params.token, - }); - } - throw err; - }) as Promise; - }; - public authenticateWithWeb3 = async (params: AuthenticateWithWeb3Params): Promise => { const { identifier, generateSignature } = params || {}; if (!(typeof generateSignature === 'function')) { diff --git a/packages/clerk-js/src/ui/Components.tsx b/packages/clerk-js/src/ui/Components.tsx index 15678e9de0..2b94fa63e5 100644 --- a/packages/clerk-js/src/ui/Components.tsx +++ b/packages/clerk-js/src/ui/Components.tsx @@ -6,7 +6,7 @@ import type { ClerkOptions, CreateOrganizationProps, EnvironmentResource, - OneTapProps, + GoogleOneTapProps, OrganizationProfileProps, SignInProps, SignUpProps, @@ -84,7 +84,7 @@ interface ComponentsProps { interface ComponentsState { appearance: Appearance | undefined; options: ClerkOptions | undefined; - googleOneTapModal: null | OneTapProps; + googleOneTapModal: null | GoogleOneTapProps; signInModal: null | SignInProps; signUpModal: null | SignUpProps; userProfileModal: null | UserProfileProps; diff --git a/packages/clerk-js/src/ui/components/GoogleOneTap/index.tsx b/packages/clerk-js/src/ui/components/GoogleOneTap/index.tsx index 28c1756432..3a755535db 100644 --- a/packages/clerk-js/src/ui/components/GoogleOneTap/index.tsx +++ b/packages/clerk-js/src/ui/components/GoogleOneTap/index.tsx @@ -1,4 +1,4 @@ -import type { OneTapProps } from '@clerk/types'; +import type { GoogleOneTapProps } from '@clerk/types'; import React from 'react'; import { withCoreSessionSwitchGuard } from '../../contexts'; @@ -22,4 +22,4 @@ function OneTapRoutes(): JSX.Element { OneTapRoutes.displayName = 'OneTap'; -export const OneTap: React.ComponentType = withCoreSessionSwitchGuard(OneTapRoutes); +export const OneTap: React.ComponentType = withCoreSessionSwitchGuard(OneTapRoutes); diff --git a/packages/clerk-js/src/ui/components/GoogleOneTap/one-tap-start.tsx b/packages/clerk-js/src/ui/components/GoogleOneTap/one-tap-start.tsx index 715553d4ae..2217882187 100644 --- a/packages/clerk-js/src/ui/components/GoogleOneTap/one-tap-start.tsx +++ b/packages/clerk-js/src/ui/components/GoogleOneTap/one-tap-start.tsx @@ -29,10 +29,10 @@ function _OneTapStart(): JSX.Element | null { async function oneTapCallback(response: GISCredentialResponse) { isPromptedRef.current = false; try { - const res = await clerk.__experimental_authenticateWithGoogleOneTap({ + const res = await clerk.authenticateWithGoogleOneTap({ token: response.credential, }); - await clerk.__experimental_handleGoogleOneTapCallback( + await clerk.handleGoogleOneTapCallback( res, { signInUrl, @@ -80,7 +80,7 @@ function _OneTapStart(): JSX.Element | null { // Close the modal, when the user clicks outside the prompt or cancels if (notification.getMomentType() === 'skipped') { // Unmounts the component will cause the useEffect cleanup function from below to be called - clerk.__experimental_closeGoogleOneTap(); + clerk.closeGoogleOneTap(); } }); isPromptedRef.current = true; diff --git a/packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx b/packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx index bf6f623383..fa0cc67619 100644 --- a/packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx +++ b/packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx @@ -14,7 +14,7 @@ import { useRouter } from '../router'; import type { AvailableComponentCtx, CreateOrganizationCtx, - OneTapCtx, + GoogleOneTapCtx, OrganizationListCtx, OrganizationProfileCtx, OrganizationSwitcherCtx, @@ -497,12 +497,12 @@ export const useCreateOrganizationContext = () => { }; export const useGoogleOneTapContext = () => { - const { componentName, ...ctx } = (React.useContext(ComponentContext) || {}) as OneTapCtx; + const { componentName, ...ctx } = (React.useContext(ComponentContext) || {}) as GoogleOneTapCtx; const options = useOptions(); const { displayConfig } = useEnvironment(); const { queryParams } = useRouter(); - if (componentName !== 'OneTap') { + if (componentName !== 'GoogleOneTap') { throw new Error('Clerk: useGoogleOneTapContext called outside GoogleOneTap.'); } diff --git a/packages/clerk-js/src/ui/lazyModules/components.ts b/packages/clerk-js/src/ui/lazyModules/components.ts index ed3cf52935..5cac14588f 100644 --- a/packages/clerk-js/src/ui/lazyModules/components.ts +++ b/packages/clerk-js/src/ui/lazyModules/components.ts @@ -12,13 +12,15 @@ const componentImportPaths = { import(/* webpackChunkName: "organizationswitcher" */ './../components/OrganizationSwitcher'), OrganizationList: () => import(/* webpackChunkName: "organizationlist" */ './../components/OrganizationList'), ImpersonationFab: () => import(/* webpackChunkName: "impersonationfab" */ './../components/ImpersonationFab'), - OneTap: () => import(/* webpackChunkName: "oneTap" */ './../components/GoogleOneTap'), + GoogleOneTap: () => import(/* webpackChunkName: "oneTap" */ './../components/GoogleOneTap'), } as const; export const SignIn = lazy(() => componentImportPaths.SignIn().then(module => ({ default: module.SignIn }))); export const SignInModal = lazy(() => componentImportPaths.SignIn().then(module => ({ default: module.SignInModal }))); -export const OneTap = lazy(() => componentImportPaths.OneTap().then(module => ({ default: module.OneTap }))); +export const GoogleOneTap = lazy(() => + componentImportPaths.GoogleOneTap().then(module => ({ default: module.OneTap })), +); export const SignUp = lazy(() => componentImportPaths.SignUp().then(module => ({ default: module.SignUp }))); @@ -79,7 +81,7 @@ export const ClerkComponents = { UserProfileModal, OrganizationProfileModal, CreateOrganizationModal, - OneTap, + GoogleOneTap, }; export type ClerkComponentName = keyof typeof ClerkComponents; diff --git a/packages/clerk-js/src/ui/lazyModules/providers.tsx b/packages/clerk-js/src/ui/lazyModules/providers.tsx index df472b1930..e1f63635e5 100644 --- a/packages/clerk-js/src/ui/lazyModules/providers.tsx +++ b/packages/clerk-js/src/ui/lazyModules/providers.tsx @@ -146,9 +146,9 @@ export const LazyOneTapRenderer = (props: LazyOneTapRendererProps) => { > ); diff --git a/packages/clerk-js/src/ui/types.ts b/packages/clerk-js/src/ui/types.ts index fa9ee82c3a..ce03e28c09 100644 --- a/packages/clerk-js/src/ui/types.ts +++ b/packages/clerk-js/src/ui/types.ts @@ -1,6 +1,6 @@ import type { CreateOrganizationProps, - OneTapProps, + GoogleOneTapProps, OrganizationListProps, OrganizationProfileProps, OrganizationSwitcherProps, @@ -11,7 +11,7 @@ import type { } from '@clerk/types'; export type { - OneTapProps, + GoogleOneTapProps, SignInProps, SignUpProps, UserButtonProps, @@ -74,8 +74,8 @@ export type OrganizationListCtx = OrganizationListProps & { mode?: ComponentMode; }; -export type OneTapCtx = OneTapProps & { - componentName: 'OneTap'; +export type GoogleOneTapCtx = GoogleOneTapProps & { + componentName: 'GoogleOneTap'; }; export type AvailableComponentCtx = @@ -87,4 +87,4 @@ export type AvailableComponentCtx = | CreateOrganizationCtx | OrganizationSwitcherCtx | OrganizationListCtx - | OneTapCtx; + | GoogleOneTapCtx; diff --git a/packages/nextjs/src/client-boundary/uiComponents.tsx b/packages/nextjs/src/client-boundary/uiComponents.tsx index d0451cdee0..50599e5df8 100644 --- a/packages/nextjs/src/client-boundary/uiComponents.tsx +++ b/packages/nextjs/src/client-boundary/uiComponents.tsx @@ -26,7 +26,7 @@ export { SignOutButton, SignUpButton, UserButton, - __experimental_GoogleOneTap, + GoogleOneTap, } from '@clerk/clerk-react'; // The assignment of UserProfile with BaseUserProfile props is used diff --git a/packages/nextjs/src/index.ts b/packages/nextjs/src/index.ts index c921e52551..8a7e7513fe 100644 --- a/packages/nextjs/src/index.ts +++ b/packages/nextjs/src/index.ts @@ -30,7 +30,7 @@ export { SignUpButton, UserButton, UserProfile, - __experimental_GoogleOneTap, + GoogleOneTap, } from './client-boundary/uiComponents'; /** diff --git a/packages/react/src/components/index.ts b/packages/react/src/components/index.ts index 64fde71b88..39b52fdda1 100644 --- a/packages/react/src/components/index.ts +++ b/packages/react/src/components/index.ts @@ -7,7 +7,7 @@ export { OrganizationProfile, CreateOrganization, OrganizationList, - __experimental_GoogleOneTap, + GoogleOneTap, } from './uiComponents'; export { diff --git a/packages/react/src/components/uiComponents.tsx b/packages/react/src/components/uiComponents.tsx index 3ed6416700..f5c17e2866 100644 --- a/packages/react/src/components/uiComponents.tsx +++ b/packages/react/src/components/uiComponents.tsx @@ -2,7 +2,7 @@ import { logErrorInDevMode, without } from '@clerk/shared'; import { isDeeplyEqual } from '@clerk/shared/react'; import type { CreateOrganizationProps, - OneTapProps, + GoogleOneTapProps, OrganizationListProps, OrganizationProfileProps, OrganizationSwitcherProps, @@ -301,12 +301,12 @@ export const OrganizationList = withClerk(({ clerk, ...props }: WithClerkProp) => { +export const GoogleOneTap = withClerk(({ clerk, ...props }: WithClerkProp) => { return ( ); -}, 'OneTap'); +}, 'GoogleOneTap'); diff --git a/packages/react/src/isomorphicClerk.ts b/packages/react/src/isomorphicClerk.ts index a56728226e..82e66cc491 100644 --- a/packages/react/src/isomorphicClerk.ts +++ b/packages/react/src/isomorphicClerk.ts @@ -3,18 +3,19 @@ import { handleValueOrFn } from '@clerk/shared/handleValueOrFn'; import type { TelemetryCollector } from '@clerk/shared/telemetry'; import type { ActiveSessionResource, + AuthenticateWithGoogleOneTapParams, AuthenticateWithMetamaskParams, Clerk, ClientResource, CreateOrganizationParams, CreateOrganizationProps, DomainOrProxyUrl, + GoogleOneTapProps, HandleEmailLinkVerificationParams, HandleOAuthCallbackParams, InstanceType, ListenerCallback, LoadedClerk, - OneTapProps, OrganizationListProps, OrganizationProfileProps, OrganizationResource, @@ -24,11 +25,13 @@ import type { SetActiveParams, SignInProps, SignInRedirectOptions, + SignInResource, SignOut, SignOutCallback, SignOutOptions, SignUpProps, SignUpRedirectOptions, + SignUpResource, UnsubscribeCallback, UserButtonProps, UserProfileProps, @@ -83,8 +86,10 @@ type IsomorphicLoadedClerk = Without< | 'buildAfterSignOutUrl' | 'buildUrlWithAuth' | 'handleRedirectCallback' + | 'handleGoogleOneTapCallback' | 'handleUnauthenticated' | 'authenticateWithMetamask' + | 'authenticateWithGoogleOneTap' | 'createOrganization' | 'getOrganization' | 'mountUserButton' @@ -99,9 +104,13 @@ type IsomorphicLoadedClerk = Without< > & { // TODO: Align return type and parms handleRedirectCallback: (params: HandleOAuthCallbackParams) => void; + handleGoogleOneTapCallback: (signInOrUp: SignInResource | SignUpResource, params: HandleOAuthCallbackParams) => void; handleUnauthenticated: () => void; // TODO: Align Promise unknown authenticateWithMetamask: (params: AuthenticateWithMetamaskParams) => Promise; + authenticateWithGoogleOneTap: ( + params: AuthenticateWithGoogleOneTapParams, + ) => Promise; // TODO: Align return type (maybe not possible or correct) createOrganization: (params: CreateOrganizationParams) => Promise; // TODO: Align return type (maybe not possible or correct) @@ -140,7 +149,7 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { private readonly options: IsomorphicClerkOptions; private readonly Clerk: ClerkProp; private clerkjs: BrowserClerk | HeadlessBrowserClerk | null = null; - private preopenOneTap?: null | OneTapProps = null; + private preopenOneTap?: null | GoogleOneTapProps = null; private preopenSignIn?: null | SignInProps = null; private preopenSignUp?: null | SignUpProps = null; private preopenUserProfile?: null | UserProfileProps = null; @@ -345,6 +354,15 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { } }; + #waitForClerkJS(): Promise { + return new Promise(resolve => { + if (this.#loaded) { + resolve(this.clerkjs!); + } + this.addOnLoaded(() => resolve(this.clerkjs!)); + }); + } + async loadClerkJS(): Promise { if (this.mode !== 'browser' || this.#loaded) { return; @@ -462,7 +480,7 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { } if (this.preopenOneTap !== null) { - clerkjs.__experimental_openGoogleOneTap(this.preopenOneTap); + clerkjs.openGoogleOneTap(this.preopenOneTap); } if (this.preopenOrganizationProfile !== null) { @@ -597,17 +615,17 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { } }; - __experimental_openGoogleOneTap = (props?: OneTapProps): void => { + openGoogleOneTap = (props?: GoogleOneTapProps): void => { if (this.clerkjs && this.#loaded) { - this.clerkjs.__experimental_openGoogleOneTap(props); + this.clerkjs.openGoogleOneTap(props); } else { this.preopenOneTap = props; } }; - __experimental_closeGoogleOneTap = (): void => { + closeGoogleOneTap = (): void => { if (this.clerkjs && this.#loaded) { - this.clerkjs.__experimental_closeGoogleOneTap(); + this.clerkjs.closeGoogleOneTap(); } else { this.preopenOneTap = null; } @@ -929,6 +947,26 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { } }; + handleGoogleOneTapCallback = ( + signInOrUp: SignInResource | SignUpResource, + params: HandleOAuthCallbackParams, + ): void => { + const callback = () => this.clerkjs?.handleGoogleOneTapCallback(signInOrUp, params); + if (this.clerkjs && this.#loaded) { + void callback()?.catch(() => { + // This error is caused when the host app is using React18 + // and strictMode is enabled. This useEffects runs twice because + // the clerk-react ui components mounts, unmounts and mounts again + // so the clerk-js component loses its state because of the custom + // unmount callback we're using. + // This needs to be solved by tweaking the logic in uiComponents.tsx + // or by making handleRedirectCallback idempotent + }); + } else { + this.premountMethodCalls.set('handleGoogleOneTapCallback', callback); + } + }; + handleEmailLinkVerification = async (params: HandleEmailLinkVerificationParams): Promise => { const callback = () => this.clerkjs?.handleEmailLinkVerification(params); if (this.clerkjs && this.#loaded) { @@ -947,6 +985,13 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { } }; + authenticateWithGoogleOneTap = async ( + params: AuthenticateWithGoogleOneTapParams, + ): Promise => { + const clerkjs = await this.#waitForClerkJS(); + return clerkjs.authenticateWithGoogleOneTap(params); + }; + createOrganization = async (params: CreateOrganizationParams): Promise => { const callback = () => this.clerkjs?.createOrganization(params); if (this.clerkjs && this.#loaded) { diff --git a/packages/types/src/clerk.ts b/packages/types/src/clerk.ts index 4d8ad598c6..5f43f9ccd2 100644 --- a/packages/types/src/clerk.ts +++ b/packages/types/src/clerk.ts @@ -140,17 +140,15 @@ export interface Clerk { /** * Opens the Google One Tap component. - * @experimental * @param props Optional props that will be passed to the GoogleOneTap component. */ - __experimental_openGoogleOneTap: (props?: OneTapProps) => void; + openGoogleOneTap: (props?: GoogleOneTapProps) => void; /** * Opens the Google One Tap component. * If the component is not already open, results in a noop. - * @experimental */ - __experimental_closeGoogleOneTap: () => void; + closeGoogleOneTap: () => void; /** * Opens the Clerk SignUp component in a modal. @@ -442,10 +440,10 @@ export interface Clerk { redirectToAfterSignOut: () => void; /** - * Completes an Google One Tap redirection flow started by - * {@link Clerk.__experimental_authenticateWithGoogleOneTap} + * Completes a Google One Tap redirection flow started by + * {@link Clerk.authenticateWithGoogleOneTap} */ - __experimental_handleGoogleOneTapCallback: ( + handleGoogleOneTapCallback: ( signInOrUp: SignInResource | SignUpResource, params: HandleOAuthCallbackParams, customNavigate?: (to: string) => Promise, @@ -474,11 +472,10 @@ export interface Clerk { authenticateWithMetamask: (params?: AuthenticateWithMetamaskParams) => Promise; /** - * @experimental - * Authenticates user using a google token generated from google identity services. + * Authenticates user using a Google token generated from Google identity services. */ - __experimental_authenticateWithGoogleOneTap: ( - params: __experimental_AuthenticateWithGoogleOneTapParams, + authenticateWithGoogleOneTap: ( + params: AuthenticateWithGoogleOneTapParams, ) => Promise; /** @@ -731,9 +728,9 @@ export type SignInProps = RoutingOptions & { export type SignInModalProps = WithoutRouting; -type OneTapRedirectUrlProps = SignInForceRedirectUrl & SignUpForceRedirectUrl; +type GoogleOneTapRedirectUrlProps = SignInForceRedirectUrl & SignUpForceRedirectUrl; -export type OneTapProps = OneTapRedirectUrlProps & { +export type GoogleOneTapProps = GoogleOneTapRedirectUrlProps & { /** * Whether to cancel the Google One Tap request if a user clicks outside the prompt. * @default true @@ -1076,7 +1073,7 @@ export interface AuthenticateWithMetamaskParams { unsafeMetadata?: SignUpUnsafeMetadata; } -export interface __experimental_AuthenticateWithGoogleOneTapParams { +export interface AuthenticateWithGoogleOneTapParams { token: string; } diff --git a/packages/types/src/signIn.ts b/packages/types/src/signIn.ts index 52c203207b..9158bee4af 100644 --- a/packages/types/src/signIn.ts +++ b/packages/types/src/signIn.ts @@ -1,4 +1,3 @@ -import type { __experimental_AuthenticateWithGoogleOneTapParams } from './clerk'; import type { BackupCodeAttempt, BackupCodeFactor, @@ -48,11 +47,11 @@ import type { import type { ValidatePasswordCallbacks } from './passwords'; import type { AuthenticateWithRedirectParams } from './redirects'; import type { ClerkResource } from './resource'; -import type { SignUpResource } from './signUp'; import type { BackupCodeStrategy, EmailCodeStrategy, EmailLinkStrategy, + GoogleOneTapStrategy, OAuthStrategy, PasskeyStrategy, PasswordStrategy, @@ -101,13 +100,6 @@ export interface SignInResource extends ClerkResource { authenticateWithPasskey: (params?: AuthenticateWithPasskeyParams) => Promise; - /** - * @deprecated Use `Clerk.__experimental_authenticateWithGoogleOneTap` - */ - __experimental_authenticateWithGoogleOneTap: ( - params: __experimental_AuthenticateWithGoogleOneTapParams, - ) => Promise; - createEmailLinkFlow: () => CreateEmailLinkFlowReturn; validatePassword: (password: string, callbacks?: ValidatePasswordCallbacks) => void; @@ -184,6 +176,10 @@ export type SignInCreateParams = ( strategy: TicketStrategy; ticket: string; } + | { + strategy: GoogleOneTapStrategy; + token: string; + } | { strategy: PasswordStrategy; password: string; diff --git a/packages/types/src/signUp.ts b/packages/types/src/signUp.ts index f833d2dd14..8dc35b8339 100644 --- a/packages/types/src/signUp.ts +++ b/packages/types/src/signUp.ts @@ -14,6 +14,7 @@ import type { ClerkResource } from './resource'; import type { EmailCodeStrategy, EmailLinkStrategy, + GoogleOneTapStrategy, OAuthStrategy, PhoneCodeStrategy, SamlStrategy, @@ -148,12 +149,13 @@ export type SignUpCreateParams = Partial< externalAccountStrategy: string; externalAccountRedirectUrl: string; externalAccountActionCompleteRedirectUrl: string; - strategy: OAuthStrategy | SamlStrategy | TicketStrategy; + strategy: OAuthStrategy | SamlStrategy | TicketStrategy | GoogleOneTapStrategy; redirectUrl: string; actionCompleteRedirectUrl: string; transfer: boolean; unsafeMetadata: SignUpUnsafeMetadata; ticket: string; + token: string; } & SnakeToCamel> >; diff --git a/packages/types/src/strategies.ts b/packages/types/src/strategies.ts index 3d72e79dcb..ce2457dc61 100644 --- a/packages/types/src/strategies.ts +++ b/packages/types/src/strategies.ts @@ -1,6 +1,7 @@ import type { OAuthProvider } from './oauth'; import type { Web3Provider } from './web3'; +export type GoogleOneTapStrategy = 'google_one_tap'; export type PasskeyStrategy = 'passkey'; export type PasswordStrategy = 'password'; export type PhoneCodeStrategy = 'phone_code';