Search

Search documentation

Tech debt

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}.json

This is fine for development and open-source usage, but for a private/internal company registry, authentication should be implemented.


Why Authentication?

ConcernDescription
Access ControlRestrict registry access to authorized team members only
Private ComponentsProtect proprietary component implementations
Usage TrackingMonitor who is installing components and when
License ComplianceEnsure only licensed users can access components

Implementation Options

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_here

Option 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

  1. Generate secure tokens for each team/user
  2. Store tokens in environment variables on Vercel
  3. 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.json for rewrites
  • Generate tokens for authorized users
  • Update consumer documentation
  • Test authentication flow
  • Add rate limiting (optional)
  • Add access logging (optional)

Official Documentation


Priority

AspectRating
UrgencyLow (public is fine for now)
EffortMedium (2-4 hours)
ImpactHigh (required for production internal use)

This should be implemented before deploying to production for internal/private use cases.

On this page