-
Notifications
You must be signed in to change notification settings - Fork 23
/
Encodings.ts
executable file
·103 lines (91 loc) · 3.61 KB
/
Encodings.ts
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
import { InputDescriptorV1 } from '@sphereon/pex-models';
import { parse, stringify } from 'qs';
import * as ua8 from 'uint8arrays';
import { SIOPErrors } from '../types';
export function decodeUriAsJson(uri: string) {
if (!uri) {
throw new Error(SIOPErrors.BAD_PARAMS);
}
const queryString = uri.replace(/^([a-zA-Z][a-zA-Z0-9-_]*:\/\/.*[?])/, '');
if (!queryString) {
throw new Error(SIOPErrors.BAD_PARAMS);
}
const parts = parse(queryString, { plainObjects: true, depth: 10, parameterLimit: 5000, ignoreQueryPrefix: true });
const descriptors = parts?.claims?.['vp_token']?.presentation_definition?.['input_descriptors'];
if (descriptors && Array.isArray(descriptors)) {
// Whenever we have a [{'uri': 'str1'}, 'uri': 'str2'] qs changes this to {uri: ['str1','str2']} which means schema validation fails. So we have to fix that
parts.claims['vp_token'].presentation_definition['input_descriptors'] = descriptors.map((descriptor: InputDescriptorV1) => {
if (Array.isArray(descriptor.schema)) {
descriptor.schema = descriptor.schema.flatMap((val) => {
if (typeof val === 'string') {
return { uri: val };
} else if (typeof val === 'object' && Array.isArray(val.uri)) {
return val.uri.map((uri) => ({ uri: uri as string }));
}
return val;
});
}
return descriptor;
});
}
const json = {};
for (const key in parts) {
const value = parts[key];
if (!value) {
continue;
}
const isBool = typeof value == 'boolean';
const isNumber = typeof value == 'number';
const isString = typeof value == 'string';
if (isBool || isNumber) {
json[decodeURIComponent(key)] = value;
} else if (isString) {
const decoded = decodeURIComponent(value);
if (decoded.startsWith('{') && decoded.endsWith('}')) {
json[decodeURIComponent(key)] = JSON.parse(decoded);
} else {
json[decodeURIComponent(key)] = decoded;
}
}
}
return JSON.parse(JSON.stringify(json));
}
export function encodeJsonAsURI(json: unknown, _opts?: { arraysWithIndex?: string[] }): string {
if (typeof json === 'string') {
return encodeJsonAsURI(JSON.parse(json));
}
const results: string[] = [];
function encodeAndStripWhitespace(key: string): string {
return encodeURIComponent(key.replace(' ', ''));
}
for (const [key, value] of Object.entries(json)) {
if (!value) {
continue;
}
const isBool = typeof value == 'boolean';
const isNumber = typeof value == 'number';
const isString = typeof value == 'string';
const isArray = Array.isArray(value);
let encoded: string;
if (isBool || isNumber) {
encoded = `${encodeAndStripWhitespace(key)}=${value}`;
} else if (isString) {
encoded = `${encodeAndStripWhitespace(key)}=${encodeURIComponent(value)}`;
} else if (isArray && _opts?.arraysWithIndex?.includes(key)) {
encoded = `${encodeAndStripWhitespace(key)}=${stringify(value, { arrayFormat: 'brackets' })}`;
} else {
encoded = `${encodeAndStripWhitespace(key)}=${encodeURIComponent(JSON.stringify(value))}`;
}
results.push(encoded);
}
return results.join('&');
}
export function base64ToHexString(input: string, encoding?: 'base64url' | 'base64'): string {
return ua8.toString(ua8.fromString(input, encoding ?? 'base64url'), 'base16');
}
export function fromBase64(base64: string): string {
return base64.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
}
export function base64urlEncodeBuffer(buf: { toString: (arg0: 'base64') => string }): string {
return fromBase64(buf.toString('base64'));
}