TokenForge

TokenForge creates signed JWT tokens for ProcessWire modules and integrations. Supports HS256, RS256 and ES256 with ProcessWire cache integration.

TokenForge

TokenForge is a lightweight JWT/signature toolkit for ProcessWire modules and integrations. It creates, signs and caches HS256, RS256 and ES256 JWTs for external services such as Apple WeatherKit, Android/Firebase-style service accounts, Samsung services and generic bearer-token APIs.

If this project helps your work, consider supporting future development: GitHub Sponsors or smnv.org/sponsor.

Author


Maxim Semenov
smnv.org
maxim@smnv.org

What is TokenForge


TokenForge is a reusable ProcessWire module for modules that need signed tokens when authenticating with external APIs.

  • Generates JWT tokens with HS256, RS256 and ES256.
  • Supports standalone ES256 signing for Apple .p8 keys.
  • Includes admin playbooks and presets for Apple, Android/Firebase, Samsung-style service assertions and generic APIs.
  • Quick presets include demo signing material so the generator can be tested immediately.
  • Converts OpenSSL DER ECDSA signatures to JOSE R || S format.
  • Supports cached token creation through ProcessWire cache.
  • Provides strict TTL enforcement against expired payloads.
  • Loads private keys from files without storing key contents in module settings.
  • Provides a superuser-only admin UI for generation, validation, diagnostics, profiles and provider-oriented setup guidance.

What TokenForge is not


TokenForge is not a REST API authentication module. It is a reusable JWT/signature toolkit for ProcessWire modules that need to authenticate with external services.

It does not:

  • expose a REST API;
  • manage frontend or backend user login;
  • replace AppApi, RestApi or ProcessWire permissions;
  • store global service credentials in module settings.

Installation


Requires PHP 8.1+ and ProcessWire 3+.

  1. Copy the TokenForge folder into /site/modules/.
  2. In ProcessWire Admin, refresh module list.
  3. Install TokenForge (the ProcessTokenForge admin process is installed with it).
  4. Open /admin/setup/token-forge/ to use the management UI.

Module settings


TokenForge settings are intentionally limited to safe admin defaults:

  • default cached token TTL shown in generator and presets;
  • private key path hint used in placeholders;
  • cache key prefix hint used in examples;
  • visibility of official documentation links.

Settings do not store private keys, shared secrets or generated JWTs.

Basic usage


$tokenForge = $modules->get('TokenForge');
$jwt = $tokenForge->createJwt([
 'algorithm' => 'HS256',
 'secret' => 'super-long-secret',
 'payload' => [
 'iss' => 'my-service',
 'iat' => time(),
 'exp' => time() + 3600,
 'aud' => 'api.example.com',
 ],
]);

ES256 usage


ES256 requires an EC P-256 private key. Apple WeatherKit .p8 keys use this format.

$jwt = $modules->get('TokenForge')->createJwt([
 'algorithm' => 'ES256',
 'key_id' => 'ABC123DEFG',
 'private_key_path' => '/site/assets/private/AuthKey_ABC123DEFG.p8',
 'headers' => [
 'id' => 'TEAMID.org.smnv.weather',
 ],
 'payload' => [
 'iss' => 'TEAMID',
 'iat' => time(),
 'exp' => time() + 3600,
 'sub' => 'org.smnv.weather',
 ],
]);

Apple service tokens


Several Apple APIs use ES256 JWTs signed with an Apple .p8 private key. TokenForge's Apple preset is a starting point, not a WeatherKit-only form.

Common examples:

  • WeatherKit REST API: Team ID, Services ID, Key ID, .p8.
  • MapKit JS / Apple Maps Server API: Team ID, Maps ID, Key ID, .p8.
  • APNs provider tokens: Team ID, APNs Key ID, .p8.
  • App Store Connect / App Store Server APIs: Issuer ID, Key ID, .p8.
  • MusicKit / DeviceCheck: Team ID, Key ID, .p8.

