Accounts and policy¶
This page covers what a host controls about who may register, what an account may order and what
the directory tells clients. All of it is configuration on Config plus two optional interfaces.
Directory metadata¶
Meta: acmeserver.DirectoryMeta{
TermsOfService: "https://ca.example.com/terms",
Website: "https://ca.example.com",
CAAIdentities: []string{"ca.example.com"},
ExternalAccountRequired: true,
},
The meta object is included in the directory when any field is set. TermsOfService also adds a
Link header with rel="terms-of-service" to newAccount responses. CAAIdentities is
informational. The library does not check CAA records, so a CA that must honor CAA does so in the
issuance policy, see Issuance policy.
Terms of service¶
RequireTermsOfServiceAgreed refuses a newAccount whose payload does not set
termsOfServiceAgreed with a userActionRequired problem. It needs Meta.TermsOfService, so
the client can show the URL. Agreement is stored on the account.
External account binding¶
External account binding ties an ACME account to an identity the CA already knows, such as a customer or a device. The client presents a MAC over its account key, signed with a key the CA issued out of band under a key identifier.
type ExternalAccountKeys interface {
MACKey(ctx context.Context, keyID string) ([]byte, error)
}
Implement it over your key registry and set Config.ExternalAccounts. Return ErrNotFound for an
unknown identifier, which becomes an unauthorized problem. Return any other error for a lookup
failure. HS256, HS384 and HS512 are accepted. A verified binding stores the key identifier as
Account.ExternalAccountID, which is available to Policy.NewAccount and to your own audit
queries.
Meta.ExternalAccountRequired advertises the requirement and refuses accounts without a binding
with externalAccountRequired. Without it a binding is verified when present and optional
otherwise.
SingleUseExternalAccounts binds each key identifier to at most one account. The claim is stored
with the account, so the store's uniqueness check enforces it under concurrency. A client that
retries newAccount with the same account key gets its existing account back. A different account
key with the same identifier is refused with unauthorized.
Account policy¶
type Policy interface {
NewAccount(ctx context.Context, account *Account) error
NewOrder(ctx context.Context, account *Account, order *Order) error
}
Config.Policy defaults to AllowAll. Embed it and override the methods you need:
type customerPolicy struct {
acmeserver.AllowAll
customers Registry
}
func (p customerPolicy) NewOrder(ctx context.Context, account *acmeserver.Account, order *acmeserver.Order) error {
for _, id := range order.Identifiers {
if !p.customers.Owns(ctx, account.ExternalAccountID, id.Value) {
return acmeserver.NewProblem(acmeserver.ErrorRejectedIdentifier, "not authorized for this name").
WithIdentifier(id)
}
}
return nil
}
NewAccount sees the key, the contacts, the terms of service flag and the external account
identity before the account is stored. NewOrder sees the account and the order after identifier
normalization and before any authorization is created. It may change NotBefore and NotAfter,
for example to cap the requested validity, but must not change the identifiers.
A returned *Problem is sent to the client. Any other error is logged and answered with
serverInternal. Rate limiting belongs here too. Return a rateLimited problem built with
WithRetryAfter and the client backs off.
Accounts over their lifetime¶
- Registration returns the existing account when the key is already known, so a client that lost its account URL recovers it with
onlyReturnExisting. - Contacts must be
mailtoURLs with one bare address each, at most ten. Other schemes are refused withunsupportedContact. - Key rollover replaces the key through the inner JWS of RFC 8555 section 7.3.5. An in-flight validation keeps the thumbprint captured when the client responded.
- Deactivation is permanent. Pending work of the account stops: a challenge accepted but not yet validated becomes invalid without a proof being fetched. New orders are refused. Recovery of an issuance the CA may already have completed continues, because the certificate may exist.
- Order list is served at the account's
ordersURL in pages of 100 with arel="next"link.
Account IDs are random and appear only in the account URL. The store indexes accounts by ID and by key thumbprint.
Limits worth setting¶
| Field | Default | Reason to change |
|---|---|---|
MaxIdentifiers |
100 | Cap the size of one order for your CA |
OrderLifetime |
7 days | How long a client has to complete validation and finalization |
AuthorizationLifetime |
30 days | How long a validated authorization stays valid, which matters for revocation by another account |
MaxRequestBody |
64 KiB | Raise only if a policy accepts unusually large CSRs |