Apps Domain Convention and Central SSO
This document defines how portfolio-hosted apps and APIs live under the jcvolpe.me
umbrella while using a single, central authentication system on jcvolpe.me.
Domains and URL convention
- Main site + auth:
https://jcvolpe.me(NextAuth/Auth.js) - App UI:
https://<app-name>.jcvolpe.me - API:
https://<app-name>.jcvolpe.me/api
Example for YT Channel Expert:
- UI:
https://yt-channel-expert.jcvolpe.me - API:
https://yt-channel-expert.jcvolpe.me/api
Auth model (forced sign-in)
Apps do not host their own auth providers. They must use the central auth on
jcvolpe.me and gate access until a session exists.
Preferred flow: short-lived JWT exchange for APIs.
- UI checks session on
jcvolpe.me:GET https://jcvolpe.me/api/auth/session- If not logged in, redirect to:
https://jcvolpe.me/api/auth/signin?callbackUrl=<current URL>
- UI requests a short-lived API token from
jcvolpe.me:POST https://jcvolpe.me/api/apps/token- Body:
{ "app": "<app-name>" } - Response:
{ "token": "<jwt>" }
- UI calls the API with a bearer token:
Authorization: Bearer <token>
- API validates the JWT:
- Verify signature, issuer, audience, and expiry.
- Enforce the
appclaim matches the requested app.
This avoids cross-subdomain cookie sharing and keeps auth centralized.
API routing
APIs live on the same app subdomain and typically mount at /api inside the
app (no shared gateway or path rewrite required).
If you want a dedicated API hostname instead (for example,
https://api.yt-channel-expert.jcvolpe.me), deploy it independently and keep
the JWT aud value in sync with the value minted by the central auth.
Security baseline
- JWT TTL: 5-15 minutes; refresh before expiry.
- Token storage: in-memory only (avoid localStorage).
- CSP: strict
script-srcwith nonces; disableunsafe-inline. - CORS: allow only
https://<app-name>.jcvolpe.me(plus localhost for dev). - Rate-limit auth + API endpoints.
- Rotate signing keys and publish JWKS.
SEO and discoverability
Because the UI lives at https://<app-name>.jcvolpe.me, ensure:
- A prerendered or static
index.htmlwith full meta tags. <title>, description, Open Graph, and Twitter tags.- Canonical URL set to the app URL.
- A sitemap entry and robots indexing allowed.
Environment variables (apps)
PUBLIC_API_BASE=https://<app-name>.jcvolpe.me/apiPUBLIC_AUTH_BASE=https://jcvolpe.mePUBLIC_APP_ORIGIN=https://<app-name>.jcvolpe.me
Environment variables (api)
AUTH_JWT_ISSUER=https://jcvolpe.meAUTH_JWT_AUDIENCE=<app-name>.jcvolpe.me(must matchAPP_JWT_AUDIENCE)AUTH_JWT_JWKS_URL=https://jcvolpe.me/api/auth/jwks
Central auth endpoints (portfolio)
These routes live in the portfolio repo and power the token exchange:
POST /api/apps/tokenissues short-lived app JWTsGET /api/auth/jwksexposes the JWKS for API validation
Required configuration:
APP_JWT_PRIVATE_KEYandAPP_JWT_PUBLIC_KEY(RSA PEMs)APP_JWT_ALLOWED_APPSallowlist (comma-separated)APP_JWT_ALLOWED_ORIGINSCORS allowlist for/api/apps/tokenAPP_JWT_ISSUER(defaults toNEXTAUTH_URL)APP_JWT_AUDIENCE(set to the shared audience your APIs validate)APP_JWT_TTL_SECONDS(defaults to 600)APP_JWT_KEY_ID(optional)APP_JWT_ALG(defaults toRS256)
Extending to a new app
- DNS + certificates
- Create/validate DNS for
<app-name>.jcvolpe.me.
- Create/validate DNS for
- UI deploy
- Deploy UI under
https://<app-name>.jcvolpe.me. - Add a session gate (redirect to
jcvolpe.mesign-in if logged out).
- Deploy UI under
- API deploy
- Deploy service under
https://<app-name>.jcvolpe.me/api.
- Deploy service under
- Auth integration
- Add/verify
POST /api/apps/tokensupports the new app. - Add the app origin to
APP_JWT_ALLOWED_ORIGINS. - Keep
APP_JWT_AUDIENCEandAUTH_JWT_AUDIENCEaligned. - Configure JWT verification on the API.
- Add/verify
- CORS + CSP
- Allow only the app domain.
- SEO + sitemap
- Add metadata and sitemap entry for the app.
- Documentation
- Add an app-specific doc in its repo and link back here.
Related documentation
- Authentication - NextAuth.js setup
- Deployment Overview - Infra + deployment workflow
