@sneekin/sdk 1.1.0 → 1.1.2

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.cjs CHANGED
@@ -24,10 +24,37 @@ __export(src_exports, {
24
24
  SneekApiError: () => SneekApiError,
25
25
  SneekClient: () => SneekClient,
26
26
  SneekPayClient: () => SneekPayClient,
27
- deriveKeys: () => deriveKeys
27
+ deriveKeys: () => deriveKeys,
28
+ isValidApiKey: () => isValidApiKey
28
29
  });
29
30
  module.exports = __toCommonJS(src_exports);
30
31
 
32
+ // src/core/derive-keys.ts
33
+ var import_node_crypto = require("crypto");
34
+ var KEY_LABELS = {
35
+ qrSigningKey: "sneek-qr-v1",
36
+ webhookSigningKey: "sneek-webhook-v1",
37
+ proofKey: "sneek-proof-v1"
38
+ };
39
+ var API_KEY_PATTERN = /^sneek_[A-Za-z0-9_-]{43}$/;
40
+ function isValidApiKey(apiKey) {
41
+ return API_KEY_PATTERN.test(apiKey);
42
+ }
43
+ function deriveKeys(apiKey) {
44
+ if (typeof apiKey !== "string" || !isValidApiKey(apiKey)) {
45
+ throw new TypeError("apiKey must use the sneek_ format with a 43-character secret");
46
+ }
47
+ const inputKeyMaterial = Buffer.from(apiKey, "utf8");
48
+ const derive = (label) => Buffer.from(
49
+ (0, import_node_crypto.hkdfSync)("sha256", inputKeyMaterial, Buffer.alloc(0), label, 32)
50
+ );
51
+ return {
52
+ qrSigningKey: derive(KEY_LABELS.qrSigningKey),
53
+ webhookSigningKey: derive(KEY_LABELS.webhookSigningKey),
54
+ proofKey: derive(KEY_LABELS.proofKey)
55
+ };
56
+ }
57
+
31
58
  // src/core/client.ts
