@kwiz/common
Version:
KWIZ common utilities and helpers for M365 platform
91 lines • 3.76 kB
JavaScript
import CryptoJS from 'crypto-js';
import { CommonLogger } from '../common-logger';
import { shiftDate } from './date';
import { GetError } from './objects';
import { isDate, isNumber } from './typecheckers';
const logger = new CommonLogger("crypto");
function toBase64Url(base64) {
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
}
function base64UrlEncodeUtf8(value) {
const wordArray = CryptoJS.enc.Utf8.parse(value);
const base64 = CryptoJS.enc.Base64.stringify(wordArray);
return toBase64Url(base64);
}
function fromBase64Url(base64Url) {
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
const padding = '='.repeat((4 - (base64.length % 4)) % 4);
return `${base64}${padding}`;
}
function base64UrlDecodeUtf8(value) {
const base64 = fromBase64Url(value);
const wordArray = CryptoJS.enc.Base64.parse(base64);
return CryptoJS.enc.Utf8.stringify(wordArray);
}
/** use the jose library from @kwiz/node for server apps */
export function sign(jwtSecret, payload, options) {
const header = { alg: "HS256", typ: "JWT" };
const iat = Math.floor(Date.now() / 1000);
const exp = isNumber(options === null || options === void 0 ? void 0 : options.exp)
? options === null || options === void 0 ? void 0 : options.exp
: isDate(options === null || options === void 0 ? void 0 : options.exp)
? Math.floor(options.exp.getTime() / 1000)
: Math.floor(shiftDate("h1").getTime() / 1000);
const base64Header = base64UrlEncodeUtf8(JSON.stringify(header));
const base64Payload = base64UrlEncodeUtf8(JSON.stringify({
...payload,
iat, exp
}));
const unsignedToken = `${base64Header}.${base64Payload}`;
const signatureBase64 = CryptoJS.HmacSHA256(unsignedToken, jwtSecret).toString(CryptoJS.enc.Base64);
const signature = toBase64Url(signatureBase64);
const token = `${unsignedToken}.${signature}`;
return token;
}
/** use the jose library from @kwiz/node for server apps */
export function unsign(jwtSecret, token) {
try {
if (!token) {
throw new Error("Invalid token");
}
const parts = token.split('.');
if (parts.length !== 3) {
throw new Error("Invalid token");
}
const [base64Header, base64Payload, signature] = parts;
const unsignedToken = `${base64Header}.${base64Payload}`;
const expectedSignatureBase64 = CryptoJS.HmacSHA256(unsignedToken, jwtSecret).toString(CryptoJS.enc.Base64);
const expectedSignature = toBase64Url(expectedSignatureBase64);
if (expectedSignature !== signature) {
throw new Error("Invalid token signature");
}
let parsedPayload;
try {
const payloadJson = base64UrlDecodeUtf8(base64Payload);
parsedPayload = JSON.parse(payloadJson);
}
catch {
throw new Error("Invalid token payload");
}
const nowSeconds = Math.floor(Date.now() / 1000);
const expValue = parsedPayload === null || parsedPayload === void 0 ? void 0 : parsedPayload.exp;
const expSeconds = isNumber(expValue)
? expValue
: isDate(expValue)
? Math.floor(expValue.getTime() / 1000)
: Number(expValue);
if (!Number.isNaN(expSeconds) && nowSeconds >= expSeconds) {
throw new Error("Token expired");
}
return parsedPayload;
}
catch (e) {
logger.error(GetError(e));
return null;
}
}
export function isSignatureExpired(unsigned) {
const now = Math.floor(Date.now() / 1000);
return unsigned.iat >= now && now > unsigned.exp;
}
//# sourceMappingURL=crypto.js.map