@rekey.dev/node 2.1.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 +16 -1
- package/dist/index.d.ts +378 -88
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +365 -95
- 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, OrganizationRoleDefDto, OrganizationRole, OrganizationBaseRole, 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, OrganizationRoleDefDto, OrganizationMemberDto, OrganizationInvitationDto, OrganizationRole, OrganizationBaseRole, 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';
|
|
@@ -706,7 +728,7 @@ declare class OrganizationsClient {
|
|
|
706
728
|
*/
|
|
707
729
|
listMembers(accessToken: string, organizationId: string, page?: ListPage): Promise<Paged<OrganizationMemberDto>>;
|
|
708
730
|
/**
|
|
709
|
-
* Invite a user. Returns the raw token ONCE
|
|
731
|
+
* Invite a user. Returns the raw token ONCE, surface via your own
|
|
710
732
|
* email/share channel. OWNER + ADMIN only.
|
|
711
733
|
*/
|
|
712
734
|
invite(accessToken: string, organizationId: string,
|
|
@@ -745,7 +767,7 @@ declare class OrganizationsClient {
|
|
|
745
767
|
* Remove a member (or self). Refuses removing the last OWNER.
|
|
746
768
|
*
|
|
747
769
|
* Idempotent: `removed` is `false` when the target was not a member (e.g.
|
|
748
|
-
* already removed)
|
|
770
|
+
* already removed), a no-op removal is not an error. Branch on `removed`
|
|
749
771
|
* rather than assuming it is always `true`.
|
|
750
772
|
*/
|
|
751
773
|
removeMember(accessToken: string, organizationId: string, targetEndUserId: string): Promise<{
|
|
@@ -753,7 +775,7 @@ declare class OrganizationsClient {
|
|
|
753
775
|
}>;
|
|
754
776
|
/**
|
|
755
777
|
* Self-leave. An OWNER cannot leave (payment + benefits are tied to the
|
|
756
|
-
* owner
|
|
778
|
+
* owner, `ORGANIZATION_OWNER_CANNOT_LEAVE`); transfer ownership via support
|
|
757
779
|
* first, or demote yourself to ADMIN if there is another OWNER.
|
|
758
780
|
*/
|
|
759
781
|
leave(accessToken: string, organizationId: string): Promise<{
|
|
@@ -774,7 +796,7 @@ declare class OrganizationsClient {
|
|
|
774
796
|
}>;
|
|
775
797
|
/**
|
|
776
798
|
* Make `organizationId` the active org for this session (member-only).
|
|
777
|
-
* Returns a fresh {accessToken, refreshToken} pair carrying the active org
|
|
799
|
+
* Returns a fresh {accessToken, refreshToken} pair carrying the active org,
|
|
778
800
|
* **store both**. Subsequent entitlement reads (`billing.getEntitlements`)
|
|
779
801
|
* then default to this org's view + shared pool without passing
|
|
780
802
|
* `organizationId` explicitly. The active org survives token refresh until
|
|
@@ -782,7 +804,7 @@ declare class OrganizationsClient {
|
|
|
782
804
|
*/
|
|
783
805
|
switch(accessToken: string, organizationId: string): Promise<AuthResultDto>;
|
|
784
806
|
/**
|
|
785
|
-
* Clear the active org
|
|
807
|
+
* Clear the active org, switch the session back to the personal pool.
|
|
786
808
|
* Returns a fresh token pair (no active org); **store both**.
|
|
787
809
|
*/
|
|
788
810
|
clearActive(accessToken: string): Promise<AuthResultDto>;
|
|
@@ -793,7 +815,7 @@ declare class LicensesClient {
|
|
|
793
815
|
/**
|
|
794
816
|
* Verify a license key + record an activation for this machine. Call
|
|
795
817
|
* once at app startup; you'll get a deterministic body (`ok=false` for
|
|
796
|
-
* invalid licenses
|
|
818
|
+
* invalid licenses, never an HTTP error, so your software can loop
|
|
797
819
|
* on the result without try/catch noise).
|
|
798
820
|
*
|
|
799
821
|
* `machineFingerprint` should be a stable identifier you derive client-
|
|
@@ -815,6 +837,157 @@ declare class LicensesClient {
|
|
|
815
837
|
machineFingerprint: string;
|
|
816
838
|
label?: string;
|
|
817
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
|
+
}>;
|
|
818
991
|
}
|
|
819
992
|
declare class UsageClient {
|
|
820
993
|
private readonly client;
|
|
@@ -848,7 +1021,7 @@ declare class UsageClient {
|
|
|
848
1021
|
}): Promise<UsageAggregateDto>;
|
|
849
1022
|
}
|
|
850
1023
|
/**
|
|
851
|
-
* A credit subject
|
|
1024
|
+
* A credit subject, pass `endUserId` for a personal balance, or
|
|
852
1025
|
* `organizationId` for a shared org pool (owner+beneficiary billing).
|
|
853
1026
|
*/
|
|
854
1027
|
export type CreditSubject = {
|
|
@@ -857,11 +1030,11 @@ export type CreditSubject = {
|
|
|
857
1030
|
organizationId: string;
|
|
858
1031
|
};
|
|
859
1032
|
/**
|
|
860
|
-
* Prepaid credits
|
|
1033
|
+
* Prepaid credits, the "lead pack" / pay-as-you-go drawdown model. The
|
|
861
1034
|
* customer's backend grants credits (by selling a CREDIT-kind plan, which
|
|
862
1035
|
* grants automatically on payment) and draws them down per unit consumed.
|
|
863
1036
|
*
|
|
864
|
-
* 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`,
|
|
865
1038
|
* an end-user's personal balance, or an organization's shared pool.
|
|
866
1039
|
*/
|
|
867
1040
|
declare class CreditsClient {
|
|
@@ -874,7 +1047,7 @@ declare class CreditsClient {
|
|
|
874
1047
|
* `code: "CREDITS_INSUFFICIENT"` (HTTP 402) when the balance is too low.
|
|
875
1048
|
*
|
|
876
1049
|
* Pass `idempotencyKey` (e.g. the lead id) so a retried call never
|
|
877
|
-
* double-charges
|
|
1050
|
+
* double-charges, a repeat returns the original result with `applied: false`.
|
|
878
1051
|
*/
|
|
879
1052
|
consume(input: ConsumeCreditsRequest & CreditSubject): Promise<ConsumeCreditsResultDto>;
|
|
880
1053
|
/**
|
|
@@ -884,7 +1057,7 @@ declare class CreditsClient {
|
|
|
884
1057
|
*/
|
|
885
1058
|
listLedger(subject: CreditSubject, limit?: number, offset?: number): Promise<Paged<CreditLedgerEntryDto>>;
|
|
886
1059
|
}
|
|
887
|
-
/** 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. */
|
|
888
1061
|
export interface EntitlementsDto {
|
|
889
1062
|
/** Feature flags + numeric limits, keyed by code. Gate your app on these. */
|
|
890
1063
|
features: Record<string, boolean | number | string>;
|
|
@@ -907,7 +1080,7 @@ export interface EntitlementsDto {
|
|
|
907
1080
|
* default 300) AND (b) the signature matches a constant-time compare.
|
|
908
1081
|
*
|
|
909
1082
|
* Use against the `X-Rekey-Signature` header and the raw request body
|
|
910
|
-
* BYTES (not the parsed JSON
|
|
1083
|
+
* BYTES (not the parsed JSON, any reserialization breaks the HMAC).
|
|
911
1084
|
*
|
|
912
1085
|
* @example
|
|
913
1086
|
* ```ts
|
|
@@ -933,16 +1106,37 @@ export declare function verifyWebhookSignature(args: {
|
|
|
933
1106
|
}): boolean;
|
|
934
1107
|
/** Verified claims of an RS256 end-user access token. */
|
|
935
1108
|
export interface VerifiedAccessTokenClaims {
|
|
936
|
-
/** Always `"eu_access"
|
|
1109
|
+
/** Always `"eu_access"`, other token types are refused. */
|
|
937
1110
|
typ: 'eu_access';
|
|
938
1111
|
/** EndUser id. */
|
|
939
1112
|
sub: string;
|
|
940
|
-
/** Application the token is bound to
|
|
1113
|
+
/** Application the token is bound to, check it against YOUR application id. */
|
|
941
1114
|
applicationId: string;
|
|
942
1115
|
/** Active organization id, when the session is acting as an org. */
|
|
943
1116
|
oid?: string;
|
|
944
1117
|
/** Operator id when this is an impersonation session (treat with care). */
|
|
945
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;
|
|
946
1140
|
/** App `tokenGeneration` at mint time (the API checks this; offline can't). */
|
|
947
1141
|
gen?: number;
|
|
948
1142
|
iat: number;
|
|
@@ -953,7 +1147,7 @@ export interface VerifyAccessTokenOptions {
|
|
|
953
1147
|
* The Application this token must belong to. **Required.**
|
|
954
1148
|
*
|
|
955
1149
|
* This helper verifies RS256 tokens against the deployment's JWKS, and the
|
|
956
|
-
* RS256 keypair is deployment-wide
|
|
1150
|
+
* RS256 keypair is deployment-wide, `SigningKey` has no `applicationId`
|
|
957
1151
|
* column, and `eu_access` tokens carry no `iss`/`aud`. So a token minted for
|
|
958
1152
|
* ANY Application on the same deployment is cryptographically valid here.
|
|
959
1153
|
* Without this, a multi-app self-host accepts another Application's end-user
|
|
@@ -961,24 +1155,24 @@ export interface VerifyAccessTokenOptions {
|
|
|
961
1155
|
*
|
|
962
1156
|
* (The HS256 default path is not affected: that key is derived per
|
|
963
1157
|
* Application as `HMAC-SHA256(JWT_SECRET, applicationId:tokenGeneration)`, so
|
|
964
|
-
* 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
|
|
965
1159
|
* is exactly the path this function exists for.)
|
|
966
1160
|
*
|
|
967
1161
|
* Required rather than optional-with-a-warning: a security check nobody is
|
|
968
1162
|
* forced to make is one most callers will not make, and the docblock used to
|
|
969
|
-
* tell them to compare `claims.applicationId` afterwards
|
|
1163
|
+
* tell them to compare `claims.applicationId` afterwards, which made the
|
|
970
1164
|
* shortest correct path the insecure one. 2.0.0 is not out yet, so this
|
|
971
1165
|
* breaks rc callers rather than a stable contract.
|
|
972
1166
|
*/
|
|
973
1167
|
applicationId: string;
|
|
974
1168
|
/**
|
|
975
|
-
* URL of the deployment's JWKS
|
|
1169
|
+
* URL of the deployment's JWKS, `https://<your-rekey>/.well-known/jwks.json`.
|
|
976
1170
|
* Fetched lazily and cached in-process for `cacheTtlMs` (default 5 minutes);
|
|
977
1171
|
* an unknown `kid` triggers one immediate refetch so freshly rotated keys
|
|
978
1172
|
* are picked up without waiting out the TTL.
|
|
979
1173
|
*/
|
|
980
1174
|
jwksUrl?: string;
|
|
981
|
-
/** Pre-fetched key set
|
|
1175
|
+
/** Pre-fetched key set, skips all network access. Takes precedence over `jwksUrl`. */
|
|
982
1176
|
jwks?: JwksDto;
|
|
983
1177
|
/** Optional fetch override (test stubs, custom agents). */
|
|
984
1178
|
fetch?: typeof fetch;
|
|
@@ -998,14 +1192,14 @@ export interface VerifyAccessTokenOptions {
|
|
|
998
1192
|
now?: () => number;
|
|
999
1193
|
}
|
|
1000
1194
|
/**
|
|
1001
|
-
* Verify an end-user ACCESS token **offline
|
|
1195
|
+
* Verify an end-user ACCESS token **offline**, no round-trip to the Rekey
|
|
1002
1196
|
* API. Works only for Applications that opted into RS256 tokens
|
|
1003
1197
|
* (`authConfig.tokenAlg = "RS256"`, Panel → Application → Auth); the default
|
|
1004
1198
|
* HS256 tokens are symmetric and can only be verified by the API itself
|
|
1005
1199
|
* (use `rekey.auth.getCurrentUser(token)` for those).
|
|
1006
1200
|
*
|
|
1007
1201
|
* Checks performed (same posture as the API's verifier):
|
|
1008
|
-
* - 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,
|
|
1009
1203
|
* a strict allowlist, immune to alg-confusion;
|
|
1010
1204
|
* - RSA-SHA256 signature against that public key;
|
|
1011
1205
|
* - `exp` in the future, `typ === "eu_access"` (refresh/MFA/MCP tokens
|
|
@@ -1015,6 +1209,11 @@ export interface VerifyAccessTokenOptions {
|
|
|
1015
1209
|
* user deletion. The 15-minute access lifetime bounds both; for hard
|
|
1016
1210
|
* revocation guarantees keep using `auth.getCurrentUser`.
|
|
1017
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
|
+
*
|
|
1018
1217
|
* Node-only (uses `node:crypto`). Returns the verified claims; throws
|
|
1019
1218
|
* `RekeyError` on any failure.
|
|
1020
1219
|
*
|
|
@@ -1037,21 +1236,21 @@ export interface VerifyAccessTokenOptions {
|
|
|
1037
1236
|
* it moved inside: the shortest correct path should not be the one nobody
|
|
1038
1237
|
* takes.
|
|
1039
1238
|
*
|
|
1040
|
-
* @throws {RekeyError} `TOKEN_ALG_NOT_RS256
|
|
1041
|
-
* @throws {RekeyError} `TOKEN_KID_UNKNOWN
|
|
1042
|
-
* @throws {RekeyError} `USER_TOKEN_EXPIRED
|
|
1043
|
-
* @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`.
|
|
1044
1243
|
*/
|
|
1045
1244
|
export declare function verifyAccessToken(token: string, options: VerifyAccessTokenOptions): Promise<VerifiedAccessTokenClaims>;
|
|
1046
1245
|
declare class BillingClient {
|
|
1047
1246
|
private readonly client;
|
|
1048
1247
|
constructor(client: Rekey);
|
|
1049
1248
|
/**
|
|
1050
|
-
* List the calling Application's active plans. Public
|
|
1249
|
+
* List the calling Application's active plans. Public, pricing pages
|
|
1051
1250
|
* typically render straight from this. Application API key only; no
|
|
1052
1251
|
* user JWT needed.
|
|
1053
1252
|
*
|
|
1054
|
-
* `amount` is in the smallest currency unit (cents/paise/sen)
|
|
1253
|
+
* `amount` is in the smallest currency unit (cents/paise/sen), never
|
|
1055
1254
|
* a float. Format on display: `${amount / 100} ${currency}`.
|
|
1056
1255
|
*/
|
|
1057
1256
|
getPlans(page?: ListPage): Promise<Paged<PlanDto>>;
|
|
@@ -1062,7 +1261,7 @@ declare class BillingClient {
|
|
|
1062
1261
|
* Pass the user's access token (the SDK puts it in `X-Rekey-User-Token`).
|
|
1063
1262
|
*
|
|
1064
1263
|
* `opts.includeEnded` falls back to the most recent CANCELED/EXPIRED
|
|
1065
|
-
* subscription **only when the answer would otherwise be null
|
|
1264
|
+
* subscription **only when the answer would otherwise be null**, for a
|
|
1066
1265
|
* billing page that has to tell a former subscriber what they were on and
|
|
1067
1266
|
* when it ended, rather than showing them the same blank state as somebody
|
|
1068
1267
|
* who never subscribed. It can never replace a live subscription, so it is
|
|
@@ -1079,13 +1278,22 @@ declare class BillingClient {
|
|
|
1079
1278
|
/**
|
|
1080
1279
|
* Start a hosted-checkout session. Returns the URL to redirect the user
|
|
1081
1280
|
* to and the local PENDING Subscription row. Subscription activation
|
|
1082
|
-
* happens via the provider's webhook
|
|
1281
|
+
* happens via the provider's webhook, not synchronously here.
|
|
1083
1282
|
*
|
|
1084
1283
|
* Pass `couponCode` to apply a discount. The whole checkout fails if the
|
|
1085
1284
|
* coupon doesn't validate (typed `RekeyError` with the precise reason).
|
|
1086
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
|
+
*
|
|
1087
1295
|
* If the Application's billing subject is **org** (Panel → Application →
|
|
1088
|
-
* Billing → Subject), an individual can't hold a subscription
|
|
1296
|
+
* Billing → Subject), an individual can't hold a subscription, you MUST
|
|
1089
1297
|
* pass `organizationId` of a team the user owns/admins. Omitting it throws
|
|
1090
1298
|
* `RekeyError` `code: "BILLING_ORGANIZATION_REQUIRED"`.
|
|
1091
1299
|
*
|
|
@@ -1103,6 +1311,74 @@ declare class BillingClient {
|
|
|
1103
1311
|
createCheckout(accessToken: string, input: CreateCheckoutRequest & {
|
|
1104
1312
|
couponCode?: string;
|
|
1105
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>;
|
|
1106
1382
|
/**
|
|
1107
1383
|
* Validate a coupon for the current user against a plan, *without*
|
|
1108
1384
|
* applying it. Render "$50 off" on a pricing page before submit.
|
|
@@ -1116,7 +1392,7 @@ declare class BillingClient {
|
|
|
1116
1392
|
/**
|
|
1117
1393
|
* List the billing providers configured + enabled for this Application,
|
|
1118
1394
|
* in the order the geo router would prefer them. Forward the end-user's
|
|
1119
|
-
* `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
|
|
1120
1396
|
* surface India-specific providers (Razorpay) for IN-country users, etc.
|
|
1121
1397
|
*
|
|
1122
1398
|
* Returns the resolved country (echoed back from the server's view of
|
|
@@ -1125,7 +1401,7 @@ declare class BillingClient {
|
|
|
1125
1401
|
*/
|
|
1126
1402
|
getProviders(country?: string): Promise<ProvidersListDto>;
|
|
1127
1403
|
/**
|
|
1128
|
-
* Resolve the calling end-user's current entitlements
|
|
1404
|
+
* Resolve the calling end-user's current entitlements, feature flags +
|
|
1129
1405
|
* limits, the live credit balance, and the raw entitlement list, unioned
|
|
1130
1406
|
* across their active subscriptions (and subscriptions of orgs they belong
|
|
1131
1407
|
* to). Pass `{ organizationId }` (member-only) for that org's view + shared
|
|
@@ -1140,10 +1416,24 @@ declare class BillingClient {
|
|
|
1140
1416
|
getEntitlements(accessToken: string, opts?: {
|
|
1141
1417
|
organizationId?: string;
|
|
1142
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>;
|
|
1143
1433
|
/**
|
|
1144
1434
|
* Cancel the calling end-user's current subscription.
|
|
1145
1435
|
*
|
|
1146
|
-
* Defaults to cancelling **at period end
|
|
1436
|
+
* Defaults to cancelling **at period end**, the user keeps what they paid
|
|
1147
1437
|
* for until the period they already bought runs out. A provider-backed
|
|
1148
1438
|
* subscription therefore stays ACTIVE with `cancelAt` set, and the provider
|
|
1149
1439
|
* webhook is what eventually terminates it; read `cancelAt` on the returned
|
|
@@ -1151,7 +1441,7 @@ declare class BillingClient {
|
|
|
1151
1441
|
* `{ atPeriodEnd: false }` to end it immediately, forfeiting the remainder.
|
|
1152
1442
|
*
|
|
1153
1443
|
* PENDING checkouts (and anything with no provider-side record) are
|
|
1154
|
-
* cancelled locally straight away regardless of the flag
|
|
1444
|
+
* cancelled locally straight away regardless of the flag, there is nothing
|
|
1155
1445
|
* at the provider to schedule against.
|
|
1156
1446
|
*
|
|
1157
1447
|
* Pass `organizationId` when the subscription belongs to a team; the caller
|