Files
alknet/tasks/core/auth-apikey-resources.md
glm-5.2 d149932e2a tasks: decompose review #004 findings into 4 fix tasks + review gate
W1 (call/protocol/abort-cascade-wiring): wire AbortCascade into CallAdapter
handle_stream for EVENT_ABORTED. W2 (core/endpoint-client-fingerprint):
extract TLS client cert fingerprint in dispatch_quinn/dispatch_iroh.
W3 (vault/mnemonic-debug-redaction): replace Mnemonic derive(Debug) with
redacting impl. W4 (core/auth-apikey-resources, level: research): decide
whether ApiKeyEntry should carry resources, then implement or drop from
spec. review-post-impl-fixes gates on all four. Graph: 33 tasks, 12 gens.
2026-06-24 10:02:03 +00:00

5.7 KiB
Raw Blame History

id, name, status, depends_on, scope, risk, impact, level
id name status depends_on scope risk impact level
core/auth-apikey-resources Reconcile ApiKeyEntry.resources — add field to type and populate in resolve_api_key, or drop from spec pending
narrow low component research

Description

Three-way mismatch between spec, type, and implementation for resource-scoped ACLs on API-key-authenticated identities:

  • Spec (docs/architecture/crates/core/auth.md:153):

    "Token: ... return Identity { id: prefix, scopes: entry.scopes, resources: entry.resources }." The spec references entry.resources.

  • Type (crates/alknet-core/src/config.rs:5562): ApiKeyEntry has fields prefix, hash, scopes, description, expires_at — there is no resources field. So entry.resources in the spec cannot be implemented as written.

  • Implementation (config.rs:113117): resolve_api_key constructs the resolved Identity with resources: std::collections::HashMap::new() — resources are always empty, regardless of what the API key grants.

The same gap exists in resolve_identity_from_fingerprint (config.rs:6979), which also returns resources: HashMap::new().

Impact

Latent today: no operation in the workspace uses resource-based ACLs against a token- or fingerprint-resolved identity. The AccessControl::resource_type / resource_action fields exist in OperationSpec (spec.rs:3237) and are tested (spec.rs:284303), but those tests always hand-construct Identity.resources directly — never via the resolver path. The moment an operation declares a resource-scoped ACL and a caller authenticates via API key, the ACL check will fail with "missing resource" even if the key was granted that resource in config — because resources is always empty.

This is a research/decision task, not an implementation task

The decomposer rule applies: the architecture is ambiguous on whether API keys should grant resource-scoped access. Two valid designs exist; pick one and document it before implementing. Do not implement until the decision is made.

Option A — add resources to ApiKeyEntry (matches current spec):

  • Add pub resources: HashMap<String, Vec<String>> to ApiKeyEntry.
  • Update resolve_api_key to populate Identity.resources from entry.resources.
  • Update resolve_identity_from_fingerprint similarly — either add a resources field to the fingerprint config path, or document that fingerprint auth grants scopes only (resources empty).
  • Update auth.md's token resolution example to match the new field.
  • Define the TOML schema for resources in AuthPolicy (when a TOML schema is added — currently config is built in code, not parsed).
  • Resource-scoped ACLs then work for both auth paths.

Option B — drop resources from the spec for API keys:

  • Remove entry.resources from auth.md:153.
  • Document that API keys grant scopes only; resource-scoped access requires a different identity source (e.g., a future OAuth/JWT provider that carries resource claims).
  • Identity.resources stays in the type (it's used by hand-constructed identities in tests and by CompositionAuthority::as_identity for internal calls) but token/fingerprint resolvers always return empty.
  • Resource-scoped ACLs against token identities return Forbidden — this becomes a documented limitation, not a bug.

Deliverable

Produce a short decision note (a paragraph in auth.md under "Identity Resolution" — or a new ADR if the decision feels consequential enough) that picks A or B and justifies it. Then either implement the chosen option in the same task (if small) or split a follow-up level: implementation task gated on this one.

The decision should consider: do any planned operations (in the upcoming alknet-ssh, alknet-fs, alknet-git crates) need resource-scoped ACLs on API-key identities? If yes, A. If resource ACLs are only ever applied to handler-internal composition identities (CompositionAuthority), B is fine and simpler.

Acceptance Criteria

  • Decision made: Option A or Option B
  • Decision documented in auth.md (or a new ADR if consequential)
  • If Option A: ApiKeyEntry.resources added, resolve_api_key populates Identity.resources, resolve_identity_from_fingerprint handling decided and documented, auth.md:153 matches the new shape
  • If Option B: auth.md:153 corrected to drop entry.resources, limitation documented
  • Either way: a test covering the chosen behavior (token resolves with resources, or token resolves with empty resources + documented limitation)
  • cargo test -p alknet-core succeeds
  • cargo clippy -p alknet-core --all-targets succeeds with no warnings

References

  • docs/reviews/004-post-implementation-sanity-check.md — W4 (full finding)
  • docs/architecture/crates/core/auth.md:152153 — spec text referencing entry.resources
  • crates/alknet-core/src/config.rs:5562 — ApiKeyEntry (missing resources)
  • crates/alknet-core/src/config.rs:69118 — both resolvers returning empty resources
  • crates/alknet-call/src/registry/spec.rs:77103 — AccessControl::check resource path (the consumer that would fail)
  • crates/alknet-call/src/registry/context.rs:5865 — CompositionAuthority::as_identity (the internal-call path that does populate resources)

Notes

This is a level: research task because the fix is small but the decision is not. The decomposer principle: if architecture is ambiguous, do not proceed with implementation — escalate. Make the decision first, then implement. If the decision is A and the implementation is more than ~30 lines, split a follow-up level: implementation task (core/auth-apikey-resources-impl) depending on this one.