@@ -0,0 +1,56 @@
|
||||
# Auth model
|
||||
|
||||
How sherlock obtains and distributes credentials.
|
||||
|
||||
## The two layers
|
||||
|
||||
1. **Operator → Authentik.** A single OAuth 2.1 authorization-code + PKCE flow against Authentik (`id.alexandru.macocian.me`). The broker owns the resulting ID/access/refresh tokens. This is the only browser flow per workstation per session.
|
||||
2. **Broker → downstream service.** For each per-service request from an MCP, the broker mints a service-scoped token using one of the three grant kinds below.
|
||||
|
||||
## Grant kinds
|
||||
|
||||
Set per service in `~/.config/sherlock/services.d/<name>.toml` (see [service-registry.md](service-registry.md)).
|
||||
|
||||
### `oidc-federated`
|
||||
|
||||
The downstream service already federates against Authentik (Gitea, Grafana, Caddy-protected apps). The broker either:
|
||||
- Hands the existing Authentik-issued ID token (when the service's expected audience matches), or
|
||||
- Performs **RFC 8693 token exchange** against Authentik's token endpoint, swapping the operator's ID token for a service-scoped access token.
|
||||
|
||||
The MCP receives the token in an env var (e.g. `GITEA_TOKEN`).
|
||||
|
||||
### `own-oauth`
|
||||
|
||||
The service runs its own OAuth/OIDC stack, not federated through Authentik (think GitHub.com, a SaaS API, …). The broker:
|
||||
- Holds a per-(operator, service) OAuth client registration.
|
||||
- Runs the auth-code + PKCE flow in a browser the first time, with the callback received on the broker's loopback listener.
|
||||
- Caches the refresh token alongside the Authentik creds.
|
||||
- Refreshes silently after that.
|
||||
|
||||
Triggered explicitly via `oauth.consent(service)` from `sherlock-mcp` (Phase 4) or via `sherlock login --service <name>`.
|
||||
|
||||
### `static-pat`
|
||||
|
||||
For services that have no OAuth at all, or where the only sane option is a long-lived PAT. The broker reads the PAT from an `age`-encrypted blob and injects it. Avoid where possible; document why every time it's used.
|
||||
|
||||
## Token storage
|
||||
|
||||
- Authentik creds + per-service tokens persisted under `~/.config/sherlock/` (encrypted at rest — exact mechanism decided at the start of Phase 1; `age`-encrypted blob is the leading candidate, `libsecret`/OS keyring under evaluation).
|
||||
- In-memory cache mirrors the on-disk store; the on-disk write is debounced and atomic.
|
||||
- Refresh is single-flight: a 401 storm across N MCPs collapses to one refresh.
|
||||
|
||||
## Audience binding
|
||||
|
||||
Per the MCP 2025-06-18 spec, downstream tokens MUST be audience-bound. The broker never hands an MCP a token whose `aud` claim doesn't name that service. This is the "confused deputy" mitigation the spec calls out.
|
||||
|
||||
## Scope minimisation
|
||||
|
||||
Default service registrations request read-only scopes. Write scopes require either:
|
||||
- The operator passing `--write` to `sherlock run` (Phase 5), or
|
||||
- An explicit `scopes` override in the service's TOML.
|
||||
|
||||
## Out of scope for sherlock
|
||||
|
||||
- Issuing SSH certs (gssh handles this — [gssh-integration.md](gssh-integration.md)).
|
||||
- Multi-user / shared tenancy. Sherlock is per-operator, full stop.
|
||||
- Acting as a Resource Server itself (Phase 4's `sherlock-mcp` is a stdio MCP, not HTTP — the agent CLI talks to it over a pipe, no inbound auth).
|
||||
Reference in New Issue
Block a user