@rekey.dev/node 2.0.0 → 2.2.0-rc.1
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/README.md +17 -2
- package/dist/index.d.ts +409 -93
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +394 -97
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @rekey.dev/node
|
|
2
|
+
* @rekey.dev/node, server SDK for Rekey.
|
|
3
3
|
*
|
|
4
4
|
* One client instance per Application. Construct with the Application's
|
|
5
5
|
* secret key (`rp_live_…` or `rp_test_…`) and the URL of your Rekey
|
|
6
|
-
* deployment. Never ship the secret key to the browser
|
|
6
|
+
* deployment. Never ship the secret key to the browser, for browser code
|
|
7
7
|
* use `@rekey.dev/react` with the Application's public key instead.
|
|
8
8
|
*
|
|
9
9
|
* @example Smoke-test your credentials
|
|
@@ -19,15 +19,15 @@
|
|
|
19
19
|
* console.log(`Connected to "${me.name}" (${me.slug})`);
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
|
-
import type { ApplicationDto, AuthResultDto, ListPage, Paged, JwksDto, ChangePasswordRequest, CheckoutResultDto, ConsumeCreditsRequest, ConsumeCreditsResultDto, CreateCheckoutRequest, CreditBalanceDto, CreditLedgerEntryDto, EndUserDto, ForgotPasswordRequest, ForgotPasswordResultDto, LicenseVerifyResultDto, MfaVerifyRequest, OAuthAuthServerMetadata, OAuthIntrospectionResponse, OrganizationDto, OrganizationInvitationDto, OrganizationMemberDto, OrganizationWithRoleDto, PlanDto, ProvidersListDto, ResetPasswordRequest, SignInOutcomeDto, SignInRequest, SignUpRequest, SubscriptionDto, UsageAggregateDto, UsageRecordDto, ValidateCouponRequest, ValidateCouponResultDto } from '@rekey.dev/shared-types';
|
|
22
|
+
import type { ApplicationDto, AuthResultDto, ListPage, Paged, JwksDto, ChangePasswordRequest, CheckoutResultDto, ConsumeCreditsRequest, ConsumeCreditsResultDto, CreateCheckoutRequest, CreditBalanceDto, CreditLedgerEntryDto, EndUserDto, ForgotPasswordRequest, ForgotPasswordResultDto, LicenseVerifyResultDto, LicenseDeactivateRequest, LicenseDeactivateResultDto, DeviceDto, EndUserDeviceDto, TrialEligibilityDto, DeviceStatusType, MfaVerifyRequest, OAuthAuthServerMetadata, OAuthIntrospectionResponse, OrganizationDto, OrganizationInvitationDto, OrganizationMemberDto, OrganizationWithRoleDto, OrganizationRoleDefDto, OrganizationRole, OrganizationBaseRole, PlanDto, ProvidersListDto, ResetPasswordRequest, SignInOutcomeDto, SignInRequest, DeviceBindingRequest, SignUpRequest, SubscriptionDto, UsageAggregateDto, UsageRecordDto, ValidateCouponRequest, ValidateCouponResultDto } from '@rekey.dev/shared-types';
|
|
23
23
|
import { RekeyError } from '@rekey.dev/shared-types/error';
|
|
24
|
-
export type { ApplicationDto, EndUserDto, ApiKeyDto, AuthResultDto, MfaChallengeResultDto, MfaVerifyRequest, SignInOutcomeDto, SignInRequest, SignUpRequest, RefreshRequest, ForgotPasswordRequest, ForgotPasswordResultDto, ResetPasswordRequest, ChangePasswordRequest, PlanDto, SubscriptionDto, CreateCheckoutRequest, CheckoutResultDto, CouponDto, ProvidersListDto, BillingProviderInfoDto, ValidateCouponRequest, ValidateCouponResultDto, CouponDiscountTypeValue, PlanIntervalType, PlanKindType, LicenseKindType, CreditReasonType, CreditBalanceDto, CreditLedgerEntryDto, ConsumeCreditsRequest, ConsumeCreditsResultDto, OrganizationDto, OrganizationWithRoleDto, OrganizationMemberDto, OrganizationInvitationDto, OrganizationRole, LicenseDto, LicenseStatusType, LicenseVerifyResultDto, UsageRecordDto, UsageAggregateDto, SubscriptionStatusType, RekeyErrorShape, AuthConfig, BillingConfig, BillingProvider, TokenAlg, JwkRsaPublic, JwksDto, OAuthIntrospectionResponse, OAuthAuthServerMetadata, } from '@rekey.dev/shared-types';
|
|
24
|
+
export type { ApplicationDto, EndUserDto, ApiKeyDto, AuthResultDto, MfaChallengeResultDto, MfaVerifyRequest, SignInOutcomeDto, SignInRequest, DeviceBindingRequest, SignUpRequest, RefreshRequest, ForgotPasswordRequest, ForgotPasswordResultDto, ResetPasswordRequest, ChangePasswordRequest, PlanDto, SubscriptionDto, CreateCheckoutRequest, CheckoutResultDto, CouponDto, ProvidersListDto, BillingProviderInfoDto, ValidateCouponRequest, ValidateCouponResultDto, BillingProviderCapabilities, TrialEligibilityDto, TrialEligibilityItemDto, TrialPolicyType, CouponDiscountTypeValue, PlanIntervalType, PlanKindType, LicenseKindType, CreditReasonType, CreditBalanceDto, CreditLedgerEntryDto, ConsumeCreditsRequest, ConsumeCreditsResultDto, OrganizationDto, OrganizationWithRoleDto, OrganizationRoleDefDto, OrganizationMemberDto, OrganizationInvitationDto, OrganizationRole, OrganizationBaseRole, LicenseDto, LicenseStatusType, LicenseVerifyResultDto, LicenseActivationDto, LicenseDeactivateRequest, LicenseDeactivateResultDto, DeviceDto, EndUserDeviceDto, DeviceLimitDetails, DeviceStatusType, UsageRecordDto, UsageAggregateDto, SubscriptionStatusType, RekeyErrorShape, AuthConfig, BillingConfig, BillingProvider, TokenAlg, JwkRsaPublic, JwksDto, OAuthIntrospectionResponse, OAuthAuthServerMetadata, } from '@rekey.dev/shared-types';
|
|
25
25
|
/**
|
|
26
26
|
* Default per-request deadline, in milliseconds. Matches the timeout the Rekey
|
|
27
27
|
* API itself uses when it POSTs your outbound webhooks.
|
|
28
28
|
*
|
|
29
|
-
* Without a deadline the effective timeout is undici's `headersTimeout
|
|
30
|
-
* minutes
|
|
29
|
+
* Without a deadline the effective timeout is undici's `headersTimeout`, five
|
|
30
|
+
* minutes, so a single unreachable Rekey deployment can pin one of your
|
|
31
31
|
* request handlers for that long. Ten seconds is long enough for any endpoint
|
|
32
32
|
* this SDK calls and short enough to fail a page instead of hanging it.
|
|
33
33
|
*/
|
|
@@ -36,13 +36,13 @@ export declare const DEFAULT_TIMEOUT_MS = 10000;
|
|
|
36
36
|
export interface RekeyConfig {
|
|
37
37
|
/** Base URL of the Rekey API. e.g. `https://rekey.example.com` */
|
|
38
38
|
apiUrl: string;
|
|
39
|
-
/** Secret key for one Application
|
|
39
|
+
/** Secret key for one Application, `rp_live_…` or `rp_test_…`. Never ship to the browser. */
|
|
40
40
|
secretKey: string;
|
|
41
41
|
/** Optional fetch override (test stubs, custom keep-alive agents, etc.). */
|
|
42
42
|
fetch?: typeof fetch;
|
|
43
43
|
/**
|
|
44
44
|
* Deadline for every request this client makes, in milliseconds.
|
|
45
|
-
* Default {@link DEFAULT_TIMEOUT_MS} (10 000). Pass `0` to disable
|
|
45
|
+
* Default {@link DEFAULT_TIMEOUT_MS} (10 000). Pass `0` to disable, only do
|
|
46
46
|
* that if something upstream of you already bounds the call.
|
|
47
47
|
*
|
|
48
48
|
* On expiry the promise rejects with a `RekeyError` whose code is
|
|
@@ -50,7 +50,7 @@ export interface RekeyConfig {
|
|
|
50
50
|
*/
|
|
51
51
|
timeoutMs?: number | undefined;
|
|
52
52
|
/**
|
|
53
|
-
* Client-wide abort signal
|
|
53
|
+
* Client-wide abort signal, aborting it cancels every in-flight request
|
|
54
54
|
* (server shutdown, request-scoped cancellation). Composed with, not
|
|
55
55
|
* replaced by, any per-call `signal`.
|
|
56
56
|
*/
|
|
@@ -62,7 +62,7 @@ export interface RekeyConfig {
|
|
|
62
62
|
* addable without a new overload.
|
|
63
63
|
*/
|
|
64
64
|
export interface RekeyRequestOptions {
|
|
65
|
-
/** JSON request body. Omit for GET/DELETE
|
|
65
|
+
/** JSON request body. Omit for GET/DELETE, a present body sets `Content-Type`. */
|
|
66
66
|
body?: unknown;
|
|
67
67
|
/** Extra headers, merged over the SDK's own (`Authorization`, `Content-Type`). */
|
|
68
68
|
headers?: Record<string, string> | undefined;
|
|
@@ -80,7 +80,7 @@ export interface RekeyCallOptions {
|
|
|
80
80
|
}
|
|
81
81
|
export { RekeyError };
|
|
82
82
|
/**
|
|
83
|
-
* Outbound webhook event registry
|
|
83
|
+
* Outbound webhook event registry, the events Rekey can POST to your app
|
|
84
84
|
* (verify them with `verifyWebhookSignature` below). `WEBHOOK_EVENTS` carries
|
|
85
85
|
* `{ name, description }` pairs for introspection/autocomplete;
|
|
86
86
|
* `KNOWN_WEBHOOK_EVENTS` is just the names. Mirrors the API's registry exactly.
|
|
@@ -89,7 +89,7 @@ export { RekeyError };
|
|
|
89
89
|
* ```ts
|
|
90
90
|
* import { WEBHOOK_EVENTS, isKnownWebhookEvent, type WebhookEventEnvelope } from '@rekey.dev/node';
|
|
91
91
|
*
|
|
92
|
-
* for (const e of WEBHOOK_EVENTS) console.log(`${e.name}
|
|
92
|
+
* for (const e of WEBHOOK_EVENTS) console.log(`${e.name}, ${e.description}`);
|
|
93
93
|
*
|
|
94
94
|
* const event = req.body as WebhookEventEnvelope; // after verifyWebhookSignature(...)
|
|
95
95
|
* if (event.type === 'subscription.activated') unlockPlan(event.data);
|
|
@@ -102,7 +102,7 @@ export type { WebhookEventType, WebhookEventEnvelope } from '@rekey.dev/shared-t
|
|
|
102
102
|
* this subscriber the rest of the period they paid for?
|
|
103
103
|
*
|
|
104
104
|
* Exported because a cancel confirmation has to say which outcome the customer
|
|
105
|
-
* is about to get, and it has to say so BEFORE the call
|
|
105
|
+
* is about to get, and it has to say so BEFORE the call, there is no response
|
|
106
106
|
* to read it off. It is the same function the API decides from, not a
|
|
107
107
|
* description of it, so a UI built on it cannot promise a behaviour the server
|
|
108
108
|
* does not have. See its docblock for the cases that still end immediately.
|
|
@@ -131,23 +131,27 @@ export declare class Rekey {
|
|
|
131
131
|
private readonly config;
|
|
132
132
|
/** Operations on the calling Application itself. */
|
|
133
133
|
readonly applications: ApplicationsClient;
|
|
134
|
-
/** Auth operations
|
|
134
|
+
/** Auth operations, sign-in, sign-up, sessions, passkeys, magic-link. */
|
|
135
135
|
readonly auth: AuthClient;
|
|
136
|
-
/** Billing operations
|
|
136
|
+
/** Billing operations, plans, checkout, subscriptions, coupons. */
|
|
137
137
|
readonly billing: BillingClient;
|
|
138
|
-
/** End-user organizations
|
|
138
|
+
/** End-user organizations, create, invite, members, role changes. */
|
|
139
139
|
readonly organizations: OrganizationsClient;
|
|
140
140
|
/** License key verification + activation. */
|
|
141
141
|
readonly licenses: LicensesClient;
|
|
142
|
-
/**
|
|
142
|
+
/** Devices, list and release the machines an end-user signs in from. */
|
|
143
|
+
readonly devices: DevicesClient;
|
|
144
|
+
/** End-users, lookup by id or email, bulk import. */
|
|
145
|
+
readonly users: UsersClient;
|
|
146
|
+
/** Usage metering, record events, aggregate windows. */
|
|
143
147
|
readonly usage: UsageClient;
|
|
144
|
-
/** Prepaid credits
|
|
148
|
+
/** Prepaid credits, balance reads, idempotent drawdown, ledger. */
|
|
145
149
|
readonly credits: CreditsClient;
|
|
146
|
-
/** MCP
|
|
150
|
+
/** MCP, validate Rekey-issued MCP tokens from your own MCP server. */
|
|
147
151
|
readonly mcp: McpClient;
|
|
148
152
|
constructor(config: RekeyConfig);
|
|
149
153
|
/**
|
|
150
|
-
* A clone of this client with different call options
|
|
154
|
+
* A clone of this client with different call options, the per-call knob for
|
|
151
155
|
* every wrapped method.
|
|
152
156
|
*
|
|
153
157
|
* Each namespace method (`billing.getPlans()`, `auth.signIn()`, …) has a
|
|
@@ -167,7 +171,7 @@ export declare class Rekey {
|
|
|
167
171
|
* });
|
|
168
172
|
* ```
|
|
169
173
|
*
|
|
170
|
-
* Cheap
|
|
174
|
+
* Cheap, it rebuilds the namespace objects, holds no connections, and
|
|
171
175
|
* shares the same `fetch`.
|
|
172
176
|
*/
|
|
173
177
|
with(options: RekeyCallOptions): Rekey;
|
|
@@ -175,7 +179,7 @@ export declare class Rekey {
|
|
|
175
179
|
* Call a Rekey endpoint this SDK does not wrap yet.
|
|
176
180
|
*
|
|
177
181
|
* This is a **supported** escape hatch, not an internal: when the API grows a
|
|
178
|
-
* route before the SDK does, use this instead of hand-rolling `fetch
|
|
182
|
+
* route before the SDK does, use this instead of hand-rolling `fetch`, you
|
|
179
183
|
* keep the auth header, the `{ success, data }` unwrapping, the `RekeyError`
|
|
180
184
|
* mapping (including transport failures) and the deadline. It takes an
|
|
181
185
|
* options object precisely so a future knob does not need a new overload.
|
|
@@ -200,7 +204,7 @@ export declare class Rekey {
|
|
|
200
204
|
}
|
|
201
205
|
/**
|
|
202
206
|
* MCP helpers for customers running their OWN MCP server behind Rekey auth.
|
|
203
|
-
* The hosted MCP server (account tools) is consumed by MCP clients directly
|
|
207
|
+
* The hosted MCP server (account tools) is consumed by MCP clients directly,
|
|
204
208
|
* this client is for the "bring your own MCP server" path: validate incoming
|
|
205
209
|
* Rekey-issued tokens, and read the OAuth metadata.
|
|
206
210
|
*/
|
|
@@ -230,7 +234,7 @@ declare class ApplicationsClient {
|
|
|
230
234
|
constructor(client: Rekey);
|
|
231
235
|
/**
|
|
232
236
|
* Verify credentials and fetch the calling Application. Use this as your
|
|
233
|
-
* SDK smoke test
|
|
237
|
+
* SDK smoke test, if it returns, your secret key is good and you're
|
|
234
238
|
* pointed at the right Rekey deployment.
|
|
235
239
|
*
|
|
236
240
|
* @example
|
|
@@ -252,7 +256,7 @@ declare class AuthClient {
|
|
|
252
256
|
* (e.g. `getCurrentUser(accessToken)`) and a `refreshToken` to renew it.
|
|
253
257
|
*
|
|
254
258
|
* Unless the Application turns `authConfig.sendVerificationEmailOnSignUp`
|
|
255
|
-
* off, Rekey also emails the verification link
|
|
259
|
+
* off, Rekey also emails the verification link, best-effort, so it never
|
|
256
260
|
* fails the sign-up, and `sendVerificationEmail` re-sends on demand.
|
|
257
261
|
*
|
|
258
262
|
* @example
|
|
@@ -261,7 +265,7 @@ declare class AuthClient {
|
|
|
261
265
|
* email: 'alice@example.com',
|
|
262
266
|
* password: 'correct-horse-battery-staple',
|
|
263
267
|
* });
|
|
264
|
-
* // store both in your session
|
|
268
|
+
* // store both in your session, the access token expires in 15 minutes
|
|
265
269
|
* ```
|
|
266
270
|
*
|
|
267
271
|
* @throws {RekeyError} `EMAIL_ALREADY_EXISTS` (409) if the email is taken in this Application.
|
|
@@ -278,14 +282,14 @@ declare class AuthClient {
|
|
|
278
282
|
* Prompt the user for their TOTP / backup code and call
|
|
279
283
|
* `mfaVerify({ mfaChallengeToken, code })` to receive a real session.
|
|
280
284
|
*
|
|
281
|
-
* **Branch on `result.mfaRequired` before reading `accessToken
|
|
285
|
+
* **Branch on `result.mfaRequired` before reading `accessToken`**, the
|
|
282
286
|
* MFA-required branch has no session tokens.
|
|
283
287
|
*
|
|
284
|
-
* @throws {RekeyError} `INVALID_CREDENTIALS` (401)
|
|
288
|
+
* @throws {RekeyError} `INVALID_CREDENTIALS` (401), single code on purpose.
|
|
285
289
|
* Don't try to distinguish wrong-email from wrong-password from the SDK side either.
|
|
286
290
|
* @throws {RekeyError} `EMAIL_NOT_VERIFIED` (403) when the Application sets
|
|
287
291
|
* `authConfig.requireEmailVerification` and the user hasn't confirmed their
|
|
288
|
-
* address. The password was correct
|
|
292
|
+
* address. The password was correct, prompt for the emailed link (or call
|
|
289
293
|
* `sendVerificationEmail`), not for the password again.
|
|
290
294
|
*/
|
|
291
295
|
signIn(input: SignInRequest): Promise<SignInOutcomeDto>;
|
|
@@ -316,18 +320,23 @@ declare class AuthClient {
|
|
|
316
320
|
magicLinkToken: string | null;
|
|
317
321
|
}>;
|
|
318
322
|
/**
|
|
319
|
-
* Consume a magic-link token. Returns `SignInOutcome
|
|
323
|
+
* Consume a magic-link token. Returns `SignInOutcome`, branch on
|
|
320
324
|
* `mfaRequired` before reading `accessToken`. For MFA-enrolled users
|
|
321
325
|
* the response carries `mfaChallengeToken` and you must complete via
|
|
322
326
|
* `mfaVerify(...)`.
|
|
323
327
|
*/
|
|
324
328
|
verifyMagicLink(input: {
|
|
325
329
|
token: string;
|
|
330
|
+
/** Bind the session to a device, see docs/devices.md. */
|
|
331
|
+
device?: {
|
|
332
|
+
fingerprint: string;
|
|
333
|
+
label?: string;
|
|
334
|
+
};
|
|
326
335
|
}): Promise<SignInOutcomeDto>;
|
|
327
336
|
/**
|
|
328
337
|
* Begin a passkey authentication ceremony. Returns the WebAuthn options
|
|
329
338
|
* to forward to the browser (`navigator.credentials.get(...)`) along
|
|
330
|
-
* with `expectedChallenge
|
|
339
|
+
* with `expectedChallenge`, bind the challenge to your session and
|
|
331
340
|
* pass both back via `verifyPasskeyAuthentication(...)`.
|
|
332
341
|
*/
|
|
333
342
|
startPasskeyAuthentication(input?: {
|
|
@@ -338,12 +347,17 @@ declare class AuthClient {
|
|
|
338
347
|
}>;
|
|
339
348
|
/**
|
|
340
349
|
* Complete a passkey authentication. Returns the same `SignInOutcome`
|
|
341
|
-
* shape as `signIn
|
|
350
|
+
* shape as `signIn`, but passkeys are themselves a strong factor, so
|
|
342
351
|
* `mfaRequired` will always be `false` in practice.
|
|
343
352
|
*/
|
|
344
353
|
verifyPasskeyAuthentication(input: {
|
|
345
354
|
response: unknown;
|
|
346
355
|
expectedChallenge: string;
|
|
356
|
+
/** Bind the session to a device, see docs/devices.md. */
|
|
357
|
+
device?: {
|
|
358
|
+
fingerprint: string;
|
|
359
|
+
label?: string;
|
|
360
|
+
};
|
|
347
361
|
}): Promise<SignInOutcomeDto>;
|
|
348
362
|
/**
|
|
349
363
|
* Begin a passkey registration ceremony for an authenticated user.
|
|
@@ -366,7 +380,7 @@ declare class AuthClient {
|
|
|
366
380
|
/**
|
|
367
381
|
* List the user's registered passkeys, newest first.
|
|
368
382
|
*
|
|
369
|
-
* Returns `{items, page}
|
|
383
|
+
* Returns `{items, page}`, `page.total` is the number of passkeys the user
|
|
370
384
|
* has, independent of the window served.
|
|
371
385
|
*/
|
|
372
386
|
listPasskeys(accessToken: string, page?: ListPage): Promise<Paged<{
|
|
@@ -391,7 +405,7 @@ declare class AuthClient {
|
|
|
391
405
|
activeOrganizationId: string | null;
|
|
392
406
|
}>;
|
|
393
407
|
/**
|
|
394
|
-
* Update the end-user behind a presented access token
|
|
408
|
+
* Update the end-user behind a presented access token, their OWN record,
|
|
395
409
|
* and only ever their own: the token identifies the subject, so there is no
|
|
396
410
|
* user id to pass and no way to aim this at anyone else.
|
|
397
411
|
*
|
|
@@ -424,16 +438,18 @@ declare class AuthClient {
|
|
|
424
438
|
}>;
|
|
425
439
|
/**
|
|
426
440
|
* Exchange a refresh token for a fresh {access, refresh} pair. The presented
|
|
427
|
-
* refresh is revoked atomically
|
|
441
|
+
* refresh is revoked atomically, call this **once** and store the new
|
|
428
442
|
* `refreshToken` from the response immediately.
|
|
429
443
|
*
|
|
430
444
|
* @throws {RekeyError} `REFRESH_TOKEN_REUSED` (401) if you replay an already-used token.
|
|
431
445
|
* This is a strong signal the original was leaked; treat as compromise.
|
|
432
446
|
* @throws {RekeyError} `REFRESH_TOKEN_EXPIRED` (401) after the 30-day refresh window.
|
|
433
447
|
*/
|
|
434
|
-
refresh(refreshToken: string
|
|
448
|
+
refresh(refreshToken: string, options?: {
|
|
449
|
+
device?: DeviceBindingRequest;
|
|
450
|
+
}): Promise<AuthResultDto>;
|
|
435
451
|
/**
|
|
436
|
-
* Revoke a refresh token. Idempotent
|
|
452
|
+
* Revoke a refresh token. Idempotent, no-op for unknown tokens. The
|
|
437
453
|
* access token paired with this refresh remains valid until its short
|
|
438
454
|
* (15 min) expiry; for true "log out everywhere" semantics, also clear
|
|
439
455
|
* the access token from your client.
|
|
@@ -442,14 +458,14 @@ declare class AuthClient {
|
|
|
442
458
|
signedOut: true;
|
|
443
459
|
}>;
|
|
444
460
|
/**
|
|
445
|
-
* Request a password reset for an email. Always succeeds
|
|
461
|
+
* Request a password reset for an email. Always succeeds, never tells you
|
|
446
462
|
* whether the email exists.
|
|
447
463
|
*
|
|
448
464
|
* **Branch on the result.** When the Application has an email transport
|
|
449
465
|
* (BYO Resend/SMTP, or a deployment-wide `RESEND_DEFAULT_API_KEY`) Rekey sends
|
|
450
466
|
* the mail itself and `resetToken` is null. With no transport it falls back to
|
|
451
|
-
* the original contract and hands the raw token to you
|
|
452
|
-
* only
|
|
467
|
+
* the original contract and hands the raw token to you, a secret-key caller
|
|
468
|
+
* only, so you can deliver it with your own provider.
|
|
453
469
|
*
|
|
454
470
|
* @example
|
|
455
471
|
* ```ts
|
|
@@ -473,7 +489,7 @@ declare class AuthClient {
|
|
|
473
489
|
}>;
|
|
474
490
|
/**
|
|
475
491
|
* Authenticated password change. Pass the user's *current* access token.
|
|
476
|
-
* On success, every refresh token for the user is revoked
|
|
492
|
+
* On success, every refresh token for the user is revoked, other devices
|
|
477
493
|
* are signed out.
|
|
478
494
|
*/
|
|
479
495
|
changePassword(accessToken: string, input: ChangePasswordRequest): Promise<{
|
|
@@ -481,8 +497,8 @@ declare class AuthClient {
|
|
|
481
497
|
}>;
|
|
482
498
|
/**
|
|
483
499
|
* Revoke every refresh token for the calling user. "Sign out of all
|
|
484
|
-
* devices." The caller's access token remains valid until 15-min expiry
|
|
485
|
-
*
|
|
500
|
+
* devices." The caller's access token remains valid until 15-min expiry,
|
|
501
|
+
* clear it client-side for full logout.
|
|
486
502
|
*/
|
|
487
503
|
signOutEverywhere(accessToken: string): Promise<{
|
|
488
504
|
revokedCount: number;
|
|
@@ -503,7 +519,7 @@ declare class AuthClient {
|
|
|
503
519
|
verificationToken: string | null;
|
|
504
520
|
}>;
|
|
505
521
|
/**
|
|
506
|
-
* Re-send a verification link to an address, with **no session
|
|
522
|
+
* Re-send a verification link to an address, with **no session**, the
|
|
507
523
|
* sessionless sibling of `sendVerificationEmail`.
|
|
508
524
|
*
|
|
509
525
|
* This is the route for a user locked out by
|
|
@@ -511,17 +527,17 @@ declare class AuthClient {
|
|
|
511
527
|
* session `sendVerificationEmail` needs, so a user whose first mail never
|
|
512
528
|
* arrived cannot ask for another. Takes the address instead of a token.
|
|
513
529
|
*
|
|
514
|
-
* **Branch on the result**, exactly as with `requestPasswordReset
|
|
530
|
+
* **Branch on the result**, exactly as with `requestPasswordReset`, the
|
|
515
531
|
* contract is the same one. It never throws for an unknown address and never
|
|
516
532
|
* discloses whether the address exists, is already verified, or was mailed:
|
|
517
533
|
* a publishable-key caller gets one constant body whatever happened. A
|
|
518
|
-
* secret-key caller
|
|
534
|
+
* secret-key caller, this SDK, gets the real outcome, and the raw
|
|
519
535
|
* `verificationToken` when the Application has no email transport configured,
|
|
520
536
|
* so you can deliver it with your own provider.
|
|
521
537
|
*
|
|
522
538
|
* Pass `verifyUrl` containing `{token}` to template the link target. Unlike
|
|
523
539
|
* `sendVerificationEmail`, nothing is sent and no token is minted when no
|
|
524
|
-
* link can be built at all
|
|
540
|
+
* link can be built at all, pass `verifyUrl`, or set the Application URL
|
|
525
541
|
* (Panel → Application → Auth). Mailing a locked-out user a verification
|
|
526
542
|
* message with no button in it helps nobody.
|
|
527
543
|
*
|
|
@@ -565,7 +581,7 @@ declare class AuthClient {
|
|
|
565
581
|
userAgent: string | null;
|
|
566
582
|
ip: string | null;
|
|
567
583
|
}>>;
|
|
568
|
-
/** Revoke one session by id. Idempotent
|
|
584
|
+
/** Revoke one session by id. Idempotent, `{ revoked: false }` if it isn't this user's. */
|
|
569
585
|
revokeSession(accessToken: string, sessionId: string): Promise<{
|
|
570
586
|
revoked: boolean;
|
|
571
587
|
}>;
|
|
@@ -578,7 +594,7 @@ declare class AuthClient {
|
|
|
578
594
|
/**
|
|
579
595
|
* Begin TOTP enrollment: mints a secret (as an `otpauthUrl` for the QR) and
|
|
580
596
|
* 10 single-show backup codes. **Not enrolled until `confirmMfaSetup(...)`.**
|
|
581
|
-
* Only SHA-256 hashes of the backup codes are stored
|
|
597
|
+
* Only SHA-256 hashes of the backup codes are stored, show them once.
|
|
582
598
|
*/
|
|
583
599
|
mfaSetup(accessToken: string): Promise<{
|
|
584
600
|
otpauthUrl: string;
|
|
@@ -591,7 +607,7 @@ declare class AuthClient {
|
|
|
591
607
|
}>;
|
|
592
608
|
/**
|
|
593
609
|
* Verify a TOTP or backup code as a step-up check (does NOT issue a session).
|
|
594
|
-
* Backup codes are single-use
|
|
610
|
+
* Backup codes are single-use, consumed on success. Returns `{ ok }`.
|
|
595
611
|
*/
|
|
596
612
|
mfaChallenge(accessToken: string, code: string): Promise<{
|
|
597
613
|
ok: boolean;
|
|
@@ -609,10 +625,16 @@ declare class AuthClient {
|
|
|
609
625
|
}>;
|
|
610
626
|
/**
|
|
611
627
|
* Exchange the provider `code` for a Rekey session. Returns a
|
|
612
|
-
* `SignInOutcome
|
|
628
|
+
* `SignInOutcome`, branch on `mfaRequired` before reading `accessToken`.
|
|
613
629
|
* Verify the `state` CSRF value yourself before calling.
|
|
614
630
|
*/
|
|
615
|
-
completeOAuth(provider: string, code: string
|
|
631
|
+
completeOAuth(provider: string, code: string, options?: {
|
|
632
|
+
/** Bind the session to a device, see docs/devices.md. */
|
|
633
|
+
device?: {
|
|
634
|
+
fingerprint: string;
|
|
635
|
+
label?: string;
|
|
636
|
+
};
|
|
637
|
+
}): Promise<SignInOutcomeDto>;
|
|
616
638
|
/** List the OAuth providers linked to the current user. */
|
|
617
639
|
listOAuthIdentities(accessToken: string): Promise<Array<{
|
|
618
640
|
provider: string;
|
|
@@ -625,7 +647,7 @@ declare class AuthClient {
|
|
|
625
647
|
authorizationUrl: string;
|
|
626
648
|
}>;
|
|
627
649
|
/**
|
|
628
|
-
* Complete an OAuth link
|
|
650
|
+
* Complete an OAuth link, attaches the provider identity to the current
|
|
629
651
|
* user. Refuses on unverified provider emails (account-takeover guard) or
|
|
630
652
|
* when the provider account already belongs to a different user.
|
|
631
653
|
*/
|
|
@@ -648,7 +670,7 @@ declare class AuthClient {
|
|
|
648
670
|
*
|
|
649
671
|
* Re-exported from `@rekey.dev/shared-types` so the SDK, the API and the panel
|
|
650
672
|
* all name one shape. Every list method returns {@link Paged}, whose `page`
|
|
651
|
-
* tells you whether there is another window
|
|
673
|
+
* tells you whether there is another window, you no longer have to infer it
|
|
652
674
|
* by asking for one row more than you need.
|
|
653
675
|
*/
|
|
654
676
|
export type { ListPage, PageMeta, Paged } from '@rekey.dev/shared-types';
|
|
@@ -665,8 +687,25 @@ declare class OrganizationsClient {
|
|
|
665
687
|
membership: {
|
|
666
688
|
id: string;
|
|
667
689
|
role: 'OWNER';
|
|
690
|
+
baseRole: 'OWNER';
|
|
668
691
|
};
|
|
669
692
|
}>;
|
|
693
|
+
/**
|
|
694
|
+
* List the organization roles assignable in this Application.
|
|
695
|
+
*
|
|
696
|
+
* Returns the built-in OWNER / ADMIN / MEMBER plus any custom roles the
|
|
697
|
+
* operator defined (`editor`, `content-manager`, …). Call this to populate a
|
|
698
|
+
* role picker. An org OWNER/ADMIN assigns roles with their own session via
|
|
699
|
+
* `setMemberRole` / `invite`, so their UI needs to know which names exist.
|
|
700
|
+
*
|
|
701
|
+
* `baseRole` is the authority tier a name maps to. Gate your own features on
|
|
702
|
+
* that, never on the name: Rekey enforces the tier and treats the name as
|
|
703
|
+
* opaque, so a `content-manager` on tier MEMBER can do exactly what MEMBER
|
|
704
|
+
* can and nothing more.
|
|
705
|
+
*
|
|
706
|
+
* Bounded by construction, being an operator-curated catalog, so it is not paged.
|
|
707
|
+
*/
|
|
708
|
+
listRoles(accessToken: string): Promise<OrganizationRoleDefDto[]>;
|
|
670
709
|
/**
|
|
671
710
|
* List organizations the calling user belongs to, with their role.
|
|
672
711
|
*
|
|
@@ -689,12 +728,17 @@ declare class OrganizationsClient {
|
|
|
689
728
|
*/
|
|
690
729
|
listMembers(accessToken: string, organizationId: string, page?: ListPage): Promise<Paged<OrganizationMemberDto>>;
|
|
691
730
|
/**
|
|
692
|
-
* Invite a user. Returns the raw token ONCE
|
|
731
|
+
* Invite a user. Returns the raw token ONCE, surface via your own
|
|
693
732
|
* email/share channel. OWNER + ADMIN only.
|
|
694
733
|
*/
|
|
695
|
-
invite(accessToken: string, organizationId: string,
|
|
734
|
+
invite(accessToken: string, organizationId: string,
|
|
735
|
+
/**
|
|
736
|
+
* `role` is a catalog NAME from `listRoles()`, not a fixed tier. Omit it to
|
|
737
|
+
* use the Application's default organization role.
|
|
738
|
+
*/
|
|
739
|
+
input: {
|
|
696
740
|
email: string;
|
|
697
|
-
role
|
|
741
|
+
role?: OrganizationRole;
|
|
698
742
|
}): Promise<{
|
|
699
743
|
invitation: OrganizationInvitationDto;
|
|
700
744
|
token: string;
|
|
@@ -707,19 +751,23 @@ declare class OrganizationsClient {
|
|
|
707
751
|
* Change a member's role. OWNER manages anyone; ADMIN manages MEMBER
|
|
708
752
|
* only. Last-OWNER guard refuses demoting the only OWNER.
|
|
709
753
|
*/
|
|
710
|
-
setMemberRole(accessToken: string, organizationId: string, targetEndUserId: string,
|
|
711
|
-
|
|
754
|
+
setMemberRole(accessToken: string, organizationId: string, targetEndUserId: string,
|
|
755
|
+
/** `role` is a catalog NAME from `listRoles()`, not a fixed tier. */
|
|
756
|
+
input: {
|
|
757
|
+
role: OrganizationRole;
|
|
712
758
|
}): Promise<{
|
|
713
759
|
id: string;
|
|
714
760
|
organizationId: string;
|
|
715
761
|
endUserId: string;
|
|
716
|
-
role:
|
|
762
|
+
role: OrganizationRole;
|
|
763
|
+
/** The tier the name maps to. Gate your own features on this. */
|
|
764
|
+
baseRole: OrganizationBaseRole;
|
|
717
765
|
}>;
|
|
718
766
|
/**
|
|
719
767
|
* Remove a member (or self). Refuses removing the last OWNER.
|
|
720
768
|
*
|
|
721
769
|
* Idempotent: `removed` is `false` when the target was not a member (e.g.
|
|
722
|
-
* already removed)
|
|
770
|
+
* already removed), a no-op removal is not an error. Branch on `removed`
|
|
723
771
|
* rather than assuming it is always `true`.
|
|
724
772
|
*/
|
|
725
773
|
removeMember(accessToken: string, organizationId: string, targetEndUserId: string): Promise<{
|
|
@@ -727,7 +775,7 @@ declare class OrganizationsClient {
|
|
|
727
775
|
}>;
|
|
728
776
|
/**
|
|
729
777
|
* Self-leave. An OWNER cannot leave (payment + benefits are tied to the
|
|
730
|
-
* owner
|
|
778
|
+
* owner, `ORGANIZATION_OWNER_CANNOT_LEAVE`); transfer ownership via support
|
|
731
779
|
* first, or demote yourself to ADMIN if there is another OWNER.
|
|
732
780
|
*/
|
|
733
781
|
leave(accessToken: string, organizationId: string): Promise<{
|
|
@@ -748,7 +796,7 @@ declare class OrganizationsClient {
|
|
|
748
796
|
}>;
|
|
749
797
|
/**
|
|
750
798
|
* Make `organizationId` the active org for this session (member-only).
|
|
751
|
-
* Returns a fresh {accessToken, refreshToken} pair carrying the active org
|
|
799
|
+
* Returns a fresh {accessToken, refreshToken} pair carrying the active org,
|
|
752
800
|
* **store both**. Subsequent entitlement reads (`billing.getEntitlements`)
|
|
753
801
|
* then default to this org's view + shared pool without passing
|
|
754
802
|
* `organizationId` explicitly. The active org survives token refresh until
|
|
@@ -756,7 +804,7 @@ declare class OrganizationsClient {
|
|
|
756
804
|
*/
|
|
757
805
|
switch(accessToken: string, organizationId: string): Promise<AuthResultDto>;
|
|
758
806
|
/**
|
|
759
|
-
* Clear the active org
|
|
807
|
+
* Clear the active org, switch the session back to the personal pool.
|
|
760
808
|
* Returns a fresh token pair (no active org); **store both**.
|
|
761
809
|
*/
|
|
762
810
|
clearActive(accessToken: string): Promise<AuthResultDto>;
|
|
@@ -767,7 +815,7 @@ declare class LicensesClient {
|
|
|
767
815
|
/**
|
|
768
816
|
* Verify a license key + record an activation for this machine. Call
|
|
769
817
|
* once at app startup; you'll get a deterministic body (`ok=false` for
|
|
770
|
-
* invalid licenses
|
|
818
|
+
* invalid licenses, never an HTTP error, so your software can loop
|
|
771
819
|
* on the result without try/catch noise).
|
|
772
820
|
*
|
|
773
821
|
* `machineFingerprint` should be a stable identifier you derive client-
|
|
@@ -789,6 +837,157 @@ declare class LicensesClient {
|
|
|
789
837
|
machineFingerprint: string;
|
|
790
838
|
label?: string;
|
|
791
839
|
}): Promise<LicenseVerifyResultDto>;
|
|
840
|
+
/**
|
|
841
|
+
* Give back the seat this machine holds; call it before a re-image or on
|
|
842
|
+
* uninstall so the next machine can verify. Same deterministic body as
|
|
843
|
+
* `verify`; `released: false` means the machine held no seat.
|
|
844
|
+
*
|
|
845
|
+
* @example
|
|
846
|
+
* ```ts
|
|
847
|
+
* await rekey.licenses.deactivate({ key, machineFingerprint });
|
|
848
|
+
* ```
|
|
849
|
+
*/
|
|
850
|
+
deactivate(input: LicenseDeactivateRequest): Promise<LicenseDeactivateResultDto>;
|
|
851
|
+
}
|
|
852
|
+
/**
|
|
853
|
+
* End-users' devices (docs/devices.md), both surfaces.
|
|
854
|
+
*
|
|
855
|
+
* `list` / `release` are the SERVER surface: secret key only, addressed by
|
|
856
|
+
* end-user id, because they read and mutate OTHER users' devices.
|
|
857
|
+
*
|
|
858
|
+
* `listMine` / `releaseMine` are the END-USER surface, the one
|
|
859
|
+
* `DEVICE_LIMIT_REACHED` tells you to offer. They take that user's own access
|
|
860
|
+
* token and act only on their own devices, so they are what a "your signed-in
|
|
861
|
+
* machines" screen calls, and what lets a user release a machine themselves
|
|
862
|
+
* instead of contacting support.
|
|
863
|
+
*/
|
|
864
|
+
declare class DevicesClient {
|
|
865
|
+
private readonly client;
|
|
866
|
+
constructor(client: Rekey);
|
|
867
|
+
/** An end-user's devices, newest activity first. Optional `status` filter. */
|
|
868
|
+
list(endUserId: string, options?: {
|
|
869
|
+
status?: DeviceStatusType;
|
|
870
|
+
limit?: number;
|
|
871
|
+
offset?: number;
|
|
872
|
+
}): Promise<Paged<DeviceDto>>;
|
|
873
|
+
/** Release a device: gives its slot back and revokes every session on it. */
|
|
874
|
+
release(deviceId: string, endUserId: string): Promise<{
|
|
875
|
+
device: DeviceDto;
|
|
876
|
+
sessionsRevoked: number;
|
|
877
|
+
}>;
|
|
878
|
+
/**
|
|
879
|
+
* The calling end-user's OWN devices, newest activity first.
|
|
880
|
+
*
|
|
881
|
+
* `GET /api/v1/users/me/devices`, authorized by the user's access token
|
|
882
|
+
* rather than by an end-user id. Operator notes (`blockedReason`) and IPs are
|
|
883
|
+
* not on this surface, which is why it resolves to `EndUserDeviceDto`.
|
|
884
|
+
*
|
|
885
|
+
* The device the current session is bound to is the one whose `id` matches
|
|
886
|
+
* the access token's `dev` claim (see {@link VerifiedAccessTokenClaims}), so
|
|
887
|
+
* a "your devices" screen can mark "this device" without a second call.
|
|
888
|
+
*
|
|
889
|
+
* @example
|
|
890
|
+
* ```ts
|
|
891
|
+
* const { items } = await rekey.devices.listMine(accessToken, { status: 'ACTIVE' });
|
|
892
|
+
* ```
|
|
893
|
+
*/
|
|
894
|
+
listMine(accessToken: string, options?: {
|
|
895
|
+
status?: DeviceStatusType;
|
|
896
|
+
limit?: number;
|
|
897
|
+
offset?: number;
|
|
898
|
+
}): Promise<Paged<EndUserDeviceDto>>;
|
|
899
|
+
/**
|
|
900
|
+
* Release one of the calling end-user's own devices.
|
|
901
|
+
*
|
|
902
|
+
* This is the flow `DEVICE_LIMIT_REACHED` names: that refusal carries
|
|
903
|
+
* `details.limit` and `details.devices` (typed as `DeviceLimitDetails`), so
|
|
904
|
+
* you can show the user their machines and release one here rather than
|
|
905
|
+
* leaving them at a dead end.
|
|
906
|
+
*
|
|
907
|
+
* Gives the slot back and revokes every session minted on that device,
|
|
908
|
+
* INCLUDING the current one when it is the same device, so treat a release of
|
|
909
|
+
* `claims.dev` as a sign-out. Idempotent for an already-released device; a
|
|
910
|
+
* BLOCKED device refuses with `DEVICE_BLOCKED` (only an operator can unblock).
|
|
911
|
+
*
|
|
912
|
+
* @example
|
|
913
|
+
* ```ts
|
|
914
|
+
* try {
|
|
915
|
+
* await rekey.auth.signIn({ email, password, device: { fingerprint } });
|
|
916
|
+
* } catch (e) {
|
|
917
|
+
* if (e instanceof RekeyError && e.code === 'DEVICE_LIMIT_REACHED') {
|
|
918
|
+
* const { devices } = e.details as DeviceLimitDetails;
|
|
919
|
+
* // …let the user pick one, then, with a token from a session that has one:
|
|
920
|
+
* await rekey.devices.releaseMine(accessToken, devices[0]!.id);
|
|
921
|
+
* }
|
|
922
|
+
* }
|
|
923
|
+
* ```
|
|
924
|
+
*/
|
|
925
|
+
releaseMine(accessToken: string, deviceId: string): Promise<{
|
|
926
|
+
device: EndUserDeviceDto;
|
|
927
|
+
sessionsRevoked: number;
|
|
928
|
+
}>;
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* Server-side end-user lookup (secret key only). `/users/me` answers "who is
|
|
932
|
+
* this token"; these answer "who is this id / email" for a backend that holds
|
|
933
|
+
* no token.
|
|
934
|
+
*/
|
|
935
|
+
declare class UsersClient {
|
|
936
|
+
private readonly client;
|
|
937
|
+
constructor(client: Rekey);
|
|
938
|
+
/** Exact, case-insensitive email match in the calling Application. Throws END_USER_NOT_FOUND. */
|
|
939
|
+
getByEmail(email: string): Promise<EndUserDto>;
|
|
940
|
+
/** By id, scoped to the calling Application. Throws END_USER_NOT_FOUND. */
|
|
941
|
+
get(endUserId: string): Promise<EndUserDto>;
|
|
942
|
+
/**
|
|
943
|
+
* Import up to 500 users from another auth system in one call. Password
|
|
944
|
+
* hashes (argon2id or bcrypt) are stored as given and verified as-is at
|
|
945
|
+
* sign-in; bcrypt is upgraded to argon2id on first success. Existing
|
|
946
|
+
* addresses are skipped, never updated.
|
|
947
|
+
*
|
|
948
|
+
* @example
|
|
949
|
+
* ```ts
|
|
950
|
+
* const { created, skipped } = await rekey.users.import([
|
|
951
|
+
* { email: 'a@example.com', passwordHash: '$2b$10$…', emailVerified: true },
|
|
952
|
+
* { email: 'b@example.com', oauthIdentities: [{ provider: 'google', providerAccountId: '1234' }] },
|
|
953
|
+
* ]);
|
|
954
|
+
* ```
|
|
955
|
+
*/
|
|
956
|
+
import(users: ImportUserInput[]): Promise<ImportUsersResult>;
|
|
957
|
+
}
|
|
958
|
+
export interface ImportUserInput {
|
|
959
|
+
email: string;
|
|
960
|
+
/** `$argon2id$…` or `$2a$`/`$2b$`/`$2y$…`. Omit for OAuth-only users. */
|
|
961
|
+
passwordHash?: string;
|
|
962
|
+
emailVerified?: boolean;
|
|
963
|
+
role?: string;
|
|
964
|
+
metadata?: Record<string, unknown>;
|
|
965
|
+
oauthIdentities?: Array<{
|
|
966
|
+
provider: string;
|
|
967
|
+
providerAccountId: string;
|
|
968
|
+
email?: string;
|
|
969
|
+
}>;
|
|
970
|
+
}
|
|
971
|
+
export interface ImportUsersResult {
|
|
972
|
+
created: Array<{
|
|
973
|
+
id: string;
|
|
974
|
+
email: string;
|
|
975
|
+
}>;
|
|
976
|
+
skipped: Array<{
|
|
977
|
+
email: string;
|
|
978
|
+
reason: string;
|
|
979
|
+
}>;
|
|
980
|
+
/**
|
|
981
|
+
* OAuth identities that were NOT linked because the provider account is
|
|
982
|
+
* already attached to another end-user in this Application. The user was
|
|
983
|
+
* still created; their sign-in through that provider lands on the OTHER
|
|
984
|
+
* account until one of the two is fixed.
|
|
985
|
+
*/
|
|
986
|
+
unlinked: Array<{
|
|
987
|
+
email: string;
|
|
988
|
+
provider: string;
|
|
989
|
+
providerAccountId: string;
|
|
990
|
+
}>;
|
|
792
991
|
}
|
|
793
992
|
declare class UsageClient {
|
|
794
993
|
private readonly client;
|
|
@@ -822,7 +1021,7 @@ declare class UsageClient {
|
|
|
822
1021
|
}): Promise<UsageAggregateDto>;
|
|
823
1022
|
}
|
|
824
1023
|
/**
|
|
825
|
-
* A credit subject
|
|
1024
|
+
* A credit subject, pass `endUserId` for a personal balance, or
|
|
826
1025
|
* `organizationId` for a shared org pool (owner+beneficiary billing).
|
|
827
1026
|
*/
|
|
828
1027
|
export type CreditSubject = {
|
|
@@ -831,11 +1030,11 @@ export type CreditSubject = {
|
|
|
831
1030
|
organizationId: string;
|
|
832
1031
|
};
|
|
833
1032
|
/**
|
|
834
|
-
* Prepaid credits
|
|
1033
|
+
* Prepaid credits, the "lead pack" / pay-as-you-go drawdown model. The
|
|
835
1034
|
* customer's backend grants credits (by selling a CREDIT-kind plan, which
|
|
836
1035
|
* grants automatically on payment) and draws them down per unit consumed.
|
|
837
1036
|
*
|
|
838
|
-
* All calls are server-to-server (secret key) and scoped to a `CreditSubject
|
|
1037
|
+
* All calls are server-to-server (secret key) and scoped to a `CreditSubject`,
|
|
839
1038
|
* an end-user's personal balance, or an organization's shared pool.
|
|
840
1039
|
*/
|
|
841
1040
|
declare class CreditsClient {
|
|
@@ -848,7 +1047,7 @@ declare class CreditsClient {
|
|
|
848
1047
|
* `code: "CREDITS_INSUFFICIENT"` (HTTP 402) when the balance is too low.
|
|
849
1048
|
*
|
|
850
1049
|
* Pass `idempotencyKey` (e.g. the lead id) so a retried call never
|
|
851
|
-
* double-charges
|
|
1050
|
+
* double-charges, a repeat returns the original result with `applied: false`.
|
|
852
1051
|
*/
|
|
853
1052
|
consume(input: ConsumeCreditsRequest & CreditSubject): Promise<ConsumeCreditsResultDto>;
|
|
854
1053
|
/**
|
|
@@ -858,7 +1057,7 @@ declare class CreditsClient {
|
|
|
858
1057
|
*/
|
|
859
1058
|
listLedger(subject: CreditSubject, limit?: number, offset?: number): Promise<Paged<CreditLedgerEntryDto>>;
|
|
860
1059
|
}
|
|
861
|
-
/** What an end-user (or org) is entitled to right now
|
|
1060
|
+
/** What an end-user (or org) is entitled to right now, from active subs. */
|
|
862
1061
|
export interface EntitlementsDto {
|
|
863
1062
|
/** Feature flags + numeric limits, keyed by code. Gate your app on these. */
|
|
864
1063
|
features: Record<string, boolean | number | string>;
|
|
@@ -881,7 +1080,7 @@ export interface EntitlementsDto {
|
|
|
881
1080
|
* default 300) AND (b) the signature matches a constant-time compare.
|
|
882
1081
|
*
|
|
883
1082
|
* Use against the `X-Rekey-Signature` header and the raw request body
|
|
884
|
-
* BYTES (not the parsed JSON
|
|
1083
|
+
* BYTES (not the parsed JSON, any reserialization breaks the HMAC).
|
|
885
1084
|
*
|
|
886
1085
|
* @example
|
|
887
1086
|
* ```ts
|
|
@@ -907,16 +1106,37 @@ export declare function verifyWebhookSignature(args: {
|
|
|
907
1106
|
}): boolean;
|
|
908
1107
|
/** Verified claims of an RS256 end-user access token. */
|
|
909
1108
|
export interface VerifiedAccessTokenClaims {
|
|
910
|
-
/** Always `"eu_access"
|
|
1109
|
+
/** Always `"eu_access"`, other token types are refused. */
|
|
911
1110
|
typ: 'eu_access';
|
|
912
1111
|
/** EndUser id. */
|
|
913
1112
|
sub: string;
|
|
914
|
-
/** Application the token is bound to
|
|
1113
|
+
/** Application the token is bound to, check it against YOUR application id. */
|
|
915
1114
|
applicationId: string;
|
|
916
1115
|
/** Active organization id, when the session is acting as an org. */
|
|
917
1116
|
oid?: string;
|
|
918
1117
|
/** Operator id when this is an impersonation session (treat with care). */
|
|
919
1118
|
imp?: string;
|
|
1119
|
+
/**
|
|
1120
|
+
* Refresh-token family this access token belongs to, the `sid` claim.
|
|
1121
|
+
*
|
|
1122
|
+
* Optional because the API only mints it when the session flow supplies one
|
|
1123
|
+
* (see `issueUserAccessToken`), so treat an absent `sid` as "this deployment
|
|
1124
|
+
* or flow did not bind one", never as a session that ended.
|
|
1125
|
+
*
|
|
1126
|
+
* Offline verification cannot see a revocation, the whole point of not
|
|
1127
|
+
* calling the API. Use this to correlate a token with your own record of the
|
|
1128
|
+
* session, so you can drop one you already know is gone.
|
|
1129
|
+
*/
|
|
1130
|
+
sid?: string;
|
|
1131
|
+
/**
|
|
1132
|
+
* Device this session is bound to, the `dev` claim. Present only when the
|
|
1133
|
+
* sign-in bound a device.
|
|
1134
|
+
*
|
|
1135
|
+
* This is the id that matches a row from `devices.listMine()`, so a client
|
|
1136
|
+
* can mark "this device" in its own device list, and the id to pass to
|
|
1137
|
+
* `devices.releaseMine()` to sign the CURRENT machine out.
|
|
1138
|
+
*/
|
|
1139
|
+
dev?: string;
|
|
920
1140
|
/** App `tokenGeneration` at mint time (the API checks this; offline can't). */
|
|
921
1141
|
gen?: number;
|
|
922
1142
|
iat: number;
|
|
@@ -927,7 +1147,7 @@ export interface VerifyAccessTokenOptions {
|
|
|
927
1147
|
* The Application this token must belong to. **Required.**
|
|
928
1148
|
*
|
|
929
1149
|
* This helper verifies RS256 tokens against the deployment's JWKS, and the
|
|
930
|
-
* RS256 keypair is deployment-wide
|
|
1150
|
+
* RS256 keypair is deployment-wide, `SigningKey` has no `applicationId`
|
|
931
1151
|
* column, and `eu_access` tokens carry no `iss`/`aud`. So a token minted for
|
|
932
1152
|
* ANY Application on the same deployment is cryptographically valid here.
|
|
933
1153
|
* Without this, a multi-app self-host accepts another Application's end-user
|
|
@@ -935,24 +1155,24 @@ export interface VerifyAccessTokenOptions {
|
|
|
935
1155
|
*
|
|
936
1156
|
* (The HS256 default path is not affected: that key is derived per
|
|
937
1157
|
* Application as `HMAC-SHA256(JWT_SECRET, applicationId:tokenGeneration)`, so
|
|
938
|
-
* a foreign token fails the signature. This is the RS256 opt-in only
|
|
1158
|
+
* a foreign token fails the signature. This is the RS256 opt-in only, which
|
|
939
1159
|
* is exactly the path this function exists for.)
|
|
940
1160
|
*
|
|
941
1161
|
* Required rather than optional-with-a-warning: a security check nobody is
|
|
942
1162
|
* forced to make is one most callers will not make, and the docblock used to
|
|
943
|
-
* tell them to compare `claims.applicationId` afterwards
|
|
1163
|
+
* tell them to compare `claims.applicationId` afterwards, which made the
|
|
944
1164
|
* shortest correct path the insecure one. 2.0.0 is not out yet, so this
|
|
945
1165
|
* breaks rc callers rather than a stable contract.
|
|
946
1166
|
*/
|
|
947
1167
|
applicationId: string;
|
|
948
1168
|
/**
|
|
949
|
-
* URL of the deployment's JWKS
|
|
1169
|
+
* URL of the deployment's JWKS, `https://<your-rekey>/.well-known/jwks.json`.
|
|
950
1170
|
* Fetched lazily and cached in-process for `cacheTtlMs` (default 5 minutes);
|
|
951
1171
|
* an unknown `kid` triggers one immediate refetch so freshly rotated keys
|
|
952
1172
|
* are picked up without waiting out the TTL.
|
|
953
1173
|
*/
|
|
954
1174
|
jwksUrl?: string;
|
|
955
|
-
/** Pre-fetched key set
|
|
1175
|
+
/** Pre-fetched key set, skips all network access. Takes precedence over `jwksUrl`. */
|
|
956
1176
|
jwks?: JwksDto;
|
|
957
1177
|
/** Optional fetch override (test stubs, custom agents). */
|
|
958
1178
|
fetch?: typeof fetch;
|
|
@@ -972,14 +1192,14 @@ export interface VerifyAccessTokenOptions {
|
|
|
972
1192
|
now?: () => number;
|
|
973
1193
|
}
|
|
974
1194
|
/**
|
|
975
|
-
* Verify an end-user ACCESS token **offline
|
|
1195
|
+
* Verify an end-user ACCESS token **offline**, no round-trip to the Rekey
|
|
976
1196
|
* API. Works only for Applications that opted into RS256 tokens
|
|
977
1197
|
* (`authConfig.tokenAlg = "RS256"`, Panel → Application → Auth); the default
|
|
978
1198
|
* HS256 tokens are symmetric and can only be verified by the API itself
|
|
979
1199
|
* (use `rekey.auth.getCurrentUser(token)` for those).
|
|
980
1200
|
*
|
|
981
1201
|
* Checks performed (same posture as the API's verifier):
|
|
982
|
-
* - header `alg` must be `RS256` and `kid` must exist in the JWKS
|
|
1202
|
+
* - header `alg` must be `RS256` and `kid` must exist in the JWKS,
|
|
983
1203
|
* a strict allowlist, immune to alg-confusion;
|
|
984
1204
|
* - RSA-SHA256 signature against that public key;
|
|
985
1205
|
* - `exp` in the future, `typ === "eu_access"` (refresh/MFA/MCP tokens
|
|
@@ -989,6 +1209,11 @@ export interface VerifyAccessTokenOptions {
|
|
|
989
1209
|
* user deletion. The 15-minute access lifetime bounds both; for hard
|
|
990
1210
|
* revocation guarantees keep using `auth.getCurrentUser`.
|
|
991
1211
|
*
|
|
1212
|
+
* Nor can it see any server-side revocation: a locally verified token stays
|
|
1213
|
+
* valid until it expires, even after sign-out everywhere, a password change,
|
|
1214
|
+
* a session revoke or a device release. Call the API when immediate
|
|
1215
|
+
* revocation matters.
|
|
1216
|
+
*
|
|
992
1217
|
* Node-only (uses `node:crypto`). Returns the verified claims; throws
|
|
993
1218
|
* `RekeyError` on any failure.
|
|
994
1219
|
*
|
|
@@ -1011,21 +1236,21 @@ export interface VerifyAccessTokenOptions {
|
|
|
1011
1236
|
* it moved inside: the shortest correct path should not be the one nobody
|
|
1012
1237
|
* takes.
|
|
1013
1238
|
*
|
|
1014
|
-
* @throws {RekeyError} `TOKEN_ALG_NOT_RS256
|
|
1015
|
-
* @throws {RekeyError} `TOKEN_KID_UNKNOWN
|
|
1016
|
-
* @throws {RekeyError} `USER_TOKEN_EXPIRED
|
|
1017
|
-
* @throws {RekeyError} `USER_TOKEN_INVALID
|
|
1239
|
+
* @throws {RekeyError} `TOKEN_ALG_NOT_RS256`, token is HS256 (app hasn't opted in) or another alg.
|
|
1240
|
+
* @throws {RekeyError} `TOKEN_KID_UNKNOWN`, `kid` not in the JWKS (forged, or key deleted).
|
|
1241
|
+
* @throws {RekeyError} `USER_TOKEN_EXPIRED`, `exp` passed; refresh the session.
|
|
1242
|
+
* @throws {RekeyError} `USER_TOKEN_INVALID`, malformed, bad signature, or wrong `typ`.
|
|
1018
1243
|
*/
|
|
1019
1244
|
export declare function verifyAccessToken(token: string, options: VerifyAccessTokenOptions): Promise<VerifiedAccessTokenClaims>;
|
|
1020
1245
|
declare class BillingClient {
|
|
1021
1246
|
private readonly client;
|
|
1022
1247
|
constructor(client: Rekey);
|
|
1023
1248
|
/**
|
|
1024
|
-
* List the calling Application's active plans. Public
|
|
1249
|
+
* List the calling Application's active plans. Public, pricing pages
|
|
1025
1250
|
* typically render straight from this. Application API key only; no
|
|
1026
1251
|
* user JWT needed.
|
|
1027
1252
|
*
|
|
1028
|
-
* `amount` is in the smallest currency unit (cents/paise/sen)
|
|
1253
|
+
* `amount` is in the smallest currency unit (cents/paise/sen), never
|
|
1029
1254
|
* a float. Format on display: `${amount / 100} ${currency}`.
|
|
1030
1255
|
*/
|
|
1031
1256
|
getPlans(page?: ListPage): Promise<Paged<PlanDto>>;
|
|
@@ -1036,7 +1261,7 @@ declare class BillingClient {
|
|
|
1036
1261
|
* Pass the user's access token (the SDK puts it in `X-Rekey-User-Token`).
|
|
1037
1262
|
*
|
|
1038
1263
|
* `opts.includeEnded` falls back to the most recent CANCELED/EXPIRED
|
|
1039
|
-
* subscription **only when the answer would otherwise be null
|
|
1264
|
+
* subscription **only when the answer would otherwise be null**, for a
|
|
1040
1265
|
* billing page that has to tell a former subscriber what they were on and
|
|
1041
1266
|
* when it ended, rather than showing them the same blank state as somebody
|
|
1042
1267
|
* who never subscribed. It can never replace a live subscription, so it is
|
|
@@ -1053,13 +1278,22 @@ declare class BillingClient {
|
|
|
1053
1278
|
/**
|
|
1054
1279
|
* Start a hosted-checkout session. Returns the URL to redirect the user
|
|
1055
1280
|
* to and the local PENDING Subscription row. Subscription activation
|
|
1056
|
-
* happens via the provider's webhook
|
|
1281
|
+
* happens via the provider's webhook, not synchronously here.
|
|
1057
1282
|
*
|
|
1058
1283
|
* Pass `couponCode` to apply a discount. The whole checkout fails if the
|
|
1059
1284
|
* coupon doesn't validate (typed `RekeyError` with the precise reason).
|
|
1060
1285
|
*
|
|
1286
|
+
* A buyer who has already used their free trial is refused with
|
|
1287
|
+
* `BILLING_TRIAL_ALREADY_USED` (409). The escape hatch is
|
|
1288
|
+
* `allowWithoutTrial: true` AND a fresh `Idempotency-Key`, but send it only
|
|
1289
|
+
* after the buyer has been told they are paying today. Read
|
|
1290
|
+
* {@link getTrialEligibility} and render the paid price instead of retrying
|
|
1291
|
+
* blindly: a buyer who merely abandoned a trial checkout still reads
|
|
1292
|
+
* `eligible: true`, and acknowledging on their behalf charges them today for
|
|
1293
|
+
* the trial the next checkout was about to grant.
|
|
1294
|
+
*
|
|
1061
1295
|
* If the Application's billing subject is **org** (Panel → Application →
|
|
1062
|
-
* Billing → Subject), an individual can't hold a subscription
|
|
1296
|
+
* Billing → Subject), an individual can't hold a subscription, you MUST
|
|
1063
1297
|
* pass `organizationId` of a team the user owns/admins. Omitting it throws
|
|
1064
1298
|
* `RekeyError` `code: "BILLING_ORGANIZATION_REQUIRED"`.
|
|
1065
1299
|
*
|
|
@@ -1077,6 +1311,74 @@ declare class BillingClient {
|
|
|
1077
1311
|
createCheckout(accessToken: string, input: CreateCheckoutRequest & {
|
|
1078
1312
|
couponCode?: string;
|
|
1079
1313
|
}): Promise<CheckoutResultDto>;
|
|
1314
|
+
/**
|
|
1315
|
+
* Put the calling end-user on the Application's free tier
|
|
1316
|
+
* (`billingConfig.defaultPlanSlug`). No payment provider is involved and none
|
|
1317
|
+
* needs to be configured: the plan costs nothing.
|
|
1318
|
+
*
|
|
1319
|
+
* This is how a freemium product hands a new signup their included credits,
|
|
1320
|
+
* licence or quota. `defaultPlanSlug` alone covers only the read-time half
|
|
1321
|
+
* (feature flags and included usage); CREDIT and LICENSE entitlements are
|
|
1322
|
+
* stateful and need a real subscription, which is what this creates.
|
|
1323
|
+
*
|
|
1324
|
+
* **Idempotent**, and the answer says which happened: `activated: true` is a
|
|
1325
|
+
* first activation (201, `subscription.activated` emitted), `activated:
|
|
1326
|
+
* false` means they were already entitled and nothing was written,
|
|
1327
|
+
* re-provisioned or re-announced.
|
|
1328
|
+
*
|
|
1329
|
+
* Pass `organizationId` on an org-billed Application; the caller must be an
|
|
1330
|
+
* OWNER or ADMIN of it. Omit it and the session's active organization is used.
|
|
1331
|
+
*
|
|
1332
|
+
* @throws {RekeyError} `BILLING_NO_FREE_PLAN` (404) when the Application
|
|
1333
|
+
* nominates no default plan; `BILLING_FREE_PLAN_NOT_FREE` (409) when that
|
|
1334
|
+
* plan charges money, use {@link createCheckout} instead;
|
|
1335
|
+
* `BILLING_FREE_TIER_ALREADY_CLAIMED` (409) when the plan grants credits or a
|
|
1336
|
+
* licence and this caller already claimed it for a different beneficiary.
|
|
1337
|
+
*
|
|
1338
|
+
* @example
|
|
1339
|
+
* ```ts
|
|
1340
|
+
* const { subscription, activated } = await rekey.billing.subscribe(accessToken);
|
|
1341
|
+
* if (activated) welcomeWithStarterCredits(subscription);
|
|
1342
|
+
* ```
|
|
1343
|
+
*/
|
|
1344
|
+
subscribe(accessToken: string, input?: {
|
|
1345
|
+
organizationId?: string;
|
|
1346
|
+
}): Promise<{
|
|
1347
|
+
subscription: SubscriptionDto;
|
|
1348
|
+
activated: boolean;
|
|
1349
|
+
}>;
|
|
1350
|
+
/**
|
|
1351
|
+
* Whether THIS buyer may start each plan's free trial, under the
|
|
1352
|
+
* Application's `trialPolicy`.
|
|
1353
|
+
*
|
|
1354
|
+
* Read this before offering a trial: a buyer who is not eligible should be
|
|
1355
|
+
* shown the paid price, not a trial that checkout refuses with
|
|
1356
|
+
* `BILLING_TRIAL_ALREADY_USED`. Feed the result straight into
|
|
1357
|
+
* `<PricingTable trialEligibility={…}>` from `@rekey.dev/react`.
|
|
1358
|
+
*
|
|
1359
|
+
* **Advisory.** The authoritative decision is taken under a lock at checkout,
|
|
1360
|
+
* so two tabs can both read `eligible: true` and only one gets the trial.
|
|
1361
|
+
* Treat a 409 at checkout as normal, not as a contradiction.
|
|
1362
|
+
*
|
|
1363
|
+
* **Provider-dependent.** `PLAN_TRIAL_MISCONFIGURED` can be the resolved
|
|
1364
|
+
* provider's answer, so the response echoes `provider`; re-read this when the
|
|
1365
|
+
* buyer changes processor. Pass `country` (ISO 3166-1 alpha-2) to steer the
|
|
1366
|
+
* geo router the way {@link getProviders} does.
|
|
1367
|
+
*
|
|
1368
|
+
* @example
|
|
1369
|
+
* ```ts
|
|
1370
|
+
* const { items, policy } = await rekey.billing.getTrialEligibility(accessToken);
|
|
1371
|
+
* const pro = items.find((i) => i.planSlug === 'pro');
|
|
1372
|
+
* const label = pro?.eligible ? `Start ${pro.trialDays} days free` : 'Subscribe';
|
|
1373
|
+
* ```
|
|
1374
|
+
*/
|
|
1375
|
+
getTrialEligibility(accessToken: string, opts?: {
|
|
1376
|
+
organizationId?: string;
|
|
1377
|
+
planSlug?: string;
|
|
1378
|
+
limit?: number;
|
|
1379
|
+
offset?: number;
|
|
1380
|
+
country?: string;
|
|
1381
|
+
}): Promise<TrialEligibilityDto>;
|
|
1080
1382
|
/**
|
|
1081
1383
|
* Validate a coupon for the current user against a plan, *without*
|
|
1082
1384
|
* applying it. Render "$50 off" on a pricing page before submit.
|
|
@@ -1090,7 +1392,7 @@ declare class BillingClient {
|
|
|
1090
1392
|
/**
|
|
1091
1393
|
* List the billing providers configured + enabled for this Application,
|
|
1092
1394
|
* in the order the geo router would prefer them. Forward the end-user's
|
|
1093
|
-
* `country` (ISO 3166-1 alpha-2) when you have it
|
|
1395
|
+
* `country` (ISO 3166-1 alpha-2) when you have it, the panel/SDK will
|
|
1094
1396
|
* surface India-specific providers (Razorpay) for IN-country users, etc.
|
|
1095
1397
|
*
|
|
1096
1398
|
* Returns the resolved country (echoed back from the server's view of
|
|
@@ -1099,7 +1401,7 @@ declare class BillingClient {
|
|
|
1099
1401
|
*/
|
|
1100
1402
|
getProviders(country?: string): Promise<ProvidersListDto>;
|
|
1101
1403
|
/**
|
|
1102
|
-
* Resolve the calling end-user's current entitlements
|
|
1404
|
+
* Resolve the calling end-user's current entitlements, feature flags +
|
|
1103
1405
|
* limits, the live credit balance, and the raw entitlement list, unioned
|
|
1104
1406
|
* across their active subscriptions (and subscriptions of orgs they belong
|
|
1105
1407
|
* to). Pass `{ organizationId }` (member-only) for that org's view + shared
|
|
@@ -1114,10 +1416,24 @@ declare class BillingClient {
|
|
|
1114
1416
|
getEntitlements(accessToken: string, opts?: {
|
|
1115
1417
|
organizationId?: string;
|
|
1116
1418
|
}): Promise<EntitlementsDto>;
|
|
1419
|
+
/**
|
|
1420
|
+
* The same union as `getEntitlements`, for an end-user you name rather than
|
|
1421
|
+
* one whose token you hold. Secret key only, for a licence server, a
|
|
1422
|
+
* support tool or a batch job.
|
|
1423
|
+
*
|
|
1424
|
+
* @example
|
|
1425
|
+
* ```ts
|
|
1426
|
+
* const { features } = await rekey.billing.getEntitlementsFor(endUserId);
|
|
1427
|
+
* if (features.max_devices !== undefined) capDevices(features.max_devices);
|
|
1428
|
+
* ```
|
|
1429
|
+
*/
|
|
1430
|
+
getEntitlementsFor(endUserId: string, opts?: {
|
|
1431
|
+
organizationId?: string;
|
|
1432
|
+
}): Promise<EntitlementsDto>;
|
|
1117
1433
|
/**
|
|
1118
1434
|
* Cancel the calling end-user's current subscription.
|
|
1119
1435
|
*
|
|
1120
|
-
* Defaults to cancelling **at period end
|
|
1436
|
+
* Defaults to cancelling **at period end**, the user keeps what they paid
|
|
1121
1437
|
* for until the period they already bought runs out. A provider-backed
|
|
1122
1438
|
* subscription therefore stays ACTIVE with `cancelAt` set, and the provider
|
|
1123
1439
|
* webhook is what eventually terminates it; read `cancelAt` on the returned
|
|
@@ -1125,7 +1441,7 @@ declare class BillingClient {
|
|
|
1125
1441
|
* `{ atPeriodEnd: false }` to end it immediately, forfeiting the remainder.
|
|
1126
1442
|
*
|
|
1127
1443
|
* PENDING checkouts (and anything with no provider-side record) are
|
|
1128
|
-
* cancelled locally straight away regardless of the flag
|
|
1444
|
+
* cancelled locally straight away regardless of the flag, there is nothing
|
|
1129
1445
|
* at the provider to schedule against.
|
|
1130
1446
|
*
|
|
1131
1447
|
* Pass `organizationId` when the subscription belongs to a team; the caller
|