32
59
  var SneekApiError = class extends Error {
33
60
  constructor(status, type, title, detail) {
@@ -52,6 +79,7 @@ var SneekClient = class {
52
79
  constructor(options) {
53
80
  if (!options.apiUrl) throw new Error("apiUrl is required");
54
81
  if (!options.apiKey) throw new Error("apiKey is required");
82
+ if (!isValidApiKey(options.apiKey)) throw new Error("apiKey must use the sneek_ format with a 43-character secret");
55
83
  this.apiUrl = options.apiUrl.replace(/\/+$/, "");
56
84
  this.apiKey = options.apiKey;
57
85
  this.fetchImpl = options.fetch ?? fetch;
@@ -123,28 +151,6 @@ function asProblem(value) {
123
151
  };
124
152
  }
125
153
 
126
- // src/core/derive-keys.ts
127
- var import_node_crypto = require("crypto");
128
- var KEY_LABELS = {
129
- qrSigningKey: "sneek-qr-v1",
130
- webhookSigningKey: "sneek-webhook-v1",
131
- proofKey: "sneek-proof-v1"
132
- };
133
- function deriveKeys(apiKey) {
134
- if (typeof apiKey !== "string" || apiKey.length === 0) {
135
- throw new TypeError("apiKey must be a nonempty string");
136
- }
137
- const inputKeyMaterial = Buffer.from(apiKey, "utf8");
138
- const derive = (label) => Buffer.from(
139
- (0, import_node_crypto.hkdfSync)("sha256", inputKeyMaterial, Buffer.alloc(0), label, 32)
140
- );
141
- return {
142
- qrSigningKey: derive(KEY_LABELS.qrSigningKey),
143
- webhookSigningKey: derive(KEY_LABELS.webhookSigningKey),
144
- proofKey: derive(KEY_LABELS.proofKey)
145
- };
146
- }
147
-
148
154
  // src/pay/payments.ts
149
155
  var SneekPayClient = class {
150
156
  client;
@@ -219,5 +225,6 @@ var OrdersClient = class {
219
225
  SneekApiError,
220
226
  SneekClient,
221
227
  SneekPayClient,
222
- deriveKeys
228
+ deriveKeys,
229
+ isValidApiKey
223
230
  });
package/dist/index.js CHANGED
@@ -1,3 +1,29 @@
1
+ // src/core/derive-keys.ts
2
+ import { hkdfSync } from "crypto";
3
+ var KEY_LABELS = {
4
+ qrSigningKey: "sneek-qr-v1",
5
+ webhookSigningKey: "sneek-webhook-v1",
6
+ proofKey: "sneek-proof-v1"
7
+ };
8
+ var API_KEY_PATTERN = /^sneek_[A-Za-z0-9_-]{43}$/;
9
+ function isValidApiKey(apiKey) {
10
+ return API_KEY_PATTERN.test(apiKey);
11
+ }
12
+ function deriveKeys(apiKey) {
13
+ if (typeof apiKey !== "string" || !isValidApiKey(apiKey)) {
14
+ throw new TypeError("apiKey must use the sneek_ format with a 43-character secret");
15
+ }
16
+ const inputKeyMaterial = Buffer.from(apiKey, "utf8");
17
+ const derive = (label) => Buffer.from(
18
+ hkdfSync("sha256", inputKeyMaterial, Buffer.alloc(0), label, 32)
19
+ );
20
+ return {
21
+ qrSigningKey: derive(KEY_LABELS.qrSigningKey),
22
+ webhookSigningKey: derive(KEY_LABELS.webhookSigningKey),
23
+ proofKey: derive(KEY_LABELS.proofKey)
24
+ };
25
+ }
26
+
1
27
  // src/core/client.ts
2
28
  var SneekApiError = class extends Error {
3
29
  constructor(status, type, title, detail) {
@@ -22,6 +48,7 @@ var SneekClient = class {
22
48
  constructor(options) {
23
49
  if (!options.apiUrl) throw new Error("apiUrl is required");
24
50
  if (!options.apiKey) throw new Error("apiKey is required");
51
+ if (!isValidApiKey(options.apiKey)) throw new Error("apiKey must use the sneek_ format with a 43-character secret");
25
52
  this.apiUrl = options.apiUrl.replace(/\/+$/, "");
26
53
  this.apiKey = options.apiKey;
27
54
  this.fetchImpl = options.fetch ?? fetch;
@@ -93,28 +120,6 @@ function asProblem(value) {
93
120
  };
94
121
  }
95
122
 
96
- // src/core/derive-keys.ts
97
- import { hkdfSync } from "crypto";
98
- var KEY_LABELS = {
99
- qrSigningKey: "sneek-qr-v1",
100
- webhookSigningKey: "sneek-webhook-v1",
101
- proofKey: "sneek-proof-v1"
102
- };
103
- function deriveKeys(apiKey) {
104
- if (typeof apiKey !== "string" || apiKey.length === 0) {
105
- throw new TypeError("apiKey must be a nonempty string");
106
- }
107
- const inputKeyMaterial = Buffer.from(apiKey, "utf8");
108
- const derive = (label) => Buffer.from(
109
- hkdfSync("sha256", inputKeyMaterial, Buffer.alloc(0), label, 32)
110
- );
111
- return {
112
- qrSigningKey: derive(KEY_LABELS.qrSigningKey),
113
- webhookSigningKey: derive(KEY_LABELS.webhookSigningKey),
114
- proofKey: derive(KEY_LABELS.proofKey)
115
- };
116
- }
117
-
118
123
  // src/pay/payments.ts
119
124
  var SneekPayClient = class {
120
125
  client;
@@ -188,5 +193,6 @@ export {
188
193
  SneekApiError,
189
194
  SneekClient,
190
195
  SneekPayClient,
191
- deriveKeys
196
+ deriveKeys,
197
+ isValidApiKey
192
198
  };
@@ -23,10 +23,37 @@ __export(messages_exports, {
23
23
  SneekApiError: () => SneekApiError,
24
24
  SneekClient: () => SneekClient,
25
25
  SneekMessagesClient: () => SneekMessagesClient,
26
- deriveKeys: () => deriveKeys
26
+ deriveKeys: () => deriveKeys,
27
+ isValidApiKey: () => isValidApiKey
27
28
  });
28
29
  module.exports = __toCommonJS(messages_exports);
29
30
 
31
+ // src/core/derive-keys.ts
32
+ var import_node_crypto = require("crypto");
33
+ var KEY_LABELS = {
34
+ qrSigningKey: "sneek-qr-v1",
35
+ webhookSigningKey: "sneek-webhook-v1",
36
+ proofKey: "sneek-proof-v1"
37
+ };
38
+ var API_KEY_PATTERN = /^sneek_[A-Za-z0-9_-]{43}$/;
39
+ function isValidApiKey(apiKey) {
40
+ return API_KEY_PATTERN.test(apiKey);
41
+ }
42
+ function deriveKeys(apiKey) {
43
+ if (typeof apiKey !== "string" || !isValidApiKey(apiKey)) {
44
+ throw new TypeError("apiKey must use the sneek_ format with a 43-character secret");
45
+ }
46
+ const inputKeyMaterial = Buffer.from(apiKey, "utf8");
47
+ const derive = (label) => Buffer.from(
48
+ (0, import_node_crypto.hkdfSync)("sha256", inputKeyMaterial, Buffer.alloc(0), label, 32)
49
+ );
50
+ return {
51
+ qrSigningKey: derive(KEY_LABELS.qrSigningKey),
52
+ webhookSigningKey: derive(KEY_LABELS.webhookSigningKey),
53
+ proofKey: derive(KEY_LABELS.proofKey)
54
+ };
55
+ }
56
+
30
57
  // src/core/client.ts
31
58
  var SneekApiError = class extends Error {
32
59
  constructor(status, type, title, detail) {
@@ -51,6 +78,7 @@ var SneekClient = class {
51
78
  constructor(options) {
52
79
  if (!options.apiUrl) throw new Error("apiUrl is required");
53
80
  if (!options.apiKey) throw new Error("apiKey is required");
81
+ if (!isValidApiKey(options.apiKey)) throw new Error("apiKey must use the sneek_ format with a 43-character secret");
54
82
  this.apiUrl = options.apiUrl.replace(/\/+$/, "");
55
83
  this.apiKey = options.apiKey;
56
84
  this.fetchImpl = options.fetch ?? fetch;
@@ -122,28 +150,6 @@ function asProblem(value) {
122
150
  };
123
151
  }
124
152
 
125
- // src/core/derive-keys.ts
126
- var import_node_crypto = require("crypto");
127
- var KEY_LABELS = {
128
- qrSigningKey: "sneek-qr-v1",
129
- webhookSigningKey: "sneek-webhook-v1",
130
- proofKey: "sneek-proof-v1"
131
- };
132
- function deriveKeys(apiKey) {
133
- if (typeof apiKey !== "string" || apiKey.length === 0) {
134
- throw new TypeError("apiKey must be a nonempty string");
135
- }
136
- const inputKeyMaterial = Buffer.from(apiKey, "utf8");
137
- const derive = (label) => Buffer.from(
138
- (0, import_node_crypto.hkdfSync)("sha256", inputKeyMaterial, Buffer.alloc(0), label, 32)
139
- );
140
- return {
141
- qrSigningKey: derive(KEY_LABELS.qrSigningKey),
142
- webhookSigningKey: derive(KEY_LABELS.webhookSigningKey),
143
- proofKey: derive(KEY_LABELS.proofKey)
144
- };
145
- }
146
-
147
153
  // src/messages/client.ts
148
154
  var SneekMessagesClient = class {
149
155
  client;
@@ -166,5 +172,6 @@ var SneekMessagesClient = class {
166
172
  SneekApiError,
167
173
  SneekClient,
168
174
  SneekMessagesClient,
169
- deriveKeys
175
+ deriveKeys,
176
+ isValidApiKey
170
177
  });
@@ -1,3 +1,29 @@
1
+ // src/core/derive-keys.ts
2
+ import { hkdfSync } from "crypto";
3
+ var KEY_LABELS = {
4
+ qrSigningKey: "sneek-qr-v1",
5
+ webhookSigningKey: "sneek-webhook-v1",
6
+ proofKey: "sneek-proof-v1"
7
+ };
8
+ var API_KEY_PATTERN = /^sneek_[A-Za-z0-9_-]{43}$/;
9
+ function isValidApiKey(apiKey) {
10
+ return API_KEY_PATTERN.test(apiKey);
11
+ }
12
+ function deriveKeys(apiKey) {
13
+ if (typeof apiKey !== "string" || !isValidApiKey(apiKey)) {
14
+ throw new TypeError("apiKey must use the sneek_ format with a 43-character secret");
15
+ }
16
+ const inputKeyMaterial = Buffer.from(apiKey, "utf8");
17
+ const derive = (label) => Buffer.from(
18
+ hkdfSync("sha256", inputKeyMaterial, Buffer.alloc(0), label, 32)
19
+ );
20
+ return {
21
+ qrSigningKey: derive(KEY_LABELS.qrSigningKey),
22
+ webhookSigningKey: derive(KEY_LABELS.webhookSigningKey),
23
+ proofKey: derive(KEY_LABELS.proofKey)
24
+ };
25
+ }
26
+
1
27
  // src/core/client.ts
2
28
  var SneekApiError = class extends Error {
3
29
  constructor(status, type, title, detail) {
@@ -22,6 +48,7 @@ var SneekClient = class {
22
48
  constructor(options) {
23
49
  if (!options.apiUrl) throw new Error("apiUrl is required");
24
50
  if (!options.apiKey) throw new Error("apiKey is required");
51
+ if (!isValidApiKey(options.apiKey)) throw new Error("apiKey must use the sneek_ format with a 43-character secret");
25
52
  this.apiUrl = options.apiUrl.replace(/\/+$/, "");
26
53
  this.apiKey = options.apiKey;
27
54
  this.fetchImpl = options.fetch ?? fetch;
@@ -93,28 +120,6 @@ function asProblem(value) {
93
120
  };
94
121
  }
95
122
 
96
- // src/core/derive-keys.ts
97
- import { hkdfSync } from "crypto";
98
- var KEY_LABELS = {
99
- qrSigningKey: "sneek-qr-v1",
100
- webhookSigningKey: "sneek-webhook-v1",
101
- proofKey: "sneek-proof-v1"
102
- };
103
- function deriveKeys(apiKey) {
104
- if (typeof apiKey !== "string" || apiKey.length === 0) {
105
- throw new TypeError("apiKey must be a nonempty string");
106
- }
107
- const inputKeyMaterial = Buffer.from(apiKey, "utf8");
108
- const derive = (label) => Buffer.from(
109
- hkdfSync("sha256", inputKeyMaterial, Buffer.alloc(0), label, 32)
110
- );
111
- return {
112
- qrSigningKey: derive(KEY_LABELS.qrSigningKey),
113
- webhookSigningKey: derive(KEY_LABELS.webhookSigningKey),
114
- proofKey: derive(KEY_LABELS.proofKey)
115
- };
116
- }
117
-
118
123
  // src/messages/client.ts
119
124
  var SneekMessagesClient = class {
120
125
  client;
@@ -136,5 +141,6 @@ export {
136
141
  SneekApiError,
137
142
  SneekClient,
138
143
  SneekMessagesClient,
139
- deriveKeys
144
+ deriveKeys,
145
+ isValidApiKey
140
146
  };
@@ -24,10 +24,37 @@ __export(pay_exports, {
24
24
  SneekApiError: () => SneekApiError,
25
25
  SneekClient: () => SneekClient,
26
26
  SneekPayClient: () => SneekPayClient,
27
- deriveKeys: () => deriveKeys
27
+ deriveKeys: () => deriveKeys,
28
+ isValidApiKey: () => isValidApiKey
28
29
  });
29
30
  module.exports = __toCommonJS(pay_exports);
30
31
 
32
+ // src/core/derive-keys.ts
33
+ var import_node_crypto = require("crypto");
34
+ var KEY_LABELS = {
35
+ qrSigningKey: "sneek-qr-v1",
36
+ webhookSigningKey: "sneek-webhook-v1",
37
+ proofKey: "sneek-proof-v1"
38
+ };
39
+ var API_KEY_PATTERN = /^sneek_[A-Za-z0-9_-]{43}$/;
40
+ function isValidApiKey(apiKey) {
41
+ return API_KEY_PATTERN.test(apiKey);
42
+ }
43
+ function deriveKeys(apiKey) {
44
+ if (typeof apiKey !== "string" || !isValidApiKey(apiKey)) {
45
+ throw new TypeError("apiKey must use the sneek_ format with a 43-character secret");
46
+ }
47
+ const inputKeyMaterial = Buffer.from(apiKey, "utf8");
48
+ const derive = (label) => Buffer.from(
49
+ (0, import_node_crypto.hkdfSync)("sha256", inputKeyMaterial, Buffer.alloc(0), label, 32)
50
+ );
51
+ return {
52
+ qrSigningKey: derive(KEY_LABELS.qrSigningKey),
53
+ webhookSigningKey: derive(KEY_LABELS.webhookSigningKey),
54
+ proofKey: derive(KEY_LABELS.proofKey)
55
+ };
56
+ }
57
+
31
58
  // src/core/client.ts
32
59
  var SneekApiError = class extends Error {
33
60
  constructor(status, type, title, detail) {
@@ -52,6 +79,7 @@ var SneekClient = class {
52
79
  constructor(options) {
53
80
  if (!options.apiUrl) throw new Error("apiUrl is required");
54
81
  if (!options.apiKey) throw new Error("apiKey is required");
82
+ if (!isValidApiKey(options.apiKey)) throw new Error("apiKey must use the sneek_ format with a 43-character secret");
55
83
  this.apiUrl = options.apiUrl.replace(/\/+$/, "");
56
84
  this.apiKey = options.apiKey;
57
85
  this.fetchImpl = options.fetch ?? fetch;
@@ -123,28 +151,6 @@ function asProblem(value) {
123
151
  };
124
152
  }
125
153
 
126
- // src/core/derive-keys.ts
127
- var import_node_crypto = require("crypto");
128
- var KEY_LABELS = {
129
- qrSigningKey: "sneek-qr-v1",
130
- webhookSigningKey: "sneek-webhook-v1",
131
- proofKey: "sneek-proof-v1"
132
- };
133
- function deriveKeys(apiKey) {
134
- if (typeof apiKey !== "string" || apiKey.length === 0) {
135
- throw new TypeError("apiKey must be a nonempty string");
136
- }
137
- const inputKeyMaterial = Buffer.from(apiKey, "utf8");
138
- const derive = (label) => Buffer.from(
139
- (0, import_node_crypto.hkdfSync)("sha256", inputKeyMaterial, Buffer.alloc(0), label, 32)
140
- );
141
- return {
142
- qrSigningKey: derive(KEY_LABELS.qrSigningKey),
143
- webhookSigningKey: derive(KEY_LABELS.webhookSigningKey),
144
- proofKey: derive(KEY_LABELS.proofKey)
145
- };
146
- }
147
-
148
154
  // src/pay/payments.ts
149
155
  var SneekPayClient = class {
150
156
  client;
@@ -219,5 +225,6 @@ var OrdersClient = class {
219
225
  SneekApiError,
220
226
  SneekClient,
221
227
  SneekPayClient,
222
- deriveKeys
228
+ deriveKeys,
229
+ isValidApiKey
223
230
  });
package/dist/pay/index.js CHANGED
@@ -1,3 +1,29 @@
1
+ // src/core/derive-keys.ts
2
+ import { hkdfSync } from "crypto";
3
+ var KEY_LABELS = {
4
+ qrSigningKey: "sneek-qr-v1",
5
+ webhookSigningKey: "sneek-webhook-v1",
6
+ proofKey: "sneek-proof-v1"
7
+ };
8
+ var API_KEY_PATTERN = /^sneek_[A-Za-z0-9_-]{43}$/;
9
+ function isValidApiKey(apiKey) {
10
+ return API_KEY_PATTERN.test(apiKey);
11
+ }
12
+ function deriveKeys(apiKey) {
13
+ if (typeof apiKey !== "string" || !isValidApiKey(apiKey)) {
14
+ throw new TypeError("apiKey must use the sneek_ format with a 43-character secret");
15
+ }
16
+ const inputKeyMaterial = Buffer.from(apiKey, "utf8");
17
+ const derive = (label) => Buffer.from(
18
+ hkdfSync("sha256", inputKeyMaterial, Buffer.alloc(0), label, 32)
19
+ );
20
+ return {
21
+ qrSigningKey: derive(KEY_LABELS.qrSigningKey),
22
+ webhookSigningKey: derive(KEY_LABELS.webhookSigningKey),
23
+ proofKey: derive(KEY_LABELS.proofKey)
24
+ };
25
+ }
26
+
1
27
  // src/core/client.ts
2
28
  var SneekApiError = class extends Error {
3
29
  constructor(status, type, title, detail) {
@@ -22,6 +48,7 @@ var SneekClient = class {
22
48
  constructor(options) {
23
49
  if (!options.apiUrl) throw new Error("apiUrl is required");
24
50
  if (!options.apiKey) throw new Error("apiKey is required");
51
+ if (!isValidApiKey(options.apiKey)) throw new Error("apiKey must use the sneek_ format with a 43-character secret");
25
52
  this.apiUrl = options.apiUrl.replace(/\/+$/, "");
26
53
  this.apiKey = options.apiKey;
27
54
  this.fetchImpl = options.fetch ?? fetch;
@@ -93,28 +120,6 @@ function asProblem(value) {
93
120
  };
94
121
  }
95
122
 
96
- // src/core/derive-keys.ts
97
- import { hkdfSync } from "crypto";
98
- var KEY_LABELS = {
99
- qrSigningKey: "sneek-qr-v1",
100
- webhookSigningKey: "sneek-webhook-v1",
101
- proofKey: "sneek-proof-v1"
102
- };
103
- function deriveKeys(apiKey) {
104
- if (typeof apiKey !== "string" || apiKey.length === 0) {
105
- throw new TypeError("apiKey must be a nonempty string");
106
- }
107
- const inputKeyMaterial = Buffer.from(apiKey, "utf8");
108
- const derive = (label) => Buffer.from(
109
- hkdfSync("sha256", inputKeyMaterial, Buffer.alloc(0), label, 32)
110
- );
111
- return {
112
- qrSigningKey: derive(KEY_LABELS.qrSigningKey),
113
- webhookSigningKey: derive(KEY_LABELS.webhookSigningKey),
114
- proofKey: derive(KEY_LABELS.proofKey)
115
- };
116
- }
117
-
118
123
  // src/pay/payments.ts
119
124
  var SneekPayClient = class {
120
125
  client;
@@ -188,5 +193,6 @@ export {
188
193
  SneekApiError,
189
194
  SneekClient,
190
195
  SneekPayClient,
191
- deriveKeys
196
+ deriveKeys,
197
+ isValidApiKey
192
198
  };
@@ -38,12 +38,12 @@ export declare const SneekVerifyActionSchema: z.ZodUnion<[z.ZodObject<{
38
38
  }, "strict", z.ZodTypeAny, {
39
39
  action: "switch";
40
40
  requestId: string;
41
- channel: "email" | "sms" | "whatsapp" | "push";
41
+ channel: "sms" | "whatsapp" | "email" | "push";
42
42
  proof: string;
43
43
  }, {
44
44
  action: "switch";
45
45
  requestId: string;
46
- channel: "email" | "sms" | "whatsapp" | "push";
46
+ channel: "sms" | "whatsapp" | "email" | "push";
47
47
  proof: string;
48
48
  }>, z.ZodObject<{
49
49
  action: z.ZodLiteral<"verify">;
@@ -23,10 +23,37 @@ __export(verify_exports, {
23
23
  SneekApiError: () => SneekApiError,
24
24
  SneekClient: () => SneekClient,
25
25
  SneekVerifyHandler: () => SneekVerifyHandler,
26
- deriveKeys: () => deriveKeys
26
+ deriveKeys: () => deriveKeys,
27
+ isValidApiKey: () => isValidApiKey
27
28
  });
28
29
  module.exports = __toCommonJS(verify_exports);
29
30
 
31
+ // src/core/derive-keys.ts
32
+ var import_node_crypto = require("crypto");
33
+ var KEY_LABELS = {
34
+ qrSigningKey: "sneek-qr-v1",
35
+ webhookSigningKey: "sneek-webhook-v1",
36
+ proofKey: "sneek-proof-v1"
37
+ };
38
+ var API_KEY_PATTERN = /^sneek_[A-Za-z0-9_-]{43}$/;
39
+ function isValidApiKey(apiKey) {
40
+ return API_KEY_PATTERN.test(apiKey);
41
+ }
42
+ function deriveKeys(apiKey) {
43
+ if (typeof apiKey !== "string" || !isValidApiKey(apiKey)) {
44
+ throw new TypeError("apiKey must use the sneek_ format with a 43-character secret");
45
+ }
46
+ const inputKeyMaterial = Buffer.from(apiKey, "utf8");
47
+ const derive = (label) => Buffer.from(
48
+ (0, import_node_crypto.hkdfSync)("sha256", inputKeyMaterial, Buffer.alloc(0), label, 32)
49
+ );
50
+ return {
51
+ qrSigningKey: derive(KEY_LABELS.qrSigningKey),
52
+ webhookSigningKey: derive(KEY_LABELS.webhookSigningKey),
53
+ proofKey: derive(KEY_LABELS.proofKey)
54
+ };
55
+ }
56
+
30
57
  // src/core/client.ts
31
58
  var SneekApiError = class extends Error {
32
59
  constructor(status, type, title, detail) {
@@ -51,6 +78,7 @@ var SneekClient = class {
51
78
  constructor(options) {
52
79
  if (!options.apiUrl) throw new Error("apiUrl is required");
53
80
  if (!options.apiKey) throw new Error("apiKey is required");
81
+ if (!isValidApiKey(options.apiKey)) throw new Error("apiKey must use the sneek_ format with a 43-character secret");
54
82
  this.apiUrl = options.apiUrl.replace(/\/+$/, "");
55
83
  this.apiKey = options.apiKey;
56
84
  this.fetchImpl = options.fetch ?? fetch;
@@ -122,28 +150,6 @@ function asProblem(value) {
122
150
  };
123
151
  }
124
152
 
125
- // src/core/derive-keys.ts
126
- var import_node_crypto = require("crypto");
127
- var KEY_LABELS = {
128
- qrSigningKey: "sneek-qr-v1",
129
- webhookSigningKey: "sneek-webhook-v1",
130
- proofKey: "sneek-proof-v1"
131
- };
132
- function deriveKeys(apiKey) {
133
- if (typeof apiKey !== "string" || apiKey.length === 0) {
134
- throw new TypeError("apiKey must be a nonempty string");
135
- }
136
- const inputKeyMaterial = Buffer.from(apiKey, "utf8");
137
- const derive = (label) => Buffer.from(
138
- (0, import_node_crypto.hkdfSync)("sha256", inputKeyMaterial, Buffer.alloc(0), label, 32)
139
- );
140
- return {
141
- qrSigningKey: derive(KEY_LABELS.qrSigningKey),
142
- webhookSigningKey: derive(KEY_LABELS.webhookSigningKey),
143
- proofKey: derive(KEY_LABELS.proofKey)
144
- };
145
- }
146
-
147
153
  // src/verify/handler.ts
148
154
  var import_node_crypto2 = require("crypto");
149
155
  var import_jose = require("jose");
@@ -334,8 +340,16 @@ var SneekVerifyHandler = class {
334
340
  const partner = await this.client.request("GET", "/api/verify/qr/partner-config");
335
341
  let payload;
336
342
  try {
337
- const jwks = this.jwksByIssuer.get(partner.issuer) ?? (0, import_jose.createRemoteJWKSet)(new URL("/oauth/jwks", partner.issuer));
338
- this.jwksByIssuer.set(partner.issuer, jwks);
343
+ let jwks = this.jwksByIssuer.get(partner.issuer);
344
+ if (!jwks) {
345
+ try {
346
+ const localJwksData = await this.client.request("GET", "/oauth/jwks");
347
+ jwks = (0, import_jose.createLocalJWKSet)(localJwksData);
348
+ } catch {
349
+ jwks = (0, import_jose.createRemoteJWKSet)(new URL("/oauth/jwks", partner.issuer));
350
+ }
351
+ this.jwksByIssuer.set(partner.issuer, jwks);
352
+ }
339
353
  ({ payload } = await (0, import_jose.jwtVerify)(result.idToken, jwks, {
340
354
  issuer: partner.issuer,
341
355
  audience: partner.clientId
@@ -419,11 +433,11 @@ var SneekVerifyHandler = class {
419
433
  const partner = await this.partnerConfig();
420
434
  if (!partner.origins.includes(origin)) return jsonResponse({ error: "Origin is not registered for QR verification" }, 403);
421
435
  const nonce = (0, import_node_crypto2.randomBytes)(32).toString("hex");
422
- const ref = (0, import_node_crypto2.createHash)("sha256").update(nonce).digest("hex");
436
+ const ref = (0, import_node_crypto2.createHash)("sha256").update(Buffer.from(nonce, "hex")).digest("hex");
423
437
  const issuedAt = Math.floor(Date.now() / 1e3);
424
438
  const expiresAt = (issuedAt + partner.qrTtlSeconds) * 1e3;
425
439
  const bindingProof = (0, import_node_crypto2.randomBytes)(32).toString("base64url");
426
- const signature = (0, import_node_crypto2.createHmac)("sha256", this.keys.qrSigningKey).update(["sneek-qr-v1", partner.applicationId, nonce, origin, String(issuedAt)].join("\n")).digest().subarray(0, 16).toString("base64url");
440
+ const signature = (0, import_node_crypto2.createHmac)("sha256", this.keys.qrSigningKey).update(["sneek-qr-v1", partner.applicationId, partner.kid, nonce, origin, String(issuedAt)].join("\n")).digest().subarray(0, 16).toString("base64url");
427
441
  await this.approvalStore.createQrSession({
428
442
  ref,
429
443
  applicationId: partner.applicationId,
@@ -431,7 +445,7 @@ var SneekVerifyHandler = class {
431
445
  bindingProofHash: this.hash(bindingProof),
432
446
  expiresAt
433
447
  });
434
- return jsonResponse({ ref, nonce, issuedAt, expiresAt, signature, bindingProof }, 200);
448
+ return jsonResponse({ ref, nonce, issuedAt, expiresAt, kid: partner.kid, signature, bindingProof }, 200);
435
449
  }
436
450
  async qrStatus(ref, bindingProof, origin) {
437
451
  const session = await this.qrSession(ref, bindingProof, origin);
@@ -467,5 +481,6 @@ function jsonResponse(body, status) {
467
481
  SneekApiError,
468
482
  SneekClient,
469
483
  SneekVerifyHandler,
470
- deriveKeys
484
+ deriveKeys,
485
+ isValidApiKey
471
486
  });