Skip to content

feat: per-user access keys with individual revocation#5

Open
znaniye wants to merge 1 commit into
VictorMinemu:mainfrom
znaniye:feat/per-user-access-keys
Open

feat: per-user access keys with individual revocation#5
znaniye wants to merge 1 commit into
VictorMinemu:mainfrom
znaniye:feat/per-user-access-keys

Conversation

@znaniye

@znaniye znaniye commented Jul 8, 2026

Copy link
Copy Markdown

Add authorizedKeys: a named allowlist of static access keys, one per person (sk-proxy-… tokens), accepted alongside the shared proxySecret. Each key is granted and revoked individually via cc-router keys add/list/revoke. Since Claude Code only sends a static ANTHROPIC_AUTH_TOKEN (it can't present a client certificate), per-user keys authorize specific people without an nginx/mTLS layer in front.

Auth is a structured, testable outcome so connection failures are diagnosable from the server log without ever leaking the secret. Each rejection says exactly what happened — no credentials, malformed header, unknown token, or a correct-but-disabled key — with the carrying header, token length, and a short sha256 fingerprint that correlates with the fingerprint shown by cc-router keys list. Accepted requests log once per key and are attributed to their owner in the dashboard.

What this PR does

Type of change

  • Bug fix
  • New feature
  • Refactor
  • Documentation

Testing

  • npm run build passes
  • npm test passes
  • Tested manually on:

Related issues

Add authorizedKeys: a named allowlist of static access keys, one per
person (sk-proxy-… tokens), accepted alongside the legacy shared
proxySecret. Each key is granted and revoked individually via
`cc-router keys add/list/revoke`. Since Claude Code only sends a static
ANTHROPIC_AUTH_TOKEN (it can't present a client certificate), per-user
keys authorize specific people without an nginx/mTLS layer in front.

Auth is a structured, testable outcome so connection failures are
diagnosable from the server log without ever leaking the secret. Each
rejection says exactly what happened — no credentials, malformed header,
unknown token, or a correct-but-disabled key — with the carrying header,
token length, and a short sha256 fingerprint that correlates with the
fingerprint shown by `cc-router keys list`. Accepted requests log once
per key and are attributed to their owner in the dashboard.

- config: AuthorizedKey type + add/list/revoke helpers, generateUserKey
- proxy/auth: buildCredentials + extractPresented + authenticate +
  describeAuthFailure + fingerprint (all pure, constant-time compare)
- proxy/server: multi-credential auth middleware, _authUser attribution,
  clientIpOf (X-Forwarded-For aware), accept/reject server logging
- proxy/logger: logAuthReject/logAuthAccept, [user] on logRoute
- stats: per-user request counters exposed as usageByUser on /health
- ui: [user] label on activity rows + per-key request tally
- cli: cc-router keys command group (list shows fingerprints)
- tests: 18 cases covering config round-trip, extraction, auth outcomes,
  fingerprinting, and secret-safe failure messages
- docs: README auth section, security note, CLI reference
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant