Registry Authentication
Secure your private component registry with token-based authentication.
Tech Debt Item - This feature is not yet implemented. The registry is currently public.
Current State
The Evolphin UI registry is currently public and accessible to anyone at:
https://evolphin-ui.vercel.app/r/{component}.jsonThis is fine for development and open-source usage, but for a private/internal company registry, authentication should be implemented.
Why Authentication?
| Concern | Description |
|---|---|
| Access Control | Restrict registry access to authorized team members only |
| Private Components | Protect proprietary component implementations |
| Usage Tracking | Monitor who is installing components and when |
| License Compliance | Ensure only licensed users can access components |
Implementation Options
Option 1: Token-Based Authentication (Recommended)
The shadcn CLI supports Bearer token authentication via the headers field:
Consumer's components.json:
{
"registries": {
"@evolphin": {
"url": "https://evolphin-ui.vercel.app/r/{name}.json",
"headers": {
"Authorization": "Bearer ${REGISTRY_TOKEN}"
}
}
}
}Consumer's .env:
REGISTRY_TOKEN=your_secret_token_hereOption 2: API Key Authentication
For more complex setups with workspace isolation:
{
"registries": {
"@evolphin": {
"url": "https://evolphin-ui.vercel.app/r/{name}.json",
"headers": {
"X-API-Key": "${API_KEY}",
"X-Workspace-Id": "${WORKSPACE_ID}"
}
}
}
}Option 3: Query Parameter Authentication
Simpler setup, but less secure (token visible in logs):
{
"registries": {
"@evolphin": {
"url": "https://evolphin-ui.vercel.app/r/{name}.json",
"params": {
"token": "${ACCESS_TOKEN}"
}
}
}
}This produces URLs like: https://evolphin-ui.vercel.app/r/button.json?token=xxx
Server-Side Implementation Steps
Step 1: Create API Routes for Registry
Currently, registry files are served statically from public/r/. To add authentication, we need to serve them via API routes.
Create src/app/api/r/[name]/route.ts:
import { NextRequest, NextResponse } from "next/server";
import fs from "fs";
import path from "path";
const VALID_TOKENS = [
process.env.REGISTRY_TOKEN_1,
process.env.REGISTRY_TOKEN_2,
];
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ name: string }> }
) {
const { name } = await params;
// Check authorization
const authHeader = request.headers.get("authorization");
const token = authHeader?.replace("Bearer ", "");
if (!token || !VALID_TOKENS.includes(token)) {
return NextResponse.json(
{ error: "Unauthorized" },
{ status: 401 }
);
}
// Serve the registry file
const filePath = path.join(process.cwd(), "public", "r", `${name}.json`);
if (!fs.existsSync(filePath)) {
return NextResponse.json(
{ error: "Component not found" },
{ status: 404 }
);
}
const content = fs.readFileSync(filePath, "utf-8");
return NextResponse.json(JSON.parse(content));
}Step 2: Update Vercel Configuration
Remove CORS from static files and route through API:
{
"rewrites": [
{
"source": "/r/:name.json",
"destination": "/api/r/:name"
}
]
}Step 3: Generate and Distribute Tokens
- Generate secure tokens for each team/user
- Store tokens in environment variables on Vercel
- Distribute tokens to authorized consumers
Step 4: Add Rate Limiting (Optional)
Use Vercel Edge Middleware or a service like Upstash Redis for rate limiting.
Checklist
- Decide on authentication method (Bearer token recommended)
- Create API route for registry serving
- Add token validation logic
- Set up environment variables on Vercel
- Update
vercel.jsonfor rewrites - Generate tokens for authorized users
- Update consumer documentation
- Test authentication flow
- Add rate limiting (optional)
- Add access logging (optional)
Official Documentation
shadcn Authentication Docs
Official guide for securing your registry
Vercel Edge Middleware
Add auth at the edge for better performance
Priority
| Aspect | Rating |
|---|---|
| Urgency | Low (public is fine for now) |
| Effort | Medium (2-4 hours) |
| Impact | High (required for production internal use) |
This should be implemented before deploying to production for internal/private use cases.