-
Notifications
You must be signed in to change notification settings - Fork 353
Expand file tree
/
Copy pathutils.ts
More file actions
250 lines (204 loc) · 6.19 KB
/
utils.ts
File metadata and controls
250 lines (204 loc) · 6.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
import {
err,
fromAsyncThrowable,
fromPromise,
fromThrowable,
ok,
Result,
} from "neverthrow";
import {
compactVerify,
importJWK,
importSPKI,
importX509,
KeyLike,
} from "jose";
import nodeForge from "node-forge";
import base64url from "base64url";
import { algDictionary } from "@/features/common/values/jws-alg-header-parameter-values.dictionary";
export const extractJwt = (value: string): string => {
if (!value) {
return "";
}
// Check if it's a JWT string with newlines - compact it if so
if (value.trim().startsWith("ey") && (value.match(/\./g) || []).length >= 2) {
// It looks like a valid JWT, so remove all whitespace (including newlines)
return value.replace(/\s+/g, "");
}
// Otherwise, use the extraction logic to find JWT in a larger text block
const jwt = value.split(/\s+/).filter((element) => element.startsWith("ey"));
return jwt[0] || value;
};
export const safeParseInt = fromThrowable(parseInt, (e) => {
if (e instanceof Error) {
return e.message;
}
return "Cannot parse string into number.";
});
export const safeJsonParse = fromThrowable(JSON.parse, (e) => {
if (e instanceof Error) {
return e.message;
}
return "Cannot parse string into JSON.";
});
export const safeBase64url = fromThrowable(base64url, (e) => {
if (e instanceof Error) {
return e.message;
}
return "Cannot encode value into base64url.";
});
export const safeDecodeBase64url = fromThrowable(base64url.decode, (e) => {
if (e instanceof Error) {
return e.message;
}
return "Cannot decode value from base64url.";
});
export const safeBase64urlToBuffer = fromThrowable(base64url.toBuffer, (e) => {
if (e instanceof Error) {
return e.message;
}
return "Cannot convert base64url-encoded value to a Buffer containing the decoded bytes.";
});
export const safeBufferFrom = fromThrowable(Buffer.from, (e) => {
if (e instanceof Error) {
return e.message;
}
return "Cannot creates a new Buffer using the passed value";
});
export const safeJsonStringify = fromThrowable(JSON.stringify, (e) => {
if (e instanceof Error) {
return e.message;
}
return "Cannot convert object into JSON string.";
});
export const safeNewUint8ArrayFromBuffer = fromThrowable(
(buffer: Buffer) => new Uint8Array(buffer),
(e) => {
if (e instanceof Error) {
return e.message;
}
return "Can't create array of 8-bit unsigned integers.";
},
);
/**
* Why do we need to create this binding?
* Source: https://ardislu.dev/illegal-invocation-on-map
*/
const encode = TextEncoder.prototype.encode.bind(new TextEncoder());
export const safeTextEncode = fromThrowable(encode, (e) => {
if (e instanceof Error) {
return e.message;
}
return "Unable to encode given string into an array of 8-bit unsigned integers.";
});
export const getTimeClaimProcessedValue = (
time: string,
): Result<string, string> => {
try {
if (/\d+,?$/.test(time)) {
return ok(new Date(parseInt(time, 10) * 1000).toString());
} else {
return ok(new Date(time.replace(/[",]/g, "")).toString());
}
} catch (e) {
return err("Invalid date");
}
};
export const operationExceptionDictionary = {
TypeError:
"Operation could not be performed as value is not of the expected type.",
NotSupportedError: "The algorithm is not supported.",
SyntaxError: "A required parameter was missing or out-of-range.",
InvalidAccessError:
"The requested operation is not valid for the provided key.",
DataError: "Data provided to the operation does not meet requirements.",
OperationError: "The operation failed for an operation-specific reason.",
};
export const getOperationException = ({
e,
defaultMessage,
}: {
e: unknown;
defaultMessage: string;
}): string => {
if (!(e instanceof Error)) {
return defaultMessage;
}
if (e.name === "TypeError") {
return operationExceptionDictionary.TypeError;
}
if (e.name === "NotSupportedError" || e.name === "JOSENotSupported") {
return operationExceptionDictionary.NotSupportedError;
}
if (e.name === "SyntaxError") {
return operationExceptionDictionary.SyntaxError;
}
if (e.name === "InvalidAccessError") {
return operationExceptionDictionary.InvalidAccessError;
}
if (e.name === "DataError") {
return operationExceptionDictionary.DataError;
}
if (e.name === "OperationError") {
return operationExceptionDictionary.OperationError;
}
return e.message || defaultMessage;
};
export const safeImportSPKI = fromAsyncThrowable(importSPKI, (e) => {
const message =
"Cannot import PEM-encoded SPKI string as a runtime-specific public key representation.";
return getOperationException({
e,
defaultMessage: message,
});
});
export const safeImportX509 = fromAsyncThrowable(importX509, (e) => {
const message = `"x509" must be X.509 formatted string. [Learn more](https://en.wikipedia.org/wiki/X.509).`;
return getOperationException({
e,
defaultMessage: message,
});
});
export const safeImportJWK = fromAsyncThrowable(importJWK, (e) => {
const message = "Cannot import JWK to a runtime-specific key representation.";
return getOperationException({
e,
defaultMessage: message,
});
});
export const safeCompactVerify = (jws: string, key: Uint8Array | KeyLike) => {
return fromPromise(compactVerify(jws, key), (e) => {
if (e instanceof Error) {
if (e.name === "JWSSignatureVerificationFailed") {
return new Error(
"Signature verification failed. Try again with another value.",
);
}
return new Error(e.message);
}
return new Error("Unable to verify signature using that value.");
});
};
export const safePublicKeyFromPem = fromThrowable(
nodeForge.pki.publicKeyFromPem,
(e) => {
if (e instanceof Error) {
return e.message;
}
return "Unable to get public key from PEM string.";
},
);
export const safePublicKeyToPem = fromThrowable(
nodeForge.pki.publicKeyToPem,
(e) => {
if (e instanceof Error) {
return e.message;
}
return "Unable to transform public key to PEM string.";
},
);
export const getAlgName = (value: string) => {
return value === algDictionary.Ed25519 || value === algDictionary.Ed448
? algDictionary.EdDSA
: value;
};