Skip to main content
Connect Clerk to authenticate end users with your Char widget. Configure your Clerk application and set up Char for token validation.
Clerk provides developer-focused authentication with built-in UI components. This integration uses Clerk’s JWT templates feature for seamless token validation.

Prerequisites

  • A Clerk account and application
  • Access to your Clerk Dashboard
  • Access to the Char dashboard

SDK References

Configuration Steps

1

Create or Select Your Clerk Application

  1. Sign in to the Clerk Dashboard
  2. Select your application (or create a new one)
  3. Note your application instance (Development or Production)
Clerk provides separate Development and Production instances. Use Development for testing and Production for live applications.
2

Create a JWT Template

By default, Clerk session tokens may not include all required claims. Create a JWT template to ensure proper token format:
  1. Navigate to JWT Templates in the left sidebar
  2. Click New template
  3. Select Blank template
  4. Name it char-ai (or your preferred name)
  5. Configure the template:
{
  "aud": "{{env.CLERK_PUBLISHABLE_KEY}}",
  "email": "{{user.primary_email_address}}",
  "name": "{{user.full_name}}",
  "picture": "{{user.image_url}}"
}
  1. Click Save
The aud claim is critical for security. Using your Publishable Key ensures tokens are scoped to your application.
3

Note Your Keys and Issuer

From the Clerk Dashboard:
  1. Navigate to API Keys
  2. Copy your Publishable key (starts with pk_)
  3. Find your Issuer URL in JWT Templates → your template → Issuer
The issuer URL format is: https://{your-frontend-api-domain}For example:
  • Development: https://grateful-macaw-1.clerk.accounts.dev
  • Production: https://clerk.yourapp.com (or custom domain)
Clerk API Keys

Find your API keys in the Clerk Dashboard

4

Configure Char

In the Char Dashboard:
  1. Navigate to SettingsIntegration
  2. Under SSO Configuration, select Custom OIDC as the provider
  3. Enter your configuration:
FieldValue
Client IDYour Clerk Publishable Key (or configured aud)
Issuer URLYour Clerk Frontend API domain
  1. Click Test Connection to verify
  2. Click Save Changes

Configuration Reference

Char FieldClerk ValueExample
Provider TypeCustom OIDCcustom_oidc
Client IDPublishable Key or configured audiencepk_live_...
Issuer URLFrontend API domainhttps://clerk.yourapp.com
Your Clerk Frontend API domain varies based on your setup:
  • Development: https://{random}.clerk.accounts.dev
  • Production (default): https://clerk.{your-domain}.com
  • Production (custom): Your configured custom domain

Token Requirements

Char validates Clerk tokens with these requirements:
ClaimRequirement
issMust match your Clerk Frontend API domain
audMust include your configured audience (Publishable Key)
subRequired - Clerk user ID
expMust not be expired
azpMust match your Publishable Key

Standard Clerk Session Claims

ClaimDescription
subClerk user ID (e.g., user_2abc...)
azpAuthorized party (your Publishable Key)
sidSession ID
emailUser’s email (if configured in template)
nameUser’s name (if configured in template)
org_idOrganization ID (if using Organizations)
org_roleOrganization role (if using Organizations)

Example: Obtaining and Passing the Token

import { useAuth } from '@clerk/clerk-react';
import { useEffect } from 'react';
import "@mcp-b/embedded-agent/web-component";

function App() {
  const { isSignedIn, getToken } = useAuth();

  useEffect(() => {
    if (isSignedIn) {
      // Get token from your JWT template
      getToken({ template: 'char-ai' }).then((token) => {
        if (token) {
          const agent = document.querySelector("webmcp-agent");
          if (agent) {
            agent.setAttribute("auth-token", token);
          }
        }
      });
    }
  }, [isSignedIn, getToken]);

  return <webmcp-agent />;
}

// App wrapper with ClerkProvider
import { ClerkProvider } from '@clerk/clerk-react';

function Root() {
  return (
    <ClerkProvider publishableKey="pk_live_...">
      <App />
    </ClerkProvider>
  );
}

Using Session Token (Without JWT Template)

If you prefer to use the default session token without a JWT template:
import { useAuth } from '@clerk/clerk-react';
import "@mcp-b/embedded-agent/web-component";

function App() {
  const { isSignedIn, getToken } = useAuth();

  useEffect(() => {
    if (isSignedIn) {
      // Get default session token (no template)
      getToken().then((token) => {
        if (token) {
          const agent = document.querySelector("webmcp-agent");
          if (agent) {
            agent.setAttribute("auth-token", token);
          }
        }
      });
    }
  }, [isSignedIn, getToken]);

  return <webmcp-agent />;
}
Default session tokens may have the azp claim as the audience instead of aud. Configure Char accordingly or use a JWT template for consistent audience handling.

Clerk Organizations

If you’re using Clerk Organizations for B2B scenarios:
import { useAuth, useOrganization } from '@clerk/clerk-react';
import "@mcp-b/embedded-agent/web-component";

function App() {
  const { isSignedIn, getToken } = useAuth();
  const { organization } = useOrganization();

  useEffect(() => {
    if (isSignedIn && organization) {
      // Token will include org_id and org_role claims
      getToken({ template: 'char-ai' }).then((token) => {
        if (token) {
          const agent = document.querySelector("webmcp-agent");
          if (agent) {
            agent.setAttribute("auth-token", token);
          }
        }
      });
    }
  }, [isSignedIn, organization, getToken]);

  return <webmcp-agent />;
}
Add organization claims to your JWT template:
{
  "aud": "{{env.CLERK_PUBLISHABLE_KEY}}",
  "email": "{{user.primary_email_address}}",
  "org_id": "{{org.id}}",
  "org_name": "{{org.name}}",
  "org_role": "{{org_membership.role}}"
}

Troubleshooting

The issuer URL doesn’t match:
  1. Check your Frontend API domain in Clerk Dashboard → API Keys
  2. Development vs Production: Ensure you’re using the correct environment’s domain
  3. Custom domains: If using a custom domain, use that as the issuer
  4. No trailing slash: The issuer URL should not end with /
The token’s audience doesn’t match the configured Client ID:
  • Verify you’re using a JWT template with the correct aud claim
  • If using default tokens, check the azp claim value
  • Ensure the Client ID in Char matches exactly (Publishable Key or custom audience)
If getToken() returns null:
  • Ensure the user is signed in (isSignedIn is true)
  • Check that the JWT template name is correct
  • Verify the template exists in your Clerk Dashboard
  • Check the browser console for Clerk errors
Char couldn’t reach Clerk’s JWKS endpoint:
  • JWKS endpoint: {issuer}/.well-known/jwks.json
  • Verify your Frontend API domain is correct
  • Use Test Connection in the Char dashboard
If custom claims aren’t appearing:
  • Verify you’re using the correct JWT template name in getToken({ template: 'name' })
  • Check template syntax in Clerk Dashboard
  • Ensure the template is enabled

Custom Domains

When using a custom domain with Clerk:
  1. Configure your custom domain in Clerk Dashboard → Domains
  2. Update your issuer URL in Char to match the custom domain
  3. Update your application’s Clerk configuration
<ClerkProvider
  publishableKey="pk_live_..."
  domain="auth.yourapp.com" // Custom domain
>

Security Best Practices

  • Use JWT templates to control exactly what claims are included
  • Rotate keys periodically using Clerk’s key rotation feature
  • Enable MFA for sensitive applications
  • Use Organizations for proper B2B access control
  • Monitor sign-in activity in Clerk Dashboard
  • Configure allowed origins in Clerk Dashboard
  • Use Production instance for live applications (separate from Development)