| Safe Haskell | None |
|---|---|
| Language | GHC2021 |
Ecluse.Credential
Description
The outbound-credential handle: minting the bearer token Écluse uses to write approved packages to the mirror target.
This is one of the two cloud handles (the other is Ecluse.Queue); it is separate
from the protocol handle Ecluse.Registry because protocol and authentication are
orthogonal axes — every managed npm registry (AWS CodeArtifact, GCP Artifact
Registry, a self-hosted Verdaccio) speaks the same npm protocol and differs only
in how its bearer token is obtained (see
docs/architecture/cloud-backends.md → "Credential Provider").
A CredentialProvider is used only for the mirror-target write, never to
read on a user's behalf: private-upstream reads forward the client's own
credential and public reads are anonymous (see
docs/architecture/registry-model.md → "Credential flow and authority"). So a
deployment configures exactly one provider.
Like the other handles, the effectful field returns IO, not App: an
adapter closes over its own backend state (an amazonka env, an HTTP manager)
and never imports the proxy's Env/App, so backends stay decoupled from the
core (see docs/architecture/technology-stack.md → "Key Decisions").
This module provides the handle and its payload types. staticProvider is the
in-memory leaf: a fixed token with no expiry. The generic refresh/cache/expiry
policy that wraps a per-cloud token mint lives in Ecluse.Credential.Refresh.
Synopsis
- newtype CredentialProvider = CredentialProvider {}
- data AuthToken = AuthToken {}
- data Secret
- mkSecret :: Text -> Secret
- unSecret :: Secret -> Text
- staticProvider :: AuthToken -> CredentialProvider
Provider handle
newtype CredentialProvider Source #
The credential handle: yields the bearer token currently valid for the mirror target, refreshing it before expiry internally (a caller never sees a stale token in the common case, and never blocks on a mint on the request hot path).
It is a record of functions (the Handle pattern): the single field is the
operation, and a backend's smart constructor returns a CredentialProvider
whose closure captures that backend's private state. currentToken returns
IO, not App so adapters stay decoupled from the core (see the module
header).
Constructors
| CredentialProvider | |
Fields
| |
Tokens
A bearer token for a registry endpoint, with its expiry when known.
The expiry is what a refresh wrapper schedules against (cloud token lifetimes
range from CodeArtifact's ~12h to ADC's ~1h, so refresh is driven off the
token's own authExpiresAt rather than a fixed interval). A static token has no
expiry (Nothing).
Constructors
| AuthToken | |
Fields
| |
Secrets
A short-lived bearer secret (an access token).
Opaque, and its Show is redacted: the underlying token text is never
rendered, so a Secret can be embedded in any value — an AuthToken, a log
record, an error — without risking disclosure (token material must never reach a
log, metric, or trace; see docs/architecture/observability.md). This
redaction is a load-bearing security property.
Build one with mkSecret and read the real value back only at the point of
use with unSecret (e.g. when setting the Authorization header).
Equality is constant-time: two secrets are compared over their UTF-8 bytes
without a content-dependent early out (see the Eq instance). The default
derived equality would be Text's short-circuiting compare, which returns
as soon as two tokens first differ and so leaks, through timing, how long a
shared prefix is. Folding that property into the type itself means no comparison
on a Secret — the inbound edge-auth gate above all — can accidentally become
non-constant-time. (A constant-time compare can still reveal the token length;
that residual leak is accepted, but the content is never short-circuited on.)
Instances
| Show Secret Source # | Renders a fixed placeholder, never the secret text. This is the whole
point of the type: it makes accidental disclosure through any Defined via |
| Eq Secret Source # | Constant-time equality over the UTF-8 encoding of the wrapped token.
|
unSecret :: Secret -> Text Source #
Recover the raw token text from a Secret. Call this only at the point
of use (setting the auth header); never log or otherwise render the result.
In-memory double
staticProvider :: AuthToken -> CredentialProvider Source #
An in-memory CredentialProvider that always returns a fixed token.
This is the static leaf: it never expires and never refreshes, so it is the
right provider for a registry reached with a long-lived credential, and it is
the trivial double for tests of code that consumes a CredentialProvider.