@pallasoft/ps-utilities 2.5.0 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/common/constants/email-preview-config.d.ts +2 -1
- package/dist/common/constants/email-preview-config.js +2 -1
- package/dist/common/constants/index.d.ts +2 -0
- package/dist/common/constants/index.js +2 -0
- package/dist/common/constants/signed-token-config.d.ts +17 -0
- package/dist/common/constants/signed-token-config.js +14 -0
- package/dist/common/constants/user-token-config.d.ts +6 -0
- package/dist/common/constants/user-token-config.js +9 -0
- package/dist/common/email-preview-service.d.ts +1 -0
- package/dist/common/email-preview-service.js +6 -2
- package/dist/common/index.d.ts +2 -0
- package/dist/common/index.js +2 -0
- package/dist/common/signed-token-service.d.ts +19 -0
- package/dist/common/signed-token-service.js +233 -0
- package/dist/common/user-token-service.d.ts +11 -0
- package/dist/common/user-token-service.js +115 -0
- package/dist/index.d.ts +7 -0
- package/dist/models/email-preview-input.d.ts +1 -0
- package/dist/models/signed-token-header.d.ts +4 -0
- package/dist/models/signed-token-header.js +2 -0
- package/dist/models/signed-token-key-pair.d.ts +4 -0
- package/dist/models/signed-token-key-pair.js +2 -0
- package/dist/models/user-token-claims.d.ts +12 -0
- package/dist/models/user-token-claims.js +2 -0
- package/dist/models/user-token-issue-options.d.ts +6 -0
- package/dist/models/user-token-issue-options.js +2 -0
- package/dist/models/user-token-request.d.ts +4 -0
- package/dist/models/user-token-request.js +2 -0
- package/dist/models/user-token-subject.d.ts +5 -0
- package/dist/models/user-token-subject.js +2 -0
- package/dist/models/user-token-verify-options.d.ts +6 -0
- package/dist/models/user-token-verify-options.js +2 -0
- package/package.json +1 -1
|
@@ -4,6 +4,7 @@ export declare const EmailPreviewConfig: {
|
|
|
4
4
|
readonly imageName: "email.png";
|
|
5
5
|
readonly imageContentType: "image/png";
|
|
6
6
|
readonly fallbackSubject: "Email";
|
|
7
|
+
readonly dateFormat: "D MMM YYYY, h:mm A";
|
|
7
8
|
readonly page: {
|
|
8
9
|
readonly width: 860;
|
|
9
10
|
readonly padding: 48;
|
|
@@ -13,7 +14,7 @@ export declare const EmailPreviewConfig: {
|
|
|
13
14
|
readonly bodySize: 15;
|
|
14
15
|
readonly lineHeight: 24;
|
|
15
16
|
readonly maxLines: 140;
|
|
16
|
-
readonly minHeight:
|
|
17
|
+
readonly minHeight: 320;
|
|
17
18
|
readonly charWidth: 7.4;
|
|
18
19
|
readonly subjectWidthRatio: 1.45;
|
|
19
20
|
readonly metaWidthRatio: 0.9;
|
|
@@ -7,6 +7,7 @@ exports.EmailPreviewConfig = {
|
|
|
7
7
|
imageName: 'email.png',
|
|
8
8
|
imageContentType: 'image/png',
|
|
9
9
|
fallbackSubject: 'Email',
|
|
10
|
+
dateFormat: 'D MMM YYYY, h:mm A',
|
|
10
11
|
page: {
|
|
11
12
|
width: 860,
|
|
12
13
|
padding: 48,
|
|
@@ -16,7 +17,7 @@ exports.EmailPreviewConfig = {
|
|
|
16
17
|
bodySize: 15,
|
|
17
18
|
lineHeight: 24,
|
|
18
19
|
maxLines: 140,
|
|
19
|
-
minHeight:
|
|
20
|
+
minHeight: 320,
|
|
20
21
|
charWidth: 7.4,
|
|
21
22
|
subjectWidthRatio: 1.45,
|
|
22
23
|
metaWidthRatio: 0.9,
|
|
@@ -15,3 +15,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./email-preview-config"), exports);
|
|
18
|
+
__exportStar(require("./signed-token-config"), exports);
|
|
19
|
+
__exportStar(require("./user-token-config"), exports);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare const SignedTokenConfig: {
|
|
2
|
+
readonly algorithm: "ES256";
|
|
3
|
+
readonly type: "JWT";
|
|
4
|
+
readonly keyAlgorithm: {
|
|
5
|
+
readonly name: "ECDSA";
|
|
6
|
+
readonly namedCurve: "P-256";
|
|
7
|
+
};
|
|
8
|
+
readonly signAlgorithm: {
|
|
9
|
+
readonly name: "ECDSA";
|
|
10
|
+
readonly hash: "SHA-256";
|
|
11
|
+
};
|
|
12
|
+
readonly privateKeyFormat: "pkcs8";
|
|
13
|
+
readonly publicKeyFormat: "spki";
|
|
14
|
+
readonly separator: ".";
|
|
15
|
+
readonly partCount: 3;
|
|
16
|
+
readonly keyCacheLimit: 20;
|
|
17
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SignedTokenConfig = void 0;
|
|
4
|
+
exports.SignedTokenConfig = {
|
|
5
|
+
algorithm: 'ES256',
|
|
6
|
+
type: 'JWT',
|
|
7
|
+
keyAlgorithm: { name: 'ECDSA', namedCurve: 'P-256' },
|
|
8
|
+
signAlgorithm: { name: 'ECDSA', hash: 'SHA-256' },
|
|
9
|
+
privateKeyFormat: 'pkcs8',
|
|
10
|
+
publicKeyFormat: 'spki',
|
|
11
|
+
separator: '.',
|
|
12
|
+
partCount: 3,
|
|
13
|
+
keyCacheLimit: 20,
|
|
14
|
+
};
|
|
@@ -2,6 +2,7 @@ import type { EmailPreviewInput } from '../models/email-preview-input';
|
|
|
2
2
|
export declare class EmailPreviewService {
|
|
3
3
|
static escape(value: string): string;
|
|
4
4
|
static hasContent(input: EmailPreviewInput): boolean;
|
|
5
|
+
static receivedOf(input: EmailPreviewInput): string;
|
|
5
6
|
static subjectOf(input: EmailPreviewInput): string;
|
|
6
7
|
static toText(input: EmailPreviewInput): string;
|
|
7
8
|
static toDocument(input: EmailPreviewInput): string;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.EmailPreviewService = void 0;
|
|
4
4
|
var email_preview_config_1 = require("./constants/email-preview-config");
|
|
5
|
+
var date_service_1 = require("./date-service");
|
|
5
6
|
var EmailPreviewService = /** @class */ (function () {
|
|
6
7
|
function EmailPreviewService() {
|
|
7
8
|
}
|
|
@@ -17,6 +18,9 @@ var EmailPreviewService = /** @class */ (function () {
|
|
|
17
18
|
return ((input.html !== null && input.html.trim() !== '') ||
|
|
18
19
|
(input.text !== null && input.text.trim() !== ''));
|
|
19
20
|
};
|
|
21
|
+
EmailPreviewService.receivedOf = function (input) {
|
|
22
|
+
return date_service_1.DateService.formatDateInTimezone(input.receivedAt, input.timezone, email_preview_config_1.EmailPreviewConfig.dateFormat);
|
|
23
|
+
};
|
|
20
24
|
EmailPreviewService.subjectOf = function (input) {
|
|
21
25
|
return input.subject === null || input.subject.trim() === ''
|
|
22
26
|
? email_preview_config_1.EmailPreviewConfig.fallbackSubject
|
|
@@ -34,13 +38,13 @@ var EmailPreviewService = /** @class */ (function () {
|
|
|
34
38
|
var body = input.html !== null && input.html.trim() !== ''
|
|
35
39
|
? EmailPreviewService.body(input.html)
|
|
36
40
|
: "<pre style=\"white-space:pre-wrap;font-family:inherit;margin:0;\">".concat(EmailPreviewService.escape(input.text === null ? '' : input.text), "</pre>");
|
|
37
|
-
return "<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t\t<title>".concat(EmailPreviewService.escape(subject), "</title>\n\t</head>\n\t<body style=\"margin:0;padding:24px;background:#f5f7fa;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;color:#101828;\">\n\t\t<div style=\"max-width:760px;margin:0 auto;background:#ffffff;border:1px solid #e4e8ee;border-radius:12px;overflow:hidden;\">\n\t\t\t<div style=\"padding:16px 20px;border-bottom:1px solid #e4e8ee;background:#f9fafb;\">\n\t\t\t\t<div style=\"font-size:15px;font-weight:700;\">").concat(EmailPreviewService.escape(subject), "</div>\n\t\t\t\t<div style=\"margin-top:4px;font-size:12px;color:#667085;\">From ").concat(EmailPreviewService.escape(input.from), " \u00B7 ").concat(EmailPreviewService.escape(input
|
|
41
|
+
return "<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t\t<title>".concat(EmailPreviewService.escape(subject), "</title>\n\t</head>\n\t<body style=\"margin:0;padding:24px;background:#f5f7fa;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;color:#101828;\">\n\t\t<div style=\"max-width:760px;margin:0 auto;background:#ffffff;border:1px solid #e4e8ee;border-radius:12px;overflow:hidden;\">\n\t\t\t<div style=\"padding:16px 20px;border-bottom:1px solid #e4e8ee;background:#f9fafb;\">\n\t\t\t\t<div style=\"font-size:15px;font-weight:700;\">").concat(EmailPreviewService.escape(subject), "</div>\n\t\t\t\t<div style=\"margin-top:4px;font-size:12px;color:#667085;\">From ").concat(EmailPreviewService.escape(input.from), " \u00B7 ").concat(EmailPreviewService.escape(EmailPreviewService.receivedOf(input)), "</div>\n\t\t\t</div>\n\t\t\t<div style=\"padding:20px;\">").concat(body, "</div>\n\t\t</div>\n\t</body>\n</html>");
|
|
38
42
|
};
|
|
39
43
|
EmailPreviewService.toImageMarkup = function (input) {
|
|
40
44
|
var page = email_preview_config_1.EmailPreviewConfig.page;
|
|
41
45
|
var width = page.width - page.padding * 2;
|
|
42
46
|
var subject = EmailPreviewService.clip(EmailPreviewService.subjectOf(input), Math.floor(width / (page.charWidth * page.subjectWidthRatio)));
|
|
43
|
-
var meta = EmailPreviewService.clip("From ".concat(input.from, " \u00B7 ").concat(input
|
|
47
|
+
var meta = EmailPreviewService.clip("From ".concat(input.from, " \u00B7 ").concat(EmailPreviewService.receivedOf(input)), Math.floor(width / (page.charWidth * page.metaWidthRatio)));
|
|
44
48
|
var lines = EmailPreviewService.wrap(EmailPreviewService.toText(input), Math.floor(width / page.charWidth)).slice(0, page.maxLines);
|
|
45
49
|
var metaBaseline = page.padding + page.subjectSize + 10 + page.metaSize;
|
|
46
50
|
var bodyTop = metaBaseline + page.headerGap;
|
package/dist/common/index.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export * from './envelope-crypto-service';
|
|
|
7
7
|
export * from './date-service';
|
|
8
8
|
export * from './number-format-service';
|
|
9
9
|
export * from './recaptcha-service';
|
|
10
|
+
export * from './signed-token-service';
|
|
10
11
|
export * from './url-service';
|
|
11
12
|
export * from './string-service';
|
|
12
13
|
export * from './transform-service';
|
|
14
|
+
export * from './user-token-service';
|
package/dist/common/index.js
CHANGED
|
@@ -23,6 +23,8 @@ __exportStar(require("./envelope-crypto-service"), exports);
|
|
|
23
23
|
__exportStar(require("./date-service"), exports);
|
|
24
24
|
__exportStar(require("./number-format-service"), exports);
|
|
25
25
|
__exportStar(require("./recaptcha-service"), exports);
|
|
26
|
+
__exportStar(require("./signed-token-service"), exports);
|
|
26
27
|
__exportStar(require("./url-service"), exports);
|
|
27
28
|
__exportStar(require("./string-service"), exports);
|
|
28
29
|
__exportStar(require("./transform-service"), exports);
|
|
30
|
+
__exportStar(require("./user-token-service"), exports);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { SignedTokenKeyPair } from '../models/signed-token-key-pair';
|
|
2
|
+
export declare class SignedTokenService {
|
|
3
|
+
private static _textEncoder;
|
|
4
|
+
private static _textDecoder;
|
|
5
|
+
private static _privateKeys;
|
|
6
|
+
private static _publicKeys;
|
|
7
|
+
static generateKeyPair(): Promise<SignedTokenKeyPair>;
|
|
8
|
+
static sign<Claims extends object>(claims: Claims, privateKey: string): Promise<string>;
|
|
9
|
+
static verify<Claims extends object>(token: string, publicKeys: string[]): Promise<Claims | null>;
|
|
10
|
+
private static _privateKey;
|
|
11
|
+
private static _publicKey;
|
|
12
|
+
private static _remember;
|
|
13
|
+
private static _encodeJson;
|
|
14
|
+
private static _decodeJson;
|
|
15
|
+
private static _toBase64;
|
|
16
|
+
private static _fromBase64;
|
|
17
|
+
private static _toBase64Url;
|
|
18
|
+
private static _fromBase64Url;
|
|
19
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
13
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
+
function step(op) {
|
|
16
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
18
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
+
switch (op[0]) {
|
|
21
|
+
case 0: case 1: t = op; break;
|
|
22
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
+
default:
|
|
26
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
+
if (t[2]) _.ops.pop();
|
|
31
|
+
_.trys.pop(); continue;
|
|
32
|
+
}
|
|
33
|
+
op = body.call(thisArg, _);
|
|
34
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.SignedTokenService = void 0;
|
|
40
|
+
var signed_token_config_1 = require("./constants/signed-token-config");
|
|
41
|
+
var SignedTokenService = /** @class */ (function () {
|
|
42
|
+
function SignedTokenService() {
|
|
43
|
+
}
|
|
44
|
+
SignedTokenService.generateKeyPair = function () {
|
|
45
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
46
|
+
var pair, privateKey, publicKey;
|
|
47
|
+
return __generator(this, function (_a) {
|
|
48
|
+
switch (_a.label) {
|
|
49
|
+
case 0: return [4 /*yield*/, crypto.subtle.generateKey(signed_token_config_1.SignedTokenConfig.keyAlgorithm, true, [
|
|
50
|
+
'sign',
|
|
51
|
+
'verify',
|
|
52
|
+
])];
|
|
53
|
+
case 1:
|
|
54
|
+
pair = (_a.sent());
|
|
55
|
+
return [4 /*yield*/, crypto.subtle.exportKey(signed_token_config_1.SignedTokenConfig.privateKeyFormat, pair.privateKey)];
|
|
56
|
+
case 2:
|
|
57
|
+
privateKey = _a.sent();
|
|
58
|
+
return [4 /*yield*/, crypto.subtle.exportKey(signed_token_config_1.SignedTokenConfig.publicKeyFormat, pair.publicKey)];
|
|
59
|
+
case 3:
|
|
60
|
+
publicKey = _a.sent();
|
|
61
|
+
return [2 /*return*/, {
|
|
62
|
+
privateKey: this._toBase64(new Uint8Array(privateKey)),
|
|
63
|
+
publicKey: this._toBase64(new Uint8Array(publicKey)),
|
|
64
|
+
}];
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
SignedTokenService.sign = function (claims, privateKey) {
|
|
70
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
71
|
+
var header, signingInput, signature, _a, _b, _c;
|
|
72
|
+
return __generator(this, function (_d) {
|
|
73
|
+
switch (_d.label) {
|
|
74
|
+
case 0:
|
|
75
|
+
header = {
|
|
76
|
+
alg: signed_token_config_1.SignedTokenConfig.algorithm,
|
|
77
|
+
typ: signed_token_config_1.SignedTokenConfig.type,
|
|
78
|
+
};
|
|
79
|
+
signingInput = "".concat(this._encodeJson(header)).concat(signed_token_config_1.SignedTokenConfig.separator).concat(this._encodeJson(claims));
|
|
80
|
+
_b = (_a = crypto.subtle).sign;
|
|
81
|
+
_c = [signed_token_config_1.SignedTokenConfig.signAlgorithm];
|
|
82
|
+
return [4 /*yield*/, this._privateKey(privateKey)];
|
|
83
|
+
case 1: return [4 /*yield*/, _b.apply(_a, _c.concat([_d.sent(), this._textEncoder.encode(signingInput)]))];
|
|
84
|
+
case 2:
|
|
85
|
+
signature = _d.sent();
|
|
86
|
+
return [2 /*return*/, "".concat(signingInput).concat(signed_token_config_1.SignedTokenConfig.separator).concat(this._toBase64Url(new Uint8Array(signature)))];
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
SignedTokenService.verify = function (token, publicKeys) {
|
|
92
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
93
|
+
var parts, encodedHeader, encodedClaims, encodedSignature, header, signature, signingInput, _i, publicKeys_1, publicKey, key, isValid;
|
|
94
|
+
return __generator(this, function (_a) {
|
|
95
|
+
switch (_a.label) {
|
|
96
|
+
case 0:
|
|
97
|
+
parts = token.split(signed_token_config_1.SignedTokenConfig.separator);
|
|
98
|
+
if (parts.length !== signed_token_config_1.SignedTokenConfig.partCount)
|
|
99
|
+
return [2 /*return*/, null];
|
|
100
|
+
encodedHeader = parts[0], encodedClaims = parts[1], encodedSignature = parts[2];
|
|
101
|
+
header = this._decodeJson(encodedHeader);
|
|
102
|
+
if (header === null ||
|
|
103
|
+
header.alg !== signed_token_config_1.SignedTokenConfig.algorithm ||
|
|
104
|
+
header.typ !== signed_token_config_1.SignedTokenConfig.type)
|
|
105
|
+
return [2 /*return*/, null];
|
|
106
|
+
signature = this._fromBase64Url(encodedSignature);
|
|
107
|
+
if (signature === null)
|
|
108
|
+
return [2 /*return*/, null];
|
|
109
|
+
signingInput = this._textEncoder.encode("".concat(encodedHeader).concat(signed_token_config_1.SignedTokenConfig.separator).concat(encodedClaims));
|
|
110
|
+
_i = 0, publicKeys_1 = publicKeys;
|
|
111
|
+
_a.label = 1;
|
|
112
|
+
case 1:
|
|
113
|
+
if (!(_i < publicKeys_1.length)) return [3 /*break*/, 5];
|
|
114
|
+
publicKey = publicKeys_1[_i];
|
|
115
|
+
return [4 /*yield*/, this._publicKey(publicKey).catch(function () { return null; })];
|
|
116
|
+
case 2:
|
|
117
|
+
key = _a.sent();
|
|
118
|
+
if (key === null)
|
|
119
|
+
return [3 /*break*/, 4];
|
|
120
|
+
return [4 /*yield*/, crypto.subtle
|
|
121
|
+
.verify(signed_token_config_1.SignedTokenConfig.signAlgorithm, key, signature, signingInput)
|
|
122
|
+
.catch(function () { return false; })];
|
|
123
|
+
case 3:
|
|
124
|
+
isValid = _a.sent();
|
|
125
|
+
if (isValid)
|
|
126
|
+
return [2 /*return*/, this._decodeJson(encodedClaims)];
|
|
127
|
+
_a.label = 4;
|
|
128
|
+
case 4:
|
|
129
|
+
_i++;
|
|
130
|
+
return [3 /*break*/, 1];
|
|
131
|
+
case 5: return [2 /*return*/, null];
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
};
|
|
136
|
+
SignedTokenService._privateKey = function (privateKey) {
|
|
137
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
138
|
+
var cached, key;
|
|
139
|
+
return __generator(this, function (_a) {
|
|
140
|
+
switch (_a.label) {
|
|
141
|
+
case 0:
|
|
142
|
+
cached = this._privateKeys.get(privateKey);
|
|
143
|
+
if (cached !== undefined)
|
|
144
|
+
return [2 /*return*/, cached];
|
|
145
|
+
return [4 /*yield*/, crypto.subtle.importKey(signed_token_config_1.SignedTokenConfig.privateKeyFormat, this._fromBase64(privateKey), signed_token_config_1.SignedTokenConfig.keyAlgorithm, false, ['sign'])];
|
|
146
|
+
case 1:
|
|
147
|
+
key = _a.sent();
|
|
148
|
+
this._remember(this._privateKeys, privateKey, key);
|
|
149
|
+
return [2 /*return*/, key];
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
};
|
|
154
|
+
SignedTokenService._publicKey = function (publicKey) {
|
|
155
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
156
|
+
var cached, key;
|
|
157
|
+
return __generator(this, function (_a) {
|
|
158
|
+
switch (_a.label) {
|
|
159
|
+
case 0:
|
|
160
|
+
cached = this._publicKeys.get(publicKey);
|
|
161
|
+
if (cached !== undefined)
|
|
162
|
+
return [2 /*return*/, cached];
|
|
163
|
+
return [4 /*yield*/, crypto.subtle.importKey(signed_token_config_1.SignedTokenConfig.publicKeyFormat, this._fromBase64(publicKey), signed_token_config_1.SignedTokenConfig.keyAlgorithm, false, ['verify'])];
|
|
164
|
+
case 1:
|
|
165
|
+
key = _a.sent();
|
|
166
|
+
this._remember(this._publicKeys, publicKey, key);
|
|
167
|
+
return [2 /*return*/, key];
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
};
|
|
172
|
+
SignedTokenService._remember = function (cache, source, key) {
|
|
173
|
+
if (cache.size >= signed_token_config_1.SignedTokenConfig.keyCacheLimit) {
|
|
174
|
+
var oldest = cache.keys().next().value;
|
|
175
|
+
if (oldest !== undefined)
|
|
176
|
+
cache.delete(oldest);
|
|
177
|
+
}
|
|
178
|
+
cache.set(source, key);
|
|
179
|
+
};
|
|
180
|
+
SignedTokenService._encodeJson = function (value) {
|
|
181
|
+
return this._toBase64Url(this._textEncoder.encode(JSON.stringify(value)));
|
|
182
|
+
};
|
|
183
|
+
SignedTokenService._decodeJson = function (encoded) {
|
|
184
|
+
var bytes = this._fromBase64Url(encoded);
|
|
185
|
+
if (bytes === null)
|
|
186
|
+
return null;
|
|
187
|
+
try {
|
|
188
|
+
var value = JSON.parse(this._textDecoder.decode(bytes));
|
|
189
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
190
|
+
? value
|
|
191
|
+
: null;
|
|
192
|
+
}
|
|
193
|
+
catch (_a) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
SignedTokenService._toBase64 = function (bytes) {
|
|
198
|
+
var binary = '';
|
|
199
|
+
for (var index = 0; index < bytes.length; index += 1) {
|
|
200
|
+
binary += String.fromCharCode(bytes[index]);
|
|
201
|
+
}
|
|
202
|
+
return btoa(binary);
|
|
203
|
+
};
|
|
204
|
+
SignedTokenService._fromBase64 = function (value) {
|
|
205
|
+
var binary = atob(value.trim());
|
|
206
|
+
var bytes = new Uint8Array(binary.length);
|
|
207
|
+
for (var index = 0; index < binary.length; index += 1) {
|
|
208
|
+
bytes[index] = binary.charCodeAt(index);
|
|
209
|
+
}
|
|
210
|
+
return bytes;
|
|
211
|
+
};
|
|
212
|
+
SignedTokenService._toBase64Url = function (bytes) {
|
|
213
|
+
return this._toBase64(bytes).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
214
|
+
};
|
|
215
|
+
SignedTokenService._fromBase64Url = function (value) {
|
|
216
|
+
if (!/^[A-Za-z0-9_-]*$/.test(value))
|
|
217
|
+
return null;
|
|
218
|
+
var base64 = value.replace(/-/g, '+').replace(/_/g, '/');
|
|
219
|
+
var padding = base64.length % 4 === 0 ? '' : '='.repeat(4 - (base64.length % 4));
|
|
220
|
+
try {
|
|
221
|
+
return this._fromBase64("".concat(base64).concat(padding));
|
|
222
|
+
}
|
|
223
|
+
catch (_a) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
SignedTokenService._textEncoder = new TextEncoder();
|
|
228
|
+
SignedTokenService._textDecoder = new TextDecoder();
|
|
229
|
+
SignedTokenService._privateKeys = new Map();
|
|
230
|
+
SignedTokenService._publicKeys = new Map();
|
|
231
|
+
return SignedTokenService;
|
|
232
|
+
}());
|
|
233
|
+
exports.SignedTokenService = SignedTokenService;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { UserTokenClaims } from '../models/user-token-claims';
|
|
2
|
+
import type { UserTokenIssueOptions } from '../models/user-token-issue-options';
|
|
3
|
+
import type { UserTokenRequest } from '../models/user-token-request';
|
|
4
|
+
import type { UserTokenSubject } from '../models/user-token-subject';
|
|
5
|
+
import type { UserTokenVerifyOptions } from '../models/user-token-verify-options';
|
|
6
|
+
export declare class UserTokenService {
|
|
7
|
+
static issue(subject: UserTokenSubject, request: UserTokenRequest, options: UserTokenIssueOptions): Promise<string>;
|
|
8
|
+
static verify(token: string, request: UserTokenRequest, options: UserTokenVerifyOptions): Promise<UserTokenClaims | null>;
|
|
9
|
+
private static _isWellFormed;
|
|
10
|
+
private static _now;
|
|
11
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
13
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
+
function step(op) {
|
|
16
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
18
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
+
switch (op[0]) {
|
|
21
|
+
case 0: case 1: t = op; break;
|
|
22
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
+
default:
|
|
26
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
+
if (t[2]) _.ops.pop();
|
|
31
|
+
_.trys.pop(); continue;
|
|
32
|
+
}
|
|
33
|
+
op = body.call(thisArg, _);
|
|
34
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.UserTokenService = void 0;
|
|
40
|
+
var user_token_config_1 = require("./constants/user-token-config");
|
|
41
|
+
var signed_token_service_1 = require("./signed-token-service");
|
|
42
|
+
var UserTokenService = /** @class */ (function () {
|
|
43
|
+
function UserTokenService() {
|
|
44
|
+
}
|
|
45
|
+
UserTokenService.issue = function (subject, request, options) {
|
|
46
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
47
|
+
var issuedAt, lifetime, claims;
|
|
48
|
+
return __generator(this, function (_a) {
|
|
49
|
+
issuedAt = this._now();
|
|
50
|
+
lifetime = typeof options.lifetimeSeconds === 'number'
|
|
51
|
+
? Math.min(options.lifetimeSeconds, user_token_config_1.UserTokenConfig.maxLifetimeSeconds)
|
|
52
|
+
: user_token_config_1.UserTokenConfig.defaultLifetimeSeconds;
|
|
53
|
+
claims = {
|
|
54
|
+
iss: options.issuer,
|
|
55
|
+
aud: options.audience,
|
|
56
|
+
sub: subject.id,
|
|
57
|
+
email: subject.email.trim().toLowerCase(),
|
|
58
|
+
name: subject.displayName,
|
|
59
|
+
htm: request.method.toUpperCase(),
|
|
60
|
+
htu: request.path,
|
|
61
|
+
iat: issuedAt,
|
|
62
|
+
exp: issuedAt + lifetime,
|
|
63
|
+
jti: crypto.randomUUID(),
|
|
64
|
+
};
|
|
65
|
+
return [2 /*return*/, signed_token_service_1.SignedTokenService.sign(claims, options.privateKey)];
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
UserTokenService.verify = function (token, request, options) {
|
|
70
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
71
|
+
var claims, now, maxLifetime;
|
|
72
|
+
return __generator(this, function (_a) {
|
|
73
|
+
switch (_a.label) {
|
|
74
|
+
case 0: return [4 /*yield*/, signed_token_service_1.SignedTokenService.verify(token, options.publicKeys)];
|
|
75
|
+
case 1:
|
|
76
|
+
claims = _a.sent();
|
|
77
|
+
if (claims === null || !this._isWellFormed(claims))
|
|
78
|
+
return [2 /*return*/, null];
|
|
79
|
+
now = this._now();
|
|
80
|
+
maxLifetime = typeof options.maxLifetimeSeconds === 'number'
|
|
81
|
+
? options.maxLifetimeSeconds
|
|
82
|
+
: user_token_config_1.UserTokenConfig.maxLifetimeSeconds;
|
|
83
|
+
if (claims.iss !== options.issuer ||
|
|
84
|
+
claims.aud !== options.audience ||
|
|
85
|
+
claims.htm !== request.method.toUpperCase() ||
|
|
86
|
+
claims.htu !== request.path ||
|
|
87
|
+
claims.iat > now + user_token_config_1.UserTokenConfig.clockSkewSeconds ||
|
|
88
|
+
claims.exp <= now - user_token_config_1.UserTokenConfig.clockSkewSeconds ||
|
|
89
|
+
claims.exp - claims.iat > maxLifetime)
|
|
90
|
+
return [2 /*return*/, null];
|
|
91
|
+
return [2 /*return*/, claims];
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
UserTokenService._isWellFormed = function (claims) {
|
|
97
|
+
return (typeof claims.iss === 'string' &&
|
|
98
|
+
typeof claims.aud === 'string' &&
|
|
99
|
+
typeof claims.sub === 'string' &&
|
|
100
|
+
typeof claims.email === 'string' &&
|
|
101
|
+
claims.email !== '' &&
|
|
102
|
+
typeof claims.name === 'string' &&
|
|
103
|
+
typeof claims.htm === 'string' &&
|
|
104
|
+
typeof claims.htu === 'string' &&
|
|
105
|
+
typeof claims.iat === 'number' &&
|
|
106
|
+
typeof claims.exp === 'number' &&
|
|
107
|
+
typeof claims.jti === 'string' &&
|
|
108
|
+
claims.jti !== '');
|
|
109
|
+
};
|
|
110
|
+
UserTokenService._now = function () {
|
|
111
|
+
return Math.floor(Date.now() / user_token_config_1.UserTokenConfig.millisecondsPerSecond);
|
|
112
|
+
};
|
|
113
|
+
return UserTokenService;
|
|
114
|
+
}());
|
|
115
|
+
exports.UserTokenService = UserTokenService;
|
package/dist/index.d.ts
CHANGED
|
@@ -3,5 +3,12 @@ import * as CommonNamespace from './common';
|
|
|
3
3
|
export declare const Common: typeof CommonNamespace;
|
|
4
4
|
export declare const Client: typeof ClientNamespace;
|
|
5
5
|
export type { EmailPreviewInput } from './models/email-preview-input';
|
|
6
|
+
export type { SignedTokenHeader } from './models/signed-token-header';
|
|
7
|
+
export type { SignedTokenKeyPair } from './models/signed-token-key-pair';
|
|
8
|
+
export type { UserTokenClaims } from './models/user-token-claims';
|
|
9
|
+
export type { UserTokenIssueOptions } from './models/user-token-issue-options';
|
|
10
|
+
export type { UserTokenRequest } from './models/user-token-request';
|
|
11
|
+
export type { UserTokenSubject } from './models/user-token-subject';
|
|
12
|
+
export type { UserTokenVerifyOptions } from './models/user-token-verify-options';
|
|
6
13
|
export type { EncryptedFieldsSpec } from './common/encrypted-fields-service';
|
|
7
14
|
export type { DataKeyPair } from './common/envelope-crypto-service';
|