This guide demonstrates how to integrate Auth0, add authentication, and display user profile information in a Single-Page Application (SPA) that uses Cap’n Web RPC, using the Auth0 SPA SDK.
This Quickstart is currently in Beta. We’d love to hear your feedback!
Use AI to integrate Auth0
If you use an AI coding assistant like Claude Code, Cursor, or GitHub Copilot, you can add Auth0 authentication automatically in minutes using agent skills.Install:
Report incorrect code
Copy
Ask AI
npx skills add auth0/agent-skills
Then ask your AI assistant:
Report incorrect code
Copy
Ask AI
Add Auth0 authentication to my Cap'n Web app
Your AI assistant will automatically create your Auth0 application, fetch credentials, install required packages, create secure WebSocket RPC authentication, and set up your configuration. Full agent skills documentation →
This quickstart demonstrates how to add Auth0 authentication to a Cap’n Web application. You’ll build a modern RPC-based web application with secure login functionality using Cap’n Web’s JavaScript framework and the Auth0 SPA SDK.
1
Create a new project
Create a new Cap’n Web project and set up the basic structure:
Report incorrect code
Copy
Ask AI
mkdir capnweb-auth0-app && cd capnweb-auth0-app
Initialize the project and configure it for ES modules:
@auth0/auth0-spa-js is used on the client side to handle user authentication, login flows, and token management in the browser.@auth0/auth0-api-js is used on the server side to verify access tokens and validate JWT signatures using Auth0’s JWKS.
Set up the start script in package.json:
Report incorrect code
Copy
Ask AI
npm pkg set scripts.start="node server/index.js"
3
Setup your Auth0 App
Next up, you need to create a new app on your Auth0 tenant and add the environment variables to your project.You can choose to do this automatically by running a CLI command or do it manually via the Dashboard:
CLI
Dashboard
Run the following shell command on your project’s root directory to create an Auth0 app and generate a .env file:
Before you start, create a .env file on your project’s root directory
Click on Applications > Applications > Create Application
In the popup, enter a name for your app, select Single Page Web Application as the app type and click Create
Switch to the Settings tab on the Application Details page
Replace YOUR_AUTH0_APP_DOMAIN and YOUR_AUTH0_APP_CLIENT_ID on the .env file with the Domain and Client ID values from the dashboard
Finally, on the Settings tab of your Application Details page, configure the following URLs:Allowed Callback URLs:
Report incorrect code
Copy
Ask AI
http://localhost:3000
Allowed Logout URLs:
Report incorrect code
Copy
Ask AI
http://localhost:3000
Allowed Web Origins:
Report incorrect code
Copy
Ask AI
http://localhost:3000
Allowed Callback URLs are a critical security measure to ensure users are safely returned to your application after authentication. Without a matching URL, the login process will fail, and users will be blocked by an Auth0 error page instead of accessing your app.Allowed Logout URLs are essential for providing a seamless user experience upon signing out. Without a matching URL, users will not be redirected back to your application after logout and will instead be left on a generic Auth0 page.Allowed Web Origins is critical for silent authentication. Without it, users will be logged out when they refresh the page or return to your app later.
Required: Create an Auth0 APIYou must create an Auth0 API for token validation to work:
In the Auth0 Dashboard, go to APIs > Create API
Set these values:
Name: Cap'n Web API
Identifier: https://capnweb-api.yourproject.com (use your own unique identifier)
Signing Algorithm: RS256 (default)
Click Create
In the Scopes tab, add these scopes:
read:profile - Read user profile data
write:profile - Update user profile data
Update your .env file with the API identifier as AUTH0_AUDIENCE
Without creating an Auth0 API, you’ll get an “access_denied” error during login. The API identifier must match your AUTH0_AUDIENCE environment variable exactly.
5
Create the server
Create the Cap’n Web server with Auth0 integration:
server/index.js
Report incorrect code
Copy
Ask AI
import { RpcTarget, newWebSocketRpcSession } from 'capnweb';import { WebSocketServer } from 'ws';import { ApiClient } from '@auth0/auth0-api-js';import http from 'http';import { readFileSync } from 'fs';import { dirname, join } from 'path';import { fileURLToPath } from 'url';import dotenv from 'dotenv';dotenv.config();const __dirname = dirname(fileURLToPath(import.meta.url));const userProfiles = new Map();// Auth0 configurationconst AUTH0_DOMAIN = process.env.AUTH0_DOMAIN;const AUTH0_CLIENT_ID = process.env.AUTH0_CLIENT_ID;const AUTH0_AUDIENCE = process.env.AUTH0_AUDIENCE;const PORT = process.env.PORT || 3000;if (!AUTH0_DOMAIN || !AUTH0_CLIENT_ID || !AUTH0_AUDIENCE) { console.error('❌ Missing required Auth0 environment variables'); if (!AUTH0_DOMAIN) console.error(' - AUTH0_DOMAIN is required'); if (!AUTH0_CLIENT_ID) console.error(' - AUTH0_CLIENT_ID is required'); if (!AUTH0_AUDIENCE) console.error(' - AUTH0_AUDIENCE is required'); process.exit(1);}// Initialize Auth0 API client for token verificationconst auth0ApiClient = new ApiClient({ domain: AUTH0_DOMAIN, audience: AUTH0_AUDIENCE});async function verifyToken(token) { try { const payload = await auth0ApiClient.verifyAccessToken({ accessToken: token }); return payload; } catch (error) { throw new Error(`Token verification failed: ${error.message}`); }}// Profile Service - Cap'n Web RPC Targetclass ProfileService extends RpcTarget { async getProfile(accessToken) { const decoded = await verifyToken(accessToken); const userId = decoded.sub; const profile = userProfiles.get(userId) || { bio: '' }; return { id: userId, email: decoded.email || 'Unknown User', bio: profile.bio }; } async updateProfile(accessToken, bio) { const decoded = await verifyToken(accessToken); const userId = decoded.sub; userProfiles.set(userId, { bio }); return { success: true, message: 'Profile updated successfully' }; }}// Create HTTP serverconst server = http.createServer(async (req, res) => { res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); if (req.method === 'OPTIONS') { res.writeHead(200); res.end(); return; } if (req.url === '/api/config') { const config = { auth0: { domain: AUTH0_DOMAIN, clientId: AUTH0_CLIENT_ID, audience: AUTH0_AUDIENCE } }; res.setHeader('Content-Type', 'application/json'); res.writeHead(200); res.end(JSON.stringify(config)); return; } // Handle root path and Auth0 callback if (req.url === '/' || req.url === '/index.html' || req.url.startsWith('/?code=') || req.url.startsWith('/?error=')) { const html = readFileSync(join(__dirname, '../client/index.html'), 'utf8'); res.setHeader('Content-Type', 'text/html'); res.writeHead(200); res.end(html); return; } if (req.url === '/app.js') { const js = readFileSync(join(__dirname, '../client/app.js'), 'utf8'); res.setHeader('Content-Type', 'application/javascript'); res.writeHead(200); res.end(js); return; } // Serve Auth0 SPA JS SDK from node_modules if (req.url === '/@auth0/auth0-spa-js') { const modulePath = join(__dirname, '../node_modules/@auth0/auth0-spa-js/dist/auth0-spa-js.production.esm.js'); const js = readFileSync(modulePath, 'utf8'); res.setHeader('Content-Type', 'application/javascript'); res.writeHead(200); res.end(js); return; } // Serve capnweb from node_modules if (req.url === '/capnweb') { const modulePath = join(__dirname, '../node_modules/capnweb/dist/index.js'); const js = readFileSync(modulePath, 'utf8'); res.setHeader('Content-Type', 'application/javascript'); res.writeHead(200); res.end(js); return; } res.writeHead(404); res.end('Not found');});// WebSocket server for Cap'n Web RPCconst wss = new WebSocketServer({ server });wss.on('connection', (ws, req) => { // Only handle RPC connections on /api path if (req.url === '/api') { console.log('🔗 New Cap\'n Web RPC connection'); // Create a new ProfileService instance for this connection const profileService = new ProfileService(); // Use capnweb's newWebSocketRpcSession to handle the connection newWebSocketRpcSession(ws, profileService); }});// Start serverserver.listen(PORT, () => { console.log(`🚀 Cap'n Web Auth0 Server Started`); console.log(`📍 Server running on http://localhost:${PORT}`); console.log(`🔐 Auth0 Domain: ${AUTH0_DOMAIN}`); console.log(`🆔 Client ID: ${AUTH0_CLIENT_ID.substring(0, 8)}...`); console.log(`🎯 API Audience: ${AUTH0_AUDIENCE}`);});
6
Create the client interface
Create the frontend HTML and JavaScript files:
7
Run your app
Report incorrect code
Copy
Ask AI
npm run start
CheckpointYou should now have a fully functional Auth0 login page running on your localhost