UNPKG

@kwiz/common

Version:

KWIZ common utilities and helpers for M365 platform

91 lines 3.76 kB
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