@openapi-qraft/react 1.8.0 → 1.10.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/CHANGELOG.md +10 -0
- package/README.md +61 -55
- package/dist/Unstable_QraftSecureRequestFn.d.ts +81 -0
- package/dist/Unstable_QraftSecureRequestFn.d.ts.map +1 -0
- package/dist/Unstable_QraftSecureRequestFn.js +187 -0
- package/dist/Unstable_QraftSecureRequestFn.js.map +1 -0
- package/dist/lib/jwt-decode/index.d.ts +29 -0
- package/dist/lib/jwt-decode/index.d.ts.map +1 -0
- package/dist/lib/jwt-decode/index.js +61 -0
- package/dist/lib/jwt-decode/index.js.map +1 -0
- package/dist/lib/jwt-decode/index.test.d.ts +2 -0
- package/dist/lib/jwt-decode/index.test.d.ts.map +1 -0
- package/dist/lib/jwt-decode/index.test.js +99 -0
- package/dist/lib/jwt-decode/index.test.js.map +1 -0
- package/dist/lib/requestFn.d.ts +3 -1
- package/dist/lib/requestFn.d.ts.map +1 -1
- package/dist/lib/requestFn.js +1 -1
- package/dist/lib/requestFn.js.map +1 -1
- package/package.json +5 -5
- package/src/Unstable_QraftSecureRequestFn.ts +365 -0
- package/src/lib/jwt-decode/index.test.ts +131 -0
- package/src/lib/jwt-decode/index.ts +105 -0
- package/src/lib/requestFn.ts +4 -2
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
jwtDecode,
|
|
5
|
+
InvalidTokenError,
|
|
6
|
+
JwtPayload,
|
|
7
|
+
b64DecodeUnicode,
|
|
8
|
+
} from './index.js';
|
|
9
|
+
|
|
10
|
+
const token =
|
|
11
|
+
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmb28iOiJiYXIiLCJleHAiOjEzOTMyODY4OTMsImlhdCI6MTM5MzI2ODg5M30.4-iaDojEVl0pJQMjrbM1EzUIfAZgsbK_kgnVyVxFSVo';
|
|
12
|
+
|
|
13
|
+
describe('jwt-decode', () => {
|
|
14
|
+
it('should return default and custom claims', () => {
|
|
15
|
+
const decoded = jwtDecode<JwtPayload & { foo: string }>(token);
|
|
16
|
+
expect(decoded.exp).toEqual(1393286893);
|
|
17
|
+
expect(decoded.iat).toEqual(1393268893);
|
|
18
|
+
expect(decoded.foo).toEqual('bar');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should return header information', () => {
|
|
22
|
+
const decoded = jwtDecode(token, { header: true });
|
|
23
|
+
expect(decoded.typ).toEqual('JWT');
|
|
24
|
+
expect(decoded.alg).toEqual('HS256');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should work with utf8 tokens', () => {
|
|
28
|
+
const utf8Token =
|
|
29
|
+
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiSm9zw6kiLCJpYXQiOjE0MjU2NDQ5NjZ9.1CfFtdGUPs6q8kT3OGQSVlhEMdbuX0HfNSqum0023a0';
|
|
30
|
+
const decoded = jwtDecode<JwtPayload & { name: string }>(utf8Token);
|
|
31
|
+
expect(decoded.name).toEqual('José');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should work with binary tokens', () => {
|
|
35
|
+
const binaryToken =
|
|
36
|
+
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiSm9z6SIsImlhdCI6MTQyNTY0NDk2Nn0.cpnplCBxiw7Xqz5thkqs4Mo_dymvztnI0CI4BN0d1t8';
|
|
37
|
+
const decoded = jwtDecode<JwtPayload & { name: string }>(binaryToken);
|
|
38
|
+
expect(decoded.name).toEqual('José');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should work with double padding', () => {
|
|
42
|
+
const utf8Token =
|
|
43
|
+
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ikpvc8OpIiwiaWF0IjoxNTE2MjM5MDIyfQ.7A3F5SUH2gbBSYVon5mas_Y-KCrWojorKQg7UKGVEIA';
|
|
44
|
+
const decoded = jwtDecode<JwtPayload & { name: string }>(utf8Token);
|
|
45
|
+
expect(decoded.name).toEqual('José');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should work with single padding', () => {
|
|
49
|
+
const utf8Token =
|
|
50
|
+
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ikpvc8OpZSIsImlhdCI6MTUxNjIzOTAyMn0.tbjJzDAylkKSV0_YGR5xBJBlFK01C82nZPLIcA3JX1g';
|
|
51
|
+
const decoded = jwtDecode<JwtPayload & { name: string }>(utf8Token);
|
|
52
|
+
expect(decoded.name).toEqual('Josée');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should throw InvalidTokenError on not a string', () => {
|
|
56
|
+
const badToken = null;
|
|
57
|
+
expect(() => {
|
|
58
|
+
jwtDecode(badToken as unknown as string);
|
|
59
|
+
}).toThrow(InvalidTokenError);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should throw InvalidTokenError on string that is not a token', () => {
|
|
63
|
+
const badToken = 'foobar';
|
|
64
|
+
expect(() => {
|
|
65
|
+
jwtDecode(badToken);
|
|
66
|
+
}).toThrow(InvalidTokenError);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should throw InvalidTokenErrors when token is null', () => {
|
|
70
|
+
const badToken = null;
|
|
71
|
+
expect(() => {
|
|
72
|
+
jwtDecode(badToken as unknown as string, { header: true });
|
|
73
|
+
}).toThrow(
|
|
74
|
+
new InvalidTokenError('Invalid token specified: must be a string')
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should throw InvalidTokenErrors when missing part #1', () => {
|
|
79
|
+
const badToken = '.FAKE_TOKEN';
|
|
80
|
+
expect(() => {
|
|
81
|
+
jwtDecode(badToken, { header: true });
|
|
82
|
+
}).toThrow(/Invalid token specified: invalid json for part #1/);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should throw InvalidTokenErrors when part #1 is not valid base64', () => {
|
|
86
|
+
const badToken = 'TOKEN';
|
|
87
|
+
expect(() => {
|
|
88
|
+
jwtDecode(badToken, { header: true });
|
|
89
|
+
}).toThrow(/Invalid token specified: invalid base64 for part #1/);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should throw InvalidTokenErrors when part #1 is not valid JSON', () => {
|
|
93
|
+
const badToken = 'FAKE.TOKEN';
|
|
94
|
+
expect(() => {
|
|
95
|
+
jwtDecode(badToken, { header: true });
|
|
96
|
+
}).toThrow(/Invalid token specified: invalid json for part #1/);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should throw InvalidTokenErrors when missing part #2', () => {
|
|
100
|
+
const badToken = 'FAKE_TOKEN';
|
|
101
|
+
expect(() => {
|
|
102
|
+
jwtDecode(badToken);
|
|
103
|
+
}).toThrow(
|
|
104
|
+
new InvalidTokenError('Invalid token specified: missing part #2')
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should throw InvalidTokenErrors when part #2 is not valid base64', () => {
|
|
109
|
+
const badToken = 'FAKE.TOKEN';
|
|
110
|
+
expect(() => {
|
|
111
|
+
jwtDecode(badToken);
|
|
112
|
+
}).toThrow(/Invalid token specified: invalid base64 for part #2/);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should throw InvalidTokenErrors when part #2 is not valid JSON', () => {
|
|
116
|
+
const badToken = 'FAKE.TOKEN2';
|
|
117
|
+
expect(() => {
|
|
118
|
+
jwtDecode(badToken);
|
|
119
|
+
}).toThrow(/Invalid token specified: invalid json for part #2/);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
describe('decodeBase64', () => {
|
|
124
|
+
it('decodes base64', () => {
|
|
125
|
+
expect(b64DecodeUnicode('aGVsbG8=')).toEqual('hello');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('decodes base64 non latin characters', () => {
|
|
129
|
+
expect(b64DecodeUnicode('5L2g5aW9')).toEqual('你好');
|
|
130
|
+
});
|
|
131
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
export interface JwtDecodeOptions {
|
|
2
|
+
header?: boolean;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export interface JwtHeader {
|
|
6
|
+
typ?: string;
|
|
7
|
+
alg?: string;
|
|
8
|
+
kid?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface JwtPayload {
|
|
12
|
+
iss?: string;
|
|
13
|
+
sub?: string;
|
|
14
|
+
aud?: string[] | string;
|
|
15
|
+
exp?: number;
|
|
16
|
+
nbf?: number;
|
|
17
|
+
iat?: number;
|
|
18
|
+
jti?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class InvalidTokenError extends Error {}
|
|
22
|
+
|
|
23
|
+
InvalidTokenError.prototype.name = 'InvalidTokenError';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Decode a base64 string to a UTF-8 string.
|
|
27
|
+
* Could be used in Node.js or in the browser.
|
|
28
|
+
*/
|
|
29
|
+
export function b64DecodeUnicode(data: string): string {
|
|
30
|
+
if (typeof atob === 'function')
|
|
31
|
+
return decodeURIComponent(Array.from(atob(data), byteToPercent).join(''));
|
|
32
|
+
|
|
33
|
+
return Buffer.from(data, 'base64').toString('utf-8');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function base64UrlDecode(str: string) {
|
|
37
|
+
let output = str.replace(/-/g, '+').replace(/_/g, '/');
|
|
38
|
+
switch (output.length % 4) {
|
|
39
|
+
case 0:
|
|
40
|
+
break;
|
|
41
|
+
case 2:
|
|
42
|
+
output += '==';
|
|
43
|
+
break;
|
|
44
|
+
case 3:
|
|
45
|
+
output += '=';
|
|
46
|
+
break;
|
|
47
|
+
default:
|
|
48
|
+
throw new Error('base64 string is not of the correct length');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
return b64DecodeUnicode(output);
|
|
53
|
+
} catch (err) {
|
|
54
|
+
return atob(output);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function jwtDecode<T = JwtHeader>(
|
|
59
|
+
token: string,
|
|
60
|
+
options: JwtDecodeOptions & { header: true }
|
|
61
|
+
): T;
|
|
62
|
+
export function jwtDecode<T = JwtPayload>(
|
|
63
|
+
token: string,
|
|
64
|
+
options?: JwtDecodeOptions
|
|
65
|
+
): T;
|
|
66
|
+
export function jwtDecode<T = JwtHeader | JwtPayload>(
|
|
67
|
+
token: string,
|
|
68
|
+
options?: JwtDecodeOptions
|
|
69
|
+
): T {
|
|
70
|
+
if (typeof token !== 'string') {
|
|
71
|
+
throw new InvalidTokenError('Invalid token specified: must be a string');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
options ||= {};
|
|
75
|
+
|
|
76
|
+
const pos = options.header === true ? 0 : 1;
|
|
77
|
+
const part = token.split('.')[pos];
|
|
78
|
+
|
|
79
|
+
if (typeof part !== 'string') {
|
|
80
|
+
throw new InvalidTokenError(
|
|
81
|
+
`Invalid token specified: missing part #${pos + 1}`
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let decoded: string;
|
|
86
|
+
try {
|
|
87
|
+
decoded = base64UrlDecode(part);
|
|
88
|
+
} catch (e) {
|
|
89
|
+
throw new InvalidTokenError(
|
|
90
|
+
`Invalid token specified: invalid base64 for part #${pos + 1} (${(e as Error).message})`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
return JSON.parse(decoded) as T;
|
|
96
|
+
} catch (e) {
|
|
97
|
+
throw new InvalidTokenError(
|
|
98
|
+
`Invalid token specified: invalid json for part #${pos + 1} (${(e as Error).message})`
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function byteToPercent(b: string) {
|
|
104
|
+
return `%${`00${b.charCodeAt(0).toString(16)}`.slice(-2)}`;
|
|
105
|
+
}
|
package/src/lib/requestFn.ts
CHANGED
|
@@ -141,7 +141,7 @@ function getQueryString(params: Record<string, any>): string {
|
|
|
141
141
|
return '';
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
function mergeHeaders(...allHeaders: (HeadersOptions | undefined)[]) {
|
|
144
|
+
export function mergeHeaders(...allHeaders: (HeadersOptions | undefined)[]) {
|
|
145
145
|
const headers = new Headers();
|
|
146
146
|
|
|
147
147
|
for (const headerSet of allHeaders) {
|
|
@@ -300,6 +300,8 @@ export interface OperationSchema {
|
|
|
300
300
|
* @example application/json
|
|
301
301
|
*/
|
|
302
302
|
readonly mediaType?: string;
|
|
303
|
+
|
|
304
|
+
readonly security?: string[];
|
|
303
305
|
}
|
|
304
306
|
|
|
305
307
|
export interface RequestFnPayload {
|
|
@@ -337,7 +339,7 @@ export interface RequestFnPayload {
|
|
|
337
339
|
signal?: AbortSignal | null;
|
|
338
340
|
}
|
|
339
341
|
|
|
340
|
-
interface RequestFnInfo
|
|
342
|
+
export interface RequestFnInfo
|
|
341
343
|
extends RequestFnPayload,
|
|
342
344
|
Omit<RequestInit, 'headers' | 'method' | 'body' | 'signal'> {
|
|
343
345
|
/**
|