JWTs

The AAM_Framework_Service_Jwts service provides a complete JWT (JSON Web Token) lifecycle management layer for AAM.

It supports:

The service is designed specifically for user access levels and integrates tightly with WordPress user meta storage.

Definition

class AAM_Framework_Service_Jwts {

    public get_tokens() : array
    public get_token_by(mixed $search, string $claim = null) : array|WP_Error

    public issue(array $claims = [], array $settings = []) : array
    public validate(string $token) : bool|WP_Error
    public revoke(string $token) : bool
    public refresh(string $token) : array
    public reset() : bool

}

Service Scope

The JWT service can only operate within the context of a user access level.

Internally, the service validates this during initialization:

if ($access_level->type !== AAM_Framework_Type_AccessLevel::USER) {
    throw new LogicException(
        'The JWT service expects ONLY user access level'
    );
}

This means:

Token Registry

AAM maintains a registry of revocable tokens in WordPress user meta.

Registry storage key is aam_jwt_registry

Registry entries may be stored as either:

[
    'jwt-token-string',
    'another-token'
]

or with metadata:

[
    [
        'token' => 'jwt-token-string',
        'description' => 'API integration token'
    ]
]

The registry is used for:

Issuing Tokens

public function issue(array $claims = [], array $settings = [])

Parameters

$claims

Custom JWT claims to include in the payload.

Example:

[
    'role' => 'api_client',
    'scope' => [
        'read', 
        'write'
    ]
]

$settings

Optional token behavior settings.

Supported settings:

Setting Type Default Description
ttl string int +24 hours
revocable bool true Whether token can be revoked
refreshable bool false Whether token can be refreshed
description string null Optional registry label

TTL Handling

TTL can be defined either as:

Relative time string

[
    'ttl' => '+7 days'
]

Numeric seconds

[
    'ttl' => 3600
]

Internally numeric values are normalized into:

+3600 seconds

Invalid TTL values throw:

InvalidArgumentException('Invalid token ttl')

Default Claims

The service automatically injects several claims into every token.

Claim Description
jti Unique token identifier
iat Issued-at timestamp
iss WordPress site URL
exp Expiration timestamp
user_id WordPress user ID
revocable Revocation support flag
refreshable Refresh support flag

Example payload:

{
  "jti": "5b4f6a2a-1b6e-4bdf-95b8-8393c9f7af12",
  "iat": 1777420000,
  "iss": "https://example.com",
  "exp": 1777506400,
  "user_id": 15,
  "revocable": true,
  "refreshable": false,
  "role": "api_client"
}

Claim Customization Hook

Before token generation, claims pass through:

apply_filters('aam_jwt_claims_filter', $claims)

Example:

add_filter('aam_jwt_claims_filter', function($claims) {
    $claims['environment'] = 'production';

    return $claims;
});

Example: Issue Token

$jwt_service = AAM::api()->jwts();

$result = $jwt_service->issue(
    [
        'scope' => ['read', 'write']
    ],
    [
        'ttl' => '+30 days',
        'refreshable' => true,
        'description' => 'Mobile App Token'
    ]
);

Example response:

[
    'token' => 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...',
    'claims' => [ ... ],
    'is_valid' => true
]

Validating Tokens

public function validate(string $token)

Return Types

Return Meaning
true Token is valid
WP_Error Validation failed

Validation Flow

Validation occurs in two stages.

Stage 1 — Cryptographic Validation

The utility class verifies:

Stage 2 — Registry Validation

If token claim:

'revocable' => true

then the token must exist in the registry.

Otherwise validation fails with:

RuntimeException('Unregistered token')

Example: Validate Token

$result = $jwt_service->validate($token);

if (is_wp_error($result)) {
    echo $result->get_error_message();
} else {
    echo 'Valid token';
}

Revoking Tokens

public function revoke(string $token)

Behavior

Revocation removes the token from the registry.

Only revocable tokens can be revoked.

Once revoked:

Example

$jwt_service->revoke($token);

Possible error:

Provided token is not registered

Refreshing Tokens

public function refresh(string $token)

Requirements

The token must:

'refreshable' => true

Refresh Process

When refreshing:

  1. Original token is validated
  2. Claims are decoded
  3. TTL duration is preserved
  4. rat claim is added
  5. New token is issued
  6. Old token is revoked
  7. New token is registered

Refresh Claim

Refreshed tokens receive:

'rat' => time()

This represents:

Example

$new_token = $jwt_service->refresh($old_token);

Listing Tokens

public function get_tokens()

Returns all tokens from the registry.

Each token is normalized into a consistent structure.

Response Structure

[
    [
        'token' => 'jwt-token',
        'claims' => [ ... ],
        'is_valid' => true,
        'description' => 'Optional label'
    ]
]

If token validation fails:

[
    'token' => 'jwt-token',
    'claims' => [ ... ],
    'is_valid' => false,
    'error' => 'Expired token'
]

Example

$tokens = $jwt_service->get_tokens();

Finding Tokens

public function get_token_by($search, $claim = null)

Lookup by Raw Token

$jwt_service->get_token_by($token);

Lookup by Claim

$jwt_service->get_token_by(15, 'user_id');

Example:

$jwt_service->get_token_by('api_client', 'role');

Return Structure

[
    'token' => 'jwt-token',
    'claims' => [ ... ],
    'is_valid' => true
]

Resetting Tokens

public function reset()

Deletes the entire registry for the current user. This effectively revokes all revocable tokens.

Example

$jwt_service->reset();

Supported Algorithms

The utility supports the following signing algorithms.

Algorithm Type
HS256 HMAC SHA256
HS384 HMAC SHA384
HS512 HMAC SHA512
RS256 RSA SHA256
RS384 RSA SHA384
RS512 RSA SHA512
ES256 ECDSA SHA256
ES384 ECDSA SHA384
EdDSA libsodium

Configuration

Signing Algorithm

service.jwt.signing_algorithm

Default:

HS256

HMAC Secret

Used for symmetric algorithms.

service.jwt.signing_secret

Default:

SECURE_AUTH_KEY

Registry Size

Controls maximum stored revocable tokens.

service.jwt.registry_size

Default:

10

When exceeded:


Default Expiration

service.jwt.expires_in

Default:

+24 hours

RSA / ECDSA Certificate Configuration

Asymmetric algorithms require certificate paths.

Private Key

service.jwt.private_cert_path

Optional passphrase:

service.jwt.private_cert_passphrase

Public Key

service.jwt.public_cert_path

Path Placeholder

Certificate paths may include:

{ABSPATH}

Example:

{ABSPATH}/certs/private.pem

Security Considerations

Revocable vs Non-Revocable Tokens

Revocable Tokens

Advantages:

Disadvantages:

Non-Revocable Tokens

Advantages:

Disadvantages:

Use Short Expiration

Prefer:

'+15 minutes'

instead of:

'+365 days'

Use Refreshable Tokens Carefully

Refreshable tokens are useful for:

Avoid unlimited refresh loops.

Prefer Revocable Tokens

For administrative access and privileged integrations.

Protect Signing Keys

Never expose:

Use HTTPS

JWTs should never travel over unsecured HTTP connections.

Error Handling

Most public methods return either:

Always validate responses.

Example:

$result = $jwt_service->validate($token);

if (is_wp_error($result)) {
    error_log($result->get_error_message());
}

Common Errors

Error Meaning
Expired token Token expired
Invalid token signature Signature mismatch
Wrong number of segments Malformed JWT
Algorithm not supported Unsupported JWT algorithm
Unregistered token Revoked or unknown token
Cannot take token prior to ... iat or nbf violation
Provided token is not registered Revoke operation failed