@powersync/service-core 0.11.0 → 0.12.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 +11 -0
- package/dist/auth/KeySpec.d.ts +1 -0
- package/dist/auth/KeySpec.js +10 -8
- package/dist/auth/KeySpec.js.map +1 -1
- package/dist/auth/RemoteJWKSCollector.js +2 -2
- package/dist/auth/RemoteJWKSCollector.js.map +1 -1
- package/package.json +2 -2
- package/src/auth/KeySpec.ts +12 -9
- package/src/auth/RemoteJWKSCollector.ts +2 -2
- package/test/src/auth.test.ts +54 -21
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
package/dist/auth/KeySpec.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as jose from 'jose';
|
|
2
2
|
export declare const HS_ALGORITHMS: string[];
|
|
3
3
|
export declare const RSA_ALGORITHMS: string[];
|
|
4
|
+
export declare const OKP_ALGORITHMS: string[];
|
|
4
5
|
export declare const SUPPORTED_ALGORITHMS: string[];
|
|
5
6
|
export interface KeyOptions {
|
|
6
7
|
/**
|
package/dist/auth/KeySpec.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as jose from 'jose';
|
|
2
2
|
export const HS_ALGORITHMS = ['HS256', 'HS384', 'HS512'];
|
|
3
3
|
export const RSA_ALGORITHMS = ['RS256', 'RS384', 'RS512'];
|
|
4
|
-
export const
|
|
4
|
+
export const OKP_ALGORITHMS = ['EdDSA'];
|
|
5
|
+
export const SUPPORTED_ALGORITHMS = [...HS_ALGORITHMS, ...RSA_ALGORITHMS, ...OKP_ALGORITHMS];
|
|
5
6
|
export class KeySpec {
|
|
6
7
|
static async importKey(key, options) {
|
|
7
8
|
const parsed = (await jose.importJWK(key));
|
|
@@ -17,18 +18,19 @@ export class KeySpec {
|
|
|
17
18
|
}
|
|
18
19
|
matchesAlgorithm(jwtAlg) {
|
|
19
20
|
if (this.source.alg) {
|
|
20
|
-
return jwtAlg
|
|
21
|
+
return jwtAlg === this.source.alg;
|
|
21
22
|
}
|
|
22
|
-
else if (this.source.kty
|
|
23
|
+
else if (this.source.kty === 'RSA') {
|
|
23
24
|
return RSA_ALGORITHMS.includes(jwtAlg);
|
|
24
25
|
}
|
|
25
|
-
else if (this.source.kty
|
|
26
|
+
else if (this.source.kty === 'oct') {
|
|
26
27
|
return HS_ALGORITHMS.includes(jwtAlg);
|
|
27
28
|
}
|
|
28
|
-
else {
|
|
29
|
-
|
|
30
|
-
return false;
|
|
29
|
+
else if (this.source.kty === 'OKP') {
|
|
30
|
+
return OKP_ALGORITHMS.includes(jwtAlg);
|
|
31
31
|
}
|
|
32
|
+
// 'EC' is unsupported
|
|
33
|
+
return false;
|
|
32
34
|
}
|
|
33
35
|
async isValidSignature(token) {
|
|
34
36
|
try {
|
|
@@ -36,7 +38,7 @@ export class KeySpec {
|
|
|
36
38
|
return true;
|
|
37
39
|
}
|
|
38
40
|
catch (e) {
|
|
39
|
-
if (e.code
|
|
41
|
+
if (e.code === 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED') {
|
|
40
42
|
return false;
|
|
41
43
|
}
|
|
42
44
|
else {
|
package/dist/auth/KeySpec.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KeySpec.js","sourceRoot":"","sources":["../../src/auth/KeySpec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AACzD,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAC1D,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,GAAG,aAAa,EAAE,GAAG,cAAc,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"KeySpec.js","sourceRoot":"","sources":["../../src/auth/KeySpec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AACzD,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAC1D,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAAO,CAAC,CAAC;AACxC,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,GAAG,aAAa,EAAE,GAAG,cAAc,EAAE,GAAG,cAAc,CAAC,CAAC;AAgB7F,MAAM,OAAO,OAAO;IAKlB,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,GAAa,EAAE,OAAoB;QACxD,MAAM,MAAM,GAAG,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAiB,CAAC;QAC3D,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED,YAAY,MAAgB,EAAE,GAAiB,EAAE,OAAoB;QACnE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,OAAO,GAAG,OAAO,IAAI,EAAE,CAAC;IAC/B,CAAC;IAED,IAAI,GAAG;QACL,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC;IACzB,CAAC;IAED,gBAAgB,CAAC,MAAc;QAC7B,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;YACpB,OAAO,MAAM,KAAK,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC;QACpC,CAAC;aAAM,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,KAAK,KAAK,EAAE,CAAC;YACrC,OAAO,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACzC,CAAC;aAAM,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,KAAK,KAAK,EAAE,CAAC;YACrC,OAAO,aAAa,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACxC,CAAC;aAAM,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,KAAK,KAAK,EAAE,CAAC;YACrC,OAAO,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACzC,CAAC;QAED,sBAAsB;QACtB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,KAAa;QAClC,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;YAC1C,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,CAAC,IAAI,KAAK,uCAAuC,EAAE,CAAC;gBACvD,OAAO,KAAK,CAAC;YACf,CAAC;iBAAM,CAAC;gBACN,iCAAiC;gBACjC,MAAM,CAAC,CAAC;YACV,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
|
|
@@ -50,8 +50,8 @@ export class RemoteJWKSCollector {
|
|
|
50
50
|
}
|
|
51
51
|
let keys = [];
|
|
52
52
|
for (let keyData of data.keys) {
|
|
53
|
-
if (keyData.kty != 'RSA') {
|
|
54
|
-
//
|
|
53
|
+
if (keyData.kty != 'RSA' && keyData.kty != 'OKP') {
|
|
54
|
+
// HS (oct) keys not allowed because they are symmetric
|
|
55
55
|
continue;
|
|
56
56
|
}
|
|
57
57
|
if (typeof keyData.use == 'string') {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RemoteJWKSCollector.js","sourceRoot":"","sources":["../../src/auth/RemoteJWKSCollector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AACpC,OAAO,EAAE,MAAM,WAAW,CAAC;AAC3B,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,GAAG,MAAM,KAAK,CAAC;AAC3B,OAAO,KAAK,MAAM,YAAY,CAAC;AAE/B,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAUvC;;GAEG;AACH,MAAM,OAAO,mBAAmB;IAG9B,YACE,GAAW,EACD,OAAoC;QAApC,YAAO,GAAP,OAAO,CAA6B;QAE9C,IAAI,CAAC;YACH,IAAI,CAAC,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAC1B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CAAC,qBAAqB,GAAG,EAAE,CAAC,CAAC;QAC9C,CAAC;QAED,sDAAsD;QACtD,kEAAkE;QAClE,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,IAAI,QAAQ,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,IAAI,OAAO,EAAE,CAAC;YAClE,MAAM,IAAI,KAAK,CAAC,gDAAgD,GAAG,EAAE,CAAC,CAAC;QACzE,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC;QAC9C,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;YAC9B,eAAe,CAAC,KAAK,EAAE,CAAC;QAC1B,CAAC,EAAE,KAAM,CAAC,CAAC;QAEX,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE;YAChC,MAAM,EAAE,KAAK;YACb,OAAO,EAAE;gBACP,MAAM,EAAE,kBAAkB;aAC3B;YACD,MAAM,EAAE,eAAe,CAAC,MAAM;YAC9B,KAAK,EAAE,MAAM,IAAI,CAAC,YAAY,EAAE;SACjC,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,4BAA4B,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;QAClF,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAQ,CAAC;QAEvC,YAAY,CAAC,OAAO,CAAC,CAAC;QAEtB,oGAAoG;QACpG,IACE,IAAI,CAAC,IAAI,IAAI,IAAI;YACjB,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;YACzB,CAAE,IAAI,CAAC,IAAc,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,GAAG,IAAI,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EACnF,CAAC;YACD,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,mCAAmC,CAAC,CAAC,EAAE,CAAC;QAClG,CAAC;QAED,IAAI,IAAI,GAAc,EAAE,CAAC;QACzB,KAAK,IAAI,OAAO,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAC9B,IAAI,OAAO,CAAC,GAAG,IAAI,KAAK,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"RemoteJWKSCollector.js","sourceRoot":"","sources":["../../src/auth/RemoteJWKSCollector.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
8
|
-
"version": "0.
|
|
8
|
+
"version": "0.12.0",
|
|
9
9
|
"main": "dist/index.js",
|
|
10
10
|
"license": "FSL-1.1-Apache-2.0",
|
|
11
11
|
"type": "module",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@powersync/service-jsonbig": "0.17.10",
|
|
38
38
|
"@powersync/service-rsocket-router": "0.0.14",
|
|
39
39
|
"@powersync/service-sync-rules": "0.22.0",
|
|
40
|
-
"@powersync/service-types": "0.
|
|
40
|
+
"@powersync/service-types": "0.5.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/async": "^3.2.24",
|
package/src/auth/KeySpec.ts
CHANGED
|
@@ -2,7 +2,8 @@ import * as jose from 'jose';
|
|
|
2
2
|
|
|
3
3
|
export const HS_ALGORITHMS = ['HS256', 'HS384', 'HS512'];
|
|
4
4
|
export const RSA_ALGORITHMS = ['RS256', 'RS384', 'RS512'];
|
|
5
|
-
export const
|
|
5
|
+
export const OKP_ALGORITHMS = ['EdDSA'];
|
|
6
|
+
export const SUPPORTED_ALGORITHMS = [...HS_ALGORITHMS, ...RSA_ALGORITHMS, ...OKP_ALGORITHMS];
|
|
6
7
|
|
|
7
8
|
export interface KeyOptions {
|
|
8
9
|
/**
|
|
@@ -38,17 +39,19 @@ export class KeySpec {
|
|
|
38
39
|
return this.source.kid;
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
matchesAlgorithm(jwtAlg: string) {
|
|
42
|
+
matchesAlgorithm(jwtAlg: string): boolean {
|
|
42
43
|
if (this.source.alg) {
|
|
43
|
-
return jwtAlg
|
|
44
|
-
} else if (this.source.kty
|
|
44
|
+
return jwtAlg === this.source.alg;
|
|
45
|
+
} else if (this.source.kty === 'RSA') {
|
|
45
46
|
return RSA_ALGORITHMS.includes(jwtAlg);
|
|
46
|
-
} else if (this.source.kty
|
|
47
|
+
} else if (this.source.kty === 'oct') {
|
|
47
48
|
return HS_ALGORITHMS.includes(jwtAlg);
|
|
48
|
-
} else {
|
|
49
|
-
|
|
50
|
-
return false;
|
|
49
|
+
} else if (this.source.kty === 'OKP') {
|
|
50
|
+
return OKP_ALGORITHMS.includes(jwtAlg);
|
|
51
51
|
}
|
|
52
|
+
|
|
53
|
+
// 'EC' is unsupported
|
|
54
|
+
return false;
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
async isValidSignature(token: string): Promise<boolean> {
|
|
@@ -56,7 +59,7 @@ export class KeySpec {
|
|
|
56
59
|
await jose.compactVerify(token, this.key);
|
|
57
60
|
return true;
|
|
58
61
|
} catch (e) {
|
|
59
|
-
if (e.code
|
|
62
|
+
if (e.code === 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED') {
|
|
60
63
|
return false;
|
|
61
64
|
} else {
|
|
62
65
|
// Token format error most likely
|
|
@@ -73,8 +73,8 @@ export class RemoteJWKSCollector implements KeyCollector {
|
|
|
73
73
|
|
|
74
74
|
let keys: KeySpec[] = [];
|
|
75
75
|
for (let keyData of data.keys) {
|
|
76
|
-
if (keyData.kty != 'RSA') {
|
|
77
|
-
//
|
|
76
|
+
if (keyData.kty != 'RSA' && keyData.kty != 'OKP') {
|
|
77
|
+
// HS (oct) keys not allowed because they are symmetric
|
|
78
78
|
continue;
|
|
79
79
|
}
|
|
80
80
|
|
package/test/src/auth.test.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { CachedKeyCollector } from '@/auth/CachedKeyCollector.js';
|
|
2
|
-
import { KeyResult } from '@/auth/KeyCollector.js';
|
|
3
|
-
import { KeySpec } from '@/auth/KeySpec.js';
|
|
4
|
-
import { KeyStore } from '@/auth/KeyStore.js';
|
|
5
|
-
import { RemoteJWKSCollector } from '@/auth/RemoteJWKSCollector.js';
|
|
6
|
-
import { StaticKeyCollector } from '@/auth/StaticKeyCollector.js';
|
|
7
|
-
import * as jose from 'jose';
|
|
8
1
|
import { describe, expect, test } from 'vitest';
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
import { StaticKeyCollector } from '../../src/auth/StaticKeyCollector.js';
|
|
3
|
+
import * as jose from 'jose';
|
|
4
|
+
import { KeyStore } from '../../src/auth/KeyStore.js';
|
|
5
|
+
import { KeySpec } from '../../src/auth/KeySpec.js';
|
|
6
|
+
import { RemoteJWKSCollector } from '../../src/auth/RemoteJWKSCollector.js';
|
|
7
|
+
import { KeyResult } from '../../src/auth/KeyCollector.js';
|
|
8
|
+
import { CachedKeyCollector } from '../../src/auth/CachedKeyCollector.js';
|
|
9
|
+
import { JwtPayload } from '@/index.js';
|
|
10
|
+
|
|
11
|
+
const publicKeyRSA: jose.JWK = {
|
|
11
12
|
use: 'sig',
|
|
12
13
|
kty: 'RSA',
|
|
13
14
|
e: 'AQAB',
|
|
@@ -29,6 +30,16 @@ const sharedKey2: jose.JWK = {
|
|
|
29
30
|
k: Buffer.from('mysecret2', 'utf-8').toString('base64url')
|
|
30
31
|
};
|
|
31
32
|
|
|
33
|
+
const privateKeyEdDSA: jose.JWK = {
|
|
34
|
+
use: 'sig',
|
|
35
|
+
kty: 'OKP',
|
|
36
|
+
crv: 'Ed25519',
|
|
37
|
+
kid: 'k2',
|
|
38
|
+
x: 'nfaqgxakPaiiEdAtRGrubgh_SQ1mr6gAUx3--N-ehvo',
|
|
39
|
+
d: 'wweBqMbTrME6oChSEMYAOyYzxsGisQb-C1t0XMjb_Ng',
|
|
40
|
+
alg: 'EdDSA'
|
|
41
|
+
};
|
|
42
|
+
|
|
32
43
|
describe('JWT Auth', () => {
|
|
33
44
|
test('KeyStore basics', async () => {
|
|
34
45
|
const keys = await StaticKeyCollector.importKeys([sharedKey]);
|
|
@@ -86,20 +97,20 @@ describe('JWT Auth', () => {
|
|
|
86
97
|
});
|
|
87
98
|
|
|
88
99
|
test('Algorithm validation', async () => {
|
|
89
|
-
const keys = await StaticKeyCollector.importKeys([
|
|
100
|
+
const keys = await StaticKeyCollector.importKeys([publicKeyRSA]);
|
|
90
101
|
const store = new KeyStore(keys);
|
|
91
102
|
|
|
92
103
|
// Bad attempt at signing token with rsa public key
|
|
93
104
|
const spoofedKey: jose.JWK = {
|
|
94
105
|
kty: 'oct',
|
|
95
|
-
kid:
|
|
106
|
+
kid: publicKeyRSA.kid!,
|
|
96
107
|
alg: 'HS256',
|
|
97
|
-
k:
|
|
108
|
+
k: publicKeyRSA.n!
|
|
98
109
|
};
|
|
99
110
|
const signKey = (await jose.importJWK(spoofedKey)) as jose.KeyLike;
|
|
100
111
|
|
|
101
112
|
const signedJwt = await new jose.SignJWT({})
|
|
102
|
-
.setProtectedHeader({ alg: 'HS256', kid:
|
|
113
|
+
.setProtectedHeader({ alg: 'HS256', kid: publicKeyRSA.kid! })
|
|
103
114
|
.setSubject('f1')
|
|
104
115
|
.setIssuedAt()
|
|
105
116
|
.setIssuer('tester')
|
|
@@ -116,7 +127,7 @@ describe('JWT Auth', () => {
|
|
|
116
127
|
});
|
|
117
128
|
|
|
118
129
|
test('key selection for key with kid', async () => {
|
|
119
|
-
const keys = await StaticKeyCollector.importKeys([
|
|
130
|
+
const keys = await StaticKeyCollector.importKeys([publicKeyRSA, sharedKey, sharedKey2]);
|
|
120
131
|
const store = new KeyStore(keys);
|
|
121
132
|
const signKey = (await jose.importJWK(sharedKey)) as jose.KeyLike;
|
|
122
133
|
const signKey2 = (await jose.importJWK(sharedKey2)) as jose.KeyLike;
|
|
@@ -296,30 +307,30 @@ describe('JWT Auth', () => {
|
|
|
296
307
|
|
|
297
308
|
currentResponse = Promise.resolve({
|
|
298
309
|
errors: [],
|
|
299
|
-
keys: [await KeySpec.importKey(
|
|
310
|
+
keys: [await KeySpec.importKey(publicKeyRSA)]
|
|
300
311
|
});
|
|
301
312
|
|
|
302
313
|
let key = (await cached.getKeys()).keys[0];
|
|
303
|
-
expect(key.kid).toEqual(
|
|
314
|
+
expect(key.kid).toEqual(publicKeyRSA.kid!);
|
|
304
315
|
|
|
305
316
|
currentResponse = undefined as any;
|
|
306
317
|
|
|
307
318
|
key = (await cached.getKeys()).keys[0];
|
|
308
|
-
expect(key.kid).toEqual(
|
|
319
|
+
expect(key.kid).toEqual(publicKeyRSA.kid!);
|
|
309
320
|
|
|
310
321
|
cached.addTimeForTests(301_000);
|
|
311
322
|
currentResponse = Promise.reject('refresh failed');
|
|
312
323
|
|
|
313
324
|
// Uses the promise, refreshes in the background
|
|
314
325
|
let response = await cached.getKeys();
|
|
315
|
-
expect(response.keys[0].kid).toEqual(
|
|
326
|
+
expect(response.keys[0].kid).toEqual(publicKeyRSA.kid!);
|
|
316
327
|
expect(response.errors).toEqual([]);
|
|
317
328
|
|
|
318
329
|
// Wait for refresh to finish
|
|
319
330
|
await cached.addTimeForTests(0);
|
|
320
331
|
response = await cached.getKeys();
|
|
321
332
|
// Still have the cached key, but also have the error
|
|
322
|
-
expect(response.keys[0].kid).toEqual(
|
|
333
|
+
expect(response.keys[0].kid).toEqual(publicKeyRSA.kid!);
|
|
323
334
|
expect(response.errors[0].message).toMatch('Failed to fetch');
|
|
324
335
|
|
|
325
336
|
await cached.addTimeForTests(3601_000);
|
|
@@ -331,12 +342,34 @@ describe('JWT Auth', () => {
|
|
|
331
342
|
|
|
332
343
|
currentResponse = Promise.resolve({
|
|
333
344
|
errors: [],
|
|
334
|
-
keys: [await KeySpec.importKey(
|
|
345
|
+
keys: [await KeySpec.importKey(publicKeyRSA)]
|
|
335
346
|
});
|
|
336
347
|
|
|
337
348
|
// After a delay, we can refresh again
|
|
338
349
|
await cached.addTimeForTests(30_000);
|
|
339
350
|
key = (await cached.getKeys()).keys[0];
|
|
340
|
-
expect(key.kid).toEqual(
|
|
351
|
+
expect(key.kid).toEqual(publicKeyRSA.kid!);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
test('signing with EdDSA', async () => {
|
|
355
|
+
const keys = await StaticKeyCollector.importKeys([privateKeyEdDSA]);
|
|
356
|
+
const store = new KeyStore(keys);
|
|
357
|
+
const signKey = (await jose.importJWK(privateKeyEdDSA)) as jose.KeyLike;
|
|
358
|
+
|
|
359
|
+
const signedJwt = await new jose.SignJWT({ claim: 'test-claim' })
|
|
360
|
+
.setProtectedHeader({ alg: 'EdDSA', kid: 'k2' })
|
|
361
|
+
.setSubject('f1')
|
|
362
|
+
.setIssuedAt()
|
|
363
|
+
.setIssuer('tester')
|
|
364
|
+
.setAudience('tests')
|
|
365
|
+
.setExpirationTime('5m')
|
|
366
|
+
.sign(signKey);
|
|
367
|
+
|
|
368
|
+
const verified = (await store.verifyJwt(signedJwt, {
|
|
369
|
+
defaultAudiences: ['tests'],
|
|
370
|
+
maxAge: '6m'
|
|
371
|
+
})) as JwtPayload & { claim: string };
|
|
372
|
+
|
|
373
|
+
expect(verified.claim).toEqual('test-claim');
|
|
341
374
|
});
|
|
342
375
|
});
|