diff --git a/docs.json b/docs.json index ec7b7e8..29f453d 100644 --- a/docs.json +++ b/docs.json @@ -177,7 +177,8 @@ "embedding/embed-snippet-generator", "embedding/prefill-booking-form-embed", "embedding/utm-tracking-embed", - "embedding/embed-auto-forward-query-params" + "embedding/embed-auto-forward-query-params", + "embedding/onboarding-embed" ] }, { diff --git a/embedding/onboarding-embed.mdx b/embedding/onboarding-embed.mdx new file mode 100644 index 0000000..6e903a4 --- /dev/null +++ b/embedding/onboarding-embed.mdx @@ -0,0 +1,241 @@ +--- +title: "Onboarding embed" +description: "Let users create a Cal.com account, complete onboarding, and authorize your app — all without leaving your site." +--- + +The onboarding embed allows you to add a "Continue with Cal.com" button to your application. When a user clicks it, a dialog opens where they can sign up for Cal.com, set up their profile, connect a calendar, and authorize your app with OAuth — all in one flow, without ever leaving your site. + +This is useful if you are building an integration that needs access to a user's Cal.com account. Instead of sending users to Cal.com separately, you can embed the entire signup and authorization experience directly in your app. + + +The onboarding embed is a React component available in the `@calcom/atoms` package. You need an OAuth client ID from Cal.com to use it. See [OAuth setup](/apps-and-integrations/oauth) for details on obtaining credentials. + + +## How it works + +The onboarding embed guides users through four steps inside a modal dialog: + +1. **Sign up or log in** — The user creates a new Cal.com account or signs in to an existing one. +2. **Profile setup** — The user enters their name, username, and bio. +3. **Calendar connection** — The user optionally connects a calendar (Google Calendar, Apple Calendar, etc.). +4. **OAuth authorization** — The user reviews the permissions your app is requesting and clicks "Allow" or "Deny". + +If a user closes the dialog before finishing, they can reopen it and resume where they left off. + +## Installation + +Install the `@calcom/atoms` package: + +```bash +npm install @calcom/atoms +``` + +## Basic usage + +Import the `OnboardingEmbed` component and add it to your page: + +```jsx +import { OnboardingEmbed } from "@calcom/atoms"; + +function App() { + const state = crypto.randomUUID(); + + return ( + { + // Exchange this code for an access token on your server + console.log("Authorization code:", code); + }} + onAuthorizationDenied={() => { + console.log("User denied access"); + }} + onError={(error) => { + console.error("Onboarding error:", error); + }} + /> + ); +} +``` + +By default, the component renders a "Continue with Cal.com" button. Clicking it opens the onboarding dialog. + +## Configuration + +### Required props + +| Prop | Type | Description | +| --- | --- | --- | +| `oAuthClientId` | `string` | Your OAuth client ID registered with Cal.com. | +| `authorization` | `object` | OAuth authorization parameters (see below). | + +### Authorization object + +| Property | Type | Required | Description | +| --- | --- | --- | --- | +| `redirectUri` | `string` | Yes | One of the redirect URIs registered on your OAuth client. Must share the same origin as the page hosting the embed. | +| `scope` | `string[]` | Yes | OAuth scopes to request, such as `EVENT_TYPE_READ`, `BOOKING_WRITE`, or `SCHEDULE_READ`. | +| `state` | `string` | Yes | A random string to prevent CSRF attacks. Use `crypto.randomUUID()` to generate one. | +| `codeChallenge` | `string` | No | PKCE code challenge for public clients that cannot store a client secret. Uses the S256 method. | + +### Optional props + +| Prop | Type | Default | Description | +| --- | --- | --- | --- | +| `host` | `string` | `https://app.cal.com` | The Cal.com host URL. Change this if you are using a self-hosted instance. | +| `theme` | `"light"` or `"dark"` | `"light"` | Controls the appearance of the button and dialog. | +| `user` | `object` | — | Pre-fill the signup form with `email`, `name`, and `username`. | +| `trigger` | `ReactNode` | "Continue with Cal.com" button | A custom element to open the onboarding dialog. | +| `onAuthorizationAllowed` | `function` | — | Called when the user clicks "Allow". Receives `{ code }` with the authorization code. | +| `onAuthorizationDenied` | `function` | — | Called when the user clicks "Deny". | +| `onError` | `function` | — | Called when an error occurs. Receives an error object with `code` and `message`. | +| `onClose` | `function` | — | Called when the dialog is dismissed. | + +## Authorization modes + +The onboarding embed supports two ways to handle the authorization result. + +### Callback mode + +If you provide the `onAuthorizationAllowed` prop, the component calls your function with the authorization code and closes the dialog. This keeps the user on your page. + +```jsx + { + // Send the code to your server to exchange for tokens + fetch("/api/exchange-token", { + method: "POST", + body: JSON.stringify({ code }), + }); + }} + onAuthorizationDenied={() => { + // Handle denial + }} +/> +``` + +### Redirect mode + +If you do not provide `onAuthorizationAllowed`, the browser redirects to your `redirectUri` with the authorization code and state as URL parameters: + +``` +https://yourapp.com/callback?code=AUTHORIZATION_CODE&state=YOUR_STATE +``` + +If the user denies access, the redirect includes an error parameter: + +``` +https://yourapp.com/callback?error=access_denied&state=YOUR_STATE +``` + +## Pre-filling user details + +You can pass a `user` prop to pre-fill the signup form: + +```jsx + { + console.log("Authorization code:", code); + }} +/> +``` + +## Using PKCE + +For public clients that cannot securely store a client secret, use PKCE (Proof Key for Code Exchange) by including a `codeChallenge` in the authorization object: + +```jsx + { + // Exchange the code along with your code_verifier + console.log("Authorization code:", code); + }} +/> +``` + +## Custom trigger element + +By default, the embed renders a "Continue with Cal.com" button. You can replace it with your own element: + +```jsx +Connect your calendar} + onAuthorizationAllowed={({ code }) => { + console.log("Authorization code:", code); + }} +/> +``` + +## Error handling + +The `onError` callback receives an error object with a `code` and `message`. The possible error codes are: + +| Error code | Description | +| --- | --- | +| `INVALID_PROPS` | Required props are missing (such as `oAuthClientId` or `authorization`). | +| `SIGNUP_FAILED` | Account creation failed. | +| `ONBOARDING_FAILED` | A step in the onboarding process failed. | +| `AUTHORIZATION_FAILED` | The OAuth authorization step failed. | +| `STATE_MISMATCH` | The state parameter in the response does not match the one you provided. This may indicate a CSRF attack. | +| `UNKNOWN` | An unexpected error occurred. | + +```jsx + { + switch (error.code) { + case "INVALID_PROPS": + console.error("Check your component configuration"); + break; + case "STATE_MISMATCH": + console.error("Possible security issue — state does not match"); + break; + default: + console.error("Onboarding error:", error.message); + } + }} + onAuthorizationAllowed={({ code }) => { + console.log("Authorization code:", code); + }} +/> +```