> ## Documentation Index > Fetch the complete documentation index at: https://www.canva.dev/docs/apps/llms.txt > Use this file to discover all available pages before exploring further. # Multi-provider authentication Multiple OAuth provider authentication integration for external platforms. ## Running this example To run this example locally: 1. If you haven't already, create a new app in the [Developer Portal](https://www.canva.com/developers/apps). For more information, refer to our [Quickstart guide](https://www.canva.dev/docs/apps/quickstart/). 2. In your app's configuration on the [Developer Portal](https://www.canva.com/developers/apps), ensure the "Development URL" is set to `http://localhost:8080`. 3. Clone the starter kit: ```shell git clone https://github.com/canva-sdks/canva-apps-sdk-starter-kit.git cd canva-apps-sdk-starter-kit ``` 4. Install dependencies: ```shell npm install ``` 5. Run the example: ```shell npm run start:example multi_provider_authentication ``` 6. Click the **Preview URL** link shown in the terminal to open the example in the Canva editor. ## Example app source code ```typescript // For usage information, see the README.md file. import { Button, LoadingIndicator, Rows, Title, Text, Box, MultilineInput, FormField, Columns, Column, } from "@canva/app-ui-kit"; import { useMemo, useState, useEffect, useCallback } from "react"; import type { AccessTokenResponse } from "@canva/user"; import { auth } from "@canva/user"; import * as styles from "styles/components.css"; // Provider names as defined in the Developer Portal const META_PROVIDER = "meta" as const; const GOOGLE_PROVIDER = "google" as const; const metaScope = new Set(["openid"]); const googleScope = new Set(["openid", "profile", "email"]); const META_BACKEND_URL = `${BACKEND_HOST}/meta-route`; const GOOGLE_BACKEND_URL = `${BACKEND_HOST}/google-route`; type ProviderStatus = { accessTokenResponse: AccessTokenResponse | null | undefined; error: string | null; loading: boolean; responseBody: unknown | undefined; }; export function App() { // Initialize separate OAuth clients for each provider const metaOauth = useMemo( () => auth.initOauth({ type: "single_account", provider: META_PROVIDER }), [], ); const googleOauth = useMemo( () => auth.initOauth({ type: "single_account", provider: GOOGLE_PROVIDER, }), [], ); // Track state for each provider independently const [metaStatus, setMetaStatus] = useState({ accessTokenResponse: undefined, error: null, loading: false, responseBody: undefined, }); const [googleStatus, setGoogleStatus] = useState({ accessTokenResponse: undefined, error: null, loading: false, responseBody: undefined, }); useEffect(() => { // Check if users are already authenticated when the component mounts retrieveAndSetToken(META_PROVIDER); retrieveAndSetToken(GOOGLE_PROVIDER); }, [metaOauth, googleOauth]); const getOauthClient = ( provider: typeof META_PROVIDER | typeof GOOGLE_PROVIDER, ) => { return provider === META_PROVIDER ? metaOauth : googleOauth; }; const getScope = ( provider: typeof META_PROVIDER | typeof GOOGLE_PROVIDER, ) => { return provider === META_PROVIDER ? metaScope : googleScope; }; const getStatus = ( provider: typeof META_PROVIDER | typeof GOOGLE_PROVIDER, ) => { return provider === META_PROVIDER ? metaStatus : googleStatus; }; const setStatus = ( provider: typeof META_PROVIDER | typeof GOOGLE_PROVIDER, updates: Partial, ) => { if (provider === META_PROVIDER) { setMetaStatus((prev) => ({ ...prev, ...updates })); } else { setGoogleStatus((prev) => ({ ...prev, ...updates })); } }; const authorize = useCallback( async (provider: typeof META_PROVIDER | typeof GOOGLE_PROVIDER) => { const oauth = getOauthClient(provider); const scope = getScope(provider); setStatus(provider, { accessTokenResponse: undefined, error: null, loading: true, }); try { // Trigger the OAuth authorization flow for the specific provider await oauth.requestAuthorization({ scope }); await retrieveAndSetToken(provider); } catch (error) { setStatus(provider, { error: error instanceof Error ? error.message : "Unknown error", loading: false, }); } }, [metaOauth, googleOauth], ); // IMPORTANT: Always call getAccessToken when you need a token - tokens can expire. // Canva automatically handles caching and refreshing tokens for you. const retrieveAndSetToken = useCallback( async ( provider: typeof META_PROVIDER | typeof GOOGLE_PROVIDER, forceRefresh = false, ) => { const oauth = getOauthClient(provider); const scope = getScope(provider); setStatus(provider, { loading: true }); try { const accessTokenResponse = await oauth.getAccessToken({ forceRefresh, scope, }); setStatus(provider, { accessTokenResponse, loading: false, }); } catch (error) { setStatus(provider, { error: error instanceof Error ? error.message : "Unknown error", loading: false, }); } }, [metaOauth, googleOauth], ); const logout = useCallback( async (provider: typeof META_PROVIDER | typeof GOOGLE_PROVIDER) => { const oauth = getOauthClient(provider); setStatus(provider, { accessTokenResponse: undefined, loading: true, }); // Revoke the user's authorization and clear stored tokens for the specific provider await oauth.deauthorize(); setStatus(provider, { accessTokenResponse: null, loading: false, responseBody: undefined, }); }, [metaOauth, googleOauth], ); const fetchData = useCallback( async (provider: typeof META_PROVIDER | typeof GOOGLE_PROVIDER) => { const status = getStatus(provider); const accessToken = status.accessTokenResponse?.token; if (!accessToken) { return; } const backendUrl = provider === META_PROVIDER ? META_BACKEND_URL : GOOGLE_BACKEND_URL; setStatus(provider, { loading: true }); try { // Example of using the access token to make authenticated API requests const res = await fetch(backendUrl, { headers: { Authorization: `Bearer ${accessToken}`, }, }); const data = await res.json(); setStatus(provider, { responseBody: data, loading: false, }); } catch (error) { setStatus(provider, { error: error instanceof Error ? error.message : "Unknown error", loading: false, }); } }, [metaStatus.accessTokenResponse, googleStatus.accessTokenResponse], ); const renderProviderSection = ( provider: typeof META_PROVIDER | typeof GOOGLE_PROVIDER, providerDisplayName: string, ) => { const status = getStatus(provider); const loading = status.loading && status.accessTokenResponse === undefined; const isConnected = status.accessTokenResponse != null; return ( {providerDisplayName} Provider {status.error ? ( Error: {status.error} ) : loading ? ( ) : !isConnected ? ( Connect your {providerDisplayName} account to use this provider. ) : ( Connected to {providerDisplayName}! {status.responseBody ? ( ( )} /> ) : null} )} ); }; const result = (
Multi-provider authentication This example demonstrates how apps can allow users to authorize with multiple OAuth providers (Meta and Google) simultaneously. To set up please see the README.md in the /examples/fundamentals/multi_provider_authentication folder {renderProviderSection(META_PROVIDER, "Meta")} {renderProviderSection(GOOGLE_PROVIDER, "Google")}
); return result; } ```
```typescript // For usage information, see the README.md file. import { AppUiProvider } from "@canva/app-ui-kit"; import { createRoot } from "react-dom/client"; import { App } from "./app"; import "@canva/app-ui-kit/styles.css"; import type { DesignEditorIntent } from "@canva/intents/design"; import { prepareDesignEditor } from "@canva/intents/design"; async function render() { const root = createRoot(document.getElementById("root") as Element); root.render( , ); } const designEditor: DesignEditorIntent = { render }; prepareDesignEditor(designEditor); // Hot Module Replacement for development (automatically reloads the app when changes are made) if (module.hot) { module.hot.accept("./app", render); } ``` ```markdown # Multi-provider authentication Demonstrates how to implement OAuth authentication with multiple providers (Meta and Google) in a single app, allowing users to connect to different platforms simultaneously. Shows provider-specific token management, independent authorization flows, and authenticated API requests using tokens from different providers. For API reference docs and instructions on running this example, see: https://www.canva.dev/docs/apps/examples/multi-provider-authentication/. Related examples: See fundamentals/authentication for single-provider OAuth authentication, fundamentals/multi_account_authentication for multiple accounts from a single provider, or design_interaction/design_token for design-specific authentication patterns. NOTE: This example differs from what is expected for public apps to pass a Canva review: - Token storage and security is simplified for demonstration. Production apps must implement secure token storage and follow OAuth security best practices - Error handling for authentication failures is simplified for demonstration. Production apps must implement comprehensive error handling with clear user feedback and graceful failure modes - Token refresh mechanisms are not implemented. Production apps should implement proper token lifecycle management - Internationalization is not implemented. Production apps must support multiple languages using the `@canva/app-i18n-kit` package to pass Canva review requirements - The code structure is simplified: Production apps using [intents](https://www.canva.dev/docs/apps/intents/) are recommended to call the prepareDesignEditor function from src/intents/design_editor/index.tsx ``` ````markdown # Setup ## Getting started Before using this example, you'll need to configure multiple OAuth providers in the [Developer Portal](https://www.canva.com/developers/apps): 1. **Configure Meta OAuth provider**: Follow the [OAuth integration guide](https://www.canva.dev/docs/apps/authenticating-users/oauth#prerequisite-configure-developer-portal) to set up a Meta provider with provider name `meta`. 2. **Configure Google OAuth provider**: Follow the same guide to set up a Google provider with provider name `google`. Each provider needs to be configured separately in the Developer Portal with its own: - Authorization endpoint - Token endpoint - Client ID and Client Secret Once both providers are configured, simply run the example from the root of `canva-apps-sdk-starter-kit` with: ```sh npm start:example multi_provider_authentication ``` ````
## API reference * [App UI Kit](https://www.canva.dev/docs/apps/app-ui-kit/) * [`auth.initOauth`](https://www.canva.dev/docs/apps/api/latest/user-auth-init-oauth/) * [`prepareDesignEditor`](https://www.canva.dev/docs/apps/api/latest/intents-design-prepare-design-editor/) ## Need help? * Join our [Community Forum](https://community.canva.dev/) * Report issues with this example on [GitHub](https://github.com/canva-sdks/canva-apps-sdk-starter-kit/issues)