Last updated: April 29, 2026
The auth system provides pluggable authentication and signing for Smithy-Java clients. It's layered across four modules: shared identity abstractions, client-side auth scheme resolution, AWS-specific identity types, and the SigV4 signing implementation.
Source:
auth-api/— Shared abstractionsclient/client-auth-api/— Client auth scheme resolutionaws/aws-auth-api/— AWS identity typesaws/aws-sigv4/— SigV4 implementation
public interface Identity {
default Instant expirationTime() { return null; }
}Built-in identity types:
TokenIdentity— bearer token (String token())ApiKeyIdentity— API key (String apiKey())LoginIdentity— username/password
public interface IdentityResolver<IdentityT extends Identity> {
IdentityResult<IdentityT> resolveIdentity(Context requestProperties);
Class<IdentityT> identityType();
static <I extends Identity> IdentityResolver<I> chain(List<IdentityResolver<I>> resolvers);
static <I extends Identity> IdentityResolver<I> of(I identity); // static resolver
}Returns IdentityResult<T> (success-or-error wrapper) instead of throwing. Expected failures (missing env vars) return
IdentityResult.ofError(). IdentityResolverChain tries resolvers in order, returns first success.
public interface IdentityResolvers {
<T extends Identity> IdentityResolver<T> identityResolver(Class<T> identityClass);
static IdentityResolvers of(IdentityResolver<?>... resolvers);
}Type-safe registry mapping identity class → resolver. Used by AuthScheme.identityResolver(resolvers).
@FunctionalInterface
public interface Signer<RequestT, IdentityT extends Identity> extends AutoCloseable {
SignResult<RequestT> sign(RequestT request, IdentityT identity, Context properties);
}SignResult<RequestT> is a record: (RequestT signedRequest, String signature). The signature string is used as the
seed for event stream signing.
public interface AuthScheme<RequestT, IdentityT extends Identity> {
ShapeId schemeId();
Class<RequestT> requestClass();
Class<IdentityT> identityClass();
default IdentityResolver<IdentityT> identityResolver(IdentityResolvers resolvers);
default Context getSignerProperties(Context context);
default Context getIdentityProperties(Context context);
Signer<RequestT, IdentityT> signer();
default <F extends Frame<?>> FrameProcessor<F> eventSigner(...);
}An AuthScheme bundles: scheme ID (e.g., aws.auth#sigv4), identity resolver lookup, and signer. getSignerProperties()
/ getIdentityProperties() extract scheme-specific config from the client context.
@FunctionalInterface
public interface AuthSchemeResolver {
List<AuthSchemeOption> resolveAuthScheme(AuthSchemeResolverParams params);
}DefaultAuthSchemeResolver iterates operation.effectiveAuthSchemes() and wraps each in an AuthSchemeOption. The
client pipeline picks the first option with a matching scheme, compatible request class, and available identity
resolver.
public interface AuthSchemeFactory<T extends Trait> {
ShapeId schemeId();
AuthScheme<?, ?> createAuthScheme(T trait);
}Discovered via ServiceLoader. Receives the Smithy trait instance and creates a configured AuthScheme.
Auth resolution happens in ClientPipeline.doSendOrRetry() between the modifyBeforeSigning and readBeforeSigning
interceptor hooks:
- Build
AuthSchemeResolverParamswith protocol ID, operation, and context - Call
authSchemeResolver.resolveAuthScheme(params)→ priority-orderedList<AuthSchemeOption> - Iterate options, look up each
schemeIdinsupportedAuthSchemes - Check
authScheme.requestClass().isAssignableFrom(request.getClass()) - Merge identity/signer properties from scheme defaults + option overrides
- Call
authScheme.identityResolver(identityResolvers)— skip if null - Call
resolver.resolveIdentity(identityProperties) - First scheme with a non-null resolver becomes the
ResolvedScheme - After endpoint resolution, apply endpoint auth scheme property overrides
authScheme.signer().sign(request, identity, signerProperties)→ signed request
Signer properties are merged from three sources (later overrides earlier):
- Scheme defaults (
authScheme.getSignerProperties(context)) - Resolver overrides (
AuthSchemeOption.signerPropertyOverrides()) - Endpoint overrides (
applyEndpointAuthSchemeOverrides)
public interface AwsCredentialsIdentity extends Identity {
String accessKeyId();
String secretAccessKey();
default String sessionToken() { return null; }
default String accountId() { return null; }
}public interface AwsCredentialsResolver extends IdentityResolver<AwsCredentialsIdentity> {
@Override default Class<AwsCredentialsIdentity> identityType() {
return AwsCredentialsIdentity.class;
}
}public final class SigV4AuthScheme implements AuthScheme<HttpRequest, AwsCredentialsIdentity> {
public SigV4AuthScheme(String signingName);
// schemeId() → "aws.auth#sigv4"
// getSignerProperties() extracts SIGNING_NAME, REGION, CLOCK from context
// signer() → SigV4Signer.create()
// eventSigner() → SigV4EventSigner
}Its inner Factory class implements AuthSchemeFactory<SigV4Trait> and is registered via SPI.
- Extract
region,signingName,clockfrom properties - Compute payload hash (SHA-256 hex of body)
- Build canonical request: method + path + query + sorted headers + signed headers + payload hash
- Derive signing key:
HMAC(HMAC(HMAC(HMAC("AWS4"+secret, date), region), service), "aws4_request")- Cached in
SigningCache(bounded LRU, 300 entries,StampedLock-protected) - Valid for the same calendar day
- Cached in
- Compute signature:
HMAC(signingKey, stringToSign) - Build
Authorizationheader
Performance optimizations:
SigningResourcespoolsStringBuilder,MessageDigest, andMacinstancesPool<T>usesConcurrentLinkedQueue(32 max)SigningCacheusesLinkedHashMapwith FIFO eviction andStampedLock- Manual date formatting (avoids
DateTimeFormatter)
Implements chained event stream signing (AWS4-HMAC-SHA256-PAYLOAD). Each frame's signature depends on the previous
frame's signature. Produces frames with :date and :chunk-signature headers. Returns a closingFrame() that signs an
empty payload.
Generated clients: Auth schemes are hardcoded by codegen based on AuthSchemeFactory SPI.
Dynamic client: SimpleAuthDetectionPlugin discovers auth schemes at runtime via ServiceLoader, reads effective
auth schemes from the model via ServiceIndex.getEffectiveAuthSchemes(), and creates schemes via factories.
Users can customize auth at three levels:
- Client builder —
putSupportedAuthSchemes(),authSchemeResolver(),addIdentityResolver() - Plugins —
ClientPlugin.configureClient()can add schemes, resolvers, identity resolvers - Per-request —
RequestOverrideConfigcan override auth scheme resolver, add schemes, add identity resolvers