@aeternity/aepp-sdk
Version:
SDK for the æternity blockchain
112 lines (105 loc) • 3.71 kB
JavaScript
import { Buffer as _Buffer } from "buffer";
import canonicalize from 'canonicalize';
import { Encoding, decode, encode } from './encoder.js';
import { verifySignature } from './crypto.js';
import { ArgumentError, InvalidSignatureError } from './errors.js';
// TODO: use Buffer.from(data, 'base64url') after solving https://github.com/feross/buffer/issues/309
const toBase64Url = data => _Buffer.from(data).toString('base64').replaceAll('/', '_').replaceAll('+', '-').replace(/=+$/, '');
const fromBase64Url = data => _Buffer.from(data.replaceAll('_', '/').replaceAll('-', '+'), 'base64');
const objectToBase64Url = data => {
var _canonicalize;
return toBase64Url((_canonicalize = canonicalize(data)) !== null && _canonicalize !== void 0 ? _canonicalize : '');
};
const header = 'eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9'; // objectToBase64Url({ alg: 'EdDSA', typ: 'JWT' })
/**
* JWT including specific header
* @category JWT
*/
/**
* Generate a signed JWT
* Provide `"sub_jwk": undefined` in payload to omit signer public key added by default.
* @param originalPayload - Payload to sign
* @param account - Account to sign by
* @category JWT
*/
export async function signJwt(originalPayload, account) {
const payload = {
...originalPayload
};
if (!('sub_jwk' in payload)) {
payload.sub_jwk = {
kty: 'OKP',
crv: 'Ed25519',
x: toBase64Url(decode(account.address))
};
}
if (payload.sub_jwk === undefined) delete payload.sub_jwk;
const body = `${header}.${objectToBase64Url(payload)}`;
const signature = await account.unsafeSign(body);
return `${body}.${toBase64Url(signature)}`;
}
/**
* Unpack JWT. It will check signature if address or "sub_jwk" provided.
* @param jwt - JWT to unpack
* @param address - Address to check signature
* @category JWT
*/
export function unpackJwt(jwt, address) {
var _payload$sub_jwk;
const components = jwt.split('.');
if (components.length !== 3) throw new ArgumentError('JWT components count', 3, components.length);
const [h, payloadEncoded, signature] = components;
if (h !== header) throw new ArgumentError('JWT header', header, h);
const payload = JSON.parse(fromBase64Url(payloadEncoded).toString());
const jwk = (_payload$sub_jwk = payload.sub_jwk) !== null && _payload$sub_jwk !== void 0 ? _payload$sub_jwk : {};
const signer = jwk.x == null || jwk.kty !== 'OKP' || jwk.crv !== 'Ed25519' ? address : encode(fromBase64Url(jwk.x), Encoding.AccountAddress);
if (address != null && signer !== address) {
throw new ArgumentError('address', `${signer} ("sub_jwk")`, address);
}
if (signer != null && !verifySignature(_Buffer.from(`${h}.${payloadEncoded}`), fromBase64Url(signature), signer)) {
throw new InvalidSignatureError(`JWT is not signed by ${signer}`);
}
return {
payload,
signer
};
}
/**
* Check is string a JWT or not. Use to validate the user input.
* @param maybeJwt - A string to check
* @returns True if argument is a JWT
* @category JWT
*/
export function isJwt(maybeJwt) {
try {
unpackJwt(maybeJwt);
return true;
} catch (error) {
return false;
}
}
/**
* Throws an error if argument is not JWT. Use to ensure that a value is JWT.
* @param maybeJwt - A string to check
* @category JWT
*/
export function ensureJwt(maybeJwt) {
unpackJwt(maybeJwt);
}
/**
* Check is JWT signed by address from arguments or "sub_jwk"
* @param jwt - JWT to check
* @param address - Address to check signature
* @category JWT
*/
export function verifyJwt(jwt, address) {
try {
const {
signer
} = unpackJwt(jwt, address);
return signer != null;
} catch (error) {
return false;
}
}
//# sourceMappingURL=jwt.js.map