webdaemon 11.4.1 → 11.4.3
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/index.js +1627 -0
- package/dist/index.js.map +7 -0
- package/package.json +16 -3
- package/index.js +0 -17
- package/package.json.template +0 -13
- package/src/js/Alert.js +0 -33
- package/src/js/BrowserApp.js +0 -334
- package/src/js/Digest.js +0 -45
- package/src/js/KeyPair.js +0 -38
- package/src/js/ParentHelper.js +0 -302
- package/src/js/QrCode.js +0 -37
- package/src/js/README.html +0 -13
- package/src/js/README.md +0 -4
- package/src/js/README.pdf +0 -0
- package/src/js/Storage.js +0 -155
- package/src/js/Token.js +0 -529
- package/src/ts/Assertions.ts +0 -41
- package/src/ts/Lifecycle.ts +0 -307
- package/src/ts/README.html +0 -13
- package/src/ts/README.md +0 -4
- package/src/ts/README.pdf +0 -0
- package/src/ts/Requests.ts +0 -131
- package/src/ts/Responses.ts +0 -132
package/src/js/Token.js
DELETED
|
@@ -1,529 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Encapsulates the functionality associated with a bearer token normally
|
|
3
|
-
* passed in a request `x-tabserver-token` header as a base64-encoded
|
|
4
|
-
* JSON object.
|
|
5
|
-
*
|
|
6
|
-
* Note the type definition annotations which are helpful when using this
|
|
7
|
-
* Javascript class in Typescript.
|
|
8
|
-
*
|
|
9
|
-
* Example token:
|
|
10
|
-
*
|
|
11
|
-
* {
|
|
12
|
-
* "iss": "http://foo.daemon/some/public.jwk",
|
|
13
|
-
* "aud": "https://bar.daemon",
|
|
14
|
-
* "sub": "https://foo.daemon",
|
|
15
|
-
* "src": "https://some.com",
|
|
16
|
-
* "scope": {
|
|
17
|
-
* "http://localhost:9000/helloWorld1.html": "read write",
|
|
18
|
-
* "party:control": "grant revoke"
|
|
19
|
-
* }
|
|
20
|
-
* "iat": 1698576567000,
|
|
21
|
-
* "exp": 1698576344000,
|
|
22
|
-
* "sig": "long base64 string"
|
|
23
|
-
* }
|
|
24
|
-
*
|
|
25
|
-
* Generally:
|
|
26
|
-
* iss - the issuer URL references the public JWK used to verify the sig.
|
|
27
|
-
* aud - the party handling the request, expressed as an origin.
|
|
28
|
-
* sub - the counterparty making the request, expressed as an origin.
|
|
29
|
-
* src - the source HTML file making the request, protocol://host/path only.
|
|
30
|
-
* scope - a map of requested source -> scope pairs.
|
|
31
|
-
* A scope is a space-separated list of capabilities.
|
|
32
|
-
* iat - issued at time in absolute millis.
|
|
33
|
-
* exp - expiry time, ditto.
|
|
34
|
-
* sig - the base64 signature, verified by the JWK pointed to by iss.
|
|
35
|
-
*
|
|
36
|
-
* Normally the sub field must match the host portion of the iss URL. But
|
|
37
|
-
* the possiblity remains open for an intermediary trusted by the party to
|
|
38
|
-
* hold the public keys of counterparties.
|
|
39
|
-
*/
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* @typedef {object.<string, string>} Scope
|
|
43
|
-
*/
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* @typedef TokenPayload
|
|
47
|
-
* @type {object<string, Token>}
|
|
48
|
-
* @property {string} iss // URL of public key of counterparty making request.
|
|
49
|
-
* @property {string} aud // Party origin handling the request.
|
|
50
|
-
* @property {string} sub // Counterparty origin making the request.
|
|
51
|
-
* @property {string} src // Source HTML document context of the request excluding fragment and query.
|
|
52
|
-
* @property {Scope} scope // Map of scope (space-separated capabilities) keyed by source URL.
|
|
53
|
-
* @property {number} iat // Issued at time.
|
|
54
|
-
* @property {number} exp // Expiry time.
|
|
55
|
-
*
|
|
56
|
-
* @typedef SignedTokenPayload
|
|
57
|
-
* @type {TokenPayload}
|
|
58
|
-
* @property {string} sig // Counterparty signs, `iss` used to verify.
|
|
59
|
-
*/
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* @typedef Signatory
|
|
63
|
-
* @type {object}
|
|
64
|
-
* @property {string} role // Role label of this signatory.
|
|
65
|
-
* @property {JSONWebKey} jwk // Public JWK of this signatory.
|
|
66
|
-
*/
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* @typedef TokenSet
|
|
70
|
-
* @type {object<string, Token>}
|
|
71
|
-
*/
|
|
72
|
-
|
|
73
|
-
/** Must match algorithm in Keypair.js */
|
|
74
|
-
const ALGORITHM = {
|
|
75
|
-
name: 'RSASSA-PKCS1-v1_5',
|
|
76
|
-
hash: 'SHA-256'
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/** Matches an origin with protocol and no path. */
|
|
80
|
-
const MATCH_ORIGIN = /https?:\/\/[^\/]+$/
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Allow a 30s leeway to allow for clock difference when checking
|
|
84
|
-
* token issued at time. We allow our clock to be up to 30s behind
|
|
85
|
-
* the system that generated a token.
|
|
86
|
-
*/
|
|
87
|
-
const TOKEN_IAT_LEEWAY_MILLIS = 1000 * 30
|
|
88
|
-
|
|
89
|
-
export class Token {
|
|
90
|
-
|
|
91
|
-
// Standard sources.
|
|
92
|
-
static Source = {
|
|
93
|
-
PARTY_CONTROL: 'party:control' // Party control subject, a pseudo-source with scope granted to counterparties.
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Standard counterparty pattern.
|
|
97
|
-
static Counterparty = {
|
|
98
|
-
PUBLIC: 'public' // Public counterparty, matching requests with no host specified in sub.
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Standard signatory roles.
|
|
102
|
-
static SignatoryRole = {
|
|
103
|
-
PARTY: 'party',
|
|
104
|
-
PARENT: 'parent'
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Standard scope labels.
|
|
108
|
-
static Capability = {
|
|
109
|
-
OPEN: 'open', // Capability to open source files and start tab runners.
|
|
110
|
-
CLOSE: 'close', // Capability to close source files and stop tab runners.
|
|
111
|
-
GRANT: 'grant', // Capability to create relation records.
|
|
112
|
-
REVOKE: 'revoke', // Capability to delete relation records.
|
|
113
|
-
OFFER: 'offer', // Capability to create a party offer for a device to claim.
|
|
114
|
-
RETRACT: 'retract', // Capability to delete a party offer before it is claimed.
|
|
115
|
-
DELETE: 'delete', // Capability to delete devices.
|
|
116
|
-
CATALOG: 'catalog', // Capability to get sources, devices, alerts etc.
|
|
117
|
-
ALIAS: 'alias', // Capability to set the alias for a counterparty host.
|
|
118
|
-
UNALIAS: 'unalias', // Capability to unset the alias for a counterparty host.
|
|
119
|
-
GET_ITEM: 'getitem', // Capability to get a storage item.
|
|
120
|
-
SET_ITEM: 'setitem', // Capability to set (and remove) a storage item.
|
|
121
|
-
ALERT: 'alert', // Capability to post and delete alerts.
|
|
122
|
-
LOG: 'log', // Capability to view logs.
|
|
123
|
-
READ: 'read', // Capability to read from drive.
|
|
124
|
-
WRITE: 'write' // Capability to write to drive.
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
static DEFAULT_EXPIRY_MILLIS = 1000 * 60 * 60 * 24
|
|
128
|
-
static TOKEN_HEADER = 'x-tabserver-token'
|
|
129
|
-
static SCOPE_SEPARATOR = /[\s,;\|]+/ // Specification per String.split() function.
|
|
130
|
-
|
|
131
|
-
/** @type{Token} */
|
|
132
|
-
#payload = null
|
|
133
|
-
|
|
134
|
-
/** @type{string} */
|
|
135
|
-
#signatureBase64 = null
|
|
136
|
-
|
|
137
|
-
/** @type{URL} */
|
|
138
|
-
#audUrl
|
|
139
|
-
|
|
140
|
-
/** @type{URL} */
|
|
141
|
-
#subUrl
|
|
142
|
-
|
|
143
|
-
/** @type{URL} */
|
|
144
|
-
#srcUrl
|
|
145
|
-
|
|
146
|
-
/** @type {Signatory} */
|
|
147
|
-
#signatory // Set upon successful verification of signature.
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Constructor takes either a signed base64 token, or a payload object.
|
|
152
|
-
*
|
|
153
|
-
* If successful, the payload and optionally the signature fields are
|
|
154
|
-
* populated.
|
|
155
|
-
*
|
|
156
|
-
* @param {object | string} source a payload, or a base64 token
|
|
157
|
-
*/
|
|
158
|
-
constructor(source) {
|
|
159
|
-
let payload = null
|
|
160
|
-
|
|
161
|
-
if (typeof source == 'object') {
|
|
162
|
-
payload = source
|
|
163
|
-
}
|
|
164
|
-
else if (typeof source == 'string') {
|
|
165
|
-
try {
|
|
166
|
-
payload = JSON.parse(atob(source))
|
|
167
|
-
}
|
|
168
|
-
catch (_e) {
|
|
169
|
-
throw 'Invalid token format'
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
else {
|
|
173
|
-
throw `Cannot construct token from ${typeof source}`
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Separate out the signature from the payload.
|
|
177
|
-
this.#signatureBase64 = payload.sig
|
|
178
|
-
delete payload.sig
|
|
179
|
-
this.#payload = payload
|
|
180
|
-
|
|
181
|
-
const {
|
|
182
|
-
iss,
|
|
183
|
-
aud,
|
|
184
|
-
sub,
|
|
185
|
-
src,
|
|
186
|
-
scope,
|
|
187
|
-
iat,
|
|
188
|
-
exp
|
|
189
|
-
} = payload
|
|
190
|
-
|
|
191
|
-
// Check payload is complete.
|
|
192
|
-
if ((!iss || !aud || !sub || !src || !scope || !iat || !exp)) {
|
|
193
|
-
throw 'Token must include iss, aud, sub, src, scope, iat and exp'
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// Check aud, sub and src fields are origins (i.e. proto://host.name)
|
|
197
|
-
if (
|
|
198
|
-
!aud.match(MATCH_ORIGIN) ||
|
|
199
|
-
!sub.match(MATCH_ORIGIN)
|
|
200
|
-
) {
|
|
201
|
-
throw 'The aud and sub attributes must be origins'
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
this.#audUrl = new URL(aud)
|
|
205
|
-
this.#subUrl = new URL(sub)
|
|
206
|
-
|
|
207
|
-
// The `src` attribute must not have query or fragment.
|
|
208
|
-
if (
|
|
209
|
-
src.includes('?') ||
|
|
210
|
-
src.includes('#')
|
|
211
|
-
) {
|
|
212
|
-
throw 'The src attribute must have no query or fragment component.'
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
this.#srcUrl = new URL(src)
|
|
216
|
-
|
|
217
|
-
this.#payload = payload
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Generates the signature for the object using the private key provided by the
|
|
222
|
-
* supplied callback and stores the result.
|
|
223
|
-
*
|
|
224
|
-
* @param {Promise<JsonWebKey>} privateJwkPromise the private key used for signing.
|
|
225
|
-
* @return {Promise<string>} resolved with the base64 signature if signed successfully.
|
|
226
|
-
*/
|
|
227
|
-
async signWith(privateJwkPromise) {
|
|
228
|
-
if (this.#payload == null) {
|
|
229
|
-
throw 'No payload to sign'
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const privateJwk = await privateJwkPromise
|
|
233
|
-
const privateKey = await crypto.subtle.importKey('jwk', privateJwk, ALGORITHM, true, ['sign'])
|
|
234
|
-
const toSign = JSON.stringify(this.#payload)
|
|
235
|
-
const messageBuffer = new TextEncoder().encode(toSign).buffer
|
|
236
|
-
const signature = await crypto.subtle.sign(ALGORITHM, privateKey, messageBuffer)
|
|
237
|
-
const signatureBase64 = Token.bytesToBase64(new Uint8Array(signature))
|
|
238
|
-
this.#signatureBase64 = signatureBase64
|
|
239
|
-
return signatureBase64
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* Retrieves the signatory using the `iss` field, checks the signature
|
|
244
|
-
* and if valid sets the signatory property in this instance.
|
|
245
|
-
*
|
|
246
|
-
* @throws {string} if signatory cannot be retrieved or signature doesn't match.
|
|
247
|
-
*/
|
|
248
|
-
async verifySignatory() {
|
|
249
|
-
await this.fetchSignatory()
|
|
250
|
-
await this.checkSignature(Promise.resolve(this.#signatory.jwk))
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Fetches the signatory from the `iss` URL, setting the instance
|
|
255
|
-
* property.
|
|
256
|
-
*
|
|
257
|
-
* The signatory comprises the `role` label and the public `jwk`.
|
|
258
|
-
*
|
|
259
|
-
* Caching may be introduced to reduce latency and bandwidth use.
|
|
260
|
-
*
|
|
261
|
-
* @throws {string} error if fetch fails or returns an error response.
|
|
262
|
-
*/
|
|
263
|
-
async fetchSignatory() {
|
|
264
|
-
const issuer = this.getIssuer()
|
|
265
|
-
try {
|
|
266
|
-
const response = await fetch(issuer, {
|
|
267
|
-
headers: {
|
|
268
|
-
'content-type': 'application/json'
|
|
269
|
-
}
|
|
270
|
-
})
|
|
271
|
-
|
|
272
|
-
const json = await response.json()
|
|
273
|
-
if ('error' in json) {
|
|
274
|
-
throw json.error
|
|
275
|
-
}
|
|
276
|
-
this.#signatory = json.ok
|
|
277
|
-
}
|
|
278
|
-
catch (_e) {
|
|
279
|
-
throw `Failed to read signatory at ${issuer}`
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Checks the signature using the public key provided by the supplied callback,
|
|
285
|
-
* and stores the result.
|
|
286
|
-
*
|
|
287
|
-
* @param {Promise<JsonWebKey>} publicJwkPromise the public key used to verify the signature.
|
|
288
|
-
* @throws {string} exception if signature cannot be verified or the public key is null or error.
|
|
289
|
-
*/
|
|
290
|
-
async checkSignature(publicJwkPromise) {
|
|
291
|
-
const publicJwk = await publicJwkPromise
|
|
292
|
-
if (!publicJwk || 'error' in publicJwk) {
|
|
293
|
-
throw publicJwk.error
|
|
294
|
-
}
|
|
295
|
-
const publicKey = await crypto.subtle.importKey('jwk', publicJwk, ALGORITHM, true, ['verify'])
|
|
296
|
-
const toCheck = JSON.stringify(this.#payload)
|
|
297
|
-
const messageBuffer = new TextEncoder().encode(toCheck).buffer
|
|
298
|
-
const sigBuffer = Token.base64ToBytes(this.#signatureBase64).buffer
|
|
299
|
-
const isVerified = await crypto.subtle.verify(ALGORITHM, publicKey, sigBuffer, messageBuffer)
|
|
300
|
-
if (!isVerified) {
|
|
301
|
-
throw 'Signature cannot be verified'
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* Returns the payload with the `sig` property added.
|
|
307
|
-
*
|
|
308
|
-
* @return {SignedTokenPayload} the payload with additional `sig` property.
|
|
309
|
-
*/
|
|
310
|
-
getSignedPayload() {
|
|
311
|
-
if (this.#signatureBase64 === null) {
|
|
312
|
-
throw 'Not yet signed'
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
return {
|
|
316
|
-
...this.#payload,
|
|
317
|
-
sig: this.#signatureBase64
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* Returns the payload whether signed or not.
|
|
323
|
-
*
|
|
324
|
-
* @return {TokenPayload} the payload object without signature.
|
|
325
|
-
*/
|
|
326
|
-
getPayload() {
|
|
327
|
-
return this.#payload
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
/**
|
|
331
|
-
* Returns the `aud` field as a string.
|
|
332
|
-
*
|
|
333
|
-
* @return {string} the `aud` field value.
|
|
334
|
-
*/
|
|
335
|
-
getAud() {
|
|
336
|
-
return this.#audUrl.origin
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Returns the `aud` host which is the party name handling the request.
|
|
342
|
-
*
|
|
343
|
-
* @return {string} the party name.
|
|
344
|
-
*/
|
|
345
|
-
getParty() {
|
|
346
|
-
return this.#audUrl.host
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* Returns the `sub` field as a string.
|
|
351
|
-
*
|
|
352
|
-
* @return {string} the `sub` field value.
|
|
353
|
-
*/
|
|
354
|
-
getSub() {
|
|
355
|
-
return this.#subUrl.origin
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
/**
|
|
359
|
-
* Returns the `sub` host, which is the counterparty name making the
|
|
360
|
-
* request.
|
|
361
|
-
*
|
|
362
|
-
* @return {string} the counterparty name.
|
|
363
|
-
*/
|
|
364
|
-
getCounterparty() {
|
|
365
|
-
return this.#subUrl.host
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* Returns the role of the signatory who signed on behalf of the
|
|
370
|
-
* counterparty.
|
|
371
|
-
*
|
|
372
|
-
* @returns {string} role of the verified signatory.
|
|
373
|
-
*/
|
|
374
|
-
getSignatoryRole() {
|
|
375
|
-
return this.#signatory.role
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* Returns the `src` URL.
|
|
380
|
-
*
|
|
381
|
-
* @return {URL} the source URL.
|
|
382
|
-
*/
|
|
383
|
-
getSourceUrl() {
|
|
384
|
-
return this.#srcUrl
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
/**
|
|
388
|
-
* Returns the `issuer` field as a URL. The counterparty is inferred from
|
|
389
|
-
* the host name which generally matches the sub field.
|
|
390
|
-
*
|
|
391
|
-
* @return {URL} the issuer URL.
|
|
392
|
-
*/
|
|
393
|
-
getIssuer() {
|
|
394
|
-
return new URL(this.getPayload().iss)
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
/**
|
|
398
|
-
* Returns the list of zero or more sources for which scope is requested.
|
|
399
|
-
*
|
|
400
|
-
* @return {string[]} a list of source URL strings.
|
|
401
|
-
*/
|
|
402
|
-
getSources() {
|
|
403
|
-
const sources = []
|
|
404
|
-
for (const source in this.#payload.scope) {
|
|
405
|
-
sources.push(source)
|
|
406
|
-
}
|
|
407
|
-
return sources
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/**
|
|
411
|
-
* Returns the scope for the source as an array of capability tokens.
|
|
412
|
-
*
|
|
413
|
-
* These can be separated by any mix of space, comma, semicolon
|
|
414
|
-
* or vertical bar.
|
|
415
|
-
*
|
|
416
|
-
* @param {string} source whose capabilities are returned.
|
|
417
|
-
* @return {string[]} zero or more scope tokens.
|
|
418
|
-
*/
|
|
419
|
-
getCapabilities(source) {
|
|
420
|
-
const scope = this.#payload.scope
|
|
421
|
-
if (! (source in scope)) {
|
|
422
|
-
throw `Source '${source}' not in scope`
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
return scope[source].split(Token.SCOPE_SEPARATOR)
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
/**
|
|
429
|
-
* Returns true if the supplied capability is present in the token
|
|
430
|
-
* scope under the specified source, otherwise false.
|
|
431
|
-
*
|
|
432
|
-
* @param {string} source the source under which the capability is expected.
|
|
433
|
-
* @param {string} capability the capability being tested.
|
|
434
|
-
* @return {boolean} true if capability is present in the scope, otherwise false.
|
|
435
|
-
*/
|
|
436
|
-
hasCapability(source, capability) {
|
|
437
|
-
return this.getCapabilities(source).includes(capability)
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
/**
|
|
441
|
-
* Checks the token is within the period defined by the `iat` and `exp`
|
|
442
|
-
* date fields.
|
|
443
|
-
*
|
|
444
|
-
* @throws {string} exception if not within valid period.
|
|
445
|
-
*/
|
|
446
|
-
checkPeriod() {
|
|
447
|
-
const {
|
|
448
|
-
iat,
|
|
449
|
-
exp
|
|
450
|
-
} = this.#payload
|
|
451
|
-
|
|
452
|
-
const now = Date.now()
|
|
453
|
-
if (now < iat - TOKEN_IAT_LEEWAY_MILLIS) {
|
|
454
|
-
throw 'Token is not yet valid'
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
if (now > exp) {
|
|
458
|
-
throw 'Token has expired'
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
/**
|
|
463
|
-
* Returns the signed object including the `sig` property as a base64 string.
|
|
464
|
-
*
|
|
465
|
-
* @return {string} the signed object as a base64 string.
|
|
466
|
-
*/
|
|
467
|
-
asSignedBase64() {
|
|
468
|
-
return btoa(JSON.stringify(this.getSignedPayload()))
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
/**
|
|
472
|
-
* Returns a base64 JSON string corresponding to the provided byte array.
|
|
473
|
-
*
|
|
474
|
-
* @param {Uint8Array} bytes to be encoded as a base64 string.
|
|
475
|
-
* @returns {string} the encoded bytes.
|
|
476
|
-
*/
|
|
477
|
-
static bytesToBase64(bytes) {
|
|
478
|
-
const binString = Array.from(bytes, (x) => String.fromCodePoint(x)).join('');
|
|
479
|
-
return btoa(binString);
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
/**
|
|
483
|
-
* Returns the byte array decoded from the base64 string.
|
|
484
|
-
*
|
|
485
|
-
* @param {string} base64
|
|
486
|
-
* @returns {Uint8Array} the decoded bytes.
|
|
487
|
-
*/
|
|
488
|
-
static base64ToBytes(base64) {
|
|
489
|
-
const binString = atob(base64);
|
|
490
|
-
return Uint8Array.from(binString, (m) => m.codePointAt(0));
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
/**
|
|
494
|
-
* Returns a new token using the x-tabserver-token header on the
|
|
495
|
-
* request.
|
|
496
|
-
*
|
|
497
|
-
* @param {Request} the request.
|
|
498
|
-
* @return {Token | null} the token, or null if no header is present.
|
|
499
|
-
* @throws {string} exception if token cannot be constructed.
|
|
500
|
-
*/
|
|
501
|
-
static from(request) {
|
|
502
|
-
if (request.headers.has(Token.TOKEN_HEADER)) {
|
|
503
|
-
return new Token(request.headers.get(Token.TOKEN_HEADER))
|
|
504
|
-
}
|
|
505
|
-
return null
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
/**
|
|
509
|
-
* Converts an object whose string keys map to token objects
|
|
510
|
-
* into URL-friendly search params suitable for use in a
|
|
511
|
-
* query string or fragment.
|
|
512
|
-
*
|
|
513
|
-
* The string keys are normally the audience hostname, or the
|
|
514
|
-
* special key 'party' which holds the identity token whose
|
|
515
|
-
* `aud` and `sub` are the same.
|
|
516
|
-
*
|
|
517
|
-
* @param {TokenSet} tokenSet the set of tokens keyed by string.
|
|
518
|
-
* @returns {string}
|
|
519
|
-
*/
|
|
520
|
-
static toSearchString(tokenSet) {
|
|
521
|
-
const searchParams = new URLSearchParams()
|
|
522
|
-
for (const key in tokenSet) {
|
|
523
|
-
const token = tokenSet[key]
|
|
524
|
-
const tokenBase64 = token.asSignedBase64()
|
|
525
|
-
searchParams.append(key, tokenBase64)
|
|
526
|
-
}
|
|
527
|
-
return searchParams.toString()
|
|
528
|
-
}
|
|
529
|
-
}
|
package/src/ts/Assertions.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Use this type to reference a plain object with
|
|
3
|
-
* random string keys.
|
|
4
|
-
*/
|
|
5
|
-
export interface Plain {
|
|
6
|
-
// deno-lint-ignore no-explicit-any
|
|
7
|
-
[key: string]: any
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Any JSON-serialisable type.
|
|
12
|
-
*/
|
|
13
|
-
export type JSONPrimitive = string | number | boolean | null
|
|
14
|
-
export type JSONArray = JSONValue[]
|
|
15
|
-
export interface JSONObject {
|
|
16
|
-
[key: string]: JSONValue
|
|
17
|
-
}
|
|
18
|
-
export type JSONValue = JSONPrimitive | JSONObject | JSONArray
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Type predicates to be used in type-narrowing assertions, e.g.
|
|
23
|
-
*
|
|
24
|
-
* assert(notNull(someValue))
|
|
25
|
-
* ... now the type of someValue does not include null ...
|
|
26
|
-
*
|
|
27
|
-
* or
|
|
28
|
-
* if (isOk(returnValue)) {
|
|
29
|
-
* ...in this block we know returnValue.ok is present and of the correct type.
|
|
30
|
-
* }
|
|
31
|
-
*
|
|
32
|
-
* @param value the value to be tested.
|
|
33
|
-
* @returns true if not null, otherwise false.
|
|
34
|
-
*/
|
|
35
|
-
export function notNull<T>(value: T | null): value is T {
|
|
36
|
-
return value !== null
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function isDefined<T>(value: T | undefined): value is T {
|
|
40
|
-
return value !== undefined
|
|
41
|
-
}
|