App Marketplace - Plug & Play Apps with Optare ID 🔌
🎯 Overview
The App Marketplace allows you to offer your apps (Customer Service, Bot, CRM, etc.) as plug-and-play modules that tenants can instantly activate with Optare ID authentication.
🏗️ Architecture
┌─────────────────────────────────────────────────────────────┐
│ OPTARE ID (Control Plane) │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ App Marketplace │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │Customer │ │ Bot │ │ CRM │ │Analytics│ │ │
│ │ │Service │ │ │ │ │ │ │ │ │
│ │ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │ │
│ └───────┼──────────┼──────────┼──────────┼───────────┘ │
│ │ │ │ │ │
│ └──────────┴──────────┴──────────┘ │
│ │ │
│ ┌──────────┴──────────┐ │
│ │ OAuth / SSO │ │
│ │ (Auto-generated) │ │
│ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────┘🚀 How It Works
For Tenants (End Users)
1. Go to Portal → Apps
2. Browse available apps
3. Click "Install" on desired app
4. App is instantly available with:
✅ SSO already configured
✅ Users already synced
✅ Organization data connected
5. Click "Open App" → Seamless login via Optare IDFor Your Apps (Technical)
1. App is registered in the marketplace
2. When tenant installs:
- OAuth client auto-created
- Credentials sent to app via webhook
- All org members get access
3. User clicks "Open App":
- Redirected to OAuth authorize
- Auto-logged in via SSO
- Lands in app with full context📁 Files Created
Database Schema
libs/database/src/schema-apps.tsavailable_app- App cataloginstalled_app- Per-org installationsapp_permission- User access control
Services
libs/apps/src/app-service.tsgetAvailableApps()- List marketplaceinstallApp()- Install for orguninstallApp()- Remove appcheckUserAccess()- Permission checkgetAppSSOUrl()- Generate SSO link
API Routes
app/routes/api.apps.tsGET /api/apps- List available appsGET /api/apps?action=installed- List installedGET /api/apps?action=sso&slug=xxx- Get SSO URLPOST /api/apps- Install/uninstall
UI
app/routes/portal.apps._index.tsx- App marketplace page
- Install/uninstall buttons
- Open app with SSO
Scripts
scripts/seed-marketplace-apps.ts- Seeds your apps into marketplace
🔧 Setup Instructions
Step 1: Run Database Migration
# Create the new tables
pnpm drizzle-kit pushStep 2: Seed Your Apps
# Add your apps to the marketplace
pnpm tsx scripts/seed-marketplace-apps.tsStep 3: Configure App URLs
Add to your .env:
# Customer Service App
CUSTOMER_SERVICE_APP_URL=https://support.optare.one
CUSTOMER_SERVICE_CALLBACK_URL=https://support.optare.one/auth/callback
CUSTOMER_SERVICE_WEBHOOK_URL=https://support.optare.one/api/webhooks/optare
# Bot App
BOT_APP_URL=https://bot.optare.one
BOT_CALLBACK_URL=https://bot.optare.one/auth/callback
BOT_WEBHOOK_URL=https://bot.optare.one/api/webhooks/optare
# CRM App
CRM_APP_URL=https://crm.optare.one
CRM_CALLBACK_URL=https://crm.optare.one/auth/callback
CRM_WEBHOOK_URL=https://crm.optare.one/api/webhooks/optareStep 4: Configure Your Apps to Accept SSO
Each app needs to:
- Handle OAuth Callback
// /auth/callback route in your app
app.get('/auth/callback', async (req, res) => {
const { code, state } = req.query;
// Exchange code for tokens
const tokens = await exchangeCodeForTokens(code);
// Get user info
const userInfo = await getUserInfo(tokens.access_token);
// Create session in your app
await createSession(userInfo);
// Redirect to app
res.redirect(state || '/');
});- Handle Installation Webhook
// /api/webhooks/optare route
app.post('/api/webhooks/optare', async (req, res) => {
const { event, organizationId, clientId, clientSecret } = req.body;
if (event === 'app.installed') {
// Store credentials for this org
await storeOAuthCredentials(organizationId, clientId, clientSecret);
}
res.json({ received: true });
});🔐 Security
OAuth Credentials
- Auto-generated per installation
- Stored encrypted in database
- Sent to app once via webhook
- Never exposed to end users
User Access
- All org members get access by default
- Admins can revoke individual access
- Permissions stored per-user per-app
SSO Flow
- Standard OAuth 2.0 Authorization Code
- PKCE supported
- Tokens scoped to organization
📊 Database Schema
available_app (Marketplace Catalog)
CREATE TABLE available_app (
id TEXT PRIMARY KEY,
slug TEXT UNIQUE NOT NULL, -- "customer-service"
name TEXT NOT NULL, -- "Customer Service"
description TEXT,
app_url TEXT NOT NULL, -- Base URL
callback_url TEXT, -- OAuth callback
webhook_url TEXT, -- For events
is_free BOOLEAN DEFAULT false,
monthly_price TEXT, -- "29.99"
required_scopes JSONB, -- ["openid", "profile"]
features JSONB, -- Feature list
is_first_party BOOLEAN DEFAULT true,
is_published BOOLEAN DEFAULT false,
created_at TIMESTAMP,
updated_at TIMESTAMP
);installed_app (Per-Org Installations)
CREATE TABLE installed_app (
id TEXT PRIMARY KEY,
organization_id TEXT REFERENCES organization(id),
app_id TEXT REFERENCES available_app(id),
status TEXT DEFAULT 'active', -- active, inactive, suspended
installed_by TEXT REFERENCES user(id),
installed_at TIMESTAMP,
config JSONB, -- App-specific settings
client_id TEXT, -- OAuth client ID
client_secret TEXT, -- Encrypted
subscription_id TEXT, -- If paid app
created_at TIMESTAMP,
updated_at TIMESTAMP
);app_permission (User Access)
CREATE TABLE app_permission (
id TEXT PRIMARY KEY,
installed_app_id TEXT REFERENCES installed_app(id),
user_id TEXT REFERENCES user(id),
can_access BOOLEAN DEFAULT true,
is_admin BOOLEAN DEFAULT false,
permissions JSONB, -- Custom permissions
granted_by TEXT REFERENCES user(id),
granted_at TIMESTAMP
);🎯 User Flow
Installing an App
Tenant Admin → Portal → Apps
↓
Sees "Customer Service" app
↓
Clicks "Install"
↓
System:
1. Creates OAuth client
2. Sends credentials to app webhook
3. Grants access to all org members
4. Shows success message
↓
Admin clicks "Open App"
↓
Redirected to OAuth authorize
↓
Auto-logged in (SSO)
↓
Lands in Customer Service appUsing an Installed App
User → Portal → Apps
↓
Sees installed apps
↓
Clicks "Open App"
↓
GET /api/apps?action=sso&slug=customer-service
↓
Returns SSO URL
↓
Redirect to OAuth authorize
↓
User already logged in → Auto-approve
↓
Redirect to app with auth code
↓
App exchanges code for tokens
↓
User is logged in!🔌 Adding a New App
1. Add to Seed Script
// scripts/seed-marketplace-apps.ts
{
id: nanoid(),
slug: 'my-new-app',
name: 'My New App',
description: 'Description here',
appUrl: 'https://myapp.optare.one',
callbackUrl: 'https://myapp.optare.one/auth/callback',
webhookUrl: 'https://myapp.optare.one/api/webhooks/optare',
isFree: false,
monthlyPrice: '19.99',
requiredScopes: ['openid', 'profile', 'email', 'organization'],
features: ['Feature 1', 'Feature 2'],
isFirstParty: true,
isPublished: true,
}2. Add Icon Mapping
// app/routes/portal.apps._index.tsx
const appIcons: Record<string, any> = {
// ...existing
"my-new-app": MyIcon,
};3. Configure App to Accept SSO
See "Configure Your Apps to Accept SSO" section above.
🎉 Benefits
For Tenants
- ✅ One-click installation
- ✅ No configuration needed
- ✅ Seamless SSO
- ✅ All users automatically have access
- ✅ Unified billing (future)
For You (Platform Owner)
- ✅ Ecosystem lock-in
- ✅ Cross-sell opportunities
- ✅ Unified user management
- ✅ Centralized billing
- ✅ Data mesh foundation
For Your Apps
- ✅ No auth implementation needed
- ✅ User data from Optare ID
- ✅ Organization context
- ✅ Automatic user provisioning
🚀 Next Steps
- Run migrations to create tables
- Seed apps into marketplace
- Configure app URLs in environment
- Update your apps to accept SSO
- Test the flow end-to-end
Your apps are now plug-and-play with Optare ID! 🎉