Skip to main content

Crate aep_service

Crate aep_service 

Source
Expand description

§aep-service

aep-service provides the framework-neutral Service implementation for the Agent Enrollment Protocol. It builds the Inspect document, verifies client assertions, enforces replay and idempotency rules, manages enrollment lifecycle state, dispatches Grant and Revoke handlers, and authenticates protected resources.

§Install

[dependencies]
aep-service = "0.2"

§Create a Service

use std::{sync::Arc, time::Duration};

use aep_service::{
    DidWebClientAssertionVerifier, ReqwestTransport, Service, ServiceOptions,
};

let transport = Arc::new(ReqwestTransport::new(
    1 << 20,
    Duration::from_secs(10),
)?);
let verifier = Arc::new(DidWebClientAssertionVerifier::new(transport, false));
let options = ServiceOptions::new("did:web:service.example", verifier);
let service = Service::new(options)?;

The default Service supports Inspect, Enroll, and Status with in-memory enrollment, assertion replay, and command-idempotency stores. Set ServiceOptions fields to advertise Claims, authentication methods, Grant Types, OpenAPI metadata, and extensions. Replace the memory stores with durable implementations before running more than one process or retaining state across restarts.

§Connect HTTP routes

Use aep-axum for direct Axum integration or aep-tower for a reusable Tower Service and authentication layer. Both adapters are optional.

When integrating another HTTP stack, expose service.inspect_document() at /.well-known/aep. Route the four authenticated commands to Service::enroll, Service::status, Service::grant, and Service::revoke. Each command returns a ServiceResponse containing the HTTP status, headers, and a typed ResponseBody; use ResponseBody::to_json() for the wire body.

The aep-service-axum example mounts every command and protects an application resource. The aep-local-lifecycle example shows the direct Service boundary without a web framework.

Enroll, Grant, and Revoke require a non-empty Idempotency-Key value through IdempotentCommandOptions. Status uses AuthenticatedCommandOptions. The application adapter is responsible for extracting the AEP client assertion and idempotency header from the incoming request without logging either value.

The memory command-idempotency store retains completed responses for one hour after execution completes. A failed operation releases its key for a retry; an exact retry of a completed operation returns the stored response. Custom stores must retain responses for at least one hour and bind each key to its Agent, command and request body. A command retry keeps its idempotency key but uses a fresh client assertion; assertion replay protection also applies to retries.

Call Service::authenticate_protected_resource before serving an authenticated application resource. It accepts typed headers, method, and URL and returns either an AuthenticatedPrincipal or the complete 401 AEP challenge response.

The default ReqwestTransport requires Tokio and TLS 1.3 or later for HTTPS. Applications using another runtime supply an HttpTransport with equivalent HTTPS, redirect and response-size controls.

§Grant Types

Register each advertised Grant Type with a GrantTypeDefinition and a GrantTypeHandler. The handler issues a JSON object containing a non-empty, globally unique credential_id, revokes the credential targets described by RevokeRequest, and may authenticate its credential presentation at protected resources. A Service does not advertise Grant or Revoke when no Grant Types are configured.

For built-in credentials, use stored_api_key_grant_type, stored_basic_grant_type, or stored_oauth_bearer_grant_type with an issuer callback and a ServiceCredentialStore. The issuer supplies credential material, expiry, and a unique identifier; API-key secrets and Basic passwords require at least 128 bits of entropy. The memory store retains one-way secret verifiers. A custom store receives usable credentials in save. API-key stores must retain salted hashes or equivalent one-way verifiers; Basic stores must apply strong password-storage controls. Protect stored records, honor expiry and revocation, and do not log credential material. The application remains responsible for authorizing resource access using the returned principal and scopes.

§Verification boundary

DidWebClientAssertionVerifier performs real EdDSA or ES256 verification after resolving the assertion key from the Agent’s did:web document. Supply an alternate ClientAssertionVerifier for a different supported identity or hosted verification boundary. The Service independently checks audience, operation, resource, validity window, identity method, and atomic replay consumption after verification.

§Hosted Platform verification

HostedPlatformClientAssertionVerifier calls a trusted Platform’s verification endpoint using the Service’s own Platform credential. The Platform’s Authorizer must permit that Service to verify assertions for its Service DID. The Service does not need to own the Agent identity, and verification permission does not grant signing or lifecycle-management permission.

use std::sync::Arc;
use aep_service::{
    HostedPlatformClientAssertionVerifier, HostedPlatformClientAssertionVerifierOptions,
    Service, ServiceOptions,
};

let mut options = HostedPlatformClientAssertionVerifierOptions::new(
    "https://platform.example/v1/aep/verifications",
);
options.authorization = Some("Bearer service-platform-token".to_owned());
let verifier = Arc::new(HostedPlatformClientAssertionVerifier::new(options)?);
Service::new(ServiceOptions::new("did:web:api.example.com", verifier))?;

The default HTTP transport uses TLS 1.3, refuses redirects, and limits responses to 1 MiB. request_timeout defaults to 30 seconds and bounds injected transports too. Custom transports must enforce TLS 1.3, avoid redirects, and bound response reads. Configure a trusted endpoint; the Agent’s assertion does not choose where the Service sends its Platform credential.

The verification request’s idempotency key is derived from its complete body, not the Service command’s idempotency key. An exact verification retry uses the same key; a fresh assertion uses a new key. The Service still checks the returned claims and consumes its own replay record, so a cached Platform verification cannot authenticate the same assertion twice at the Service.

Hosted verification is optional. Keep local did:web verification available as required by the AEP draft; the application selects its verification policy. This helper does not automatically fall back to local verification when a Platform refuses or cannot complete a request.

See the workspace guide and the AEP specifications.

Structs§

AuthenticatedCommandOptions
AuthenticatedPrincipal
ClaimValueLimits
The bounds a Service places on the Claim Values it will accept.
ClaimsConfiguration
ClientAssertionReplayRecord
ClientAssertionVerificationContext
CommandIdempotencyInput
CommandIdempotencyRecord
CredentialAuthenticationInput
CredentialMatch
DidWebClientAssertionVerifier
EnrollmentDecision
EnrollmentRecord
GrantContext
GrantTypeDefinition
HostedPlatformClientAssertionVerifier
HostedPlatformClientAssertionVerifierOptions
IdempotentCommandOptions
MemoryClientAssertionReplayStore
MemoryCommandIdempotencyStore
MemoryEnrollmentStore
MemoryServiceCredentialStore
OpenApiConfiguration
ProtectedResourceRequest
ReqwestTransport
Service
ServiceCredentialRecord
ServiceOptions
ServiceResponse
StaticEnrollmentPolicy
StoredCredentialGrantTypeOptions
SystemClock

Enums§

AuthenticationKind
CommandIdempotencyResult
IdempotentCommand
ProtectedResourceAuthentication
ResponseBody
ServiceError

Traits§

ClientAssertionReplayStore
ClientAssertionVerifier
Clock
CommandIdempotencyStore
EnrollmentPolicy
EnrollmentStore
GrantTypeHandler
ServiceCredentialStore

Functions§

stored_api_key_grant_type
stored_basic_grant_type
stored_oauth_bearer_grant_type

Type Aliases§

BuiltInCredentialIssuer
CommandOperation
StoredApiKeyGrantTypeOptions
StoredBasicGrantTypeOptions
StoredOAuthBearerGrantTypeOptions