- Add definitions.md: normative terminology disambiguation (Interface, Service, Transport, Token, Identity, Domain, Scope, CredentialProvider, etc.) - Add credentials.md: CredentialProvider trait and CredentialSet enum for outbound auth, mirroring IdentityProvider pattern for inbound auth - Rewrite interface.md: StreamInterface/MessageInterface split (ADR-035), InterfaceRequest/InterfaceResponse, HttpInterface/DnsInterface stubs, ListenerConfig with Stream/Http/Dns variants, credential presentation table - Update auth.md: API keys in DynamicConfig (ADR-037), credential presentation per (Transport, Interface) pair, ApiKeyEntry struct in AuthPolicy - Update configuration.md: API keys, ListenerConfig with Http/Dns variants, expanded TOML config examples - Update call-protocol.md: resolve OQ-IF-01 (InterfaceEvent carries EventEnvelope + Identity), add MessageInterface awareness to protocol adapter layer - Update overview.md: three-layer model now includes StreamInterface/ MessageInterface, CredentialProvider/CredentialSet exports, definitions.md reference, ADRs 035-037 - Update open-questions.md: resolve OQ-IF-01, OQ-IF-02, add OQ-P2-01 through OQ-P2-04, add OQ-CP-01 through OQ-CP-04, add OQ-DEF-01, OQ-DEF-03, OQ-DEF-08 - Update README.md: add definitions.md, credentials.md, ADRs 035-037, phase2 research docs, current state description Key architectural decisions: - ADR-035: StreamInterface/MessageInterface split (two Layer 2 traits) - ADR-036: CredentialProvider as core type (outbound auth, alknet_core::credentials) - ADR-037: API keys as DynamicConfig auth (hash-verified bearer tokens)
83 lines
3.4 KiB
Markdown
83 lines
3.4 KiB
Markdown
# ADR-037: API Keys as DynamicConfig Auth
|
|
|
|
## Status
|
|
Accepted
|
|
|
|
## Context
|
|
|
|
Alknet's token auth uses Ed25519-signed `AuthToken`s — the same key material
|
|
used for SSH auth. This is appropriate for interactive clients (browsers, CLI)
|
|
that can generate and sign Ed25519 key pairs.
|
|
|
|
But for service accounts, automation, and simple integrations, Ed25519 key
|
|
pairs are inconvenient. A dashboard backend, a CI/CD pipeline, or a monitoring
|
|
script needs a simple bearer token that can be stored in an environment variable
|
|
or config file without managing cryptographic key pairs.
|
|
|
|
The HTTP interface (Phase 2+) requires bearer token auth for `Authorization:
|
|
Bearer <token>` headers. `AuthToken` works but requires client-side Ed25519
|
|
signing. API keys offer a simpler alternative: short bearer tokens verified by
|
|
SHA-256 hash lookup, with optional scope restrictions and TTL.
|
|
|
|
## Decision
|
|
|
|
Add `[[auth.api_keys]]` section to `DynamicConfig`:
|
|
|
|
```toml
|
|
[[auth.api_keys]]
|
|
prefix = "alk_"
|
|
hash = "sha256:abc..."
|
|
scopes = ["relay:connect", "secrets:derive"]
|
|
description = "dashboard service account"
|
|
ttl = "30d" # optional
|
|
```
|
|
|
|
`ConfigIdentityProvider::resolve_from_token()` handles both token types:
|
|
- If the input starts with the configured prefix (default `alk_`), treat it as
|
|
an API key: hash it with SHA-256 and look up the hash in the `api_keys` table.
|
|
- Otherwise, treat it as an `AuthToken`: decode, verify Ed25519 signature,
|
|
check timestamp, resolve from `authorized_keys`.
|
|
|
|
Both paths produce the same `Identity` result. In database-backed deployments,
|
|
both resolve to the same account UUID.
|
|
|
|
API keys are stored as SHA-256 hashes (like password hashing — the cleartext
|
|
key is never stored, only its hash). The prefix enables O(1) routing between
|
|
AuthToken and API key verification without trying both paths.
|
|
|
|
The full key is provided to the client exactly once (at creation time). Subsequent
|
|
verifications only compare hashes.
|
|
|
|
## Consequences
|
|
|
|
**Positive**: Simple bearer token auth for HTTP and other non-SSH interfaces.
|
|
No cryptographic key management for service accounts. Consistent with industry
|
|
practice (Stripe, GitHub, AWS all use prefixed API keys).
|
|
|
|
**Positive**: Both AuthTokens and API keys go through `resolve_from_token()`.
|
|
The caller doesn't need to know which type they're using. This keeps the
|
|
authentication layer unified.
|
|
|
|
**Positive**: Scoped API keys enable fine-grained access control for service
|
|
accounts. A monitoring tool gets `["monitoring:read"]`, not full access.
|
|
|
|
**Negative**: API keys are bearer tokens — anyone who obtains the key has the
|
|
associated permissions. The hash storage and optional TTL mitigate but do not
|
|
eliminate this risk. Ed25519 AuthTokens remain the preferred auth method for
|
|
interactive clients.
|
|
|
|
**Negative**: API key rotation requires updating `DynamicConfig` (or the
|
|
`api_keys` database table). The `ConfigReloadHandle` / `ConfigService` reload
|
|
mechanism handles this, but it's a deliberate operation, not automatic.
|
|
|
|
**Negative**: No rate limiting on API key verification is built into this ADR.
|
|
Rate limiting on the HTTP interface is a separate concern.
|
|
|
|
## References
|
|
|
|
- ADR-023 (unified auth, shared key material)
|
|
- ADR-029 (Identity as core type)
|
|
- ADR-030 (static/dynamic config split)
|
|
- [auth.md](../auth.md) — Token auth, AuthPolicy, API keys
|
|
- [configuration.md](../configuration.md) — DynamicConfig, AuthPolicy
|
|
- [research/phase2/interface-model.md](../../research/phase2/interface-model.md) — API keys in config |