struct

KemalIdentity::ApiTokens::LifetimePolicy

Inherits Struct < Value < Object

How long a personal access token may live in this deployment.

An enterprise requires every token to expire within thirty days and forbids unbounded ones; the deployment next door permits a non-expiring deploy key. Both are correct, so this is absent by default — Service.new with no policy accepts what its caller asks for, including no expiry at all, which is the shipped behaviour and stays it.

Checked at issuance, and only there

A policy describes what may be created. It is not consulted on the authentication path: every request already reads the token's own expires_at, and re-deriving a limit per request would put a policy lookup on the hot path to answer a question the row already answers.

The consequence, which docs/02-security-model.md states rather than leaves to be discovered: tightening the policy does not shorten the tokens that already exist. A ninety-day token issued under the old rule keeps its ninety days. That is the honest behaviour for a rule about creation, and it is why Service#expire exists — an organisation that must apply a new limit retroactively walks its tokens and brings each deadline forward, which is one call per token and cannot lengthen anything by mistake.

policy = KemalIdentity::ApiTokens::LifetimePolicy.new(maximum: 30.days, default: 7.days)

Constructors

new(maximum : Time::Span, default : Time::Span | Nil = nil)
Source

Instance methods

default

What to use when the caller names no expiry. nil means an issuance with no expiry is refused rather than defaulted — the stricter reading, and the one an enterprise asking for this feature usually wants: a client that forgot to ask for a deadline should be told, not quietly given one.

Source
maximum

The furthest away an expiry may be, measured from the moment of issuance.

Source
resolve(expires_at : Time | Nil, now : Time) : Time | Nil

The expiry to store, filling in default when the caller named none. Returns nil when there is nothing to fill in, which #violation then refuses.

Source
violation(expires_at : Time | Nil, now : Time) : PolicyViolation | Nil

Why this expiry is unacceptable, or nil if it is fine.

Takes now rather than reading a clock: a policy is a value, and the one thing worse than a policy that cannot be tested is one that reads the system clock while being tested.

Source