@reauth-dev/sdk 0.1.0 → 0.2.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/dist/{chunk-JX2J36FS.mjs → chunk-DMNMTW2C.mjs} +96 -29
- package/dist/chunk-EY5LQCDG.mjs +6 -0
- package/dist/index.d.mts +22 -15
- package/dist/index.d.ts +22 -15
- package/dist/index.js +95 -29
- package/dist/index.mjs +2 -1
- package/dist/react/index.d.mts +15 -5
- package/dist/react/index.d.ts +15 -5
- package/dist/react/index.js +118 -34
- package/dist/react/index.mjs +27 -8
- package/dist/server.d.mts +20 -2
- package/dist/server.d.ts +20 -2
- package/dist/server.js +57 -12
- package/dist/server.mjs +53 -11
- package/dist/{types-D8oOYbeC.d.mts → types-BqZzje-y.d.mts} +21 -3
- package/dist/{types-D8oOYbeC.d.ts → types-BqZzje-y.d.ts} +21 -3
- package/dist/webhooks.js +7 -3
- package/dist/webhooks.mjs +7 -3
- package/package.json +5 -2
package/dist/react/index.js
CHANGED
|
@@ -30,9 +30,27 @@ module.exports = __toCommonJS(react_exports);
|
|
|
30
30
|
// src/react/useAuth.ts
|
|
31
31
|
var import_react = require("react");
|
|
32
32
|
|
|
33
|
+
// src/types.ts
|
|
34
|
+
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
35
|
+
|
|
33
36
|
// src/client.ts
|
|
37
|
+
function assertHttpsUrl(url) {
|
|
38
|
+
let parsed;
|
|
39
|
+
try {
|
|
40
|
+
parsed = new URL(url);
|
|
41
|
+
} catch {
|
|
42
|
+
throw new Error("URL must use HTTPS");
|
|
43
|
+
}
|
|
44
|
+
if (parsed.protocol !== "https:") {
|
|
45
|
+
throw new Error("URL must use HTTPS");
|
|
46
|
+
}
|
|
47
|
+
}
|
|
34
48
|
function createReauthClient(config) {
|
|
35
49
|
const { domain } = config;
|
|
50
|
+
if (config.timeout !== void 0 && (!Number.isFinite(config.timeout) || config.timeout <= 0)) {
|
|
51
|
+
throw new Error("timeout must be a positive finite number in milliseconds");
|
|
52
|
+
}
|
|
53
|
+
const timeoutMs = config.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
36
54
|
const baseUrl = `https://reauth.${domain}/api/public`;
|
|
37
55
|
return {
|
|
38
56
|
/**
|
|
@@ -51,35 +69,47 @@ function createReauthClient(config) {
|
|
|
51
69
|
*/
|
|
52
70
|
async getSession() {
|
|
53
71
|
const res = await fetch(`${baseUrl}/auth/session`, {
|
|
54
|
-
credentials: "include"
|
|
72
|
+
credentials: "include",
|
|
73
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
55
74
|
});
|
|
75
|
+
if (!res.ok) {
|
|
76
|
+
throw new Error(`Failed to get session: ${res.status}`);
|
|
77
|
+
}
|
|
56
78
|
return res.json();
|
|
57
79
|
},
|
|
58
80
|
/**
|
|
59
81
|
* Refresh the access token using the refresh token.
|
|
60
82
|
* Call this when getSession() returns valid: false but no error_code.
|
|
61
|
-
* @
|
|
83
|
+
* @throws Error on failed refresh (401) or server error
|
|
62
84
|
*/
|
|
63
85
|
async refresh() {
|
|
64
86
|
const res = await fetch(`${baseUrl}/auth/refresh`, {
|
|
65
87
|
method: "POST",
|
|
66
|
-
credentials: "include"
|
|
88
|
+
credentials: "include",
|
|
89
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
67
90
|
});
|
|
68
|
-
|
|
91
|
+
if (!res.ok) {
|
|
92
|
+
throw new Error(`Failed to refresh: ${res.status}`);
|
|
93
|
+
}
|
|
69
94
|
},
|
|
70
95
|
/**
|
|
71
96
|
* Get an access token for Bearer authentication.
|
|
72
97
|
* Use this when calling your own API that uses local token verification.
|
|
73
98
|
*
|
|
74
|
-
* @returns TokenResponse with access token, or null if not authenticated
|
|
99
|
+
* @returns TokenResponse with access token, or null if not authenticated or network unreachable
|
|
100
|
+
* @throws Error on server errors (non-401 HTTP status codes) or request timeout
|
|
75
101
|
*
|
|
76
102
|
* @example
|
|
77
103
|
* ```typescript
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
104
|
+
* try {
|
|
105
|
+
* const tokenResponse = await reauth.getToken();
|
|
106
|
+
* if (tokenResponse) {
|
|
107
|
+
* fetch('/api/data', {
|
|
108
|
+
* headers: { Authorization: `Bearer ${tokenResponse.accessToken}` }
|
|
109
|
+
* });
|
|
110
|
+
* }
|
|
111
|
+
* } catch (err) {
|
|
112
|
+
* // Server error or timeout — do not log the user out
|
|
83
113
|
* }
|
|
84
114
|
* ```
|
|
85
115
|
*/
|
|
@@ -87,7 +117,8 @@ function createReauthClient(config) {
|
|
|
87
117
|
try {
|
|
88
118
|
const res = await fetch(`${baseUrl}/auth/token`, {
|
|
89
119
|
method: "GET",
|
|
90
|
-
credentials: "include"
|
|
120
|
+
credentials: "include",
|
|
121
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
91
122
|
});
|
|
92
123
|
if (!res.ok) {
|
|
93
124
|
if (res.status === 401) return null;
|
|
@@ -99,29 +130,38 @@ function createReauthClient(config) {
|
|
|
99
130
|
expiresIn: data.expires_in,
|
|
100
131
|
tokenType: data.token_type
|
|
101
132
|
};
|
|
102
|
-
} catch {
|
|
103
|
-
return null;
|
|
133
|
+
} catch (err) {
|
|
134
|
+
if (err instanceof TypeError) return null;
|
|
135
|
+
throw err;
|
|
104
136
|
}
|
|
105
137
|
},
|
|
106
138
|
/**
|
|
107
139
|
* Log out the user by clearing all session cookies.
|
|
140
|
+
* @throws Error on server error
|
|
108
141
|
*/
|
|
109
142
|
async logout() {
|
|
110
|
-
await fetch(`${baseUrl}/auth/logout`, {
|
|
143
|
+
const res = await fetch(`${baseUrl}/auth/logout`, {
|
|
111
144
|
method: "POST",
|
|
112
|
-
credentials: "include"
|
|
145
|
+
credentials: "include",
|
|
146
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
113
147
|
});
|
|
148
|
+
if (!res.ok) {
|
|
149
|
+
throw new Error(`Failed to logout: ${res.status}`);
|
|
150
|
+
}
|
|
114
151
|
},
|
|
115
152
|
/**
|
|
116
153
|
* Delete the user's own account (self-service).
|
|
117
|
-
* @
|
|
154
|
+
* @throws Error on permission denied or server error
|
|
118
155
|
*/
|
|
119
156
|
async deleteAccount() {
|
|
120
157
|
const res = await fetch(`${baseUrl}/auth/account`, {
|
|
121
158
|
method: "DELETE",
|
|
122
|
-
credentials: "include"
|
|
159
|
+
credentials: "include",
|
|
160
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
123
161
|
});
|
|
124
|
-
|
|
162
|
+
if (!res.ok) {
|
|
163
|
+
throw new Error(`Failed to delete account: ${res.status}`);
|
|
164
|
+
}
|
|
125
165
|
},
|
|
126
166
|
// ========================================================================
|
|
127
167
|
// Billing Methods
|
|
@@ -129,12 +169,16 @@ function createReauthClient(config) {
|
|
|
129
169
|
/**
|
|
130
170
|
* Get available subscription plans for the domain.
|
|
131
171
|
* Only returns public plans sorted by display order.
|
|
172
|
+
* @throws Error on server error
|
|
132
173
|
*/
|
|
133
174
|
async getPlans() {
|
|
134
175
|
const res = await fetch(`${baseUrl}/billing/plans`, {
|
|
135
|
-
credentials: "include"
|
|
176
|
+
credentials: "include",
|
|
177
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
136
178
|
});
|
|
137
|
-
if (!res.ok)
|
|
179
|
+
if (!res.ok) {
|
|
180
|
+
throw new Error(`Failed to get plans: ${res.status}`);
|
|
181
|
+
}
|
|
138
182
|
const data = await res.json();
|
|
139
183
|
return data.map(
|
|
140
184
|
(p) => ({
|
|
@@ -147,8 +191,17 @@ function createReauthClient(config) {
|
|
|
147
191
|
interval: p.interval,
|
|
148
192
|
intervalCount: p.interval_count,
|
|
149
193
|
trialDays: p.trial_days,
|
|
150
|
-
features: p.features
|
|
151
|
-
|
|
194
|
+
features: p.features.map((f) => ({
|
|
195
|
+
code: f.code,
|
|
196
|
+
name: f.name,
|
|
197
|
+
featureType: f.feature_type,
|
|
198
|
+
numericValue: f.numeric_value,
|
|
199
|
+
unitLabel: f.unit_label
|
|
200
|
+
})),
|
|
201
|
+
displayOrder: p.display_order,
|
|
202
|
+
creditsAmount: p.credits_amount,
|
|
203
|
+
planType: p.plan_type,
|
|
204
|
+
contactUrl: p.contact_url
|
|
152
205
|
})
|
|
153
206
|
);
|
|
154
207
|
},
|
|
@@ -157,8 +210,12 @@ function createReauthClient(config) {
|
|
|
157
210
|
*/
|
|
158
211
|
async getSubscription() {
|
|
159
212
|
const res = await fetch(`${baseUrl}/billing/subscription`, {
|
|
160
|
-
credentials: "include"
|
|
213
|
+
credentials: "include",
|
|
214
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
161
215
|
});
|
|
216
|
+
if (!res.ok) {
|
|
217
|
+
throw new Error(`Failed to get subscription: ${res.status}`);
|
|
218
|
+
}
|
|
162
219
|
const data = await res.json();
|
|
163
220
|
return {
|
|
164
221
|
id: data.id,
|
|
@@ -182,6 +239,7 @@ function createReauthClient(config) {
|
|
|
182
239
|
method: "POST",
|
|
183
240
|
headers: { "Content-Type": "application/json" },
|
|
184
241
|
credentials: "include",
|
|
242
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
185
243
|
body: JSON.stringify({
|
|
186
244
|
plan_code: planCode,
|
|
187
245
|
success_url: successUrl,
|
|
@@ -210,6 +268,7 @@ function createReauthClient(config) {
|
|
|
210
268
|
currentUrl,
|
|
211
269
|
currentUrl
|
|
212
270
|
);
|
|
271
|
+
assertHttpsUrl(checkoutUrl);
|
|
213
272
|
window.location.href = checkoutUrl;
|
|
214
273
|
},
|
|
215
274
|
/**
|
|
@@ -224,6 +283,7 @@ function createReauthClient(config) {
|
|
|
224
283
|
method: "POST",
|
|
225
284
|
headers: { "Content-Type": "application/json" },
|
|
226
285
|
credentials: "include",
|
|
286
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
227
287
|
body: JSON.stringify({
|
|
228
288
|
return_url: returnUrl || window.location.href
|
|
229
289
|
})
|
|
@@ -232,18 +292,22 @@ function createReauthClient(config) {
|
|
|
232
292
|
throw new Error("Failed to open billing portal");
|
|
233
293
|
}
|
|
234
294
|
const data = await res.json();
|
|
295
|
+
assertHttpsUrl(data.portal_url);
|
|
235
296
|
window.location.href = data.portal_url;
|
|
236
297
|
},
|
|
237
298
|
/**
|
|
238
299
|
* Cancel the user's subscription at period end.
|
|
239
|
-
* @
|
|
300
|
+
* @throws Error on failure or server error
|
|
240
301
|
*/
|
|
241
302
|
async cancelSubscription() {
|
|
242
303
|
const res = await fetch(`${baseUrl}/billing/cancel`, {
|
|
243
304
|
method: "POST",
|
|
244
|
-
credentials: "include"
|
|
305
|
+
credentials: "include",
|
|
306
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
245
307
|
});
|
|
246
|
-
|
|
308
|
+
if (!res.ok) {
|
|
309
|
+
throw new Error(`Failed to cancel subscription: ${res.status}`);
|
|
310
|
+
}
|
|
247
311
|
},
|
|
248
312
|
// ========================================================================
|
|
249
313
|
// Balance Methods
|
|
@@ -254,13 +318,15 @@ function createReauthClient(config) {
|
|
|
254
318
|
*/
|
|
255
319
|
async getBalance() {
|
|
256
320
|
const res = await fetch(`${baseUrl}/balance`, {
|
|
257
|
-
credentials: "include"
|
|
321
|
+
credentials: "include",
|
|
322
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
258
323
|
});
|
|
259
324
|
if (!res.ok) {
|
|
260
325
|
if (res.status === 401) throw new Error("Not authenticated");
|
|
261
326
|
throw new Error(`Failed to get balance: ${res.status}`);
|
|
262
327
|
}
|
|
263
|
-
|
|
328
|
+
const data = await res.json();
|
|
329
|
+
return { balance: data.balance };
|
|
264
330
|
},
|
|
265
331
|
/**
|
|
266
332
|
* Get the current user's balance transaction history.
|
|
@@ -274,7 +340,7 @@ function createReauthClient(config) {
|
|
|
274
340
|
const qs = params.toString();
|
|
275
341
|
const res = await fetch(
|
|
276
342
|
`${baseUrl}/balance/transactions${qs ? `?${qs}` : ""}`,
|
|
277
|
-
{ credentials: "include" }
|
|
343
|
+
{ credentials: "include", signal: AbortSignal.timeout(timeoutMs) }
|
|
278
344
|
);
|
|
279
345
|
if (!res.ok) {
|
|
280
346
|
if (res.status === 401) throw new Error("Not authenticated");
|
|
@@ -297,8 +363,11 @@ function createReauthClient(config) {
|
|
|
297
363
|
}
|
|
298
364
|
|
|
299
365
|
// src/react/useAuth.ts
|
|
366
|
+
var DEFAULT_REFRESH_INTERVAL_MS = 5 * 60 * 1e3;
|
|
300
367
|
function useAuth(config) {
|
|
301
|
-
const
|
|
368
|
+
const { refreshInterval = DEFAULT_REFRESH_INTERVAL_MS, ...clientConfig } = config;
|
|
369
|
+
const client = (0, import_react.useMemo)(() => createReauthClient(clientConfig), [clientConfig.domain]);
|
|
370
|
+
const isRefreshing = (0, import_react.useRef)(false);
|
|
302
371
|
const [state, setState] = (0, import_react.useState)({
|
|
303
372
|
user: null,
|
|
304
373
|
loading: true,
|
|
@@ -307,12 +376,15 @@ function useAuth(config) {
|
|
|
307
376
|
waitlistPosition: null
|
|
308
377
|
});
|
|
309
378
|
const checkSession = (0, import_react.useCallback)(async () => {
|
|
379
|
+
if (isRefreshing.current) return;
|
|
380
|
+
isRefreshing.current = true;
|
|
310
381
|
try {
|
|
311
382
|
let session = await client.getSession();
|
|
312
383
|
if (!session.valid && !session.error_code && !session.end_user_id) {
|
|
313
|
-
|
|
314
|
-
|
|
384
|
+
try {
|
|
385
|
+
await client.refresh();
|
|
315
386
|
session = await client.getSession();
|
|
387
|
+
} catch {
|
|
316
388
|
}
|
|
317
389
|
}
|
|
318
390
|
if (session.error_code === "ACCOUNT_SUSPENDED") {
|
|
@@ -368,11 +440,20 @@ function useAuth(config) {
|
|
|
368
440
|
isOnWaitlist: false,
|
|
369
441
|
waitlistPosition: null
|
|
370
442
|
});
|
|
443
|
+
} finally {
|
|
444
|
+
isRefreshing.current = false;
|
|
371
445
|
}
|
|
372
446
|
}, [client]);
|
|
373
447
|
(0, import_react.useEffect)(() => {
|
|
374
448
|
checkSession();
|
|
375
449
|
}, [checkSession]);
|
|
450
|
+
(0, import_react.useEffect)(() => {
|
|
451
|
+
if (refreshInterval <= 0) return;
|
|
452
|
+
const intervalId = setInterval(() => {
|
|
453
|
+
checkSession();
|
|
454
|
+
}, refreshInterval);
|
|
455
|
+
return () => clearInterval(intervalId);
|
|
456
|
+
}, [checkSession, refreshInterval]);
|
|
376
457
|
const logout = (0, import_react.useCallback)(async () => {
|
|
377
458
|
await client.logout();
|
|
378
459
|
setState({
|
|
@@ -417,8 +498,10 @@ function ProtectedRoute({
|
|
|
417
498
|
onWaitlist
|
|
418
499
|
}) {
|
|
419
500
|
const { user, loading, isOnWaitlist, login } = useAuthContext();
|
|
501
|
+
const hasRedirected = (0, import_react3.useRef)(false);
|
|
420
502
|
(0, import_react3.useEffect)(() => {
|
|
421
|
-
if (!loading && !user) {
|
|
503
|
+
if (!loading && !user && !hasRedirected.current) {
|
|
504
|
+
hasRedirected.current = true;
|
|
422
505
|
if (onUnauthenticated) {
|
|
423
506
|
onUnauthenticated();
|
|
424
507
|
} else {
|
|
@@ -427,7 +510,8 @@ function ProtectedRoute({
|
|
|
427
510
|
}
|
|
428
511
|
}, [loading, user, login, onUnauthenticated]);
|
|
429
512
|
(0, import_react3.useEffect)(() => {
|
|
430
|
-
if (!loading && isOnWaitlist && onWaitlist) {
|
|
513
|
+
if (!loading && isOnWaitlist && onWaitlist && !hasRedirected.current) {
|
|
514
|
+
hasRedirected.current = true;
|
|
431
515
|
onWaitlist();
|
|
432
516
|
}
|
|
433
517
|
}, [loading, isOnWaitlist, onWaitlist]);
|
package/dist/react/index.mjs
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createReauthClient
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-DMNMTW2C.mjs";
|
|
4
|
+
import "../chunk-EY5LQCDG.mjs";
|
|
4
5
|
|
|
5
6
|
// src/react/useAuth.ts
|
|
6
|
-
import { useState, useEffect, useCallback, useMemo } from "react";
|
|
7
|
+
import { useState, useEffect, useCallback, useMemo, useRef } from "react";
|
|
8
|
+
var DEFAULT_REFRESH_INTERVAL_MS = 5 * 60 * 1e3;
|
|
7
9
|
function useAuth(config) {
|
|
8
|
-
const
|
|
10
|
+
const { refreshInterval = DEFAULT_REFRESH_INTERVAL_MS, ...clientConfig } = config;
|
|
11
|
+
const client = useMemo(() => createReauthClient(clientConfig), [clientConfig.domain]);
|
|
12
|
+
const isRefreshing = useRef(false);
|
|
9
13
|
const [state, setState] = useState({
|
|
10
14
|
user: null,
|
|
11
15
|
loading: true,
|
|
@@ -14,12 +18,15 @@ function useAuth(config) {
|
|
|
14
18
|
waitlistPosition: null
|
|
15
19
|
});
|
|
16
20
|
const checkSession = useCallback(async () => {
|
|
21
|
+
if (isRefreshing.current) return;
|
|
22
|
+
isRefreshing.current = true;
|
|
17
23
|
try {
|
|
18
24
|
let session = await client.getSession();
|
|
19
25
|
if (!session.valid && !session.error_code && !session.end_user_id) {
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
try {
|
|
27
|
+
await client.refresh();
|
|
22
28
|
session = await client.getSession();
|
|
29
|
+
} catch {
|
|
23
30
|
}
|
|
24
31
|
}
|
|
25
32
|
if (session.error_code === "ACCOUNT_SUSPENDED") {
|
|
@@ -75,11 +82,20 @@ function useAuth(config) {
|
|
|
75
82
|
isOnWaitlist: false,
|
|
76
83
|
waitlistPosition: null
|
|
77
84
|
});
|
|
85
|
+
} finally {
|
|
86
|
+
isRefreshing.current = false;
|
|
78
87
|
}
|
|
79
88
|
}, [client]);
|
|
80
89
|
useEffect(() => {
|
|
81
90
|
checkSession();
|
|
82
91
|
}, [checkSession]);
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
if (refreshInterval <= 0) return;
|
|
94
|
+
const intervalId = setInterval(() => {
|
|
95
|
+
checkSession();
|
|
96
|
+
}, refreshInterval);
|
|
97
|
+
return () => clearInterval(intervalId);
|
|
98
|
+
}, [checkSession, refreshInterval]);
|
|
83
99
|
const logout = useCallback(async () => {
|
|
84
100
|
await client.logout();
|
|
85
101
|
setState({
|
|
@@ -115,7 +131,7 @@ function useAuthContext() {
|
|
|
115
131
|
}
|
|
116
132
|
|
|
117
133
|
// src/react/ProtectedRoute.tsx
|
|
118
|
-
import { useEffect as useEffect2 } from "react";
|
|
134
|
+
import { useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
119
135
|
import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
|
|
120
136
|
function ProtectedRoute({
|
|
121
137
|
children,
|
|
@@ -124,8 +140,10 @@ function ProtectedRoute({
|
|
|
124
140
|
onWaitlist
|
|
125
141
|
}) {
|
|
126
142
|
const { user, loading, isOnWaitlist, login } = useAuthContext();
|
|
143
|
+
const hasRedirected = useRef2(false);
|
|
127
144
|
useEffect2(() => {
|
|
128
|
-
if (!loading && !user) {
|
|
145
|
+
if (!loading && !user && !hasRedirected.current) {
|
|
146
|
+
hasRedirected.current = true;
|
|
129
147
|
if (onUnauthenticated) {
|
|
130
148
|
onUnauthenticated();
|
|
131
149
|
} else {
|
|
@@ -134,7 +152,8 @@ function ProtectedRoute({
|
|
|
134
152
|
}
|
|
135
153
|
}, [loading, user, login, onUnauthenticated]);
|
|
136
154
|
useEffect2(() => {
|
|
137
|
-
if (!loading && isOnWaitlist && onWaitlist) {
|
|
155
|
+
if (!loading && isOnWaitlist && onWaitlist && !hasRedirected.current) {
|
|
156
|
+
hasRedirected.current = true;
|
|
138
157
|
onWaitlist();
|
|
139
158
|
}
|
|
140
159
|
}, [loading, isOnWaitlist, onWaitlist]);
|
package/dist/server.d.mts
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
|
-
import { e as ReauthServerConfig, A as AuthResult, f as RequestLike, d as UserDetails, h as ChargeOptions, i as DepositOptions, b as TransactionsPaginationOptions, B as BalanceTransaction } from './types-
|
|
1
|
+
import { e as ReauthServerConfig, A as AuthResult, f as RequestLike, d as UserDetails, h as ChargeOptions, i as DepositOptions, b as TransactionsPaginationOptions, B as BalanceTransaction } from './types-BqZzje-y.mjs';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Derives a JWT signing secret from an API key using HKDF-SHA256.
|
|
5
|
+
* This must match the Rust backend implementation exactly.
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT: Returns hex-encoded string (64 chars), NOT raw bytes.
|
|
8
|
+
* The Rust API uses the ASCII bytes of the hex string as the JWT secret,
|
|
9
|
+
* so we must do the same for compatibility.
|
|
10
|
+
*
|
|
11
|
+
* @param apiKey - The raw API key (e.g., "sk_live_...")
|
|
12
|
+
* @param domainId - UUID of the domain (used as salt for domain isolation)
|
|
13
|
+
* @returns Promise<string> - 64-char hex string (to be used as ASCII bytes for JWT signing)
|
|
14
|
+
*/
|
|
15
|
+
declare function deriveJwtSecret(apiKey: string, domainId: string): Promise<string>;
|
|
16
|
+
/**
|
|
17
|
+
* Parse cookies from a cookie header string.
|
|
18
|
+
* Handles URL encoding and multiple cookies properly.
|
|
19
|
+
*/
|
|
20
|
+
declare function parseCookies(cookieHeader: string): Record<string, string>;
|
|
3
21
|
/**
|
|
4
22
|
* Create a reauth client for server-side authentication.
|
|
5
23
|
* Uses local JWT verification for fast, reliable auth checks.
|
|
@@ -185,4 +203,4 @@ declare function createServerClient(config: ReauthServerConfig): {
|
|
|
185
203
|
};
|
|
186
204
|
type ServerClient = ReturnType<typeof createServerClient>;
|
|
187
205
|
|
|
188
|
-
export { type ServerClient, createServerClient };
|
|
206
|
+
export { type ServerClient, createServerClient, deriveJwtSecret, parseCookies };
|
package/dist/server.d.ts
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
|
-
import { e as ReauthServerConfig, A as AuthResult, f as RequestLike, d as UserDetails, h as ChargeOptions, i as DepositOptions, b as TransactionsPaginationOptions, B as BalanceTransaction } from './types-
|
|
1
|
+
import { e as ReauthServerConfig, A as AuthResult, f as RequestLike, d as UserDetails, h as ChargeOptions, i as DepositOptions, b as TransactionsPaginationOptions, B as BalanceTransaction } from './types-BqZzje-y.js';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Derives a JWT signing secret from an API key using HKDF-SHA256.
|
|
5
|
+
* This must match the Rust backend implementation exactly.
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT: Returns hex-encoded string (64 chars), NOT raw bytes.
|
|
8
|
+
* The Rust API uses the ASCII bytes of the hex string as the JWT secret,
|
|
9
|
+
* so we must do the same for compatibility.
|
|
10
|
+
*
|
|
11
|
+
* @param apiKey - The raw API key (e.g., "sk_live_...")
|
|
12
|
+
* @param domainId - UUID of the domain (used as salt for domain isolation)
|
|
13
|
+
* @returns Promise<string> - 64-char hex string (to be used as ASCII bytes for JWT signing)
|
|
14
|
+
*/
|
|
15
|
+
declare function deriveJwtSecret(apiKey: string, domainId: string): Promise<string>;
|
|
16
|
+
/**
|
|
17
|
+
* Parse cookies from a cookie header string.
|
|
18
|
+
* Handles URL encoding and multiple cookies properly.
|
|
19
|
+
*/
|
|
20
|
+
declare function parseCookies(cookieHeader: string): Record<string, string>;
|
|
3
21
|
/**
|
|
4
22
|
* Create a reauth client for server-side authentication.
|
|
5
23
|
* Uses local JWT verification for fast, reliable auth checks.
|
|
@@ -185,4 +203,4 @@ declare function createServerClient(config: ReauthServerConfig): {
|
|
|
185
203
|
};
|
|
186
204
|
type ServerClient = ReturnType<typeof createServerClient>;
|
|
187
205
|
|
|
188
|
-
export { type ServerClient, createServerClient };
|
|
206
|
+
export { type ServerClient, createServerClient, deriveJwtSecret, parseCookies };
|
package/dist/server.js
CHANGED
|
@@ -30,11 +30,18 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/server.ts
|
|
31
31
|
var server_exports = {};
|
|
32
32
|
__export(server_exports, {
|
|
33
|
-
createServerClient: () => createServerClient
|
|
33
|
+
createServerClient: () => createServerClient,
|
|
34
|
+
deriveJwtSecret: () => deriveJwtSecret,
|
|
35
|
+
parseCookies: () => parseCookies
|
|
34
36
|
});
|
|
35
37
|
module.exports = __toCommonJS(server_exports);
|
|
36
38
|
var import_crypto = require("crypto");
|
|
37
39
|
var jose = __toESM(require("jose"));
|
|
40
|
+
|
|
41
|
+
// src/types.ts
|
|
42
|
+
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
43
|
+
|
|
44
|
+
// src/server.ts
|
|
38
45
|
async function deriveJwtSecret(apiKey, domainId) {
|
|
39
46
|
const salt = Buffer.from(domainId.replace(/-/g, ""), "hex");
|
|
40
47
|
const info = Buffer.from("reauth-jwt-v1");
|
|
@@ -45,6 +52,28 @@ async function deriveJwtSecret(apiKey, domainId) {
|
|
|
45
52
|
});
|
|
46
53
|
});
|
|
47
54
|
}
|
|
55
|
+
var VALID_SUBSCRIPTION_STATUSES = /* @__PURE__ */ new Set([
|
|
56
|
+
"active",
|
|
57
|
+
"past_due",
|
|
58
|
+
"canceled",
|
|
59
|
+
"trialing",
|
|
60
|
+
"incomplete",
|
|
61
|
+
"incomplete_expired",
|
|
62
|
+
"unpaid",
|
|
63
|
+
"paused",
|
|
64
|
+
"none"
|
|
65
|
+
]);
|
|
66
|
+
function isValidClaims(payload) {
|
|
67
|
+
if (typeof payload.sub !== "string") return false;
|
|
68
|
+
if (typeof payload.domain_id !== "string") return false;
|
|
69
|
+
if (typeof payload.domain !== "string") return false;
|
|
70
|
+
if (!Array.isArray(payload.roles) || !payload.roles.every((r) => typeof r === "string")) return false;
|
|
71
|
+
const subscription = payload.subscription;
|
|
72
|
+
if (typeof subscription !== "object" || subscription === null || Array.isArray(subscription)) return false;
|
|
73
|
+
const status = subscription.status;
|
|
74
|
+
if (typeof status !== "string" || !VALID_SUBSCRIPTION_STATUSES.has(status)) return false;
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
48
77
|
function transformSubscription(sub) {
|
|
49
78
|
return {
|
|
50
79
|
status: sub.status,
|
|
@@ -71,6 +100,10 @@ function parseCookies(cookieHeader) {
|
|
|
71
100
|
}
|
|
72
101
|
function createServerClient(config) {
|
|
73
102
|
const { domain, apiKey } = config;
|
|
103
|
+
if (config.timeout !== void 0 && (!Number.isFinite(config.timeout) || config.timeout <= 0)) {
|
|
104
|
+
throw new Error("timeout must be a positive finite number in milliseconds");
|
|
105
|
+
}
|
|
106
|
+
const timeoutMs = config.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
74
107
|
if (!apiKey) {
|
|
75
108
|
throw new Error(
|
|
76
109
|
"apiKey is required for createServerClient. Get one from the Reauth dashboard."
|
|
@@ -118,7 +151,11 @@ function createServerClient(config) {
|
|
|
118
151
|
// 60 seconds clock skew tolerance
|
|
119
152
|
}
|
|
120
153
|
);
|
|
121
|
-
const
|
|
154
|
+
const payloadRecord = payload;
|
|
155
|
+
if (!isValidClaims(payloadRecord)) {
|
|
156
|
+
return { valid: false, user: null, claims: null, error: "Invalid token claims: missing required fields" };
|
|
157
|
+
}
|
|
158
|
+
const claims = payloadRecord;
|
|
122
159
|
if (claims.domain !== domain) {
|
|
123
160
|
return { valid: false, user: null, claims: null, error: "Domain mismatch" };
|
|
124
161
|
}
|
|
@@ -241,10 +278,11 @@ function createServerClient(config) {
|
|
|
241
278
|
* ```
|
|
242
279
|
*/
|
|
243
280
|
async getUserById(userId) {
|
|
244
|
-
const res = await fetch(`${developerBaseUrl}/users/${userId}`, {
|
|
281
|
+
const res = await fetch(`${developerBaseUrl}/users/${encodeURIComponent(userId)}`, {
|
|
245
282
|
headers: {
|
|
246
283
|
Authorization: `Bearer ${apiKey}`
|
|
247
|
-
}
|
|
284
|
+
},
|
|
285
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
248
286
|
});
|
|
249
287
|
if (!res.ok) {
|
|
250
288
|
if (res.status === 401) {
|
|
@@ -277,16 +315,18 @@ function createServerClient(config) {
|
|
|
277
315
|
* @returns Object with the current balance
|
|
278
316
|
*/
|
|
279
317
|
async getBalance(userId) {
|
|
280
|
-
const res = await fetch(`${developerBaseUrl}/users/${userId}/balance`, {
|
|
318
|
+
const res = await fetch(`${developerBaseUrl}/users/${encodeURIComponent(userId)}/balance`, {
|
|
281
319
|
headers: {
|
|
282
320
|
Authorization: `Bearer ${apiKey}`
|
|
283
|
-
}
|
|
321
|
+
},
|
|
322
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
284
323
|
});
|
|
285
324
|
if (!res.ok) {
|
|
286
325
|
if (res.status === 401) throw new Error("Invalid API key");
|
|
287
326
|
throw new Error(`Failed to get balance: ${res.status}`);
|
|
288
327
|
}
|
|
289
|
-
|
|
328
|
+
const data = await res.json();
|
|
329
|
+
return { balance: data.balance };
|
|
290
330
|
},
|
|
291
331
|
/**
|
|
292
332
|
* Charge (deduct) credits from a user's balance.
|
|
@@ -297,12 +337,13 @@ function createServerClient(config) {
|
|
|
297
337
|
* @throws Error with status 402 if insufficient balance, 400 if invalid amount
|
|
298
338
|
*/
|
|
299
339
|
async charge(userId, opts) {
|
|
300
|
-
const res = await fetch(`${developerBaseUrl}/users/${userId}/charge`, {
|
|
340
|
+
const res = await fetch(`${developerBaseUrl}/users/${encodeURIComponent(userId)}/charge`, {
|
|
301
341
|
method: "POST",
|
|
302
342
|
headers: {
|
|
303
343
|
"Content-Type": "application/json",
|
|
304
344
|
Authorization: `Bearer ${apiKey}`
|
|
305
345
|
},
|
|
346
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
306
347
|
body: JSON.stringify({
|
|
307
348
|
amount: opts.amount,
|
|
308
349
|
request_uuid: opts.requestUuid,
|
|
@@ -326,12 +367,13 @@ function createServerClient(config) {
|
|
|
326
367
|
* @returns Object with the new balance after deposit
|
|
327
368
|
*/
|
|
328
369
|
async deposit(userId, opts) {
|
|
329
|
-
const res = await fetch(`${developerBaseUrl}/users/${userId}/deposit`, {
|
|
370
|
+
const res = await fetch(`${developerBaseUrl}/users/${encodeURIComponent(userId)}/deposit`, {
|
|
330
371
|
method: "POST",
|
|
331
372
|
headers: {
|
|
332
373
|
"Content-Type": "application/json",
|
|
333
374
|
Authorization: `Bearer ${apiKey}`
|
|
334
375
|
},
|
|
376
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
335
377
|
body: JSON.stringify({
|
|
336
378
|
amount: opts.amount,
|
|
337
379
|
request_uuid: opts.requestUuid,
|
|
@@ -359,11 +401,12 @@ function createServerClient(config) {
|
|
|
359
401
|
if (opts?.offset !== void 0) params.set("offset", String(opts.offset));
|
|
360
402
|
const qs = params.toString();
|
|
361
403
|
const res = await fetch(
|
|
362
|
-
`${developerBaseUrl}/users/${userId}/balance/transactions${qs ? `?${qs}` : ""}`,
|
|
404
|
+
`${developerBaseUrl}/users/${encodeURIComponent(userId)}/balance/transactions${qs ? `?${qs}` : ""}`,
|
|
363
405
|
{
|
|
364
406
|
headers: {
|
|
365
407
|
Authorization: `Bearer ${apiKey}`
|
|
366
|
-
}
|
|
408
|
+
},
|
|
409
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
367
410
|
}
|
|
368
411
|
);
|
|
369
412
|
if (!res.ok) {
|
|
@@ -387,5 +430,7 @@ function createServerClient(config) {
|
|
|
387
430
|
}
|
|
388
431
|
// Annotate the CommonJS export names for ESM import in node:
|
|
389
432
|
0 && (module.exports = {
|
|
390
|
-
createServerClient
|
|
433
|
+
createServerClient,
|
|
434
|
+
deriveJwtSecret,
|
|
435
|
+
parseCookies
|
|
391
436
|
});
|