NAME
Langertha::Knarr::TokenStore - Role for the stores token_auth looks access tokens up in
VERSION
version 1.103
SYNOPSIS
package My::TokenStore;
use Moose;
with 'Langertha::Knarr::TokenStore';
sub lookup_f { my ($self, $hash) = @_; ... } # Future: row or undef
sub lookup { my ($self, $hash) = @_; ... } # row or undef, blocking
sub available { ... }
# the consumer (Langertha::Knarr::Authorizer::Token):
$store->on_invalidate( sub { %cache = () } );
$store->start;
my $row = await $store->lookup_f( $store->hash_token($bearer) );
# { model => 'gpt-4o', provider => 'openai', expires => 1792512000 }
DESCRIPTION
The interface between token_auth and the place the issued tokens live. A store answers one question: which row belongs to this token hash, if the token has not expired yet. It never sees the token itself, only "hash_token" of it, and it holds no cache: the caller caches hits and throws its cache away whenever the store calls "on_invalidate".
A store calls "invalidate" when a row may have changed behind the caller's back (a notification from the database) and when it loses its connection, since a revocation sent while it was gone would otherwise be missed. While "available" is false the caller rejects every token.
Stores: Langertha::Knarr::TokenStore::Pg (PostgreSQL, LISTEN/NOTIFY) and Langertha::Knarr::TokenStore::Memory (tests).
A row is a HashRef { model, provider, expires } with expires as epoch seconds. A store returns only rows whose expires is still in the future at the time of the lookup.
lookup_f
my $row = await $store->lookup_f($hash_hex);
Required. A Future done with the row of the token hash, or with undef when there is none or it has expired. It fails only on a store error (also while the store is not "available"), with a fixed text that names no credential.
lookup
my $row = $store->lookup($hash_hex);
Required. "lookup_f" for a caller without a running event loop (the PSGI adapter): blocks, delivers pending invalidations first, then asks. Croaks on a store error.
available
Required. True while the store can answer. False while it has no connection (not yet connected, lost, reconnecting); the caller then answers 503.
start
$store->start;
Opens the store's connection if it has one and it is not open yet, without waiting for it; a second call does nothing. Langertha::Knarr calls it through "start" in Langertha::Knarr::Authorizer::Token in the process that serves; otherwise the first lookup does the same. The default does nothing.
poll
$store->poll;
Delivers the invalidations that have arrived, without waiting. A caller without an event loop (Langertha::Knarr::PSGI) calls it before it reads its cache, since nothing else would deliver them there. The default does nothing (a store that invalidates synchronously needs no poll).
on_invalidate
Optional CodeRef, read-write. The one listener the store calls through "invalidate", with the reason as its argument (notify, disconnect). Setting it replaces the previous listener: a store serves one authorizer.
invalidate
$store->invalidate('notify');
Calls "on_invalidate" with the reason, if one is set. A listener that dies is logged, not propagated: the store's own state must not depend on it.
hash_token
my $hash = $store->hash_token($token);
The lowercase hex SHA-256 of the token's bytes, the form a store looks up. The token's characters are its bytes (as a header value read off the wire is); a string with characters above 255 is encoded as UTF-8 first.
SUPPORT
Issues
Please report bugs and feature requests on GitHub at https://github.com/Getty/langertha-knarr/issues.
IRC
Join #langertha on irc.perl.org or message Getty directly.
CONTRIBUTING
Contributions are welcome! Please fork the repository and submit a pull request.
AUTHOR
Torsten Raudssus <torsten@raudssus.de> https://raudssus.de/
COPYRIGHT AND LICENSE
This software is copyright (c) 2026 by Torsten Raudssus.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.