Multi-account OAuth
Multi-account OAuth enables users to connect multiple accounts from the same OAuth provider within your app. This is particularly useful for cases when users need to switch between different profiles on the same platform.
The multi-account OAuth API is provided as a preview. Preview APIs are unstable and may change without warning. You can't release public apps using a preview API until it's stable.
Key concepts
Single-account vs multi-account mode
OAuth can be initialized in two modes:
- Single-account mode: The traditional OAuth flow where only one account per provider can be connected at a time. When a user authorizes a new account, it replaces any previously connected account.
- Multi-account mode: Allows users to connect multiple accounts from the same provider. Each account is stored separately and can be accessed, refreshed, or disconnected individually.
Provider configuration
The provider parameter identifies which OAuth configuration to use from the Developer Portal. This allows your app to:
- Support multiple OAuth providers (for example, both Google and Meta)
- Create separate OAuth instances for each provider
- Configure provider-specific settings like scopes and endpoints
Account management
In multi-account mode, each connected account includes:
- ID: A unique identifier for the account in the external provider's system
- Principal: The user's unique identifier from the OAuth provider (for example, email address)
- Display name: The user's name as provided by the OAuth provider
- Avatar URL: The user's profile picture URL
- Expiry status: Whether the account's access token has expired
When to use multi-account OAuth
Consider using multi-account OAuth when:
- Users need to manage multiple accounts from the same platform (for example, multiple social media accounts)
- Your app publishes content to external platforms and users want to choose the destination account
- Users switch between personal and business accounts on the same platform
- Your app integrates with team or organization accounts where users manage multiple profiles
If your app only needs access to a single account per provider, use the traditional OAuth flow instead.
Add multi-account OAuth to your app
Prerequisites
Before implementing multi-account OAuth:
- Configure your OAuth provider in the Developer Portal(opens in a new tab or window) following the steps in the OAuth integration guide.
- Configure the User profile endpoint and field mappings in the Developer Portal. This endpoint is used to fetch user profile information (display name, avatar) after authentication.
Profile field mapping
When configuring your OAuth provider in the Developer Portal, you must map fields from your provider's user profile response to Canva's expected fields. This mapping tells Canva how to extract user information from your OAuth provider's profile endpoint.
The required field mappings are:
externalId: Maps to the unique user identifier from your OAuth provider (for example,subfor OpenID Connect providers,idfor many social platforms).displayName: Maps to the user's display name (for example,name,display_name, orfull_name).principal: Maps to the user's primary identifier, typically their email address (for example,email).avatarUrl(optional): Maps to the user's profile picture URL (for example,picture,avatar_url, orprofile_image_url).
For example, if your OAuth provider returns a profile response like:
{"sub": "1234567890","name": "Jane Doe","picture": "https://example.com/avatar.jpg"}
Your field mappings in the Developer Portal would be:
externalId:subdisplayName:nameprincipal:emailavatarUrl:picture
The field mapping configuration is critical for multi-account OAuth to work correctly. Incorrect mappings prevents Canva from properly identifying and displaying user accounts.
Important provider configuration notes
When you create an OAuth provider in the Developer Portal, you can't change the multi-account setting. The multi-account flag is immutable after creating an OAuth provider.
If you need to switch between single-account and multi-account modes:
- Delete the existing provider from the Developer Portal.
- Create a new provider with the desired multi-account setting.
- Reconfigure all settings including endpoints, scopes, and field mappings.
Impact of provider deletion:
- All existing user authentications will be revoked. Users who previously connected their accounts will need to re-authenticate.
- All stored access and refresh tokens will be invalidated. Your app will lose access to external APIs for all users.
- User data associated with the provider may be lost. Depending on how your app stores user information.
Consider the multi-account requirement carefully during initial setup to avoid disrupting existing users.
Step 1: Initialize OAuth in multi-account mode
Import the required libraries and initialize OAuth with multi-account options:
import { auth } from "@canva/user";const oauth = auth.initOauth({type: "multi_account",provider: "meta",});
Step 2: Create state variables
Create state variables to track connected accounts and the selected account:
import { useState, useEffect } from "react";import type { OauthAccount } from "@canva/user";const scope = ["profile", "email"];export const App = () => {const [accounts, setAccounts] = useState<OauthAccount[]>([]);const [selectedAccount, setSelectedAccount] = useState<OauthAccount | null>(null);const [isLoading, setIsLoading] = useState(true);
Step 3: Fetch connected accounts
Create a function to fetch and update the list of connected accounts:
const refetchAccounts = async () => {setIsLoading(true);try {const response = await oauth.getAccounts();setAccounts(response.accounts);// Set first account as selected if none is currently selectedif (!selectedAccount && response.accounts.length > 0) {setSelectedAccount(response.accounts[0]);}} catch (error) {console.error("Failed to fetch accounts:", error);} finally {setIsLoading(false);}};
Step 4: Load accounts on mount
Use the useEffect hook to load accounts when the component mounts:
useEffect(() => {refetchAccounts();}, []);
Step 5: Implement authorization flow
Create a function to authorize new accounts:
const authorize = async () => {try {// Request authorization from the OAuth provider// This opens a popup window for the user to log inawait oauth.requestAuthorization({ scope });// Refresh the accounts list after successful authorizationawait refetchAccounts();} catch (error) {console.error("Authorization failed:", error);}};
Step 6: Implement account disconnection
Create a function to disconnect individual accounts:
const disconnectAccount = async (account: OauthAccount) => {try {// Revoke access for the specific accountawait account.deauthorize();// Clear selected account if it was the one being disconnectedif (selectedAccount?.id === account.id) {setSelectedAccount(null);}// Refresh the accounts listawait refetchAccounts();} catch (error) {console.error("Failed to disconnect account:", error);}};
Step 7: Display account list and controls
Render the list of connected accounts with controls to select or disconnect them:
import { Button, Text, Title, Rows } from "@canva/app-ui-kit";return (<Rows spacing="2u">{isLoading ? (<Text>Loading accounts...</Text>) : accounts.length === 0 ? (<><Text>No accounts connected</Text><Button variant="primary" onClick={authorize}>Connect account</Button></>) : (<>{accounts.map((account) => (<Rows key={account.id} spacing="1u"><Title>{account.displayName}</Title><Text size="small">{account.principal}</Text>{account.expired && (<Text size="small" tone="critical">Access expired - reconnect required</Text>)}{selectedAccount?.id === account.id ? (<Text tone="info">Currently selected</Text>) : (<Buttonvariant="secondary"onClick={() => setSelectedAccount(account)}>Select</Button>)}<Buttonvariant="secondary"onClick={() => disconnectAccount(account)}>Disconnect</Button></Rows>))}<Button variant="primary" onClick={authorize}>Connect another account</Button></>)}</Rows>);
Step 8: Use access tokens for API requests
When making API requests, use the access token from the selected account:
const fetchData = async () => {if (!selectedAccount) {return;}try {// Get the access token for the selected accountconst tokenResponse = await selectedAccount.getAccessToken();if (!tokenResponse) {// Access token not available, user needs to re-authorizeconsole.error("No access token available");return;}// Make an authenticated API requestconst response = await fetch("https://api.example.com/data", {headers: {Authorization: `Bearer ${tokenResponse.token}`,},});const data = await response.json();console.log("API response:", data);} catch (error) {console.error("Failed to fetch data:", error);}};
Multiple OAuth providers
Coming soon: Support for managing multiple OAuth providers within the same app is currently in development and not yet available.
This feature will allow your app to:
- Initialize separate OAuth instances for different providers (for example, Google, Meta, Twitter).
- Let users connect accounts from multiple platforms simultaneously.
- Manage multi-account authentication across different OAuth providers.
Check back for updates on this functionality.
Your app can initialize separate OAuth instances for different providers. This is useful when integrating with multiple platforms:
import { auth } from "@canva/user";// Initialize OAuth for Metaconst metaOauth = auth.initOauth({// Initialize OAuth for Meta in single-account mode// In single-account mode, authorizing a new account replaces any previously connected accountconst metaOauth = auth.initOauth({type: "single_account",provider: "meta",});// Initialize OAuth for Google in single-account modeconst googleOauth = auth.initOauth({type: "single_account",provider: "google",});// Request authorization from different providersasync function loginWithMeta() {await metaOauth.requestAuthorization();const token = await metaOauth.getAccessToken();// Use token to call Meta APIs}async function loginWithGoogle() {await googleOauth.requestAuthorization();const token = await googleOauth.getAccessToken();// Use token to call Google APIs}
Recommended practices
Account identification
- Use the
principalfield (for example, email address) to help users identify their accounts - Display the
displayNameandavatarUrlto provide visual context - Show the
expiredstatus to alert users when re-authorization is needed
User experience
- Clearly indicate which account is currently selected
- Provide a way to switch between accounts without disconnecting
- Make it easy to add additional accounts
- Show account information (name, profile picture) in selection interfaces
Error handling
- Handle cases where
getAccessToken()returnsnull(token unavailable or expired) - Provide clear error messages when authorization fails
- Gracefully handle network errors when fetching account lists
- Prompt users to re-authorize when tokens expire
Security
- Always call
getAccessToken()to get the latest token; don't cache tokens yourself - Use the
deauthorize()method to properly revoke access when disconnecting accounts - Validate that the selected account exists before making API requests
- Handle token refresh automatically by relying on Canva's token management
API reference
For more information about the multi-account OAuth API, see: