paseto-ts
Version:
PASETO v4 (encrypt, decrypt, sign & verify) in TypeScript
68 lines (67 loc) • 3.4 kB
JavaScript
import { MAX_DEPTH_DEFAULT, MAX_KEYS_DEFAULT, TOKEN_MAGIC_BYTES, TOKEN_MAGIC_STRINGS } from "../lib/magic.js";
import { concat, payloadToUint8Array } from "../lib/uint8array.js";
import { parseAssertion, parseFooter, parseKeyData, parsePayload } from "../lib/parse.js";
import { PAE } from "../lib/pae.js";
import { base64UrlEncode } from "../lib/base64url.js";
import { sign as ed25519Sign } from '@stablelib/ed25519';
const EMPTY_BUFFER = new Uint8Array(0);
/**
* Signs a payload using an Ed25519 secret key and returns a PASETO v4.public token.
* The secret key must have the version and purpose of `k4.secret`.
* @param {string | Uint8Array} key Ed25519 secret key to sign with
* @param {Payload | string | Uint8Array} payload Payload to sign
* @param {object} options Options
* @param {Footer | string | Uint8Array} options.footer Optional footer
* @param {Assertion | string | Uint8Array} options.assertion Optional assertion
* @param {boolean} options.addIat Add an iat claim if one is not provided; defaults to true
* @param {boolean} options.addExp Add an exp claim if one is not provided; defaults to true
* @param {number} options.maxDepth Maximum depth of nested objects in the payload; defaults to 32
* @param {number} options.maxKeys Maximum number of keys in an object in the payload; defaults to 128
* @returns {string} PASETO v4.public token
* @see https://github.com/paseto-standard/paseto-spec/blob/master/docs/01-Protocol-Versions/Version4.md#sign
*/
export function sign(key, payload, { footer = new Uint8Array(0), assertion = new Uint8Array(0), addExp = true, // Add an exp claim if one is not provided: https://github.com/paseto-standard/paseto-spec/blob/master/docs/02-Implementation-Guide/05-API-UX.md#default-expiration-claims
addIat = true, // Add an iat claim if one is not provided
maxDepth = MAX_DEPTH_DEFAULT, // Maximum depth of nested objects
maxKeys = MAX_KEYS_DEFAULT, // Maximum number of keys in an object
validatePayload = true, // Validate the payload
} = {
footer: new Uint8Array(0),
assertion: new Uint8Array(0),
addExp: true,
addIat: true,
maxDepth: MAX_DEPTH_DEFAULT,
maxKeys: MAX_KEYS_DEFAULT,
validatePayload: true,
}) {
// Assert that the key is a v4.secret key, and parse it.
key = parseKeyData('secret', key);
const payloadUint8 = payloadToUint8Array(parsePayload(payload, {
addExp: !!addExp,
addIat: !!addIat,
maxDepth,
maxKeys,
validate: !!validatePayload,
}));
const footerUint8 = parseFooter(footer, {
maxDepth,
maxKeys,
validate: !!validatePayload,
});
// Assert assertion is a string or Uint8Array
assertion = parseAssertion(assertion);
// Pack header, payload, footer and assertion together using PAE
const m2 = PAE(TOKEN_MAGIC_BYTES.v4.public, payloadUint8, footerUint8, assertion);
// Sign m2 using Ed25519 secret key
const sig = ed25519Sign(key, m2);
let result = '';
// If footer is empty, return the token without a footer
// Otherwise, return the token with a footer
if (footer.length === 0) {
result = `${TOKEN_MAGIC_STRINGS.v4.public}${base64UrlEncode(concat(payloadUint8, sig))}`;
}
else {
result = `${TOKEN_MAGIC_STRINGS.v4.public}${base64UrlEncode(concat(payloadUint8, sig))}.${base64UrlEncode(footerUint8)}`;
}
return result;
}