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.