enum

KemalIdentity::FailureReason

Inherits Enum < Comparable < Value < Object

Why an authentication attempt did not produce a principal.

This is for the audit log, not for the response. Branching a user-facing response on the reason is an enumeration oracle: DisabledAccount and InvalidCredential producing different messages tells an attacker which logins exist. Every credential failure renders one identical response — see docs/02-security-model.md.

Constants

InvalidCredential = 0

The identifier is unknown, or the secret does not verify. One reason for both cases, so that the two cannot be told apart even by accident downstream.

MalformedCredential = 1

The presented credential was rejected on its shape alone, before any I/O: wrong length, illegal characters, an oversized cookie value.

Expired = 2

Past idle_expires_at or absolute_expires_at. Evaluated on read, never deferred to the sweeper.

Revoked = 3

revoked_at is set: logout, password change, or bulk revocation.

DisabledAccount = 4

The account exists and the credential was valid, but the account is disabled.

StaleAuthVersion = 5

The session was minted before the account's auth_version was bumped. The belt to revocation's braces — invalidates every session for an account without enumerating rows.

RateLimited = 6

The rate limiter denied the attempt before the password was verified. Failed carries a retry_after in this case.

RateLimiterUnavailable = 7

The rate limiter could not reach its storage, and the attempt was refused rather than run unmetered.

Kept apart from RateLimited for the same reason InvalidClaim is kept apart from InvalidCredential: the two mean opposite things to whoever reads the trail. RateLimited is the limiter working — somebody had their share. This one is the limiter broken, which is an operations incident and often the second half of an attack, since the cheapest way to disable rate limiting is to overwhelm the thing that stores it. A run of these deserves a page; a run of RateLimited does not.

Invisible in the response, which is identical for every reason.

ReplayedToken = 8

A single-use token was presented twice. For a remember-me token this also revokes the whole token family, because either the thief or the legitimate holder is replaying it.

InvalidClaim = 9

A token verified cryptographically, and then a claim inside it did not hold: the wrong iss, an aud naming somebody else, a missing exp, a purpose that belongs to a different flow.

Kept apart from InvalidCredential because the two mean opposite things to whoever reads the audit log. InvalidCredential is noise — a stale token, a typo, a scanner. This one says a validly signed token was presented to the wrong verifier, which is either a misconfigured client or a genuine attempt to replay a token across a trust boundary. Both are worth an alert; neither is visible in the response, which is identical for every reason.

Instance methods

disabled_account?

Returns true if this enum value equals DisabledAccount

Source
expired?

Returns true if this enum value equals Expired

Source
invalid_claim?

Returns true if this enum value equals InvalidClaim

Source
invalid_credential?

Returns true if this enum value equals InvalidCredential

Source
malformed_credential?

Returns true if this enum value equals MalformedCredential

Source
rate_limited?

Returns true if this enum value equals RateLimited

Source
rate_limiter_unavailable?

Returns true if this enum value equals RateLimiterUnavailable

Source
replayed_token?

Returns true if this enum value equals ReplayedToken

Source
revoked?

Returns true if this enum value equals Revoked

Source
stale_auth_version?

Returns true if this enum value equals StaleAuthVersion

Source