a4sex / self-signed-token
A simple self-signed token based on a secret string and creation time.
Installs: 460
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Forks: 0
pkg:composer/a4sex/self-signed-token
Requires
- php: ^8.2
- symfony/uid: *
- symfony/validator: *
Requires (Dev)
- phpdocumentor/phpdocumentor: ^3.4
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^9.0
- rector/rector: ^1.0
- symplify/easy-coding-standard: ^12.0
README
Simple, stateless, and secure tokens signed solely with a secret — no database, no network calls.
Table of contents
- About
- Installation
- Quick start
- Token structure
- Time modes and TTL
- Public API
- Token validation
- Manual signature verification
- Helpers and utilities
- Key management (KeyStore)
- Requirements
- License
About
Self‑Signed Token produces self-signed tokens composed of the following parts:
- an identifier (ID) with optional ID-section data;
- a timestamp and arbitrary payload in the second section;
- a cryptographic signature with an explicit algorithm (except for md5);
- a meta section (time interpretation type and optional keyId).
Validation is fully local using the secret. No DB state is stored or queried. The library also includes a key management (KeyStore) module for generating, rotating, and storing secrets in a file.
Installation
composer require a4sex/self-signed-token
Quick start
use A4Sex\SelfSignedToken;
$tokenService = new SelfSignedToken(
    ttl: 60,
    secret: 'my-secret',
    options: [
        'algo' => 'md5',                 // md5|sha256|sha512|sha3-256|sha3-512
        'type' => 'expired',             // expired|created
        'keyId' => null,                 // optional key identifier
        'bypass' => false,               // globally skip checks
    ]
);
// Create a token
$token = $tokenService->create(payload: ['role','admin']);
// Validate token (signature and expiration)
$isValid = $tokenService->valid($token);
Token structure
<id[:idData...]>.<time[:data...]>.<signSection>.<metaSection>
- ID section: id[:idData...]
- PAYLOAD section: time[:data1[:data2...]]
- SIGN section: if the algorithm is md5, it contains only<hash>; otherwise<algo>:<hash>
- META section: keyId[:type[:version...]]or justtype(defaults toexpired)
Examples
user42.1716309450:role:admin.sha256:1cb7e4d8....created
id1:x.1700000000:foo.sha256:deadbeef.kid-1:created:v1
id2.1700000000.abcd.expired
Time modes and TTL
- expired: the second section stores the expiration moment. If expireis not provided on create, it is computed asnow + TTL.
- created: the second section stores the creation time. Expiration is created + TTL.
Switch modes via the type option.
Public API
use A4Sex\SelfSignedToken;
use A4Sex\Entity\Token as TokenEntity;
$t = new SelfSignedToken(ttl: 120, secret: 's3cr3t', options: [
    'algo' => 'sha256',
    'type' => 'created',
    'keyId' => 'kid-1',
]);
// Generate ID (md5 based on secret + randomness)
$id = $t->generateId('prefix-');
// Create token
$token = $t->create(id: $id, expire: null, payload: ['role','admin']);
// Validate: returns token ID or false
$idOrFalse = $t->valid($token, $ignoreSign = false, $ignoreExpires = false);
// Parse into entity
$entity = $t->parse($token);            // TokenEntity
// Individual checks
$t->signed($entity);                     // true|false
$t->expired($entity);                    // true|false
// Control time/TTL
$t->setCreated(time());                  // fix the "current" time
$t->setTtl(300);                         // change TTL
// Global bypass (disable checks)
$t->setBypass(true);
TokenManager (DTO-based API)
For more structured token creation with comprehensive options validation, use TokenManager:
use A4Sex\Services\TokenManager;
use A4Sex\DTO\TokenDto;
$manager = new TokenManager(
    defaultSecret: 'my-secret',
    defaultTtl: 3600
);
// Create with options DTO
$dto = TokenDto::create();
$dto->payload = ['user_id' => 123, 'role' => 'admin'];
$dto->algo = 'sha256';
$dto->type = 'expired';
$dto->keyId = 'key-1';
$dto->version = '1.0';
$token = $manager->create($dto);
// Convenience methods
$token = $manager->createSimple(['user_id' => 456]);
$token = $manager->createWithId('custom-id', ['data' => 'value']);
$token = $manager->createExpiringAt(time() + 1800, ['action' => 'login']);
$token = $manager->createWithAlgorithm('sha512', ['secure' => true]);
$token = $manager->createWithKey('master-key', '2.0', ['access' => 'full']);
Available options in TokenDto:
- id— explicit token ID
- expire— expiration timestamp (auto-generated if not specified)
- payload— array of payload data
- secret— signing secret
- ttl— time to live in seconds
- algo— signature algorithm (- md5,- sha256,- sha512,- sha3-256,- sha3-512)
- type— token type (- expired,- created)
- keyId— key identifier
- version— token version
- created— creation timestamp (auto-generated for 'created' type if not specified)
- idData— additional ID section data
- bypass— bypass validation flags
Fields available after parse():
- TokenEntity::id()— token ID
- TokenEntity::time()— time value from the PAYLOAD section
- TokenEntity::payload()— payload array of strings
- TokenEntity::sign()— signature
- TokenEntity::alg()— signature algorithm
- TokenEntity::keyId()—- keyIdfrom META section (if any)
- TokenEntity::type()— type (- expired|- created)
- TokenEntity::version()— version (if any)
Token validation
$ignoreSignature = false;
$ignoreExpire    = false;
$isValid = $tokenService->valid($token, $ignoreSignature, $ignoreExpire);
You can disable all checks globally with setBypass(true).
Manual signature verification
use A4Sex\Services\Signer;
$entity = $tokenService->parse($token);
// Canonical string to sign: id:payload[:keyId]:secret
$canonical = $tokenService->signature($entity, 'my-secret');
$ok = Signer::verify($canonical, $entity->sign(), $entity->alg());
Helpers and utilities
- Signer: create and verify signatures. - Supported algorithms: md5,sha256,sha512,sha3-256,sha3-512.
- Example: - use A4Sex\Services\Signer; $hash = Signer::sign('data', 'sha512'); $ok = Signer::verify('data', $hash, 'sha512');
 
