Skip to content

Public API reference ​

This reference describes Solo Tokens 1.0: PHP 8.3+, psr/clock 1.0 and the Solo\Tokens namespace. Getting started walks through the setup, and Behavior in detail explains the rules below.

The public API consists of six types. The stores and Token are final; behavior is extended by implementing TokenStore, not by inheritance. Every parameter that carries a secret is marked #[\SensitiveParameter]. Exception messages are not part of the compatibility promise.

TypeRole
TokenStoreTokens of one kind: issue, find, consume and revoke
RedisTokenStoreThe store every process shares, on Redis through phpredis
InMemoryTokenStoreThe same contract in process memory, for tests and single-process tools
TokenWhat a store knows about a live token
SecretMakes and hashes secrets, for tokens kept in your own tables
Exception\TokenStoreExceptionThrown when a store fails or a stored record is damaged

TokenStore ​

issue() ​

php
/** @param array<mixed> $data */
public function issue(string $subject, int $ttlSeconds, array $data = []): string

Stores a token for $subject that lives $ttlSeconds, and returns its secret: 64 lowercase hex characters, 32 random bytes. Only the secret's SHA-256 is stored, with the subject, the data and the moment the token ends.

Throws InvalidArgumentException, before storing anything, when the subject is empty, the lifetime is outside 1 to 2_147_483_647 seconds, or the data does not encode as JSON within 512 levels. Throws TokenStoreException when the store fails.

find() ​

php
public function find(string $secret): ?Token

The live token behind $secret, which stays usable, or null when there is none or the value cannot be a secret. Throws TokenStoreException when the store fails or the token's record is damaged; a damaged record stays in place.

consume() ​

php
public function consume(string $secret): ?Token

The live token behind $secret, removed in the same step, or null. Of concurrent calls with the same secret, exactly one gets the token. A damaged record is removed as well, then reported with TokenStoreException.

revoke() ​

php
public function revoke(string $secret): void

Ends the token behind $secret, a damaged one included. A missing token or a value that cannot be a secret is ignored. Throws TokenStoreException when the store fails.

revokeAll() ​

php
public function revokeAll(string $subject): void

Ends every token issued for $subject. A subject without tokens is ignored. Throws InvalidArgumentException for an empty subject and TokenStoreException when the store fails.

RedisTokenStore ​

php
public function __construct(\Redis $redis, string $prefix)

Keeps tokens in Redis through phpredis. Each operation is one Lua script, run on the Redis clock. The connection must be in its normal, immediate mode, without OPT_SERIALIZER or OPT_COMPRESSION; a client OPT_PREFIX comes before $prefix. Requires Redis 5.0 or later, on a single server with or without replicas, not Redis Cluster. See Storage on Redis.

InMemoryTokenStore ​

php
public function __construct(ClockInterface $clock = new SystemClock())

public function purgeExpired(): int

Keeps tokens per instance. The clock decides when a token ends; omit it to use system time. issue() drops expired tokens periodically, and purgeExpired() drops them at once and returns how many it removed.

Token ​

php
final readonly class Token
{
    /** @param array<mixed> $data */
    public function __construct(
        public string $subject,
        public array $data,
        public \DateTimeImmutable $expiresAt,
    )
}

expiresAt is the moment the token ends. On Redis it comes from the Redis clock, in PHP's default time zone; in memory it comes from the store's clock, in that clock's time zone.

Secret ​

php
public const int MIN_BYTES = 32;
public const int MAX_BYTES = 64;

public static function generate(int $bytes = self::MIN_BYTES): string
public static function hash(string $secret): ?string

generate() returns $bytes random bytes in lowercase hex, so 64 to 128 characters; another length throws InvalidArgumentException. hash() returns the SHA-256 in hex, or null when the value is not a secret generate() could have made: a wrong length, an odd number of characters, upper case or anything but hex. The stores use both.

TokenStoreException ​

Solo\Tokens\Exception\TokenStoreException extends RuntimeException. It is thrown when Redis fails, when the connection is in a mode the store refuses, when a script replies with something unexpected, and when a stored record is damaged. If phpredis threw RedisException, it is available via getPrevious(). The secret never appears in its message.

MIT licensed.