### Run Caddy Reverse Proxy Server Source: https://github.com/auth0-developer-hub/auth0-b2b-saas-starter/blob/main/README-ADVANCED.md This command navigates to the repository and starts Caddy with the Caddyfile configuration. Depends on Caddy being installed and Caddyfile present. No direct inputs, outputs server logs and potential SSL certificate prompts. May need password for keychain; error if Caddyfile is missing or invalid. ```shell cd ~/work/assemble caddy run ``` -------------------------------- ### Install Caddy for Local HTTPS Source: https://github.com/auth0-developer-hub/auth0-b2b-saas-starter/blob/main/README-ADVANCED.md This code installs Caddy, a web server and reverse proxy, along with NSS for SSL management, using Homebrew on macOS. It has no dependencies other than Homebrew. Input is the command, output is installation confirmation. Limited to macOS; other OS require different install methods. ```shell brew install caddy nss ``` -------------------------------- ### Verify Domain Ownership via DNS in TypeScript Source: https://context7.com/auth0-developer-hub/auth0-b2b-saas-starter/llms.txt Checks DNS TXT records to confirm domain ownership, a prerequisite for enabling SSO. It queries the specified domain for TXT records, then iterates through them to find a record starting with a specific verification identifier. If found, it extracts the token and compares it with the organization's stored verification token. A development mode bypass is included for testing purposes. Returns true if the domain is verified, false otherwise. ```typescript // lib/domain-verification.ts import { resolveTxt } from 'node:dns/promises'; import { DOMAIN_VERIFICATION_RECORD_IDENTIFIER } from './constants'; export async function verifyDnsRecords(domain: string, organizationId: string) { // Dev mode bypass for testing if (process.env.NODE_ENV === 'development' && domain === 'example.com') { return true; } // Get organization's verification token const { data: organization } = await managementClient.organizations.get({ id: organizationId, }); // Query DNS TXT records const txtRecords = await resolveTxt(domain); // Look for matching verification record for (const record of txtRecords) { const joinedRecord = record.join(''); if (joinedRecord.startsWith(`${DOMAIN_VERIFICATION_RECORD_IDENTIFIER}=`)) { const token = joinedRecord.replace( `${DOMAIN_VERIFICATION_RECORD_IDENTIFIER}=`, '' ); // Verify token matches if (token === organization.metadata.domainVerificationToken) { return true; } } } return false; } // Usage example: Verify before creating SSO connection const domains = ['acme.com', 'acme.org']; for (const domain of domains) { const verified = await verifyDnsRecords(domain, session.user.org_id); if (!verified) { return { error: `The domain ${domain} is not verified.` }; } } // Proceed with SSO connection creation ``` -------------------------------- ### Create Caddyfile for Reverse Proxy Source: https://github.com/auth0-developer-hub/auth0-b2b-saas-starter/blob/main/README-ADVANCED.md This code navigates to the project directory and creates an empty Caddyfile for configuration. It depends on being in the correct project path. Takes directory path as implicit input, outputs a new file. Assumes repository is cloned; fails if path is incorrect or permissions lack. ```shell cd ~/Developer/auth0-b2b-saas-starter touch Caddyfile ``` -------------------------------- ### Add Domain to Local Hosts File Source: https://github.com/auth0-developer-hub/auth0-b2b-saas-starter/blob/main/README-ADVANCED.md This line adds a loopback address mapping for the fake production URL to the system's hosts file. Requires sudo privileges for editing. Input is the domain line, output is DNS resolution locally. Only effective on the local machine; persistent across reboots but editable by admins. ```shell # SaaStart 127.0.0.1 fake-production-url.com ``` -------------------------------- ### Configure Caddyfile for HTTPS Proxy Source: https://github.com/auth0-developer-hub/auth0-b2b-saas-starter/blob/main/README-ADVANCED.md This configuration sets up a reverse proxy for the domain to route HTTPS traffic to localhost port 3000 with internal TLS. It requires Caddy and a local server running on port 3000. Domain is input, outputs secure proxy. Temporarily uses self-signed cert; not for production and may trigger browser warnings. ```shell fake-production-url.com:443 { reverse_proxy 127.0.0.1:3000 tls internal } ``` -------------------------------- ### Create Organization During Onboarding (TypeScript) Source: https://context7.com/auth0-developer-hub/auth0-b2b-saas-starter/llms.txt This code snippet details the creation of an organization during user signup, assigning the new user as an administrator. It leverages the Auth0 Management API to create the organization, assign a slug, and add the user as a member with administrative privileges. Requires Auth0 Management API. ```typescript // app/onboarding/create/actions.ts 'use server'; import { redirect } from 'next/navigation'; import slugify from '@sindresorhus/slugify'; import { managementClient, onboardingClient } from '@/lib/auth0'; export async function createOrganization(formData: FormData) { const session = await onboardingClient.getSession(); if (!session) { return redirect('/onboarding/signup'); } const organizationName = formData.get('organization_name'); if (!organizationName || typeof organizationName !== 'string') { return { error: 'Organization name is required.' }; } try { // Create organization with slugified identifier const { data: organization } = await managementClient.organizations.create({ name: slugify(organizationName), display_name: organizationName, enabled_connections: [ { connection_id: process.env.DEFAULT_CONNECTION_ID }, ], }); // Add user as member await managementClient.organizations.addMembers( { id: organization.id }, { members: [session.user.sub] } ); // Assign admin role await managementClient.organizations.addMemberRoles( { id: organization.id, user_id: session.user.sub }, { roles: [process.env.AUTH0_ADMIN_ROLE_ID] } ); // Redirect to app with organization context const authParams = new URLSearchParams({ organization: organization.id, returnTo: '/dashboard', }); redirect(`/auth/login?${authParams.toString()}`); } catch (error) { console.error('failed to create an organization', error); return { error: 'Failed to create an organization.' }; } } // Frontend usage in form async function handleSubmit(formData: FormData) { const result = await createOrganization(formData); if (result?.error) { toast.error(result.error); } } ``` -------------------------------- ### Enable SCIM Configuration using TypeScript Source: https://context7.com/auth0-developer-hub/auth0-b2b-saas-starter/llms.txt Activates SCIM provisioning for automatic user and group synchronization. This function verifies connection ownership before creating the SCIM configuration. It requires the 'next/cache' and '@auth0/nextjs-auth0' libraries. The input is a connection ID and session data, and the output is either an empty object on success or an error object. ```typescript // app/dashboard/organization/sso/components/provisioning/actions.ts 'use server'; import { revalidatePath } from 'next/cache'; import { SessionData } from '@auth0/nextjs-auth0/types'; import { managementClient } from '@/lib/auth0'; import { withServerActionAuth } from '@/lib/with-server-action-auth'; export const createScimConfig = withServerActionAuth( async function(connectionId: string, session: SessionData) { // Verify connection ownership const { data: enabledConnection } = await managementClient.organizations.getEnabledConnection({ id: session.user.org_id!, connectionId, }); if (!enabledConnection) { return { error: 'Connection not found.' }; } try { await managementClient.connections.createScimConfiguration( { id: connectionId }, { user_id_attribute: 'externalId', } ); revalidatePath(`/dashboard/organization/sso/*/edit/${connectionId}/provisioning`); return {}; } catch (error) { console.error('failed to create a SCIM configuration', error); return { error: 'Failed to create a SCIM configuration.' }; } }, { role: 'admin' } ); // Frontend usage async function handleEnableScim(connectionId: string) { const result = await createScimConfig(connectionId); if (result.error) { toast.error(result.error); } else { toast.success('SCIM provisioning enabled'); } } ``` -------------------------------- ### Test HTTPS Proxy with Curl Source: https://github.com/auth0-developer-hub/auth0-b2b-saas-starter/blob/main/README-ADVANCED.md This curl command tests the HTTPS connection to the fake domain, verifying certificate and server response. Requires curl and active Caddy server. URL is input, outputs headers and SSL info. Non-zero exit if connection fails; checks for valid internal cert but not external trust. ```shell curl -vI https://fake-production-url.com ``` -------------------------------- ### Manage MFA Enrollments (Add & Remove) via Auth0 Guardian (TypeScript) Source: https://context7.com/auth0-developer-hub/auth0-b2b-saas-starter/llms.txt Server actions to create an enrollment ticket for a new MFA factor and to delete an existing enrollment. Front‑end helpers handle user confirmation, invoke the actions, and navigate or display toast messages based on the outcome. ```typescript // app/dashboard/account/security/actions.ts 'use server'; import { revalidatePath } from 'next/cache'; import { SessionData } from '@auth0/nextjs-auth0/types'; import { managementClient } from '@/lib/auth0'; import { withServerActionAuth } from '@/lib/with-server-action-auth'; export const createEnrollment = withServerActionAuth( async function(formData: FormData, session: SessionData) { const factorName = formData.get('factor_name'); if (!factorName || typeof factorName !== 'string') { return { error: 'Factor name is required.' }; } // Map SMS/Voice to phone factor const mappedFactor = ['sms', 'voice'].includes(factorName) ? 'phone' : factorName; try { // Create enrollment ticket const { data: ticket } = await managementClient.users.createEnrollmentTicket({ user_id: session.user.sub, send_mail: false, factor: mappedFactor as any, }); // Return ticket URL for frontend redirect return { ticketUrl: ticket.ticket_url }; } catch (error) { console.error('failed to create enrollment', error); return { error: 'Failed to create enrollment.' }; } }, {} ); export const deleteEnrollment = withServerActionAuth( async function(formData: FormData, session: SessionData) { const enrollmentId = formData.get('enrollment_id'); if (!enrollmentId || typeof enrollmentId !== 'string') { return { error: 'Enrollment ID is required.' }; } try { await managementClient.guardian.deleteEnrollment({ id: enrollmentId, }); revalidatePath('/dashboard/account/security'); return {}; } catch (error) { console.error('failed to delete enrollment', error); return { error: 'Failed to delete enrollment.' }; } }, {} ); ``` ```typescript // Frontend usage async function handleAddMFA(factorType: string) { const result = await createEnrollment( new FormData().append('factor_name', factorType) ); if (result.error) { toast.error(result.error); } else if (result.ticketUrl) { // Redirect to enrollment flow window.location.href = result.ticketUrl; } } async function handleRemoveMFA(enrollmentId: string) { const confirmed = confirm('Remove this MFA method?'); if (!confirmed) return; const formData = new FormData(); formData.append('enrollment_id', enrollmentId); const result = await deleteEnrollment(formData); if (result.error) { toast.error(result.error); } else { toast.success('MFA method removed'); } } ``` -------------------------------- ### Configure MFA Policy with TypeScript Source: https://context7.com/auth0-developer-hub/auth0-b2b-saas-starter/llms.txt Configures organization-wide multi-factor authentication requirements using TypeScript. It parses form data to set enforcement, allowed providers, and domain exceptions, storing the policy in the organization's metadata. Dependencies include '@auth0/nextjs-auth0' and custom utility functions for Auth0 management. ```typescript // app/dashboard/organization/security-policies/actions.ts 'use server'; import { revalidatePath } from 'next/cache'; import { SessionData } from '@auth0/nextjs-auth0/types'; import { managementClient } from '@/lib/auth0'; import { DEFAULT_MFA_POLICY, SUPPORTED_PROVIDERS } from '@/lib/mfa-policy'; import { withServerActionAuth } from '@/lib/with-server-action-auth'; // lib/mfa-policy.ts export interface MfaPolicy { enforce: boolean; providers: string[]; skipForDomains: string[]; } export const DEFAULT_MFA_POLICY: MfaPolicy = { enforce: false, providers: [], skipForDomains: [], }; export const SUPPORTED_PROVIDERS = ['otp', 'webauthn-roaming']; // app/dashboard/organization/security-policies/actions.ts export const updateMfaPolicy = withServerActionAuth( async function(formData: FormData, session: SessionData) { // Parse form data const enforce = !!formData.get('enforce'); const skipForDomains = formData.get('skip_for_domains'); const providers = SUPPORTED_PROVIDERS .map(p => formData.get(p)) .filter(Boolean); // Parse comma-separated domains const parsedSkipForDomains = skipForDomains && typeof skipForDomains === 'string' ? skipForDomains.split(',').map(d => d.trim()) : []; try { // Store policy as JSON in organization metadata await managementClient.organizations.update( { id: session.user.org_id! }, { metadata: { mfaPolicy: JSON.stringify({ ...DEFAULT_MFA_POLICY, enforce, skipForDomains: parsedSkipForDomains, providers, }), }, } ); revalidatePath('/dashboard/organization/security-policies'); return {}; } catch (error) { console.error("failed to update the organization's MFA policy", error); return { error: "Failed to update the organization's MFA policy." }; } }, { role: 'admin' } ); // Frontend form usage //
``` -------------------------------- ### Invite Users to Organization (TypeScript) Source: https://context7.com/auth0-developer-hub/auth0-b2b-saas-starter/llms.txt Allows administrators to invite new members to an organization via email, assigning them specific roles. It uses Auth0's management client and Next.js server actions. Input is form data containing email and role; output is a success or error object. ```typescript // app/dashboard/organization/members/actions.ts 'use server'; import { revalidatePath } from 'next/cache'; import { SessionData } from '@auth0/nextjs-auth0/types'; import { managementClient } from '@/lib/auth0'; import { Role, roles } from '@/lib/roles'; import { withServerActionAuth } from '@/lib/with-server-action-auth'; export const createInvitation = withServerActionAuth( async function(formData: FormData, session: SessionData) { const email = formData.get('email'); const role = formData.get('role') as Role; if (!email || typeof email !== 'string') { return { error: 'Email address is required.' }; } if (!role || !['member', 'admin'].includes(role)) { return { error: "Role is required and must be either 'member' or 'admin'." }; } try { const roleId = roles[role]; await managementClient.organizations.createInvitation( { id: session.user.org_id! }, { invitee: { email }, inviter: { name: session.user.name! }, client_id: process.env.AUTH0_CLIENT_ID, // Only assign role if not default member roles: roleId ? [roleId] : undefined, } ); revalidatePath('/dashboard/organization/members'); return {}; } catch (error) { console.error('failed to create invitation', error); return { error: 'Failed to create invitation.' }; } }, { role: 'admin' } ); // Example frontend usage /* */ ``` -------------------------------- ### Enforce MFA Policy with Auth0 Action (JavaScript) Source: https://context7.com/auth0-developer-hub/auth0-b2b-saas-starter/llms.txt A server-side Auth0 Action written in JavaScript that enforces MFA during the login process based on the organization's policy. It parses the MFA policy from organization metadata and challenges or enrolls users accordingly, with support for domain exceptions. This action is intended for deployment via Auth0 CLI or dashboard. ```javascript // actions/security-policies.js /** * Auth0 Post-Login Action that enforces organization MFA policies. * Deploy this to your Auth0 tenant via Auth0 CLI or dashboard. */ exports.onExecutePostLogin = async (event, api) => { // Only enforce for dashboard client, not onboarding if (event.client.client_id !== event.secrets.DASHBOARD_CLIENT_ID) return; // Parse MFA policy from organization metadata const mfaPolicy = JSON.parse(event.organization?.metadata.mfaPolicy || '{}'); if (mfaPolicy.enforce) { // Check if user has any MFA factors enrolled if (!event.user.multifactor || event.user.multifactor.length === 0) { // Force enrollment with configured providers return api.authentication.enrollWithAny( mfaPolicy.providers.map(p => ({ type: p })) ); } // Check domain exceptions if (mfaPolicy.skipForDomains.length > 0) { // Validate email format const splitEmail = (event.user.email || '').split('@'); if (splitEmail.length !== 2) { return api.access.deny('Email is invalid'); } const domain = splitEmail[1].toLowerCase(); // Skip MFA challenge if domain is in exception list if (!mfaPolicy.skipForDomains.includes(domain)) { api.authentication.challengeWithAny( mfaPolicy.providers.map(p => ({ type: p })) ); } } else { // No domain exceptions - challenge all users api.authentication.challengeWithAny( mfaPolicy.providers.map(p => ({ type: p })) ); } } }; // Example MFA policy metadata structure: // { // "enforce": true, // "providers": ["otp", "webauthn-roaming"], // "skipForDomains": ["acme.com"] // } ``` -------------------------------- ### Server Action for Domain Verification in TypeScript Source: https://context7.com/auth0-developer-hub/auth0-b2b-saas-starter/llms.txt Exposes the domain verification functionality as a server action, making it accessible from the frontend. This action takes a domain name and session data, then calls the `verifyDnsRecords` function to check domain ownership. It returns a JSON object indicating whether the domain was verified or if an error occurred. The action is protected by server action authentication, requiring an 'admin' role. ```typescript // app/dashboard/organization/sso/actions.ts 'use server'; import { SessionData } from '@auth0/nextjs-auth0/types'; import { verifyDnsRecords } from '@/lib/domain-verification'; import { withServerActionAuth } from '@/lib/with-server-action-auth'; export const verifyDomain = withServerActionAuth( async function(domain: string, session: SessionData) { try { const verified = await verifyDnsRecords(domain, session.user.org_id!) return { verified }; } catch (error) { console.error('failed to verify domain', error); return { error: 'Failed to verify domain.' }; } }, { role: 'admin' } ); // Frontend usage with status feedback async function handleVerifyDomain(domain: string) { const result = await verifyDomain(domain); if (result.error) { toast.error(result.error); } else if (result.verified) { toast.success('Domain verified successfully!'); } else { toast.error('Domain verification failed. Check your DNS records.'); } } ``` -------------------------------- ### Create OIDC SSO Connection with TypeScript Source: https://context7.com/auth0-developer-hub/auth0-b2b-saas-starter/llms.txt This server action creates an OpenID Connect (OIDC) SSO connection in Auth0. It validates form data, verifies domain ownership via DNS records, and then uses the Auth0 Management API to create and configure the connection. Dependencies include crypto, next/cache, @auth0/nextjs-auth0, @sindresorhus/slugify, and custom utility functions for Auth0 management and domain verification. It accepts form data as input and returns an object indicating success or an error message. ```typescript 'use server'; import crypto from 'crypto'; import { revalidatePath } from 'next/cache'; import { SessionData } from '@auth0/nextjs-auth0/types'; import slugify from '@sindresorhus/slugify'; import { managementClient } from '@/lib/auth0'; import { verifyDnsRecords } from '@/lib/domain-verification'; import { withServerActionAuth } from '@/lib/with-server-action-auth'; export const createConnection = withServerActionAuth( async function(formData: FormData, session: SessionData) { const displayName = formData.get('display_name'); const discoveryUrl = formData.get('discovery_url'); const clientId = formData.get('client_id'); const clientSecret = formData.get('client_secret'); const domainAliases = formData.get('domains'); const type = formData.get('type'); // 'front_channel' | 'back_channel' const scope = formData.get('scope'); const assignMembershipOnLogin = formData.get('assign_membership_on_login'); // Validate required fields if (!displayName || typeof displayName !== 'string') { return { error: 'Connection name is required.' }; } if (!discoveryUrl || typeof discoveryUrl !== 'string') { return { error: 'Discovery URL is required.' }; } if (!clientId || typeof clientId !== 'string') { return { error: 'Client ID is required.' }; } // Parse comma-separated domains const parsedDomains = domainAliases && typeof domainAliases === 'string' ? domainAliases.split(',').map(d => d.trim()) : []; // Verify all domains via DNS before proceeding for (const domain of parsedDomains) { const verified = await verifyDnsRecords(domain, session.user.org_id!); if (!verified) { return { error: `The domain ${domain} is not verified.` }; } } try { // Create OIDC connection with unique name const { data: connection } = await managementClient.connections.create({ display_name: displayName, // Append random suffix to ensure global uniqueness name: `${slugify(displayName)}-${crypto.randomBytes(4).toString('hex')}`, strategy: 'oidc', enabled_clients: [process.env.AUTH0_CLIENT_ID], options: { type, discovery_url: discoveryUrl, client_id: clientId, client_secret: clientSecret, domain_aliases: parsedDomains, scope, }, }); // Enable connection for organization await managementClient.organizations.addEnabledConnection( { id: session.user.org_id! }, { connection_id: connection.id, assign_membership_on_login: assignMembershipOnLogin === 'enabled', } ); revalidatePath('/dashboard/organization/sso'); return {}; } catch (error) { console.error('failed to create the SSO connection', error); return { error: 'Failed to create the SSO connection.' }; } }, { role: 'admin' } // Ensure only admins can perform this action ); ``` -------------------------------- ### Create SAML SSO Connection in TypeScript and HTML Source: https://context7.com/auth0-developer-hub/auth0-b2b-saas-starter/llms.txt This snippet shows how to create a SAML 2.0 SSO connection using an Auth0 Management API in a Next.js server action written in TypeScript, along with an HTML form for user input. It handles validation of required fields, certificate processing, domain verification, and connection creation. Dependencies include Auth0 SDK, Next.js, domain verification utilities, and slugify library; inputs are form data fields like display name, URLs, certificate file, and options; outputs include success revalidation or error messages; limitations include requiring admin role access and pre-verified domains. ```typescript // app/dashboard/organization/sso/saml/new/actions.ts 'use server'; import crypto from 'crypto'; import { revalidatePath } from 'next/cache'; import { SessionData } from '@auth0/nextjs-auth0/types'; import slugify from '@sindresorhus/slugify'; import { managementClient } from '@/lib/auth0'; import { verifyDnsRecords } from '@/lib/domain-verification'; import { withServerActionAuth } from '@/lib/with-server-action-auth'; export const createConnection = withServerActionAuth( async function(formData: FormData, session: SessionData) { const displayName = formData.get('display_name'); const signInUrl = formData.get('sign_in_url'); const signOutUrl = formData.get('sign_out_url'); const certificate = formData.get('certificate') as File; const userIdAttribute = formData.get('user_id_attribute'); const protocolBinding = formData.get('protocol_binding'); const domainAliases = formData.get('domains'); const signRequest = formData.get('sign_request'); const assignMembershipOnLogin = formData.get('assign_membership_on_login'); // Validate required fields if (!displayName || typeof displayName !== 'string') { return { error: 'Connection name is required.' }; } if (!signInUrl || typeof signInUrl !== 'string') { return { error: 'Sign-in URL is required.' }; } if (!certificate) { return { error: 'X.509 certificate is required.' }; } // Parse domains const parsedDomains = domainAliases && typeof domainAliases === 'string' ? domainAliases.split(',').map(d => d.trim()) : []; // Verify domains for (const domain of parsedDomains) { const verified = await verifyDnsRecords(domain, session.user.org_id!); if (!verified) { return { error: `The domain ${domain} is not verified.` }; } } try { // Read and encode certificate const certText = await certificate.text(); const certBase64 = Buffer.from(certText).toString('base64'); // Create SAML connection const { data: connection } = await managementClient.connections.create({ display_name: displayName, name: `${slugify(displayName)}-${crypto.randomBytes(4).toString('hex')}`, strategy: 'samlp', enabled_clients: [process.env.AUTH0_CLIENT_ID], options: { signInEndpoint: signInUrl, signOutEndpoint: signOutUrl, signSAMLRequest: signRequest === 'on', signingCert: certBase64, protocolBinding: protocolBinding || 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', user_id_attribute: userIdAttribute || undefined, domain_aliases: parsedDomains, }, }); // Enable for organization await managementClient.organizations.addEnabledConnection( { id: session.user.org_id! }, { connection_id: connection.id, assign_membership_on_login: assignMembershipOnLogin === 'enabled', } ); revalidatePath('/dashboard/organization/sso'); return {}; } catch (error) { console.error('failed to create SAML connection', error); return { error: 'Failed to create the SSO connection.' }; } }, { role: 'admin' } ); // Frontend form usage ``` ```html ``` -------------------------------- ### Frontend HTML Form for OIDC SSO Connection Creation Source: https://context7.com/auth0-developer-hub/auth0-b2b-saas-starter/llms.txt This HTML form is designed to be used with the `createConnection` server action. It includes input fields for all necessary OIDC connection parameters such as display name, discovery URL, client ID, client secret, domain aliases, connection type, scope, and membership assignment options. The form's action is set to the `createConnection` function, enabling server-side processing of the submitted data. ```html ``` -------------------------------- ### Generate and Retrieve Domain Verification Token in TypeScript Source: https://context7.com/auth0-developer-hub/auth0-b2b-saas-starter/llms.txt Creates or retrieves a cryptographic token for DNS-based domain verification for an organization. It fetches the organization's metadata; if a token exists, it's returned. Otherwise, a new 64-character hex token is generated using Node.js crypto, stored in the organization's metadata, and then returned. This function is essential for verifying domain ownership before enabling SSO. ```typescript // lib/domain-verification.ts import { randomBytes } from 'node:crypto'; import { managementClient } from './auth0'; export async function getOrCreateDomainVerificationToken(organizationId: string) { // Fetch organization metadata const { data: organization } = await managementClient.organizations.get({ id: organizationId, }); // Return existing token if present if (organization.metadata?.domainVerificationToken) { return organization.metadata.domainVerificationToken; } // Generate new 64-character hex token const domainVerificationToken = randomBytes(32).toString('hex'); // Store token in organization metadata await managementClient.organizations.update( { id: organizationId }, { metadata: { ...organization.metadata, domainVerificationToken, }, } ); return domainVerificationToken; } // Usage example: Display verification instructions const token = await getOrCreateDomainVerificationToken(orgId); console.log(`Add this TXT record to your DNS:`) console.log(`saastart-domain-verification=${token}`); ``` -------------------------------- ### Generate SCIM Bearer Token using TypeScript Source: https://context7.com/auth0-developer-hub/auth0-b2b-saas-starter/llms.txt Creates an authentication token for SCIM API endpoints. This function ensures the connection is owned by the user before generating the token. It depends on 'next/cache' and '@auth0/nextjs-auth0'. The input is a connection ID and session data; the output is an object containing the token or an error message. The token should be displayed only once. ```typescript // app/dashboard/organization/sso/components/provisioning/actions.ts export const createScimToken = withServerActionAuth( async function(connectionId: string, session: SessionData) { // Verify connection ownership const { data: enabledConnection } = await managementClient.organizations.getEnabledConnection({ id: session.user.org_id!, connectionId, }); if (!enabledConnection) { return { error: 'Connection not found.' }; } try { const { data: token } = await managementClient.connections.createScimToken( { id: connectionId }, {} ); revalidatePath(`/dashboard/organization/sso/*/edit/${connectionId}/provisioning`); return { token: token.token }; } catch (error) { console.error('failed to create a SCIM token', error); return { error: 'Failed to create a SCIM token.' }; } }, { role: 'admin' } ); // Frontend usage with secure display async function handleGenerateToken(connectionId: string) { const result = await createScimToken(connectionId); if (result.error) { toast.error(result.error); } else if (result.token) { // Display token once - user must copy immediately setScimToken(result.token); toast.success('Token generated - copy it now, it will not be shown again'); } } ``` -------------------------------- ### Extract and Manage User Roles (TypeScript) Source: https://context7.com/auth0-developer-hub/auth0-b2b-saas-starter/llms.txt This snippet demonstrates extracting user roles from Auth0 custom claims. It defines roles, retrieves them from the user profile, and determines the user's role based on the presence of specific claims. It uses Auth0 NextJS driver. ```typescript // lib/roles.ts import { User } from '@auth0/nextjs-auth0/types'; export const roles = { member: process.env.AUTH0_MEMBER_ROLE_ID, admin: process.env.AUTH0_ADMIN_ROLE_ID, }; export type Role = 'member' | 'admin'; export function getRole(user: User): Role { const customClaimsNamespace = process.env.CUSTOM_CLAIMS_NAMESPACE!; const userRoles = user[`${customClaimsNamespace}/roles`] || []; // Return first role found, default to member if (userRoles.includes(roles.admin)) return 'admin'; return 'member'; } // Example: Checking roles in a component const session = await appClient.getSession(); const userRole = getRole(session.user); if (userRole === 'admin') { // Show admin UI } ``` -------------------------------- ### Update SCIM Configuration using TypeScript Source: https://context7.com/auth0-developer-hub/auth0-b2b-saas-starter/llms.txt Modifies the user ID attribute mapping for SCIM synchronization. This function validates the input and verifies connection ownership before updating the configuration. It depends on 'next/cache' and '@auth0/nextjs-auth0'. It takes connection ID, form data, and session data as input, returning an empty object on success or an error object. ```typescript // app/dashboard/organization/sso/components/provisioning/actions.ts export const updateScimConfig = withServerActionAuth( async function(connectionId: string, formData: FormData, session: SessionData) { const userIdAttribute = formData.get('user_id_attribute') as string; if (!userIdAttribute || typeof userIdAttribute !== 'string') { return { error: 'User ID attribute is required.' }; } // Verify connection ownership const { data: enabledConnection } = await managementClient.organizations.getEnabledConnection({ id: session.user.org_id!, connectionId, }); if (!enabledConnection) { return { error: 'Connection not found.' }; } try { // Fetch existing config to preserve mapping const { data: scimConfig } = await managementClient.connections.getScimConfiguration({ id: connectionId, }); // Update with new user ID attribute await managementClient.connections.updateScimConfiguration( { id: connectionId }, { user_id_attribute: userIdAttribute, mapping: scimConfig.mapping, } ); revalidatePath(`/dashboard/organization/sso/*/edit/${connectionId}/provisioning`); return {}; } catch (error) { console.error('failed to update SCIM configuration', error); return { error: 'Failed to update SCIM configuration.' }; } }, { role: 'admin' } ); // Frontend usage ``` -------------------------------- ### Update OIDC SSO Connection with Domain Verification Source: https://context7.com/auth0-developer-hub/auth0-b2b-saas-starter/llms.txt Server action that modifies existing OIDC connection settings with domain reverification. Validates connection ownership, verifies domain DNS records, and updates both connection settings and organization assignment. Requires admin role authentication and depends on Auth0 management client and domain verification utilities. ```typescript 'use server'; import { revalidatePath } from 'next/cache'; import { SessionData } from '@auth0/nextjs-auth0/types'; import { managementClient } from '@/lib/auth0'; import { verifyDnsRecords } from '@/lib/domain-verification'; import { withServerActionAuth } from '@/lib/with-server-action-auth'; export const updateConnection = withServerActionAuth( async function(connectionId: string, formData: FormData, session: SessionData) { // Validate connection ownership const { data: enabledConnection } = await managementClient.organizations.getEnabledConnection({ id: session.user.org_id!, connectionId, }); if (!enabledConnection) { return { error: 'Connection not found.' }; } const displayName = formData.get('display_name'); const discoveryUrl = formData.get('discovery_url'); const clientId = formData.get('client_id'); const clientSecret = formData.get('client_secret'); const domainAliases = formData.get('domains'); const type = formData.get('type'); const scope = formData.get('scope'); const assignMembershipOnLogin = formData.get('assign_membership_on_login'); // Parse and verify domains const parsedDomains = domainAliases && typeof domainAliases === 'string' ? domainAliases.split(',').map(d => d.trim()) : []; for (const domain of parsedDomains) { const verified = await verifyDnsRecords(domain, session.user.org_id!); if (!verified) { return { error: `The domain ${domain} is not verified.` }; } } try { // Update connection and organization assignment in parallel await Promise.all([ managementClient.connections.update( { id: connectionId }, { display_name: displayName, options: { type, discovery_url: discoveryUrl, client_id: clientId, client_secret: clientSecret, domain_aliases: parsedDomains, scope, }, } ), managementClient.organizations.updateEnabledConnection( { id: session.user.org_id!, connectionId }, { assign_membership_on_login: assignMembershipOnLogin === 'enabled', } ), ]); revalidatePath('/dashboard/organization/sso'); return {}; } catch (error) { console.error('failed to update connection', error); return { error: 'Failed to update the SSO connection.' }; } }, { role: 'admin' } ); ``` -------------------------------- ### Disable SCIM Provisioning – TypeScript Source: https://context7.com/auth0-developer-hub/auth0-b2b-saas-starter/llms.txt Removes the entire SCIM configuration from a connection after verifying organization ownership. Calls the Auth0 Management API to delete the configuration, revalidates the dashboard path, and returns a result object. Designed for admin‑role server actions with consistent error handling. ```typescript // app/dashboard/organization/sso/components/provisioning/actions.ts export const deleteScimConfig = withServerActionAuth( async function(connectionId: string, session: SessionData) { // Verify connection ownership const { data: enabledConnection } = await managementClient.organizations.getEnabledConnection({ id: session.user.org_id!, connectionId, }); if (!enabledConnection) { return { error: 'Connection not found.' }; } try { await managementClient.connections.deleteScimConfiguration({ id: connectionId, }); revalidatePath(`/dashboard/organization/sso/*/edit/${connectionId}/provisioning`); return {}; } catch (error) { console.error('failed to delete a SCIM configuration', error); return { error: 'Failed to delete a SCIM configuration.' }; } }, { role: 'admin' } ); ``` -------------------------------- ### Delete User Account using Auth0 Management API (TypeScript) Source: https://context7.com/auth0-developer-hub/auth0-b2b-saas-starter/llms.txt Server action that permanently deletes the authenticated user from Auth0 and returns a result object. Includes a client‑side helper that asks for double confirmation before invoking the action and redirects to logout on success. ```typescript // app/dashboard/account/profile/actions.ts export const deleteAccount = withServerActionAuth( async function(_, session: SessionData) { try { // Permanently delete user from Auth0 await managementClient.users.delete({ id: session.user.sub, }); return {}; } catch (error) { console.error('failed to delete account', error); return { error: 'Failed to delete account.' }; } }, {} ); // Frontend usage with strong confirmation async function handleDeleteAccount() { const confirmed = confirm( 'Are you sure you want to delete your account? This action cannot be undone.' ); if (!confirmed) return; const doubleCheck = prompt('Type "DELETE" to confirm:'); if (doubleCheck !== 'DELETE') { toast.error('Account deletion cancelled'); return; } const result = await deleteAccount(); if (result.error) { toast.error(result.error); } else { // Redirect to logout window.location.href = '/auth/logout'; } } ``` -------------------------------- ### Update User Display Name with Auth0 Management API (TypeScript) Source: https://context7.com/auth0-developer-hub/auth0-b2b-saas-starter/llms.txt Server action that validates the new display name, updates the user profile in Auth0, and synchronizes the local session. It revalidates the dashboard page to reflect changes. No specific role is required, only an authenticated user. ```typescript // app/dashboard/account/profile/actions.ts 'use server'; import { revalidatePath } from 'next/cache'; import { managementClient, appClient } from '@/lib/auth0'; import { withServerActionAuth } from '@/lib/with-server-action-auth'; export const updateDisplayName = withServerActionAuth( async function(formData: FormData, session: SessionData) { const displayName = formData.get('display_name'); if (!displayName || typeof displayName !== 'string') { return { error: 'Display name is required.' }; } try { // Update user in Auth0 await managementClient.users.update( { id: session.user.sub }, { name: displayName } ); // Update local session await appClient.updateSession({ ...session, user: { ...session.user, name: displayName, }, }); revalidatePath('/dashboard'); return {}; } catch (error) { console.error('failed to update display name', error); return { error: 'Failed to update display name.' }; } }, {} // No specific role required - any authenticated user ); ``` -------------------------------- ### Update Organization Display Name (TypeScript) Source: https://context7.com/auth0-developer-hub/auth0-b2b-saas-starter/llms.txt This snippet demonstrates how to update an organization's display name using the Auth0 Management API. It requires admin privileges and utilizes server actions and revalidation to update the dashboard. It uses Auth0 Management API. ```typescript // app/dashboard/organization/general/actions.ts 'use server'; import { revalidatePath } from 'next/cache'; import { SessionData } from '@auth0/nextjs-auth0/types'; import { managementClient } from '@/lib/auth0'; import { withServerActionAuth } from '@/lib/with-server-action-auth'; export const updateDisplayName = withServerActionAuth( async function(formData: FormData, session: SessionData) { const displayName = formData.get('display_name'); if (!displayName || typeof displayName !== 'string') { return { error: 'Display name is required.' }; } try { await managementClient.organizations.update( { id: session.user.org_id! }, { display_name: displayName } ); revalidatePath('/dashboard/organization'); return {}; } catch (error) { console.error('failed to update organization', error); return { error: 'Failed to update organization.' }; } }, { role: 'admin' } ); ``` -------------------------------- ### Resend Verification Email using TypeScript Source: https://context7.com/auth0-developer-hub/auth0-b2b-saas-starter/llms.txt Triggers a new email verification for a user. This server action requires the user to be logged in and uses the Auth0 Management API to send the verification email. Frontend usage involves calling this action and displaying success or error messages via toast notifications. ```typescript // app/onboarding/verify/actions.ts 'use server'; import { managementClient, onboardingClient } from '@/lib/auth0'; export async function resendVerificationEmail() { const session = await onboardingClient.getSession(); if (!session) { return { error: 'You must be logged in.' }; } try { // Trigger email verification job await managementClient.jobs.verifyEmail({ user_id: session.user.sub, }); return {}; } catch (error) { console.error('failed to resend verification email', error); return { error: 'Failed to resend verification email.' }; } } // Frontend usage async function handleResendEmail() { const result = await resendVerificationEmail(); if (result.error) { toast.error(result.error); } else { toast.success('Verification email sent!'); } } ```