@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/server.mjs CHANGED
@@ -1,3 +1,7 @@
1
+ import {
2
+ DEFAULT_TIMEOUT_MS
3
+ } from "./chunk-EY5LQCDG.mjs";
4
+
1
5
  // src/server.ts
2
6
  import { hkdf } from "crypto";
3
7
  import * as jose from "jose";
@@ -11,6 +15,28 @@ async function deriveJwtSecret(apiKey, domainId) {
11
15
  });
12
16
  });
13
17
  }
18
+ var VALID_SUBSCRIPTION_STATUSES = /* @__PURE__ */ new Set([
19
+ "active",
20
+ "past_due",
21
+ "canceled",
22
+ "trialing",
23
+ "incomplete",
24
+ "incomplete_expired",
25
+ "unpaid",
26
+ "paused",
27
+ "none"
28
+ ]);
29
+ function isValidClaims(payload) {
30
+ if (typeof payload.sub !== "string") return false;
31
+ if (typeof payload.domain_id !== "string") return false;
32
+ if (typeof payload.domain !== "string") return false;
33
+ if (!Array.isArray(payload.roles) || !payload.roles.every((r) => typeof r === "string")) return false;
34
+ const subscription = payload.subscription;
35
+ if (typeof subscription !== "object" || subscription === null || Array.isArray(subscription)) return false;
36
+ const status = subscription.status;
37
+ if (typeof status !== "string" || !VALID_SUBSCRIPTION_STATUSES.has(status)) return false;
38
+ return true;
39
+ }
14
40
  function transformSubscription(sub) {
15
41
  return {
16
42
  status: sub.status,
@@ -37,6 +63,10 @@ function parseCookies(cookieHeader) {
37
63
  }
38
64
  function createServerClient(config) {
39
65
  const { domain, apiKey } = config;
66
+ if (config.timeout !== void 0 && (!Number.isFinite(config.timeout) || config.timeout <= 0)) {
67
+ throw new Error("timeout must be a positive finite number in milliseconds");
68
+ }
69
+ const timeoutMs = config.timeout ?? DEFAULT_TIMEOUT_MS;
40
70
  if (!apiKey) {
41
71
  throw new Error(
42
72
  "apiKey is required for createServerClient. Get one from the Reauth dashboard."
@@ -84,7 +114,11 @@ function createServerClient(config) {
84
114
  // 60 seconds clock skew tolerance
85
115
  }
86
116
  );
87
- const claims = payload;
117
+ const payloadRecord = payload;
118
+ if (!isValidClaims(payloadRecord)) {
119
+ return { valid: false, user: null, claims: null, error: "Invalid token claims: missing required fields" };
120
+ }
121
+ const claims = payloadRecord;
88
122
  if (claims.domain !== domain) {
89
123
  return { valid: false, user: null, claims: null, error: "Domain mismatch" };
90
124
  }
@@ -207,10 +241,11 @@ function createServerClient(config) {
207
241
  * ```
208
242
  */
209
243
  async getUserById(userId) {
210
- const res = await fetch(`${developerBaseUrl}/users/${userId}`, {
244
+ const res = await fetch(`${developerBaseUrl}/users/${encodeURIComponent(userId)}`, {
211
245
  headers: {
212
246
  Authorization: `Bearer ${apiKey}`
213
- }
247
+ },
248
+ signal: AbortSignal.timeout(timeoutMs)
214
249
  });
215
250
  if (!res.ok) {
216
251
  if (res.status === 401) {
@@ -243,16 +278,18 @@ function createServerClient(config) {
243
278
  * @returns Object with the current balance
244
279
  */
245
280
  async getBalance(userId) {
246
- const res = await fetch(`${developerBaseUrl}/users/${userId}/balance`, {
281
+ const res = await fetch(`${developerBaseUrl}/users/${encodeURIComponent(userId)}/balance`, {
247
282
  headers: {
248
283
  Authorization: `Bearer ${apiKey}`
249
- }
284
+ },
285
+ signal: AbortSignal.timeout(timeoutMs)
250
286
  });
251
287
  if (!res.ok) {
252
288
  if (res.status === 401) throw new Error("Invalid API key");
253
289
  throw new Error(`Failed to get balance: ${res.status}`);
254
290
  }
255
- return res.json();
291
+ const data = await res.json();
292
+ return { balance: data.balance };
256
293
  },
257
294
  /**
258
295
  * Charge (deduct) credits from a user's balance.
@@ -263,12 +300,13 @@ function createServerClient(config) {
263
300
  * @throws Error with status 402 if insufficient balance, 400 if invalid amount
264
301
  */
265
302
  async charge(userId, opts) {
266
- const res = await fetch(`${developerBaseUrl}/users/${userId}/charge`, {
303
+ const res = await fetch(`${developerBaseUrl}/users/${encodeURIComponent(userId)}/charge`, {
267
304
  method: "POST",
268
305
  headers: {
269
306
  "Content-Type": "application/json",
270
307
  Authorization: `Bearer ${apiKey}`
271
308
  },
309
+ signal: AbortSignal.timeout(timeoutMs),
272
310
  body: JSON.stringify({
273
311
  amount: opts.amount,
274
312
  request_uuid: opts.requestUuid,
@@ -292,12 +330,13 @@ function createServerClient(config) {
292
330
  * @returns Object with the new balance after deposit
293
331
  */
294
332
  async deposit(userId, opts) {
295
- const res = await fetch(`${developerBaseUrl}/users/${userId}/deposit`, {
333
+ const res = await fetch(`${developerBaseUrl}/users/${encodeURIComponent(userId)}/deposit`, {
296
334
  method: "POST",
297
335
  headers: {
298
336
  "Content-Type": "application/json",
299
337
  Authorization: `Bearer ${apiKey}`
300
338
  },
339
+ signal: AbortSignal.timeout(timeoutMs),
301
340
  body: JSON.stringify({
302
341
  amount: opts.amount,
303
342
  request_uuid: opts.requestUuid,
@@ -325,11 +364,12 @@ function createServerClient(config) {
325
364
  if (opts?.offset !== void 0) params.set("offset", String(opts.offset));
326
365
  const qs = params.toString();
327
366
  const res = await fetch(
328
- `${developerBaseUrl}/users/${userId}/balance/transactions${qs ? `?${qs}` : ""}`,
367
+ `${developerBaseUrl}/users/${encodeURIComponent(userId)}/balance/transactions${qs ? `?${qs}` : ""}`,
329
368
  {
330
369
  headers: {
331
370
  Authorization: `Bearer ${apiKey}`
332
- }
371
+ },
372
+ signal: AbortSignal.timeout(timeoutMs)
333
373
  }
334
374
  );
335
375
  if (!res.ok) {
@@ -352,5 +392,7 @@ function createServerClient(config) {
352
392
  };
353
393
  }
354
394
  export {
355
- createServerClient
395
+ createServerClient,
396
+ deriveJwtSecret,
397
+ parseCookies
356
398
  };
@@ -5,8 +5,11 @@ type ReauthSession = {
5
5
  email: string | null;
6
6
  roles: string[] | null;
7
7
  waitlist_position: number | null;
8
+ /** Whether the user has linked their Google account */
9
+ google_linked: boolean | null;
8
10
  error: string | null;
9
- error_code: "ACCOUNT_SUSPENDED" | null;
11
+ /** Error code (e.g., "ACCOUNT_SUSPENDED", "SESSION_VERIFICATION_FAILED") */
12
+ error_code: string | null;
10
13
  /** Subscription information (if billing is configured) */
11
14
  subscription?: {
12
15
  status: string;
@@ -62,6 +65,8 @@ type DomainEndUserClaims = {
62
65
  type ReauthConfig = {
63
66
  /** Your verified domain (e.g., "yourdomain.com") */
64
67
  domain: string;
68
+ /** Request timeout in milliseconds. Defaults to 10 seconds if not specified. */
69
+ timeout?: number;
65
70
  };
66
71
  /** Configuration for server-side reauth client */
67
72
  type ReauthServerConfig = ReauthConfig & {
@@ -112,6 +117,16 @@ type TokenResponse = {
112
117
  /** Token type (always "Bearer") */
113
118
  tokenType: string;
114
119
  };
120
+ /** A single feature attached to a subscription plan */
121
+ type PlanFeature = {
122
+ code: string;
123
+ name: string;
124
+ featureType: "boolean" | "numeric";
125
+ numericValue: number | null;
126
+ unitLabel: string | null;
127
+ };
128
+ /** Plan type determines how a user subscribes */
129
+ type PlanType = "self_service" | "sales";
115
130
  /** Subscription plan available for purchase */
116
131
  type SubscriptionPlan = {
117
132
  id: string;
@@ -123,8 +138,11 @@ type SubscriptionPlan = {
123
138
  interval: "monthly" | "yearly" | "custom";
124
139
  intervalCount: number;
125
140
  trialDays: number;
126
- features: string[];
141
+ features: PlanFeature[];
127
142
  displayOrder: number;
143
+ creditsAmount: number;
144
+ planType: PlanType;
145
+ contactUrl: string | null;
128
146
  };
129
147
  /** User's current subscription details */
130
148
  type UserSubscription = {
@@ -166,4 +184,4 @@ type TransactionsPaginationOptions = {
166
184
  offset?: number;
167
185
  };
168
186
 
169
- export type { AuthResult as A, BalanceTransaction as B, CheckoutSession as C, DomainEndUserClaims as D, ReauthConfig as R, SubscriptionPlan as S, TokenResponse as T, UserSubscription as U, ReauthSession as a, TransactionsPaginationOptions as b, User as c, UserDetails as d, ReauthServerConfig as e, RequestLike as f, SubscriptionInfo as g, ChargeOptions as h, DepositOptions as i };
187
+ export type { AuthResult as A, BalanceTransaction as B, CheckoutSession as C, DomainEndUserClaims as D, PlanFeature as P, ReauthConfig as R, SubscriptionPlan as S, TokenResponse as T, UserSubscription as U, ReauthSession as a, TransactionsPaginationOptions as b, User as c, UserDetails as d, ReauthServerConfig as e, RequestLike as f, SubscriptionInfo as g, ChargeOptions as h, DepositOptions as i };
@@ -5,8 +5,11 @@ type ReauthSession = {
5
5
  email: string | null;
6
6
  roles: string[] | null;
7
7
  waitlist_position: number | null;
8
+ /** Whether the user has linked their Google account */
9
+ google_linked: boolean | null;
8
10
  error: string | null;
9
- error_code: "ACCOUNT_SUSPENDED" | null;
11
+ /** Error code (e.g., "ACCOUNT_SUSPENDED", "SESSION_VERIFICATION_FAILED") */
12
+ error_code: string | null;
10
13
  /** Subscription information (if billing is configured) */
11
14
  subscription?: {
12
15
  status: string;
@@ -62,6 +65,8 @@ type DomainEndUserClaims = {
62
65
  type ReauthConfig = {
63
66
  /** Your verified domain (e.g., "yourdomain.com") */
64
67
  domain: string;
68
+ /** Request timeout in milliseconds. Defaults to 10 seconds if not specified. */
69
+ timeout?: number;
65
70
  };
66
71
  /** Configuration for server-side reauth client */
67
72
  type ReauthServerConfig = ReauthConfig & {
@@ -112,6 +117,16 @@ type TokenResponse = {
112
117
  /** Token type (always "Bearer") */
113
118
  tokenType: string;
114
119
  };
120
+ /** A single feature attached to a subscription plan */
121
+ type PlanFeature = {
122
+ code: string;
123
+ name: string;
124
+ featureType: "boolean" | "numeric";
125
+ numericValue: number | null;
126
+ unitLabel: string | null;
127
+ };
128
+ /** Plan type determines how a user subscribes */
129
+ type PlanType = "self_service" | "sales";
115
130
  /** Subscription plan available for purchase */
116
131
  type SubscriptionPlan = {
117
132
  id: string;
@@ -123,8 +138,11 @@ type SubscriptionPlan = {
123
138
  interval: "monthly" | "yearly" | "custom";
124
139
  intervalCount: number;
125
140
  trialDays: number;
126
- features: string[];
141
+ features: PlanFeature[];
127
142
  displayOrder: number;
143
+ creditsAmount: number;
144
+ planType: PlanType;
145
+ contactUrl: string | null;
128
146
  };
129
147
  /** User's current subscription details */
130
148
  type UserSubscription = {
@@ -166,4 +184,4 @@ type TransactionsPaginationOptions = {
166
184
  offset?: number;
167
185
  };
168
186
 
169
- export type { AuthResult as A, BalanceTransaction as B, CheckoutSession as C, DomainEndUserClaims as D, ReauthConfig as R, SubscriptionPlan as S, TokenResponse as T, UserSubscription as U, ReauthSession as a, TransactionsPaginationOptions as b, User as c, UserDetails as d, ReauthServerConfig as e, RequestLike as f, SubscriptionInfo as g, ChargeOptions as h, DepositOptions as i };
187
+ export type { AuthResult as A, BalanceTransaction as B, CheckoutSession as C, DomainEndUserClaims as D, PlanFeature as P, ReauthConfig as R, SubscriptionPlan as S, TokenResponse as T, UserSubscription as U, ReauthSession as a, TransactionsPaginationOptions as b, User as c, UserDetails as d, ReauthServerConfig as e, RequestLike as f, SubscriptionInfo as g, ChargeOptions as h, DepositOptions as i };
package/dist/webhooks.js CHANGED
@@ -43,11 +43,15 @@ function parseSignatureHeader(header) {
43
43
  let timestamp = "";
44
44
  const signatures = [];
45
45
  for (const part of parts) {
46
- const [key, value] = part.split("=", 2);
46
+ const [rawKey, rawValue] = part.trim().split("=", 2);
47
+ const key = rawKey?.trim();
48
+ const value = rawValue?.trim();
47
49
  if (key === "t") {
48
- timestamp = value;
50
+ timestamp = value ?? "";
49
51
  } else if (key === "v1") {
50
- signatures.push(value);
52
+ if (value) {
53
+ signatures.push(value);
54
+ }
51
55
  }
52
56
  }
53
57
  return { timestamp, signatures };
package/dist/webhooks.mjs CHANGED
@@ -15,11 +15,15 @@ function parseSignatureHeader(header) {
15
15
  let timestamp = "";
16
16
  const signatures = [];
17
17
  for (const part of parts) {
18
- const [key, value] = part.split("=", 2);
18
+ const [rawKey, rawValue] = part.trim().split("=", 2);
19
+ const key = rawKey?.trim();
20
+ const value = rawValue?.trim();
19
21
  if (key === "t") {
20
- timestamp = value;
22
+ timestamp = value ?? "";
21
23
  } else if (key === "v1") {
22
- signatures.push(value);
24
+ if (value) {
25
+ signatures.push(value);
26
+ }
23
27
  }
24
28
  }
25
29
  return { timestamp, signatures };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reauth-dev/sdk",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "TypeScript SDK for reauth.dev authentication",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -38,6 +38,8 @@
38
38
  "scripts": {
39
39
  "build": "tsup",
40
40
  "dev": "tsup --watch",
41
+ "test": "vitest run",
42
+ "test:watch": "vitest",
41
43
  "typecheck": "tsc --noEmit",
42
44
  "format": "prettier --write src"
43
45
  },
@@ -58,7 +60,8 @@
58
60
  "prettier": "^3.8.1",
59
61
  "react": "^18.0.0",
60
62
  "tsup": "^8.0.0",
61
- "typescript": "^5.0.0"
63
+ "typescript": "^5.0.0",
64
+ "vitest": "^4.0.18"
62
65
  },
63
66
  "keywords": [
64
67
  "auth",