Skip to content

Authentication

Every request to every Shelf Cloud API is authenticated. There are no anonymous endpoints.

Read this page to implement a client by hand, to implement the server side, or to diagnose a signature failure.

  • An access key id and secret access key

Requests are signed with AWS Signature Version 4 — the same algorithm, not an approximation of it, so that existing clients and SDKs sign correctly without modification.

A signed request carries:

HeaderContents
AuthorizationAlgorithm, credential, signed headers, signature
X-Amz-DateThe signing timestamp, ISO 8601 basic
X-Amz-Content-Sha256Required by some services; see below
Authorization: AWS4-HMAC-SHA256
Credential=AKIAIOSFODNN7EXAMPLE/20260903/hel1/ec2/aws4_request,
SignedHeaders=content-type;host;x-amz-date,
Signature=5d672d79c15b13162d9279b0855cfba...

The Credential parameter is the access key id, a slash, then the credential scope:

<access-key-id>/<date>/<region>/<service>/aws4_request

The access key id is not part of the credential scope. It is sent in the clear as the first element of Credential; the scope is the four elements after it. A verifier that splits Credential into the wrong number of fields, or that feeds the key id into the signing key derivation, produces signatures no SDK can match.

20260903/hel1/ec2/aws4_request

The scope binds a signature to a date, a region and a service. A signature produced for ec2 in hel1 cannot be replayed against iam, against another region, or on another day.

This is why the endpoint shape — one host per service per region — cannot change once customers are signing against it.

Each service documents its signing name. It equals the first label of the endpoint host unless the service’s page states otherwise.

That qualification is deliberate: AWS’s own signing names diverge from host prefixes for several services, and stating the rule as a universal would make it wrong the first time we add a service whose counterpart diverges — by which point it is inside every signature.

A service that is not regional still needs a region in the scope.

PROPOSED: global services are signed with hel1.

PROPOSED: the verifier additionally accepts a signature scoped to the caller’s own configured region for global services. SDK behaviour when an endpoint is overridden varies between languages and versions, and a customer should not receive SignatureDoesNotMatch for a difference we can absorb.

The hash of the payload is the final line of the canonical request for every SigV4 request, without exception. A request with no body signs the hash of the empty string.

What is service-specific is the X-Amz-Content-Sha256 header, not the hashing. Its absence never means the body is unsigned.

A verifier that skips payload hashing for services that do not send that header leaves request bodies unauthenticated: an intermediary could alter the body and the signature would still validate. That is a remote tampering hole, and it is the reason this paragraph is stated as emphatically as it is.

SignedHeaders must contain, and the verifier must require:

  • host
  • x-amz-date
  • every x-amz-* header actually present on the request
  • content-type, when a body is sent

Headers not listed in SignedHeaders are ignored by the server rather than acted on. A verifier that acts on an unsigned header lets an intermediary change the meaning of a signed request.

The list above is closed. A vaguer rule — “refuse if any meaningful header is unsigned” — is not implementable, because the server cannot know which headers the client considered meaningful, and two implementers would draw the line differently.

ElementNotes
Access key idSent in the clear. PROPOSED: 20 characters, uppercase alphanumeric, with a fixed prefix identifying the credential type
Secret access keyNever transmitted. PROPOSED: 40 characters of base64

Access key ids are case-sensitive and uppercase. They are the one identifier in the platform that is not lowercase, and a verifier that lowercases them before lookup breaks every signature.

The secret is returned once, at creation, and cannot be retrieved afterwards.

OPEN (Michael): whether temporary credentials exist in v1.

Until they do, a request carrying X-Amz-Security-Token is rejected with InvalidClientTokenId rather than ignored. Accepting the signature while discarding the session token would accept a credential the caller believed was time-limited and scoped — worse than refusing it.

This is not a corner case. The default AWS credential chain supplies a session token in many environments, including assumed roles and SSO, so a customer may send one without intending to.

PROPOSED: when temporary credentials do exist, X-Amz-Security-Token is part of the canonical request. AWS services differ on whether it is signed or appended after signing; we pick one and document it, because a client guessing the other way produces a mismatch every time.

OPEN (Michael): whether query-string SigV4 — X-Amz-Algorithm, X-Amz-Credential, X-Amz-Date, X-Amz-Expires, X-Amz-SignedHeaders, X-Amz-Signature — is supported.

If it is not supported, a request carrying those parameters must be rejected explicitly rather than ignored, so that a customer generating a presigned URL learns immediately rather than discovering an unauthenticated path.

PROPOSED: 15 minutes, matching what existing clients expect.

A request outside the window is rejected with RequestExpired and HTTP 400.

Our clock is in the Date header of every response, including errors. The AWS SDKs use this to correct themselves automatically, but only when the status and the error code are the ones they recognise — which is why the statuses on the errors page are not open to preference.

The skew window is the only bound on replay: a captured signed request can be resent within it by anyone who obtains it — from a log, a proxy, or a trace.

For reads this is disclosure. For creates that accept a client token the damage is bounded, since a replay returns the original result. For creates without one, a replay creates a second billable resource.

OPEN (Michael): whether to maintain a nonce cache to reject exact replays within the skew window, and at what cost.

Stated here because each has produced a real vulnerability in a shipped implementation of this exact protocol:

  1. Verify possession of the secret. Validating that a signature is well-formed, or that the scope parses, is not authentication. The signature must be recomputed from the derived signing key and compared.
  2. Compare in constant time. A byte-by-byte comparison that returns early leaks the signature through timing.
  3. Reject any algorithm other than AWS4-HMAC-SHA256. An implementation that falls through on an unrecognised algorithm is an authentication bypass.
  4. Resolve Host deliberately. Behind a load balancer, decide once whether the signature is checked against Host or a forwarded header, and never trust a client-supplied forwarded header — otherwise the host component of the signature is attacker-controlled.
  5. Do not distinguish failures. Wrong secret, wrong region and unknown key return the same error.

Authentication establishes who is calling. Authorisation is evaluated separately, against policy. A correctly signed request may still be refused.