struct

KemalIdentity::ApiTokens::Token

Inherits Struct < Value < Object

A personal access token: a long-lived credential a person creates deliberately so that a script, a CI job or a CLI can act as them.

Opaque, not a JWT

docs/06-roadmap.md puts opaque tokens first on purpose. They reuse the digest-and-revoke machinery sessions already have, and they carry none of JWT's revocation problem: revoking one is an UPDATE, and it takes effect on the very next request because validity is read from storage rather than asserted by a signature.

No scopes

A token names an account and nothing else. Scopes are authorization — what this holder may do — and docs/00-scope.md puts authorization outside this shard permanently: it answers "who", not "may they". An application that needs scoped tokens stores the scopes against id in its own table and consults them itself.

Constructors

new(id : String, account_id : String, name : String, token_digest : Bytes, created_at : Time, expires_at : Time | Nil = nil, last_used_at : Time | Nil = nil, revoked_at : Time | Nil = nil, scopes : Array(String) | Nil = nil)
Source

Class methods

decode_scopes(stored : String | Nil) : Array(String) | Nil

The stored form: nil for unrestricted, "" for attenuated-to-nothing, otherwise space-delimited. Both shipped adapters use this so they cannot drift apart.

Source
encode_scopes(scopes : Array(String) | Nil) : String | Nil

The stored form: nil for unrestricted, "" for attenuated-to-nothing, otherwise space-delimited. Both shipped adapters use this so they cannot drift apart.

Source
validate_scopes!(scopes : Array(String) | Nil) : Nil

Scopes are stored space-delimited, which is also how RFC 6749 encodes them, so a scope containing whitespace could not survive a round trip and is refused where it is cheapest to notice.

Nothing else about their shape is checked. Permission names in the shipped RBAC are lowercase dotted segments, but an application running its own Authorizer names its permissions however it likes, and this must not quietly constrain that.

Source

Instance methods

account_id
Source
created_at
Source
expired?(now : Time) : Bool

A token with no expiry is never expired.

Source
expires_at

nil means it never expires. That is a real choice for a deploy key and a bad one for a laptop, so it is the application's to make rather than a default this shard imposes.

Source
inspect(io : IO) : Nil

Never prints the digest.

Source
last_used_at

Moved forward as the token is used, but throttled the same way sessions throttle last_seen_at — otherwise every authenticated API request becomes a write. It answers "is this token still in use?" on a management screen, which does not need to be precise to the second.

Source
name

What a person calls this token in a management screen: "laptop", "deploy job". Purely for humans — nothing here reads it.

Source
revoked?
Source
revoked_at
Source
scopes

What this token may do, or nil for no restriction.

nil and [] of String are opposites and the difference is load-bearing. nil means the token is not attenuated and carries whatever its owner holds; [] means it is attenuated to nothing and permits nothing. Reading nil as an empty set would break every token issued before scopes existed; reading [] as unset would hand a deliberately powerless token the run of the application. See CredentialRef#scopes.

These are permission names, matched exactly against what the authorizer is asked about. There is no wildcard: unrestricted is nil.

Source
to_s(io : IO) : Nil

Same as #inspect(io).

Source
token_digest

SHA-256 of the raw token, as raw bytes.

Source