5.3 KiB
id, name, status, depends_on, scope, risk, impact, level
| id | name | status | depends_on | scope | risk | impact | level | |
|---|---|---|---|---|---|---|---|---|
| credential-provider-trait | Define CredentialProvider trait, CredentialSet enum, and ConfigCredentialProvider implementation | completed |
|
narrow | low | component | implementation |
Description
Define the CredentialProvider trait and CredentialSet enum in alknet_core::credentials, implementing the outbound authentication abstraction that complements the inbound IdentityProvider. This is the "Phase A" / "Phase 2.4a" work — the trait and enum must exist in core before alknet-secret (Phase 3) can wire SecretStoreCredentialProvider against them.
Per ADR-036 and research/phase2/credential-provider.md:
CredentialProviderresolves outbound credentials: "how does alknet authenticate TO external services?"CredentialSetis a structured enum of credential types:ApiKey,Basic,Bearer,S3AccessKey,OidcToken,CustomConfigCredentialProviderreads API keys and static credentials fromDynamicConfig— the Phase 2 default (simple, no secret service dependency)SecretStoreCredentialProvideris a stub that returnsNonefor all lookups until Phase 3 provides the alknet-secret dependency- Wire
CredentialProviderintoOperationEnv/OperationContextso handlers can access credentials
Relationship to IdentityProvider: These are opposite-direction abstractions. IdentityProvider resolves inbound auth (who is calling alknet). CredentialProvider resolves outbound auth (how alknet calls others). Both live at the same architectural layer.
Relationship to OperationEnv: Handlers compose through context.env. The OperationEnv needs access to CredentialProvider so that handlers calling external services can resolve credentials. This could be a dedicated field on OperationContext or accessible through the env — the implementation detail is flexible, but the behavioral contract must match: given a service name, return credentials for that service.
Acceptance Criteria
CredentialProvidertrait defined incrates/alknet-core/src/credentials/mod.rswithget_credentials(&self, service: &str) -> Option<CredentialSet>andrefresh_credentials(&self, service: &str) -> Option<CredentialSet>CredentialSetenum defined with variants:ApiKey { header_name, token },Basic { username, password },Bearer { token },S3AccessKey { access_key, secret_key, session_token },OidcToken { access_token, refresh_token, expires_at },Custom { scheme, params }ConfigCredentialProviderstruct implemented — reads credentials fromDynamicConfig.auth(or a newDynamicConfig.credentialssection). For Phase 2, this is a simple config-backed lookup returningCredentialSet::BearerorCredentialSet::ApiKeyentries.SecretStoreCredentialProviderstruct defined as a stub —get_credentials()always returnsNone. Full implementation deferred to Phase 3.CredentialProviderwired intoOperationContextorOperationEnvso handlers can access outbound credentialscredentialsmodule re-exported fromcrates/alknet-core/src/lib.rs- Unit test:
ConfigCredentialProviderreturns configured credentials for a service name - Unit test:
ConfigCredentialProviderreturnsNonefor unknown service names - Unit test:
SecretStoreCredentialProviderstub returnsNonefor all service names - Unit test:
OperationEnv/OperationContextprovides access toCredentialProviderfrom handler context CredentialSetderivesClone,Debug,serde::Serialize,serde::Deserialize
References
- docs/architecture/decisions/036-credentialprovider-core-type.md — ADR-036
- docs/research/phase2/credential-provider.md — Full design rationale
- docs/research/integration-plan.md — Phase 2.4
- crates/alknet-core/src/auth/identity.rs — IdentityProvider (opposite direction, same pattern)
- crates/alknet-core/src/call/env.rs — OperationEnv
- crates/alknet-core/src/call/context.rs — OperationContext
Notes
This task is "2.4a" — the core types and config-backed implementation. "2.4b" (SecretStoreCredentialProvider backed by SecretProtocol::Decrypt) is deferred to Phase 3 when alknet-secret exists.
For
ConfigCredentialProvider, consider whether to add a[[credentials]]section toDynamicConfigor to reuse a subsection. The simplest Phase 2 approach is a newcredentials: HashMap<String, CredentialSet>field onDynamicConfigthat stores static bearer tokens/API keys from config.
The
OperationEnv/OperationContextwiring can follow either pattern: (a) acredential_provider: Arc<dyn CredentialProvider>field onOperationContext, or (b)CredentialProvideraccessible through a registry-styleenv.credentials(service)method. The integration plan says "wire into OperationEnv so handlers can access credentials through context.env" — approach (b) aligns with the OperationEnv composition model. This is an implementation detail to resolve during implementation.
Summary
Added CredentialProvider trait, CredentialSet enum (ApiKey/Basic/Bearer/S3AccessKey/OidcToken/Custom), ConfigCredentialProvider (reads from DynamicConfig.credentials), SecretStoreCredentialProvider stub (returns None, deferred to Phase 3), wired into OperationEnv via env.credentials(service) method, and added credentials HashMap to DynamicConfig.