Skip to content

Commit

Permalink
feat(clerk-js,clerk-react,nextjs,types): Drop experimental tag from G…
Browse files Browse the repository at this point in the history
…oogleOneTap (#3392)

* feat(clerk-js,clerk-react,nextjs,types): Drop experimental tag from GoogleOneTap

* feat(clerk-js,clerk-react,nextjs,types): Add changeset

* test(chrome-extension): Update snapshots
  • Loading branch information
panteliselef committed May 20, 2024
1 parent 6888594 commit d6a9b3f
Show file tree
Hide file tree
Showing 20 changed files with 167 additions and 98 deletions.
53 changes: 53 additions & 0 deletions .changeset/shiny-yaks-wave.md
Original file line number Diff line number Diff line change
@@ -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
- `<GoogleOneTap/>`

Customize the UX of the prompt

```tsx
<GoogleOneTap
cancelOnTapOutside={false}
itpSupport={false}
fedCmSupport={false}
/>
```

### 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
})
}
},
});
```
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ exports[`public exports should not include a breaking change 1`] = `
"ClerkLoading",
"ClerkProvider",
"CreateOrganization",
"GoogleOneTap",
"OrganizationList",
"OrganizationProfile",
"OrganizationSwitcher",
Expand All @@ -26,7 +27,6 @@ exports[`public exports should not include a breaking change 1`] = `
"SignedOut",
"UserButton",
"UserProfile",
"__experimental_GoogleOneTap",
"useAuth",
"useClerk",
"useEmailLink",
Expand Down
24 changes: 10 additions & 14 deletions packages/clerk-js/src/core/clerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -28,12 +28,12 @@ import type {
DomainOrProxyUrl,
EnvironmentJSON,
EnvironmentResource,
GoogleOneTapProps,
HandleEmailLinkVerificationParams,
HandleOAuthCallbackParams,
InstanceType,
ListenerCallback,
NavigateOptions,
OneTapProps,
OrganizationListProps,
OrganizationProfileProps,
OrganizationResource,
Expand Down Expand Up @@ -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'));
};
Expand Down Expand Up @@ -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<unknown>,
Expand Down Expand Up @@ -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<SignInResource | SignUpResource> => {
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;
Expand Down
25 changes: 1 addition & 24 deletions packages/clerk-js/src/core/resources/SignIn.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { deepSnakeToCamel, isClerkAPIResponseError, Poller } from '@clerk/shared';
import { deepSnakeToCamel, Poller } from '@clerk/shared';
import type {
__experimental_AuthenticateWithGoogleOneTapParams,
AttemptFirstFactorParams,
AttemptSecondFactorParams,
AuthenticateWithPasskeyParams,
Expand All @@ -26,7 +25,6 @@ import type {
SignInSecondFactor,
SignInStartEmailLinkFlowParams,
SignInStatus,
SignUpResource,
VerificationResource,
Web3SignatureConfig,
Web3SignatureFactor,
Expand Down Expand Up @@ -225,27 +223,6 @@ export class SignIn extends BaseResource implements SignInResource {
}
};

public __experimental_authenticateWithGoogleOneTap = async (
params: __experimental_AuthenticateWithGoogleOneTapParams,
): Promise<SignInResource | SignUpResource> => {
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<SignInResource | SignUpResource>;
};

public authenticateWithWeb3 = async (params: AuthenticateWithWeb3Params): Promise<SignInResource> => {
const { identifier, generateSignature } = params || {};
if (!(typeof generateSignature === 'function')) {
Expand Down
4 changes: 2 additions & 2 deletions packages/clerk-js/src/ui/Components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type {
ClerkOptions,
CreateOrganizationProps,
EnvironmentResource,
OneTapProps,
GoogleOneTapProps,
OrganizationProfileProps,
SignInProps,
SignUpProps,
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions packages/clerk-js/src/ui/components/GoogleOneTap/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { OneTapProps } from '@clerk/types';
import type { GoogleOneTapProps } from '@clerk/types';
import React from 'react';

import { withCoreSessionSwitchGuard } from '../../contexts';
Expand All @@ -22,4 +22,4 @@ function OneTapRoutes(): JSX.Element {

OneTapRoutes.displayName = 'OneTap';

export const OneTap: React.ComponentType<OneTapProps> = withCoreSessionSwitchGuard(OneTapRoutes);
export const OneTap: React.ComponentType<GoogleOneTapProps> = withCoreSessionSwitchGuard(OneTapRoutes);
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { useRouter } from '../router';
import type {
AvailableComponentCtx,
CreateOrganizationCtx,
OneTapCtx,
GoogleOneTapCtx,
OrganizationListCtx,
OrganizationProfileCtx,
OrganizationSwitcherCtx,
Expand Down Expand Up @@ -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.');
}

Expand Down
8 changes: 5 additions & 3 deletions packages/clerk-js/src/ui/lazyModules/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 })));

Expand Down Expand Up @@ -79,7 +81,7 @@ export const ClerkComponents = {
UserProfileModal,
OrganizationProfileModal,
CreateOrganizationModal,
OneTap,
GoogleOneTap,
};

export type ClerkComponentName = keyof typeof ClerkComponents;
4 changes: 2 additions & 2 deletions packages/clerk-js/src/ui/lazyModules/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,9 @@ export const LazyOneTapRenderer = (props: LazyOneTapRendererProps) => {
>
<VirtualBodyRootPortal
startPath={props.startPath}
component={ClerkComponents['OneTap']}
component={ClerkComponents['GoogleOneTap']}
props={props.componentProps}
componentName={'OneTap'}
componentName={'GoogleOneTap'}
/>
</AppearanceProvider>
);
Expand Down
10 changes: 5 additions & 5 deletions packages/clerk-js/src/ui/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type {
CreateOrganizationProps,
OneTapProps,
GoogleOneTapProps,
OrganizationListProps,
OrganizationProfileProps,
OrganizationSwitcherProps,
Expand All @@ -11,7 +11,7 @@ import type {
} from '@clerk/types';

export type {
OneTapProps,
GoogleOneTapProps,
SignInProps,
SignUpProps,
UserButtonProps,
Expand Down Expand Up @@ -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 =
Expand All @@ -87,4 +87,4 @@ export type AvailableComponentCtx =
| CreateOrganizationCtx
| OrganizationSwitcherCtx
| OrganizationListCtx
| OneTapCtx;
| GoogleOneTapCtx;
2 changes: 1 addition & 1 deletion packages/nextjs/src/client-boundary/uiComponents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export {
SignOutButton,
SignUpButton,
UserButton,
__experimental_GoogleOneTap,
GoogleOneTap,
} from '@clerk/clerk-react';

// The assignment of UserProfile with BaseUserProfile props is used
Expand Down
2 changes: 1 addition & 1 deletion packages/nextjs/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export {
SignUpButton,
UserButton,
UserProfile,
__experimental_GoogleOneTap,
GoogleOneTap,
} from './client-boundary/uiComponents';

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export {
OrganizationProfile,
CreateOrganization,
OrganizationList,
__experimental_GoogleOneTap,
GoogleOneTap,
} from './uiComponents';

export {
Expand Down
10 changes: 5 additions & 5 deletions packages/react/src/components/uiComponents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { logErrorInDevMode, without } from '@clerk/shared';
import { isDeeplyEqual } from '@clerk/shared/react';
import type {
CreateOrganizationProps,
OneTapProps,
GoogleOneTapProps,
OrganizationListProps,
OrganizationProfileProps,
OrganizationSwitcherProps,
Expand Down Expand Up @@ -301,12 +301,12 @@ export const OrganizationList = withClerk(({ clerk, ...props }: WithClerkProp<Or
);
}, 'OrganizationList');

export const __experimental_GoogleOneTap = withClerk(({ clerk, ...props }: WithClerkProp<OneTapProps>) => {
export const GoogleOneTap = withClerk(({ clerk, ...props }: WithClerkProp<GoogleOneTapProps>) => {
return (
<Portal
open={clerk.__experimental_openGoogleOneTap}
close={clerk.__experimental_closeGoogleOneTap}
open={clerk.openGoogleOneTap}
close={clerk.closeGoogleOneTap}
props={props}
/>
);
}, 'OneTap');
}, 'GoogleOneTap');

0 comments on commit d6a9b3f

Please sign in to comment.