Apple WeatherKit example


$token = $modules->get('TokenForge')->createCachedJwt('apple_weatherkit', [
 'ttl' => 3300,
 'algorithm' => 'ES256',
 'key_id' => 'APPLE_KEY_ID',
 'private_key_path' => '/site/assets/private/AuthKey_APPLE_KEY_ID.p8',
 'headers' => [
 'id' => 'APPLE_TEAM_ID.com.example.weather',
 ],
 'payload' => [
 'iss' => 'APPLE_TEAM_ID',
 'iat' => time(),
 'exp' => time() + 3600,
 'sub' => 'com.example.weather',
 ],
]);
$url = 'https://weatherkit.apple.com/api/v1/weather/en_US/40.1013/-75.3836?dataSets=currentWeather,forecastDaily,forecastHourly';
$headers = [
 'Authorization: Bearer ' . $token,
 'Accept: application/json',
];

HS256 example


$jwt = $modules->get('TokenForge')->createJwt([
 'algorithm' => 'HS256',
 'secret' => 'super-long-secret',
 'key_id' => 'shared-secret-key',
 'payload' => [
 'iss' => 'local-service',
 'iat' => time(),
 'exp' => time() + 1800,
 ],
]);

RS256 example


$jwt = $modules->get('TokenForge')->createJwt([
 'algorithm' => 'RS256',
 'key_id' => 'rsa-key-id',
 'private_key_path' => '/site/assets/private/rsa-private-key.pem',
 'payload' => [
 'iss' => 'enterprise-service',
 'iat' => time(),
 'exp' => time() + 3600,
 ],
]);

Caching tokens


Use createCachedJwt($cacheKey, $options) for API tokens that can be reused until expiry.

$jwt = $modules->get('TokenForge')->createCachedJwt('meteo_apple_weatherkit', [
 'ttl' => 3300,
 'algorithm' => 'ES256',
 'key_id' => $keyId,
 'private_key_path' => $privateKeyPath,
 'headers' => [
 'id' => $teamId . '.' . $serviceId,
 ],
 'payload' => [
 'iss' => $teamId,
 'iat' => time(),
 'exp' => time() + 3600,
 'sub' => $serviceId,
 ],
]);

The public cache key names a logical token. Internally, TokenForge also fingerprints the token options, so changing algorithm, key id, TTL, key material or non-temporal claims creates a fresh cached JWT. Temporal claims (iat, exp, nbf) are excluded from the fingerprint so cache-friendly calls that use time() still reuse valid cached tokens until TTL expiry.

If payload.exp exists, the ProcessWire cache TTL is capped to exp - time() - 60.

Security notes


  • No private keys are stored in ProcessWire module settings.
  • Admin profiles store reusable non-secret values per user in the ProcessWire cache path.
  • Prefer private_key_path over inline private_key.
  • Use strict filesystem permissions for key files.
  • Store keys outside web-served public paths when possible.
  • TokenForge does not log private keys, secrets or generated JWT values.
  • Error messages do not include private key contents, secrets or full JWTs.

ProcessWire module integration example


Other modules can depend on TokenForge and call its public API:

$tokenForge = $this->wire('modules')->get('TokenForge');
if (!$tokenForge instanceof TokenForge) {
 throw new WireException('This integration requires TokenForge module.');
}
$jwt = $tokenForge->createCachedJwt('my_provider_token', [
 'ttl' => 3300,
 'algorithm' => 'ES256',
 'key_id' => $options['key_id'],
 'private_key_path' => $options['private_key_path'],
 'headers' => [
 'id' => $options['team_id'] . '.' . $options['service_id'],
 ],
 'payload' => [
 'iss' => $options['team_id'],
 'iat' => time(),
 'exp' => time() + 3600,
 'sub' => $options['service_id'],
 ],
]);

Meteo Apple WeatherKit integration


The intended first consumer is a future apple provider in the Meteo module.