- Supported algorithms: 
- TokenSections / entity sections: low-level classes to work with token parts ( - TokenIdSection,- TokenPayloadSection,- TokenSignSection,- TokenMetaSection).
- TrainParser: build and parse - A:B:Cstrings + JSON round-trip helpers.
- IdGenerator: simple random identifier based on - md5(salt + uniqid(...)).
- UuidGenerator: generate - uuid4,- uuid6,- ulid.
- A9Generator: fixed-length alphanumeric IDs (A–Z, 0–9) without collisions within a provided list. 
Additionally: see docs/styleguide.md for token style guidance and docs/keystore.md for key management details.
Key management (KeyStore)
A small file-backed KeyStore to manage signing secrets:
- Key: key model (id,secret,created,payload[]).
- KeySet: a set of keys keyed by id.
- KeyStorage: load/save KeySetfrom/to a text file. Line format:ID:SECRET[:CREATED[:PAYLOAD...]]. Includesreset()method to clear storage.
- KeyGenerator: generate id(viaA9Generator, default length 5) andsecret(viaIdGenerator).
- KeyManager: high-level operations — add,rotate,revokewith auto-persist viaKeyStorage.
Example:
use A4Sex\Key\KeyStorage;
use A4Sex\Key\KeyGenerator;
use A4Sex\KeyManager;
$storage = new KeyStorage(__DIR__ . '/keys.txt');
$manager = new KeyManager($storage);
// Create and add a new key
$generator = new KeyGenerator();
$key = $generator->generate(payload: ['scope','auth']);
$manager->add($key);
// Rotate secret for the existing id
$rotated = $manager->rotate($key->id());
Requirements
- PHP ≥ 8.2
- ext-hashextension
- symfony/uidpackage (for- UuidGenerator)
License
MIT licensed. See LICENSE.