KemalIdentity::AssuranceLevel
Inherits Enum < Comparable < Value < Object
How strongly the current principal was authenticated.
Deliberately not a boolean logged_in?: "typed a password just now", "typed a
password two hours ago" and "restored silently from a remember-me cookie" are three
different security situations that a boolean cannot tell apart. Combined with
Principal#authenticated_at (how recently) this is what makes
require_fresh!(within: 5.minutes) a caller decision rather than a configured global.
The numeric value is persisted in auth_sessions.assurance as a SMALLINT.
Append only. Never renumber an existing member — a renumbering silently reclassifies
every session row already on disk. The gaps of ten exist so an intermediate level can
be added later without breaking the ordering that Comparable gives us.
Constants
Restored from a remember-me cookie. The account holder has not typed a password
recently and may not be present at all. Below Password on purpose.
A long-lived API token was presented — a personal access token, not a browser session.
Above Remembered because the holder deliberately created this credential and can revoke
it, and below Password because no person typed anything: a token in a CI job's
environment proves possession of a secret, not the presence of the account holder.
Principal#fresh? is therefore false for it, so require_fresh! refuses a token-bearing
request outright. That is the intended answer. An automated client cannot re-authenticate
interactively, so a destructive account action should not be reachable with a token in the
first place.
A password (or equivalent single factor) was verified in this session's lifetime.
A single-use recovery code was spent in place of a second factor.
Above Password because a second thing was proved, and below MFA because of what that
thing is: a printed or stored list, phishing-resistant in no sense, held by whoever found
the piece of paper. A recovery code is how somebody gets back in after losing a device;
it is not how the sharpest action in the application should become available.
So a permission declared minimum_assurance: MFA — "changing payout details needs
phishing-resistant MFA" — is not satisfied by one, while everything at Password and
below is. Before this level existed the documented flow raised a recovery redemption to
MFA, and the strongest gate in the system was reachable through its weakest path with
nothing in the principal saying so (blueprints/0025, MFA-04).
25 rather than 21: the gaps of ten are for exactly this, and the midpoint leaves room on both sides.
A second factor was verified in addition to the first.