Capítulo 102 de 108
Patterns for securing a private/personalized registry: token schemes on the client (components.json), matching server-side validation, and security best practices (env vars, HTTPS, rate limiting, custom error messages).
headers: { "Authorization": "Bearer ${REGISTRY_TOKEN}" } in components.json, value pulled from .env.local.headers: { "X-API-Key": "${API_KEY}" }, optionally with extra headers like X-Workspace-Id.params: { "token": "${ACCESS_TOKEN}" } appended to the item URL — simplest scheme, weakest (token in URL/logs).${VAR_NAME} expansion: Environment variables are expanded from process.env at request time in URLs, headers and params; never logged by the CLI.{ "error": "...", "message": "..." } in the response body on 401/403; the CLI surfaces message directly to the user.{
"registries": {
"@private": {
"url": "https://registry.company.com/{name}.json",
"headers": { "Authorization": "Bearer ${REGISTRY_TOKEN}" }
}
}
}
export async function GET(request: NextRequest, { params }: { params: { name: string } }) {
const authHeader = request.headers.get("authorization")
const token = authHeader?.replace("Bearer ", "")
if (!isValidToken(token)) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 })
}
if (!hasAccessToComponent(token, params.name)) {
return NextResponse.json({ error: "Forbidden" }, { status: 403 })
}
const component = await getComponent(params.name)
return NextResponse.json(component)
}
| Auth method | components.json field | Notes |
|---|---|---|
| Bearer token | headers.Authorization | most common |
| API key | headers["X-API-Key"] | can combine with workspace/tenant header |
| Query param | params.token | simplest, least secure |
| Basic auth | headers.Authorization: "Basic ${BASE64_CREDENTIALS}" | credentials pre-encoded |
| CLI error | Meaning |
|---|---|
| 401 Unauthorized | token invalid or missing |
| 403 Forbidden | token valid but lacks permission |
| 429 Too Many Requests | rate limit exceeded |
.env.local + ${VAR} expansion, never literal secrets in components.json.https://.express-rate-limit.expiresAt check server-side.curl -H "Authorization: Bearer <token>" <url> and REGISTRY_TOKEN=<token> npx shadcn@latest add @private/button.userId, component, ip, userAgent, timestamp) for security/analytics on any private registry.message fields in 401/403 responses meaningfully improve the CLI user experience (e.g. "subscription expired, renew at...").@public, @internal, @premium) side by side in one components.json.registries object is where these auth headers/params live.