struct

KemalIdentity::CredentialRef

Inherits Struct < Value < Object

A safe reference to the credential that proved this request.

Why this exists

Without it, two personal access tokens issued to one account produce two Principals that are indistinguishable, so a token created for reading reports can perform a write its owner happens to be permitted. ApiTokens::Service has the token id in hand at the moment it authenticates and used to drop it on the floor; blueprints/0021-credential-reference.md is why it no longer does.

What is safe to put here

Identifiers and metadata. Never the raw token, the digest, the signature, the session token, or any part of a JWT beyond its jti. Nothing here needs redacting because nothing secret ever reaches it — which is a stronger property than redacting on the way out (docs/02-security-model.md).

name is for display and for an audit line. Nothing in this shard reads it to make a decision, and an application should not either: it is user-supplied text.

Constructors

new(kind : CredentialKind, id : String | Nil = nil, name : String | Nil = nil, expires_at : Time | Nil = nil, scopes : Array(String) | Nil = nil)
Source

Instance methods

expires_at

When the credential stops working, if it says.

Source
id

The credential's stable identifier: a session id, a token id, a JWT's jti.

Nilable because not every credential has one. A JWT whose issuer omits jti cannot be named, and — worth saying out loud — a credential that cannot be named is also one that cannot be revoked individually or attributed in an audit trail.

Source
kind
Source
name

A human label, when the credential has one. "deploy-token".

Source
permits?(permission : String) : Bool

Whether this credential's attenuation permits permission.

This is not an authorization check. It answers only "does the credential allow it", never "does the account hold it". The account's grant is checked first and separately; effective permission is the intersection of the two, never the union, so a scope can only ever remove. See blueprints/0021-credential-reference.md decision 6.

Source
scopes

The permissions this credential is restricted to, or nil for no restriction.

nil and [] of String mean opposite things and must never be conflated.

ValueMeaning
nilunattenuated — a browser session, or a token issued without scopes
["reports:read"]attenuated to this set
[] of Stringattenuated to nothing; valid, and permits nothing

Reading nil as an empty set would deny every session-authenticated request, since a session has no scopes to carry. Reading [] as "unset" would hand a deliberately powerless token the run of the application. One is a lockout and the other is a privilege escalation, which is why they are a nilable array rather than an array that is sometimes empty.

There is no wildcard. ["*"] is a scope literally named * and matches nothing; unrestricted is nil. blueprints/0018 refuses * in a permission for the same reason — it grants permissions that do not exist yet — and a wildcard scope is that hazard pointed at tokens.

Source
unrestricted?

Whether this credential restricts what its holder may do.

Source