JWT Voucher Auth
How the jwt_voucher_auth addon works and how to set it up — a stateless, self-service system for granting time-limited content access via signed JWT tokens.
Table of Contents
Overview
The JWT Voucher Auth addon (jwt_voucher_auth) allows you to grant temporary, self-managed access to content in your Pugpig Bolt app using short-lived JSON Web Tokens (JWTs). Each JWT acts as a "voucher" or “code” that contains a set of content entitlements and an expiry time.
When a user presents the token in the app (usually via a deeplink), Pugpig verifies its signature and grants access the specified content encoded in the token, for only as long as the token is valid. When the token expires, access is automatically revoked — no further action is required.
This is a stateless system: there is no server-side session, no call to an external entitlement API, and no Pugpig endpoint to call at the point of access. The token itself is the proof of entitlement, this means that publishers can create and distribute valid tokens to users using whatever means they like and Pugpig will simply validate if a given token is valid and not expired.
Typical use cases
- Giving advertisers or partners time-limited access to specific editions or sections of your app
- Distributing daily content access to subscribers via a third-party system
How it works
-
A shared secret and a Key ID (
kid) is generated by Pugpig (or you may define your own). These are configured on our side against your publication(s). - You generate a signed JWT using the shared secret, setting the entitlements and expiry you want to grant. You do this entirely on your own infrastructure — Pugpig does not currently expose an endpoint to generate tokens for you.
-
You pass the JWT to a user (e.g. via a deep link, QR code, or your own web/app flow).
- The deeplink url is in the format of https://[app_domain]/c/voucher/[jwt_token].
-
The app receives the token from the deeplink and attempts to apply it.
- You may provide a redirect_url param to send the user to after successfully applying the token in the app - such as the edition the token grants access to: https://[app_domain]/c/voucher/[jwt_token]?redirect_url=https%3A%2F%2F[app_domain]%2Fd%2Fid-[edition_id]
-
Pugpig verifies the token's signature server-side using the corresponding shared secret (matched by
kid). If valid and not expired, access is granted to the entitlements listed in the token. -
When
expis reached, the token is automatically invalid and access is revoked.
JWT token structure
The JWT must include standard fields as well as some Pugpig-specific claims.
Header
{
"typ": "JWT",
"alg": "HS256",
"kid": "<your-key-id>"
}The kid value must match the Key ID Pugpig has configured for your publication. The default signing algorithm is HS256, but other major algorithms (e.g. RS256, HS384) are supported on request.
Payload
{
"iat": 1772471173,
"nbf": 1772471173,
"exp": 1772474773,
"entitlements": [
"com.pugpig.editions.2026.01.28",
"com.pugpig.editions.2026.01.29"
],
"message": ""
}| Field | Type | Required | Description |
|---|---|---|---|
iat |
Unix timestamp | Yes | Issued-at time |
nbf |
Unix timestamp | Yes | Not-valid-before time |
exp |
Unix timestamp | Yes | Expiry time. After this point the token is rejected by the app |
entitlements |
Array of strings | Yes | The content entitlements this token grants. Must match the entitlement identifiers used in your Pugpig feed |
message |
String | No | Optional message displayed on the user's account/settings page in the app. Leave empty or omit if not needed |
Entitlement wildcards
The entitlements field supports wildcards:
-
*— grants access to all entitlements -
prefix-*— grants access to all entitlements beginning withprefix-
Use wildcards with care, as they can grant broad access for the full lifetime of the token.
{
"iat": 1772471173,
"nbf": 1772471173,
"exp": 1772474773,
"entitlements": [
"com.pugpig.editions.*"
],
"message": ""
}{
"iat": 1772471173,
"nbf": 1772471173,
"exp": 1772474773,
"entitlements": [
"*"
],
"message": ""
}Generating a token
You can use any standard JWT library in your language of choice (see jwt.io/libraries for a comprehensive list). The steps are:
- Construct the payload with the entitlements and timestamps you want.
- Sign using your shared secret and the configured algorithm (default:
HS256). - Include the correct
kidin the JWT header so Pugpig can identify which secret to use for verification.
Any token that fails signature verification is immediately rejected. Any token that has passed its exp time is also rejected.
Secret management
Multiple secrets can be active simultaneously to allow secret rotation without invalidating existing live tokens. The kid field in the token header tells Pugpig which secret to use for verification.
Per-account vs per-publication secrets
Pugpig can configure a single shared secret for all publications under your account, or separate secrets per publication. The per-account approach is simpler to manage; per-publication provides stronger isolation if different teams or systems will be generating tokens for different titles.
Rotating secrets
To rotate a secret without disrupting active users:
- Contact Pugpig to add a new secret with a new
kidto your configuration. - Update your token-generation system to start signing new tokens with the new
kid. - Old tokens signed with the previous
kidremain valid until theirexptime passes. - Once all old tokens have expired, contact Pugpig to remove the old secret entry.


