Documentation
¶
Overview ¶
Package api_key_cruder reads and writes the keys an account registers, and records what they mint with.
The account's own queries carry `account = $n` with the argument taken from the session token: listing, registering and revoking are things an account does to its own keys, and there is no query here that can be asked about another's.
Minting is not one of those. It arrives with no session at all -- the assertion is the whole credential -- so its queries are keyed by the key's own id, and what they answer with is what the endpoint then checks: whether the key is revoked, whether it has expired, and whose account it belongs to.
Index ¶
- Constants
- Variables
- type Cruder
- func (c *Cruder) Create(ctx context.Context, accountId string, key *public_key.Key, name string, ...) (*apiKeyTypes.ApiKey, error)
- func (c *Cruder) ReadBulkForAccount(ctx context.Context, accountId string) ([]*apiKeyTypes.ApiKey, error)
- func (c *Cruder) RecordMint(ctx context.Context, key *Key, jti string, ...) (*authenticationPkg.Authentication, error)
- func (c *Cruder) RevokeForAccount(ctx context.Context, accountId string, keyId string) error
- func (c *Cruder) SelectForMint(ctx context.Context, keyId string) (*Key, error)
- type Key
Constants ¶
const AssertionRetention = assertion.Lifetime + 2*assertion.Leeway
* AssertionRetention is how long a spent assertion's identifier is kept, counted from when it * arrived. * * From the server's clock rather than from the "exp" the assertion carried, which is the only way * the record is certain to outlive the assertion: a client's "exp" decides whether an assertion is * still presentable, and an assertion accepted at the near edge of Leeway carries one that has * already passed. Stored under that, the row would be swept by the very statement below that tidies * up after it, and the assertion could be presented again for as long as it remained acceptable. * * The "exp" of an assertion arriving now may sit as far out as Lifetime and Leeway together, and the * assertion stays acceptable for a further Leeway past it -- hence Leeway twice. * * Derived from the window rather than written down, so that lengthening how long an assertion may * be presented for cannot quietly leave it remembered for less.
const AuthenticationLifetime = 30 * 24 * time.Hour
* AuthenticationLifetime is how long the sign-in row a key mints under is kept alive, extended at * every mint. * * It is not the token's lifetime, which is minutes. The row stands for "this key is in use", so * that a client minting every quarter of an hour appears in the account's activity as one entry * rather than as ninety-six a day -- with the list capped at fifty, the second shape would push * every browser sign-in off the end within a day, which is the opposite of what the page is for. * * A generous window is not a widening: what a mint is allowed to do is decided by the key's * revocation and expiry, both checked at every mint, and the token it produces is short regardless.
Variables ¶
var ErrAssertionAlreadyUsed = errors.New("the assertion has already been used")
ErrAssertionAlreadyUsed says an assertion has been presented before: the identifier it carries is already stored against its key, which is what a replay looks like from here.
var ErrKeyRevoked = errors.New("the key has been revoked")
ErrKeyRevoked says the key was revoked by the time its mint was recorded, after the caller read it.
Functions ¶
This section is empty.
Types ¶
type Cruder ¶
func (*Cruder) Create ¶
func (c *Cruder) Create( ctx context.Context, accountId string, key *public_key.Key, name string, expiresAt *time.Time, ) (*apiKeyTypes.ApiKey, error)
Create registers a key to an account.
The thumbprint is the primary key, so registering a key that is already registered -- to this account or to any other, revoked or not -- is a unique violation rather than a second row.
func (*Cruder) ReadBulkForAccount ¶
func (c *Cruder) ReadBulkForAccount(ctx context.Context, accountId string) ([]*apiKeyTypes.ApiKey, error)
ReadBulkForAccount lists an account's keys, newest first, revoked ones included: a key that was revoked is part of what the account should see of its own history.
func (*Cruder) RecordMint ¶
func (c *Cruder) RecordMint( ctx context.Context, key *Key, jti string, metadata *authenticationPkg.ClientMetadata, ) (*authenticationPkg.Authentication, error)
* RecordMint remembers the assertion, keeps the key's sign-in alive, and returns that sign-in with * the account it belongs to, ready for a token to be minted from. * * One transaction, and the order matters. The key's row is locked first and its revocation read * again: the caller read the key before verifying the assertion, and a revoke committed since must * not be followed by a sign-in made for the key it revoked, while one arriving after waits for this * transaction and ends the sign-in it leaves. The assertion is stored next, so that a replay fails * before anything else is touched; the sign-in is then reused where there is a live one for this * key, and made where there is not, which is what keeps one key to one entry in the account's * activity however often it mints. * * How long the assertion is remembered for is counted here, by the database's own clock, and both * statements below read it within the one transaction -- so the row that was just written cannot be * swept by the sweep that follows it, whatever a client's clock or a client's "exp" had to say.
func (*Cruder) RevokeForAccount ¶
* RevokeForAccount takes a key out of use and ends what it minted, in one transaction. * * Both halves, because either alone leaves the account half revoked: the key with its sign-ins * still live would let the tokens already minted go on being renewed, and the sign-ins ended with * the key still registered would let the next mint make new ones. * * A key that is not this account's, one that does not exist, and one already revoked are all * sql.ErrNoRows, so the caller cannot learn from here that somebody else's key id is real.
func (*Cruder) SelectForMint ¶
SelectForMint reads a key by its own id, which is all a mint request names.
Nothing is decided here: a revoked or expired key comes back as it is, and the endpoint answers for it. What the caller must not do is read the account before verifying the signature.
type Key ¶
type Key struct {
Id string
Name string
Algorithm string
PublicKey []byte
AccountId string
ExpiresAt *time.Time
RevokedAt *time.Time
}
Key is a registered key as the mint path needs it: enough to verify a signature and to decide whether the key may still be used at all.