@x12i/authx-token-core
v1.0.1
Published
Pure token create, sign, encrypt, verify logic for AuthX
Readme
@x12i/authx-token-core
Pure token cryptography for AuthX: create, sign, encrypt, verify, and decrypt tokens. No network I/O, no database — safe to use in any Node.js process.
Part of the AuthX monorepo. See the root README for architecture and integration patterns.
Install
npm install @x12i/authx-token-core @x12i/authx-token-typesWhen to use this package
| Use case | Package |
| --- | --- |
| Verify tokens in your app (recommended) | @x12i/authx-token-sdk (wraps this) |
| Issue tokens without calling the HTTP service | this package |
| Low-level crypto / custom pipelines | this package |
| Persist tokens, revocations, audit | @x12i/authx-token-service or @x12i/authx-token-store |
Token format
AuthX tokens are not JWT. Format:
{prefix}.{keyVersion}.{body}.{signature}| Prefix | Constant | Meaning |
| --- | --- | --- |
| ax1 | AUTHX_TOKEN_PREFIX | Signed — body is base64url JSON |
| ax1e | AUTHX_ENCRYPTED_PREFIX | Encrypted — body is AES-256-GCM ciphertext |
Signature: HMAC-SHA256 over prefix.version.body, key derived from appSecretKey.
See docs/token-format.md.
Quick start
Create a token
import { createToken } from "@x12i/authx-token-core";
const { token, payload } = createToken({
appId: "my-app",
appSecretKey: process.env.AUTHX_APP_SECRET_KEY!,
subject: { identityId: "user-1", identityType: "user" },
features: [{ featureId: "catalog.read", enabled: true }],
scope: { organizationIds: ["org-1"] },
expiresInSeconds: 3600,
issuer: "authx",
encrypt: false, // set true for ax1e encrypted tokens
});Verify a token
import { verifyToken } from "@x12i/authx-token-core";
const result = verifyToken(token, {
appSecretKey: process.env.AUTHX_APP_SECRET_KEY!,
keyVersion: 1,
expectedAppId: "my-app",
expectedIssuer: "authx",
});
if (result.valid) {
console.log(result.payload);
} else {
console.error(result.error, { expired: result.expired });
}Key rotation
During rotation, pass both current and previous keys — verification tries the current key first, then the previous:
verifyToken(token, {
appSecretKey: currentSecret,
keyVersion: 2,
previousAppSecretKey: oldSecret,
previousKeyVersion: 1,
expectedAppId: "my-app",
});API reference
Token lifecycle
| Function | Description |
| --- | --- |
| createToken(input) | Build payload + signed (or encrypted) token string |
| signToken(payload, options) | Sign an existing payload (unsigned body) |
| encryptToken(payload, options) | Encrypt payload into ax1e token |
| verifyToken(token, options) | Verify signature, validate payload, check expiry |
| decryptToken(token, options) | Verify + decrypt encrypted tokens |
| decodeTokenUnsafe(token) | Parse signed token body without verification (encrypted tokens return null) |
Validation
| Function | Description |
| --- | --- |
| validateTokenPayload(payload, options) | Check schema, appId, issuer, audience, expiry |
| isExpired(expiresAt, now?) | Expiry check |
| calculateExpiry(issuedAt, expiresInSeconds?) | Compute expiresAt ISO string |
Crypto primitives
| Function | Description |
| --- | --- |
| generateAppSecretKey() | Generate a new app signing secret |
| deriveSigningKey(appSecretKey, keyVersion) | HMAC signing key derivation |
| deriveEncryptionKey(appSecretKey, keyVersion) | AES encryption key derivation |
| signBytes / verifySignature | Low-level HMAC |
| encryptBytes / decryptBytes | AES-256-GCM |
IDs
| Function | Description |
| --- | --- |
| generateTokenId() | New unique token ID |
| hashTokenId(tokenId) | Stable hash (for indexing) |
Constants
| Constant | Value |
| --- | --- |
| AUTHX_TOKEN_PREFIX | ax1 |
| AUTHX_ENCRYPTED_PREFIX | ax1e |
| DEFAULT_ISSUER | authx |
| DEFAULT_KEY_VERSION | 1 |
Types
interface CreateTokenInput {
appId: string;
appSecretKey: string;
subject: AuthxTokenSubject;
scope?: AuthxTokenScope;
features?: AuthxTokenFeature[];
expiresInSeconds?: number;
audience?: string[];
metadata?: Record<string, unknown>;
issuer?: string;
keyVersion?: number;
encrypt?: boolean;
}
interface VerifyTokenOptions {
appSecretKey: string;
keyVersion?: number;
previousAppSecretKey?: string;
previousKeyVersion?: number;
expectedAppId?: string;
expectedIssuer?: string;
expectedAudience?: string[];
now?: Date;
}
interface VerifyTokenResult {
valid: boolean;
payload?: AuthxTokenPayload;
encrypted?: boolean;
error?: string;
expired?: boolean;
}Verification checklist
verifyToken performs, in order:
- Parse token structure (
prefix.version.body.signature) - Verify HMAC signature (current key, then previous if configured)
- Decrypt body if
ax1e - Validate payload with Zod + optional appId/issuer/audience checks
- Check
expiresAt
Note: Local verification does not check revocation. Use the HTTP service introspection endpoint or @x12i/authx-token-sdk with introspectUrl for that.
Development
npm run build -w @x12i/authx-token-core
npm test -w @x12i/authx-token-coreSource: src/token.ts, src/crypto.ts, src/validate.ts.
Related packages
| Package | Role |
| --- | --- |
| @x12i/authx-token-types | Payload and option types |
| @x12i/authx-token-sdk | Higher-level verifier + middleware |
| @x12i/authx-token-service | HTTP API that uses core internally |
