@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 CHANGED
@@ -1,5 +1,16 @@
1
1
  # @powersync/service-core
2
2
 
3
+ ## 0.12.0
4
+
5
+ ### Minor Changes
6
+
7
+ - ebc62ff: Add EdDSA support for JWTs.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [ebc62ff]
12
+ - @powersync/service-types@0.5.0
13
+
3
14
  ## 0.11.0
4
15
 
5
16
  ### Minor Changes
@@ -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
  /**
@@ -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 SUPPORTED_ALGORITHMS = [...HS_ALGORITHMS, ...RSA_ALGORITHMS];
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 == this.source.alg;
21
+ return jwtAlg === this.source.alg;
21
22
  }
22
- else if (this.source.kty == 'RSA') {
23
+ else if (this.source.kty === 'RSA') {
23
24
  return RSA_ALGORITHMS.includes(jwtAlg);
24
25
  }
25
- else if (this.source.kty == 'oct') {
26
+ else if (this.source.kty === 'oct') {
26
27
  return HS_ALGORITHMS.includes(jwtAlg);
27
28
  }
28
- else {
29
- // We don't support 'ec' yet
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 == 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED') {
41
+ if (e.code === 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED') {
40
42
  return false;
41
43
  }
42
44
  else {
@@ -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;AAgB1E,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,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC;QACnC,CAAC;aAAM,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,KAAK,EAAE,CAAC;YACpC,OAAO,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACzC,CAAC;aAAM,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,KAAK,EAAE,CAAC;YACpC,OAAO,aAAa,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACxC,CAAC;aAAM,CAAC;YACN,4BAA4B;YAC5B,OAAO,KAAK,CAAC;QACf,CAAC;IACH,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,IAAI,uCAAuC,EAAE,CAAC;gBACtD,OAAO,KAAK,CAAC;YACf,CAAC;iBAAM,CAAC;gBACN,iCAAiC;gBACjC,MAAM,CAAC,CAAC;YACV,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
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
- // Only RSA public keys supported here
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":"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"}
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.11.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.4.0"
40
+ "@powersync/service-types": "0.5.0"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/async": "^3.2.24",
@@ -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 SUPPORTED_ALGORITHMS = [...HS_ALGORITHMS, ...RSA_ALGORITHMS];
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 == this.source.alg;
44
- } else if (this.source.kty == 'RSA') {
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 == 'oct') {
47
+ } else if (this.source.kty === 'oct') {
47
48
  return HS_ALGORITHMS.includes(jwtAlg);
48
- } else {
49
- // We don't support 'ec' yet
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 == 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED') {
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
- // Only RSA public keys supported here
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
 
@@ -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
- const publicKey: jose.JWK = {
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([publicKey]);
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: publicKey.kid!,
106
+ kid: publicKeyRSA.kid!,
96
107
  alg: 'HS256',
97
- k: publicKey.n!
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: publicKey.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([publicKey, sharedKey, sharedKey2]);
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(publicKey)]
310
+ keys: [await KeySpec.importKey(publicKeyRSA)]
300
311
  });
301
312
 
302
313
  let key = (await cached.getKeys()).keys[0];
303
- expect(key.kid).toEqual(publicKey.kid!);
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(publicKey.kid!);
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(publicKey.kid!);
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(publicKey.kid!);
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(publicKey)]
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(publicKey.kid!);
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
  });