Meteo should store these settings:

  • apple_team_id
  • apple_service_id
  • apple_key_id
  • apple_private_key_path

The provider should request:

  • currentWeather
  • forecastDaily
  • forecastHourly

TokenForge usage inside MeteoProviderApple:

$tokenForge = $this->module->wire('modules')->get('TokenForge');
if (!$tokenForge instanceof TokenForge) {
 $this->module->setLastError('Apple WeatherKit requires TokenForge module.');
 return false;
}
$jwt = $tokenForge->createCachedJwt('meteo_apple_weatherkit', [
 'ttl' => 3300,
 'algorithm' => 'ES256',
 'key_id' => $opts['apple_key_id'],
 'private_key_path' => $opts['apple_private_key_path'],
 'headers' => [
 'id' => $opts['apple_team_id'] . '.' . $opts['apple_service_id'],
 ],
 'payload' => [
 'iss' => $opts['apple_team_id'],
 'iat' => time(),
 'exp' => time() + 3600,
 'sub' => $opts['apple_service_id'],
 ],
]);

Meteo should normalize Apple WeatherKit data into its existing provider format:

  • location
  • current
  • hourly
  • daily
  • units
  • updated_at
  • provider (Apple Weather)
  • provider_url (https://developer.apple.com/weatherkit/)
  • attribution (Weather)
  • optional raw debug payload

Process module (admin UI)


After installation you get an admin page at:

  • URL: /admin/setup/token-forge/
  • Class: ProcessTokenForge

You can use it to:

  • generate signed JWTs and inspect decoded header/payload/signature parts;
  • start from Apple WeatherKit, Android/Firebase, Samsung-style or generic API presets;
  • load and export Apple WeatherKit preset values;
  • import Apple preset profile JSON from the Apple Profiles tab;
  • validate key sources before token generation;
  • view diagnostics and download diagnostics JSON;
  • clear cached JWT entries by cache key;
  • view and clear a session activity log.

Diagnostics payload format


The admin export Export diagnostics as file contains:

  • generated_at (UTC ISO timestamp)
  • checks with label, status (ok, warning, error) and details
  • context with requested_algorithm and private_key_path

Troubleshooting notes


  • If OpenSSL extension check fails, enable OpenSSL for the web PHP process.
  • If Private key path is not readable, verify file existence and filesystem permissions for the same user that runs PHP.
  • /site/... private key paths are resolved relative to the ProcessWire root.
  • If auto key validation reports an issue, correct the key source and re-run the preset/import flow.

Changelog


See CHANGELOG.md for version history and update notes.

License


MIT

See LICENSE for full terms.

More modules by Maxim Semenov

  • Context

    Export ProcessWire site context for AI development (JSON + TOON formats)
  • Ichiban (SEO control center)

    Comprehensive SEO module: meta/OG/schema, audit, redirects, revisions, email reports.
  • WireWall

    Advanced traffic firewall with VPN/Proxy/Tor detection, rate limiting, and JS challenge
  • Dimensions

    Stores product dimensions (×ばつH) and weight with selectable units of measurement.
  • Ally (a11y)

    Self-hosted accessibility widget powered by Sienna (MIT). Adds font, contrast, language, and navigation tools to any page. No external CDN — the JS bundle is served from your own server.
  • Subscribe

    Newsletter subscription handler with lists, double opt-in, honeypot, rate limiting and unsubscribe link.
  • Squad

    AI integration for ProcessWire. Supports Anthropic, OpenAI, Google, xAI, and OpenRouter.
  • Plausible Analytics

    Plausible Analytics dashboard using Stats API v2 with page-edit widget, traffic trends chart, and geo/device tabs.
  • Robots.txt

    Manage robots.txt file through the admin UI with presets and visual editor.

All modules by Maxim Semenov

Install and use modules at your own risk. Always have a site and database backup before installing new modules.

AltStyle によって変換されたページ (->オリジナル) /