@iqauth/sdk 2.2.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +134 -0
- package/dist/browser-session.d.mts +3 -3
- package/dist/browser-session.d.ts +3 -3
- package/dist/browser-session.js +89 -68
- package/dist/browser-session.mjs +2 -1
- package/dist/browser.d.mts +64 -29
- package/dist/browser.d.ts +64 -29
- package/dist/browser.js +794 -39
- package/dist/browser.mjs +44 -4
- package/dist/bundle-LUKDQYVQ.mjs +374 -0
- package/dist/chunk-3JULWS6F.mjs +106 -0
- package/dist/chunk-5T7GHBX6.mjs +1165 -0
- package/dist/{chunk-M4J6BPK7.mjs → chunk-6TDJJER7.mjs} +12 -3
- package/dist/{chunk-QZB745C2.mjs → chunk-76W5TLQQ.mjs} +264 -211
- package/dist/{chunk-D72UL5HL.mjs → chunk-BVV54LPI.mjs} +36 -4
- package/dist/chunk-LIZYFXH7.mjs +90 -0
- package/dist/chunk-MKKZULZR.mjs +241 -0
- package/dist/chunk-SL3KRS4W.mjs +54 -0
- package/dist/chunk-TKZTCPEK.mjs +232 -0
- package/dist/chunk-UKZLOHZG.mjs +83 -0
- package/dist/chunk-UNYDG2L4.mjs +209 -0
- package/dist/{chunk-MDUHPQMM.mjs → chunk-W3F4JYGP.mjs} +8 -180
- package/dist/{chunk-QEJB7WEQ.mjs → chunk-WQWBJSSS.mjs} +1 -1
- package/dist/cli/index.js +144 -36
- package/dist/cli/index.mjs +1 -1
- package/dist/{client-DXbHb2ul.d.ts → client-BNQe3AgF.d.ts} +3 -67
- package/dist/{client-Dv4v92Mj.d.mts → client-kYlJFgPv.d.mts} +3 -67
- package/dist/doctor-YYNHNMLD.mjs +198 -0
- package/dist/{express-BZmF1llh.d.mts → express-B6_1vBYZ.d.mts} +23 -2
- package/dist/{express-B4o3P8vK.d.ts → express-CHpfa7D_.d.ts} +23 -2
- package/dist/express.d.mts +77 -6
- package/dist/express.d.ts +77 -6
- package/dist/express.js +336 -74
- package/dist/express.mjs +209 -8
- package/dist/fastify.js +103 -72
- package/dist/fastify.mjs +6 -4
- package/dist/hono.js +102 -72
- package/dist/hono.mjs +5 -4
- package/dist/index.d.mts +8 -4
- package/dist/index.d.ts +8 -4
- package/dist/index.js +590 -73
- package/dist/index.mjs +30 -8
- package/dist/locales.d.mts +53 -0
- package/dist/locales.d.ts +53 -0
- package/dist/locales.js +1202 -0
- package/dist/locales.mjs +29 -0
- package/dist/mobile.d.mts +3 -3
- package/dist/mobile.d.ts +3 -3
- package/dist/mobile.js +89 -68
- package/dist/mobile.mjs +2 -1
- package/dist/next.d.mts +10 -1
- package/dist/next.d.ts +10 -1
- package/dist/next.js +101 -1618
- package/dist/next.mjs +9 -9
- package/dist/provisioningBridge-88xjOS2n.d.mts +86 -0
- package/dist/provisioningBridge-DnTfzdZK.d.ts +86 -0
- package/dist/react.d.mts +1349 -10
- package/dist/react.d.ts +1349 -10
- package/dist/react.js +2998 -569
- package/dist/react.mjs +1518 -95
- package/dist/reverify-4UEJXUS6.mjs +16 -0
- package/dist/server/handlers.d.mts +12 -1
- package/dist/server/handlers.d.ts +12 -1
- package/dist/server/handlers.js +12 -3
- package/dist/server/handlers.mjs +2 -2
- package/dist/server.d.mts +5 -4
- package/dist/server.d.ts +5 -4
- package/dist/server.js +188 -73
- package/dist/server.mjs +13 -8
- package/dist/service.d.mts +3 -3
- package/dist/service.d.ts +3 -3
- package/dist/service.js +89 -68
- package/dist/service.mjs +2 -1
- package/dist/signIn-CCY4JE5G.mjs +15 -0
- package/dist/{signIn-D_kP3v-c.d.mts → signIn-CiIBTJIh.d.mts} +232 -4
- package/dist/{signIn-BVDTIA_t.d.ts → signIn-OCr88Zf8.d.ts} +232 -4
- package/dist/test.d.mts +86 -0
- package/dist/test.d.ts +86 -0
- package/dist/test.js +289 -0
- package/dist/test.mjs +9 -0
- package/dist/tokens-DCyzzn8L.d.mts +63 -0
- package/dist/tokens-aHiGFr_E.d.ts +63 -0
- package/dist/types-6bNdxesb.d.mts +196 -0
- package/dist/types-6bNdxesb.d.ts +196 -0
- package/dist/{types-Cxl3bQHt.d.ts → types-DZAflmmq.d.mts} +6 -0
- package/dist/{types-Cxl3bQHt.d.mts → types-DZAflmmq.d.ts} +6 -0
- package/dist/webhooks.d.mts +61 -0
- package/dist/webhooks.d.ts +61 -0
- package/dist/webhooks.js +119 -0
- package/dist/webhooks.mjs +11 -0
- package/dist/ws.d.mts +73 -0
- package/dist/ws.d.ts +73 -0
- package/dist/ws.js +397 -0
- package/dist/ws.mjs +12 -0
- package/package.json +24 -3
- package/dist/doctor-XCI77BQS.mjs +0 -90
package/README.md
CHANGED
|
@@ -23,6 +23,8 @@ The canonical TypeScript SDK for **IQAuthService** — DispositionIQ's multi-ten
|
|
|
23
23
|
- [Token verification without a framework adapter](#token-verification-without-a-framework-adapter)
|
|
24
24
|
- [Native mobile (PKCE)](#native-mobile-pkce)
|
|
25
25
|
- [Service automation / API keys](#service-automation--api-keys)
|
|
26
|
+
- [Realtime: WebSocket upgrade verification](#realtime-websocket-upgrade-verification)
|
|
27
|
+
- [Integration testing with `createTestIssuer`](#integration-testing-with-createtestissuer)
|
|
26
28
|
- [Hosted auth pages and branding](#hosted-auth-pages-and-branding)
|
|
27
29
|
- [CLI](#cli)
|
|
28
30
|
- [Error handling](#error-handling)
|
|
@@ -388,6 +390,77 @@ API-key calls are scoped to the permissions granted at creation time and are sub
|
|
|
388
390
|
|
|
389
391
|
---
|
|
390
392
|
|
|
393
|
+
## Realtime: WebSocket upgrade verification
|
|
394
|
+
|
|
395
|
+
`@iqauth/sdk/ws` exposes a single `verifyWsUpgrade(req, options)` helper for Node WebSocket servers. Same option shape as the framework middlewares — `publishableKey`, `audience`, `issuer`, `clockTolerance`, cookie name. Returns `{ claims }` on success or `null` on missing/invalid/expired tokens.
|
|
396
|
+
|
|
397
|
+
It accepts a token from any of:
|
|
398
|
+
|
|
399
|
+
1. `Authorization: Bearer <jwt>` header.
|
|
400
|
+
2. The `iqauth_at` cookie on the upgrade request (override with `cookieName`).
|
|
401
|
+
3. The `Sec-WebSocket-Protocol` subprotocol value `iqauth.bearer.<jwt>` — browser `WebSocket` can't set custom headers, so the convention is to publish the token as a subprotocol value alongside the real one (e.g. `new WebSocket(url, ["iqauth.bearer." + token, "graphql-transport-ws"])`).
|
|
402
|
+
|
|
403
|
+
```ts
|
|
404
|
+
import { WebSocketServer } from "ws";
|
|
405
|
+
import { verifyWsUpgrade } from "@iqauth/sdk/ws";
|
|
406
|
+
|
|
407
|
+
const wss = new WebSocketServer({ noServer: true });
|
|
408
|
+
|
|
409
|
+
httpServer.on("upgrade", async (req, socket, head) => {
|
|
410
|
+
const result = await verifyWsUpgrade(req, {
|
|
411
|
+
publishableKey: process.env.IQAUTH_PUBLISHABLE_KEY!,
|
|
412
|
+
audience: "dispositioniq",
|
|
413
|
+
});
|
|
414
|
+
if (!result) {
|
|
415
|
+
socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
|
|
416
|
+
socket.destroy();
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
420
|
+
wss.emit("connection", ws, req, result.claims);
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
A consumer can replace IQValidate's hand-rolled `packages/shared/src/websocket-server.ts authenticateUser()` with one import and one call.
|
|
426
|
+
|
|
427
|
+
---
|
|
428
|
+
|
|
429
|
+
## Integration testing with `createTestIssuer`
|
|
430
|
+
|
|
431
|
+
`@iqauth/sdk/test` spawns an in-process HTTP server that exposes a JWKS endpoint, an OIDC discovery doc, a token endpoint that accepts code-exchange calls, and a `/api/v1/auth/me` userinfo endpoint. It mints valid RS256 JWTs against a freshly generated keypair, so integration tests don't need a live IQAuth.
|
|
432
|
+
|
|
433
|
+
```ts
|
|
434
|
+
import { createTestIssuer } from "@iqauth/sdk/test";
|
|
435
|
+
|
|
436
|
+
let issuer;
|
|
437
|
+
beforeAll(async () => { issuer = await createTestIssuer({ port: 0 }); });
|
|
438
|
+
afterAll(async () => { await issuer.close(); });
|
|
439
|
+
|
|
440
|
+
it("admin can list users", async () => {
|
|
441
|
+
const token = issuer.mintToken({ sub: "u1", roles: ["tenant_admin"] });
|
|
442
|
+
const r = await fetch("http://localhost:3000/api/users", {
|
|
443
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
444
|
+
});
|
|
445
|
+
expect(r.status).toBe(200);
|
|
446
|
+
});
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
Point your SDK / React provider at `issuer.baseUrl` — or pass `issuer.publishableKey`, which already encodes the right `iss`. To exercise the full code-exchange flow:
|
|
450
|
+
|
|
451
|
+
```ts
|
|
452
|
+
const code = issuer.mintAuthCode({ sub: "u1", roles: ["tenant_admin"] });
|
|
453
|
+
const tokens = await fetch(`${issuer.baseUrl}/oidc/token`, {
|
|
454
|
+
method: "POST",
|
|
455
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
456
|
+
body: new URLSearchParams({ grant_type: "authorization_code", code }),
|
|
457
|
+
}).then((r) => r.json());
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
Out of scope: the test issuer does not mock SSO providers (Google, Microsoft) and does not replicate the full DispositionIQ admin/permissions API surface — it implements exactly enough to satisfy the SDK's verify path and the common code-exchange flow.
|
|
461
|
+
|
|
462
|
+
---
|
|
463
|
+
|
|
391
464
|
## Hosted auth pages and branding
|
|
392
465
|
|
|
393
466
|
Sign-in, sign-up, forgot-password, and account pages are hosted at `auth.dispositioniq.com`. They render with your app's brand once you set CSS variables in the admin dashboard:
|
|
@@ -477,6 +550,48 @@ Common codes you'll handle:
|
|
|
477
550
|
|
|
478
551
|
---
|
|
479
552
|
|
|
553
|
+
## Don't intercept `/sign-in?code=…`
|
|
554
|
+
|
|
555
|
+
If your app uses an SSO bridge route (e.g. `your-app.com/sign-in` redirects users to `auth.dispositioniq.com`) and that same route is **also** the configured OAuth `redirect_uri`, your bridge logic will fire on the return trip and immediately bounce the user back to the issuer **before** the SDK can exchange the `?code=` for tokens. Symptoms: an infinite redirect loop, or a successful login that the app never sees.
|
|
556
|
+
|
|
557
|
+
**Pick one:**
|
|
558
|
+
|
|
559
|
+
1. **Recommended — use a dedicated callback path.** Configure `redirect_uri` to point at `/api/iqauth/callback` (the helper route mounted by `iqAuth({...}).attachHelpers(app)` for Express, or by `iqAuth({...})` for Next/Fastify/Hono). Your `/sign-in` route stays purely a "send user to the issuer" bridge.
|
|
560
|
+
|
|
561
|
+
2. **If you must reuse `/sign-in` as the redirect target,** guard the bridge:
|
|
562
|
+
|
|
563
|
+
```ts
|
|
564
|
+
app.get("/sign-in", (req, res, next) => {
|
|
565
|
+
// OAuth return trip — let the SDK handle it, don't redirect away.
|
|
566
|
+
if (req.query.code || req.query.error) return next();
|
|
567
|
+
return res.redirect(buildIssuerAuthorizeUrl(req));
|
|
568
|
+
});
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
…and add `inlineCallback: true` to your `iqAuth({...})` options so a GET handler is mounted on the callback path to complete the exchange and 302 to the final destination.
|
|
572
|
+
|
|
573
|
+
This is the single most common bug we see when teams add IQAuth to an app that already had its own session redirect logic.
|
|
574
|
+
|
|
575
|
+
---
|
|
576
|
+
|
|
577
|
+
## Migrating from Clerk's backend SDK
|
|
578
|
+
|
|
579
|
+
If you're moving from `@clerk/backend` to `@iqauth/sdk`, the surface is intentionally close but not identical. The full audit lives at [`docs/backend-sdk-parity.md`](../../docs/backend-sdk-parity.md); the high-leverage deltas:
|
|
580
|
+
|
|
581
|
+
- **Vocabulary.** Clerk's `organization` is IQAuth's `tenant`. `Clerk.users.banUser(id)` ↔ `iqauth.users.deactivate(id)`; `unbanUser` ↔ `reactivate`. The IQAuth modules are `tenants`, `memberships`, `roles`, `invites` — together they cover what Clerk packs into `organizations`.
|
|
582
|
+
- **Tenant scoping is explicit.** `users.create` takes `(tenantId, data)` rather than implying it from the API key. List endpoints (`users.list`, `tenants.list`, `memberships.listForTenant`) accept tenant filters.
|
|
583
|
+
- **Pagination + filters are still partial.** `users.list({ email, tenantId })` and `tenants.list({ vendorId })` work today, but Clerk-style `{ limit, offset, query, orderBy }` is on the near-term roadmap. Bulk-import scripts that page through tens of thousands of users should call the REST API directly until the helpers ship.
|
|
584
|
+
- **Admin-side user mutations.** `users.update(...)` in this SDK currently updates **only the calling user** (name, picture). Admin-on-behalf-of `update` / `delete` / `verifyPassword` and a `passwordDigest` import path for `users.create` are tracked as follow-ups; until then, use the REST API or the admin console.
|
|
585
|
+
- **Invitations.** `invites.create / validate / accept` are present. `invites.list` (pending) and `invites.revoke` are not in the SDK yet — same for the equivalent organization-invitation calls; tracked as follow-ups.
|
|
586
|
+
- **Sessions.** `sessions.list / revoke / revokeAll` are scoped to the **current user**. There is no admin-side `sessions.getSessionList({ userId })` yet; tracked as a follow-up.
|
|
587
|
+
- **Webhook signature verification.** Clerk wraps Svix's `Webhook.verify(payload, headers)`. IQAuth ships endpoint CRUD + delivery history + secret rotation, but receivers must verify HMAC signatures by hand today. A `webhooks.verifySignature(...)` helper is in flight under the existing rotation task.
|
|
588
|
+
- **Actor / impersonation tokens.** Tracked separately (see internal task F23 / #86). Not yet in the SDK.
|
|
589
|
+
- **Won't do.** JWT templates and per-instance domain CRUD are Clerk-specific and don't map to IQAuth's product surface. See the audit doc for documented alternatives.
|
|
590
|
+
|
|
591
|
+
For anything in the "won't do" / "different shape" rows of the audit, prefer the linked alternative over reaching back into REST — those alternatives are the supported path.
|
|
592
|
+
|
|
593
|
+
---
|
|
594
|
+
|
|
480
595
|
## Bundled docs
|
|
481
596
|
|
|
482
597
|
Long-form integration guides ship **inside the npm tarball** at `node_modules/@iqauth/sdk/docs/`. List them with:
|
|
@@ -504,3 +619,22 @@ These are also kept on the IQAuth admin dashboard's documentation tab.
|
|
|
504
619
|
## License
|
|
505
620
|
|
|
506
621
|
Proprietary — DispositionIQ internal use. See the LICENSE/usage terms in the bundled docs.
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
---
|
|
625
|
+
|
|
626
|
+
## Deploying with Docker / Next.js (read this before you ship)
|
|
627
|
+
|
|
628
|
+
The publishable key is baked into your build. NEXT_PUBLIC_* and VITE_* variables are inlined at build time, not run time. A docker image built against your staging issuer will continue to point at staging even when you later run it with -e NEXT_PUBLIC_IQAUTH_PUBLISHABLE_KEY=pk_live_xyz — Next.js / Vite have no way to re-resolve the constant after the bundle is emitted.
|
|
629
|
+
|
|
630
|
+
Two safe patterns:
|
|
631
|
+
|
|
632
|
+
1. Build the image per-environment (recommended for separate prod/stage stacks). Use ARG and ENV in the Dockerfile and pass --build-arg NEXT_PUBLIC_IQAUTH_PUBLISHABLE_KEY=pk_live_xyz at docker build time.
|
|
633
|
+
|
|
634
|
+
2. Read the key at request time on the server (single image, multi-env): fetch the publishable key in getServerSideProps / a Next.js Route Handler / your Express bootstrap and pass it to <IQAuthProvider publishableKey={...}> as a runtime prop. Server-side env vars ARE re-read on every container start, so the same image deploys to any environment.
|
|
635
|
+
|
|
636
|
+
Verify the build before you push: the iqauth doctor CLI compares the issuer encoded in your publishable key against the issuers discovery document and warns when they disagree:
|
|
637
|
+
|
|
638
|
+
npx iqauth doctor --issuer https://auth.example.com --publishable-key $NEXT_PUBLIC_IQAUTH_PUBLISHABLE_KEY
|
|
639
|
+
|
|
640
|
+
Wire this into CI right after next build and youll catch a wrong-environment publishable key before the image ships.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { c as IQAuthBrowserSessionClientConfig, d as SessionUser } from './types-
|
|
2
|
-
import { I as IQAuthClient } from './client-
|
|
1
|
+
import { c as IQAuthBrowserSessionClientConfig, d as SessionUser } from './types-DZAflmmq.mjs';
|
|
2
|
+
import { I as IQAuthClient } from './client-kYlJFgPv.mjs';
|
|
3
3
|
export { E as ErrorCodes, I as IQAuthError } from './errors-CDdl24MP.mjs';
|
|
4
|
-
import '
|
|
4
|
+
import './tokens-DCyzzn8L.mjs';
|
|
5
5
|
|
|
6
6
|
declare class BrowserSessionIQAuthClient extends IQAuthClient {
|
|
7
7
|
constructor(config: Omit<IQAuthBrowserSessionClientConfig, "environment">);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { c as IQAuthBrowserSessionClientConfig, d as SessionUser } from './types-
|
|
2
|
-
import { I as IQAuthClient } from './client-
|
|
1
|
+
import { c as IQAuthBrowserSessionClientConfig, d as SessionUser } from './types-DZAflmmq.js';
|
|
2
|
+
import { I as IQAuthClient } from './client-BNQe3AgF.js';
|
|
3
3
|
export { E as ErrorCodes, I as IQAuthError } from './errors-CDdl24MP.js';
|
|
4
|
-
import '
|
|
4
|
+
import './tokens-aHiGFr_E.js';
|
|
5
5
|
|
|
6
6
|
declare class BrowserSessionIQAuthClient extends IQAuthClient {
|
|
7
7
|
constructor(config: Omit<IQAuthBrowserSessionClientConfig, "environment">);
|
package/dist/browser-session.js
CHANGED
|
@@ -446,8 +446,7 @@ function parseMfaResponse(data, browserSessionMode) {
|
|
|
446
446
|
}
|
|
447
447
|
|
|
448
448
|
// src/modules/tokens.ts
|
|
449
|
-
var
|
|
450
|
-
var import_jsonwebtoken = __toESM(require("jsonwebtoken"));
|
|
449
|
+
var import_jose = require("jose");
|
|
451
450
|
var JWKS_CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
452
451
|
var DEFAULT_TOKEN_ISSUER = [
|
|
453
452
|
"https://auth.dispositioniq.com",
|
|
@@ -460,6 +459,24 @@ var DEFAULT_TOKEN_AUDIENCE = [
|
|
|
460
459
|
"iqvalidate"
|
|
461
460
|
];
|
|
462
461
|
var DEFAULT_CLOCK_TOLERANCE_SECONDS = 30;
|
|
462
|
+
function decodeProtectedHeader(token) {
|
|
463
|
+
const parts = token.split(".");
|
|
464
|
+
if (parts.length < 2) return null;
|
|
465
|
+
try {
|
|
466
|
+
const padded = parts[0] + "=".repeat((4 - parts[0].length % 4) % 4);
|
|
467
|
+
const b64 = padded.replace(/-/g, "+").replace(/_/g, "/");
|
|
468
|
+
let json;
|
|
469
|
+
if (typeof atob === "function") {
|
|
470
|
+
json = atob(b64);
|
|
471
|
+
} else {
|
|
472
|
+
const { Buffer: Buffer2 } = require("buffer");
|
|
473
|
+
json = Buffer2.from(b64, "base64").toString("utf8");
|
|
474
|
+
}
|
|
475
|
+
return JSON.parse(json);
|
|
476
|
+
} catch {
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
463
480
|
var TokensModule = class {
|
|
464
481
|
constructor(baseUrl, options = {}) {
|
|
465
482
|
this.jwksCache = null;
|
|
@@ -470,49 +487,49 @@ var TokensModule = class {
|
|
|
470
487
|
this.defaultClockTolerance = options.clockTolerance ?? DEFAULT_CLOCK_TOLERANCE_SECONDS;
|
|
471
488
|
}
|
|
472
489
|
/**
|
|
473
|
-
* Verify a JWT access token using RS256 via JWKS from
|
|
474
|
-
*
|
|
475
|
-
*
|
|
476
|
-
*
|
|
477
|
-
* clock tolerance default to client config but can be overridden per call.
|
|
490
|
+
* Verify a JWT access token using RS256/ES256 via JWKS from
|
|
491
|
+
* `/.well-known/jwks.json`. Backed by `jose` (Web Crypto) so it runs on
|
|
492
|
+
* Node, browser, and edge runtimes alike — no `node:crypto` dependency.
|
|
493
|
+
* Caches JWKS for 1 hour and refetches once on unknown `kid`.
|
|
478
494
|
*/
|
|
479
495
|
async verify(token, options = {}) {
|
|
480
|
-
const
|
|
481
|
-
if (!
|
|
496
|
+
const header = decodeProtectedHeader(token);
|
|
497
|
+
if (!header) {
|
|
482
498
|
throw new IQAuthError("TOKEN_INVALID", "Unable to decode token");
|
|
483
499
|
}
|
|
484
|
-
const kid =
|
|
500
|
+
const kid = header.kid;
|
|
485
501
|
if (!kid) {
|
|
486
502
|
throw new IQAuthError("TOKEN_INVALID", "Token missing kid header");
|
|
487
503
|
}
|
|
488
|
-
let
|
|
489
|
-
if (!
|
|
490
|
-
|
|
491
|
-
|
|
504
|
+
let cache = await this.ensureCache();
|
|
505
|
+
if (!cache.byKid.has(kid)) {
|
|
506
|
+
this.jwksCache = null;
|
|
507
|
+
cache = await this.ensureCache();
|
|
492
508
|
}
|
|
493
|
-
if (!
|
|
509
|
+
if (!cache.byKid.has(kid)) {
|
|
494
510
|
throw new IQAuthError("TOKEN_INVALID", `Unknown key ID: ${kid}`);
|
|
495
511
|
}
|
|
496
512
|
const issuer = options.issuer ?? this.defaultIssuer;
|
|
497
513
|
const audience = options.audience ?? this.defaultAudience;
|
|
498
514
|
const clockTolerance = options.clockTolerance ?? this.defaultClockTolerance;
|
|
499
|
-
const algorithms = options.algorithms ?? ["RS256"];
|
|
515
|
+
const algorithms = options.algorithms ?? ["RS256", "ES256"];
|
|
516
|
+
const verifyOptions = {
|
|
517
|
+
algorithms,
|
|
518
|
+
clockTolerance,
|
|
519
|
+
issuer,
|
|
520
|
+
audience
|
|
521
|
+
};
|
|
500
522
|
try {
|
|
501
|
-
const
|
|
502
|
-
|
|
503
|
-
clockTolerance,
|
|
504
|
-
// The jsonwebtoken types insist on tuple types for arrays; runtime
|
|
505
|
-
// accepts plain string[] so we cast to satisfy the compiler.
|
|
506
|
-
issuer,
|
|
507
|
-
audience
|
|
508
|
-
};
|
|
509
|
-
const verified = import_jsonwebtoken.default.verify(token, publicKey, verifyOptions);
|
|
510
|
-
return verified;
|
|
523
|
+
const { payload } = await (0, import_jose.jwtVerify)(token, cache.verifier, verifyOptions);
|
|
524
|
+
return payload;
|
|
511
525
|
} catch (err) {
|
|
526
|
+
if (err instanceof import_jose.errors.JWTExpired) {
|
|
527
|
+
throw new IQAuthError("TOKEN_EXPIRED", "Token has expired");
|
|
528
|
+
}
|
|
529
|
+
if (err instanceof import_jose.errors.JOSEError) {
|
|
530
|
+
throw new IQAuthError("TOKEN_INVALID", err.message);
|
|
531
|
+
}
|
|
512
532
|
if (err instanceof Error) {
|
|
513
|
-
if (err.name === "TokenExpiredError") {
|
|
514
|
-
throw new IQAuthError("TOKEN_EXPIRED", "Token has expired");
|
|
515
|
-
}
|
|
516
533
|
throw new IQAuthError("TOKEN_INVALID", err.message);
|
|
517
534
|
}
|
|
518
535
|
throw new IQAuthError("TOKEN_INVALID", "Token verification failed");
|
|
@@ -520,29 +537,40 @@ var TokensModule = class {
|
|
|
520
537
|
}
|
|
521
538
|
/**
|
|
522
539
|
* Decode a JWT without verification. Returns null if malformed.
|
|
523
|
-
*
|
|
524
|
-
* @remarks Local decode only — no network call
|
|
525
540
|
*/
|
|
526
541
|
decode(token) {
|
|
527
|
-
|
|
528
|
-
|
|
542
|
+
try {
|
|
543
|
+
const parts = token.split(".");
|
|
544
|
+
if (parts.length < 2) return null;
|
|
545
|
+
const payload = parts[1];
|
|
546
|
+
const padded = payload + "=".repeat((4 - payload.length % 4) % 4);
|
|
547
|
+
const b64 = padded.replace(/-/g, "+").replace(/_/g, "/");
|
|
548
|
+
let json;
|
|
549
|
+
if (typeof atob === "function") {
|
|
550
|
+
json = atob(b64);
|
|
551
|
+
} else {
|
|
552
|
+
const { Buffer: Buffer2 } = require("buffer");
|
|
553
|
+
json = Buffer2.from(b64, "base64").toString("utf8");
|
|
554
|
+
}
|
|
555
|
+
try {
|
|
556
|
+
json = decodeURIComponent(escape(json));
|
|
557
|
+
} catch {
|
|
558
|
+
}
|
|
559
|
+
const claims = JSON.parse(json);
|
|
560
|
+
if (!claims || typeof claims !== "object") return null;
|
|
561
|
+
return claims;
|
|
562
|
+
} catch {
|
|
563
|
+
return null;
|
|
564
|
+
}
|
|
529
565
|
}
|
|
530
|
-
/**
|
|
531
|
-
* Check if a token is expired based on the `exp` claim.
|
|
532
|
-
*
|
|
533
|
-
* @remarks Local check only — no network call
|
|
534
|
-
*/
|
|
566
|
+
/** Check if a token is expired based on the `exp` claim. */
|
|
535
567
|
isExpired(token) {
|
|
536
568
|
const claims = this.decode(token);
|
|
537
569
|
if (!claims?.exp) return true;
|
|
538
570
|
const now = Math.floor(Date.now() / 1e3);
|
|
539
571
|
return claims.exp <= now;
|
|
540
572
|
}
|
|
541
|
-
/**
|
|
542
|
-
* Get the claims from a token without verification.
|
|
543
|
-
*
|
|
544
|
-
* @remarks Local decode only — no network call
|
|
545
|
-
*/
|
|
573
|
+
/** Get the claims from a token without verification. */
|
|
546
574
|
getClaims(token) {
|
|
547
575
|
const claims = this.decode(token);
|
|
548
576
|
if (!claims) {
|
|
@@ -550,11 +578,15 @@ var TokensModule = class {
|
|
|
550
578
|
}
|
|
551
579
|
return claims;
|
|
552
580
|
}
|
|
553
|
-
async
|
|
554
|
-
if (
|
|
555
|
-
|
|
581
|
+
async ensureCache() {
|
|
582
|
+
if (this.jwksCache && Date.now() - this.jwksCache.fetchedAt <= JWKS_CACHE_TTL_MS) {
|
|
583
|
+
return this.jwksCache;
|
|
584
|
+
}
|
|
585
|
+
await this.refreshJwks();
|
|
586
|
+
if (!this.jwksCache) {
|
|
587
|
+
throw new IQAuthError("INTERNAL_ERROR", "JWKS cache unavailable after refresh");
|
|
556
588
|
}
|
|
557
|
-
return this.jwksCache
|
|
589
|
+
return this.jwksCache;
|
|
558
590
|
}
|
|
559
591
|
async refreshJwks() {
|
|
560
592
|
if (this.inFlightRefresh) {
|
|
@@ -581,35 +613,24 @@ var TokensModule = class {
|
|
|
581
613
|
"Malformed JWKS response: expected { keys: [...] }"
|
|
582
614
|
);
|
|
583
615
|
}
|
|
584
|
-
const
|
|
616
|
+
const byKid = /* @__PURE__ */ new Set();
|
|
585
617
|
for (const key of jwks.keys) {
|
|
586
|
-
if (!key || typeof key.kid !== "string" || typeof key.n !== "string" || typeof key.e !== "string") {
|
|
618
|
+
if (!key || typeof key.kid !== "string" || typeof key.n !== "string" && typeof key.x !== "string" || key.kty === "RSA" && (typeof key.n !== "string" || typeof key.e !== "string")) {
|
|
587
619
|
throw new IQAuthError(
|
|
588
620
|
"INTERNAL_ERROR",
|
|
589
621
|
"Malformed JWKS response: key missing required fields"
|
|
590
622
|
);
|
|
591
623
|
}
|
|
592
|
-
|
|
593
|
-
keys.set(key.kid, pem);
|
|
624
|
+
byKid.add(key.kid);
|
|
594
625
|
}
|
|
595
|
-
|
|
626
|
+
const verifier = (0, import_jose.createLocalJWKSet)({ keys: jwks.keys });
|
|
627
|
+
this.jwksCache = { raw: jwks.keys, byKid, verifier, fetchedAt: Date.now() };
|
|
596
628
|
} finally {
|
|
597
629
|
this.inFlightRefresh = null;
|
|
598
630
|
}
|
|
599
631
|
})();
|
|
600
632
|
return this.inFlightRefresh;
|
|
601
633
|
}
|
|
602
|
-
jwkToPem(jwk) {
|
|
603
|
-
const keyObject = import_crypto.default.createPublicKey({
|
|
604
|
-
key: {
|
|
605
|
-
kty: jwk.kty,
|
|
606
|
-
n: jwk.n,
|
|
607
|
-
e: jwk.e
|
|
608
|
-
},
|
|
609
|
-
format: "jwk"
|
|
610
|
-
});
|
|
611
|
-
return keyObject.export({ type: "spki", format: "pem" });
|
|
612
|
-
}
|
|
613
634
|
/** @internal Exposed for testing — clears JWKS cache */
|
|
614
635
|
clearCache() {
|
|
615
636
|
this.jwksCache = null;
|
|
@@ -817,7 +838,7 @@ var PermissionsModule = class {
|
|
|
817
838
|
};
|
|
818
839
|
|
|
819
840
|
// src/modules/oidc.ts
|
|
820
|
-
var
|
|
841
|
+
var import_crypto = __toESM(require("crypto"));
|
|
821
842
|
var InMemoryOidcStateStore = class {
|
|
822
843
|
constructor() {
|
|
823
844
|
this.map = /* @__PURE__ */ new Map();
|
|
@@ -898,12 +919,12 @@ var OidcModule = class {
|
|
|
898
919
|
* ready to redirect the user to.
|
|
899
920
|
*/
|
|
900
921
|
async createAuthRequest(params) {
|
|
901
|
-
const codeVerifier = base64UrlEncode(
|
|
922
|
+
const codeVerifier = base64UrlEncode(import_crypto.default.randomBytes(32));
|
|
902
923
|
const codeChallenge = base64UrlEncode(
|
|
903
|
-
|
|
924
|
+
import_crypto.default.createHash("sha256").update(codeVerifier).digest()
|
|
904
925
|
);
|
|
905
|
-
const state = base64UrlEncode(
|
|
906
|
-
const nonce = base64UrlEncode(
|
|
926
|
+
const state = base64UrlEncode(import_crypto.default.randomBytes(16));
|
|
927
|
+
const nonce = base64UrlEncode(import_crypto.default.randomBytes(16));
|
|
907
928
|
await this.stateStore.set(state, {
|
|
908
929
|
codeVerifier,
|
|
909
930
|
state,
|
package/dist/browser-session.mjs
CHANGED
package/dist/browser.d.mts
CHANGED
|
@@ -1,33 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import { S as SessionManager } from './signIn-CiIBTJIh.mjs';
|
|
2
|
+
export { n as AccountRecord, A as AccountRegistry, C as CallbackResult, k as LinkProviderInput, L as LinkedIdentity, M as MagicLinkRequestInput, m as MultiAccountTokenStore, j as PasskeyAuthInput, P as PasswordlessOptions, z as REFRESH_COOKIE, R as RefreshTokenStore, a as SessionManagerOptions, b as SessionSnapshot, c as SessionStatus, x as SignInOptions, y as SignOutOptions, U as UnlinkProviderInput, d as beginPasskeyAuthentication, e as beginPasskeyRegistration, o as buildSignInUrl, B as clearCookie, h as enrollPasskey, f as finishPasskeyAuthentication, g as finishPasskeyRegistration, D as getCookie, p as handleAuthCallback, i as linkProvider, l as listLinkedIdentities, q as redirectToSignIn, r as requestMagicLink, E as setCookie, t as signIn, s as signInWithPasskey, w as signOut, u as unlinkProvider, v as verifyMagicLink } from './signIn-CiIBTJIh.mjs';
|
|
2
3
|
export { K as KeyMode, c as ParsedPublishableKey, P as PublishableKeyPayload, e as encodePublishableKey, i as isPublishableKey, b as isSecretKey, p as parsePublishableKey } from './publishableKey-BaR0HoAH.mjs';
|
|
3
4
|
export { a as ErrorCode, E as ErrorCodes, I as IQAuthError } from './errors-CDdl24MP.mjs';
|
|
4
|
-
import './types-
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Browser-only storage helpers used by the SessionManager.
|
|
8
|
-
*
|
|
9
|
-
* Storage strategy (Phase B):
|
|
10
|
-
* - Access token: in memory only (held by SessionManager).
|
|
11
|
-
* - Refresh token: first-party cookie on the app's own domain. The cookie
|
|
12
|
-
* is set by the SDK callback handler and cleared on signOut.
|
|
13
|
-
*
|
|
14
|
-
* NOTE: Cookies set from JS cannot be httpOnly. Phase D (cookie-aware
|
|
15
|
-
* middleware) will move refresh-token cookie management into the app's
|
|
16
|
-
* backend so it can be httpOnly. Until then, the SDK uses a `Secure`,
|
|
17
|
-
* `SameSite=Lax` first-party cookie as a pragmatic stopgap. Nothing
|
|
18
|
-
* privileged ever lives in localStorage.
|
|
19
|
-
*/
|
|
20
|
-
declare const REFRESH_COOKIE = "iqauth_rt";
|
|
21
|
-
interface CookieOptions {
|
|
22
|
-
maxAgeSeconds?: number;
|
|
23
|
-
path?: string;
|
|
24
|
-
domain?: string;
|
|
25
|
-
secure?: boolean;
|
|
26
|
-
sameSite?: "lax" | "strict" | "none";
|
|
27
|
-
}
|
|
28
|
-
declare function setCookie(name: string, value: string, opts?: CookieOptions): void;
|
|
29
|
-
declare function getCookie(name: string): string | null;
|
|
30
|
-
declare function clearCookie(name: string, opts?: CookieOptions): void;
|
|
5
|
+
import './types-DZAflmmq.mjs';
|
|
31
6
|
|
|
32
7
|
/**
|
|
33
8
|
* Browser-safe PKCE + state/nonce generation using WebCrypto.
|
|
@@ -43,4 +18,64 @@ interface PkcePair {
|
|
|
43
18
|
}
|
|
44
19
|
declare function createPkcePair(): Promise<PkcePair>;
|
|
45
20
|
|
|
46
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Step-up reverification helper — Task #90 (F27, SDK side).
|
|
23
|
+
*
|
|
24
|
+
* Calls `POST {issuer}/api/v1/auth/reverify` with the user's password or
|
|
25
|
+
* MFA code, and returns a short-lived single-use token the caller
|
|
26
|
+
* should attach to subsequent sensitive requests as
|
|
27
|
+
* `X-Reverification-Token`.
|
|
28
|
+
*
|
|
29
|
+
* Designed to pair with `auth.fetch()` — see README:
|
|
30
|
+
*
|
|
31
|
+
* const { token } = await reverify(manager, { level: "password", password });
|
|
32
|
+
* await manager.fetch("/api/billing/payouts", {
|
|
33
|
+
* method: "POST",
|
|
34
|
+
* headers: { "X-Reverification-Token": token },
|
|
35
|
+
* body: JSON.stringify({...}),
|
|
36
|
+
* });
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Sessionstorage key under which we stash the admin's pre-impersonation
|
|
41
|
+
* access token so `<ImpersonationBanner/>` can restore it on Exit.
|
|
42
|
+
* Tab-scoped (sessionStorage) by design — impersonation must not survive
|
|
43
|
+
* tab close, and we never want the admin's token to leak into other tabs.
|
|
44
|
+
*/
|
|
45
|
+
declare const PRIOR_SESSION_STORAGE_KEY = "iqauth_prior_admin_session";
|
|
46
|
+
/**
|
|
47
|
+
* Stash the current admin session token, then swap to an impersonation
|
|
48
|
+
* access token minted by `POST /api/v1/admin/users/:id/actor-token`.
|
|
49
|
+
* The banner's "Exit" restores the prior session via `exitImpersonation`.
|
|
50
|
+
*/
|
|
51
|
+
declare function enterImpersonation(manager: SessionManager, actorAccessToken: string): void;
|
|
52
|
+
/**
|
|
53
|
+
* Restore the admin's pre-impersonation access token (if any) and clear
|
|
54
|
+
* the stash. Returns `true` when a prior session was restored, `false`
|
|
55
|
+
* when none was found (caller should then `signOut` instead).
|
|
56
|
+
*/
|
|
57
|
+
declare function exitImpersonation(manager: SessionManager): boolean;
|
|
58
|
+
type ReverificationLevel = "password" | "mfa";
|
|
59
|
+
interface ReverifyInput {
|
|
60
|
+
level: ReverificationLevel;
|
|
61
|
+
password?: string;
|
|
62
|
+
totp?: string;
|
|
63
|
+
method?: "totp" | "sms" | "email";
|
|
64
|
+
ttlSeconds?: number;
|
|
65
|
+
}
|
|
66
|
+
interface ReverifyResult {
|
|
67
|
+
token: string;
|
|
68
|
+
level: ReverificationLevel;
|
|
69
|
+
expiresAt: Date;
|
|
70
|
+
}
|
|
71
|
+
declare function reverify(manager: SessionManager, input: ReverifyInput, options?: {
|
|
72
|
+
path?: string;
|
|
73
|
+
}): Promise<ReverifyResult>;
|
|
74
|
+
/**
|
|
75
|
+
* Convenience wrapper: returns a `fetch`-compatible function that
|
|
76
|
+
* automatically attaches `X-Reverification-Token` on the next call,
|
|
77
|
+
* then forgets it (single-use mirror of the server semantics).
|
|
78
|
+
*/
|
|
79
|
+
declare function withReverification(manager: SessionManager, token: string): (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
80
|
+
|
|
81
|
+
export { PRIOR_SESSION_STORAGE_KEY, type ReverificationLevel, type ReverifyInput, type ReverifyResult, SessionManager, createPkcePair, enterImpersonation, exitImpersonation, randomUrlSafe, reverify, s256Challenge, withReverification };
|
package/dist/browser.d.ts
CHANGED
|
@@ -1,33 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import { S as SessionManager } from './signIn-OCr88Zf8.js';
|
|
2
|
+
export { n as AccountRecord, A as AccountRegistry, C as CallbackResult, k as LinkProviderInput, L as LinkedIdentity, M as MagicLinkRequestInput, m as MultiAccountTokenStore, j as PasskeyAuthInput, P as PasswordlessOptions, z as REFRESH_COOKIE, R as RefreshTokenStore, a as SessionManagerOptions, b as SessionSnapshot, c as SessionStatus, x as SignInOptions, y as SignOutOptions, U as UnlinkProviderInput, d as beginPasskeyAuthentication, e as beginPasskeyRegistration, o as buildSignInUrl, B as clearCookie, h as enrollPasskey, f as finishPasskeyAuthentication, g as finishPasskeyRegistration, D as getCookie, p as handleAuthCallback, i as linkProvider, l as listLinkedIdentities, q as redirectToSignIn, r as requestMagicLink, E as setCookie, t as signIn, s as signInWithPasskey, w as signOut, u as unlinkProvider, v as verifyMagicLink } from './signIn-OCr88Zf8.js';
|
|
2
3
|
export { K as KeyMode, c as ParsedPublishableKey, P as PublishableKeyPayload, e as encodePublishableKey, i as isPublishableKey, b as isSecretKey, p as parsePublishableKey } from './publishableKey-BaR0HoAH.js';
|
|
3
4
|
export { a as ErrorCode, E as ErrorCodes, I as IQAuthError } from './errors-CDdl24MP.js';
|
|
4
|
-
import './types-
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Browser-only storage helpers used by the SessionManager.
|
|
8
|
-
*
|
|
9
|
-
* Storage strategy (Phase B):
|
|
10
|
-
* - Access token: in memory only (held by SessionManager).
|
|
11
|
-
* - Refresh token: first-party cookie on the app's own domain. The cookie
|
|
12
|
-
* is set by the SDK callback handler and cleared on signOut.
|
|
13
|
-
*
|
|
14
|
-
* NOTE: Cookies set from JS cannot be httpOnly. Phase D (cookie-aware
|
|
15
|
-
* middleware) will move refresh-token cookie management into the app's
|
|
16
|
-
* backend so it can be httpOnly. Until then, the SDK uses a `Secure`,
|
|
17
|
-
* `SameSite=Lax` first-party cookie as a pragmatic stopgap. Nothing
|
|
18
|
-
* privileged ever lives in localStorage.
|
|
19
|
-
*/
|
|
20
|
-
declare const REFRESH_COOKIE = "iqauth_rt";
|
|
21
|
-
interface CookieOptions {
|
|
22
|
-
maxAgeSeconds?: number;
|
|
23
|
-
path?: string;
|
|
24
|
-
domain?: string;
|
|
25
|
-
secure?: boolean;
|
|
26
|
-
sameSite?: "lax" | "strict" | "none";
|
|
27
|
-
}
|
|
28
|
-
declare function setCookie(name: string, value: string, opts?: CookieOptions): void;
|
|
29
|
-
declare function getCookie(name: string): string | null;
|
|
30
|
-
declare function clearCookie(name: string, opts?: CookieOptions): void;
|
|
5
|
+
import './types-DZAflmmq.js';
|
|
31
6
|
|
|
32
7
|
/**
|
|
33
8
|
* Browser-safe PKCE + state/nonce generation using WebCrypto.
|
|
@@ -43,4 +18,64 @@ interface PkcePair {
|
|
|
43
18
|
}
|
|
44
19
|
declare function createPkcePair(): Promise<PkcePair>;
|
|
45
20
|
|
|
46
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Step-up reverification helper — Task #90 (F27, SDK side).
|
|
23
|
+
*
|
|
24
|
+
* Calls `POST {issuer}/api/v1/auth/reverify` with the user's password or
|
|
25
|
+
* MFA code, and returns a short-lived single-use token the caller
|
|
26
|
+
* should attach to subsequent sensitive requests as
|
|
27
|
+
* `X-Reverification-Token`.
|
|
28
|
+
*
|
|
29
|
+
* Designed to pair with `auth.fetch()` — see README:
|
|
30
|
+
*
|
|
31
|
+
* const { token } = await reverify(manager, { level: "password", password });
|
|
32
|
+
* await manager.fetch("/api/billing/payouts", {
|
|
33
|
+
* method: "POST",
|
|
34
|
+
* headers: { "X-Reverification-Token": token },
|
|
35
|
+
* body: JSON.stringify({...}),
|
|
36
|
+
* });
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Sessionstorage key under which we stash the admin's pre-impersonation
|
|
41
|
+
* access token so `<ImpersonationBanner/>` can restore it on Exit.
|
|
42
|
+
* Tab-scoped (sessionStorage) by design — impersonation must not survive
|
|
43
|
+
* tab close, and we never want the admin's token to leak into other tabs.
|
|
44
|
+
*/
|
|
45
|
+
declare const PRIOR_SESSION_STORAGE_KEY = "iqauth_prior_admin_session";
|
|
46
|
+
/**
|
|
47
|
+
* Stash the current admin session token, then swap to an impersonation
|
|
48
|
+
* access token minted by `POST /api/v1/admin/users/:id/actor-token`.
|
|
49
|
+
* The banner's "Exit" restores the prior session via `exitImpersonation`.
|
|
50
|
+
*/
|
|
51
|
+
declare function enterImpersonation(manager: SessionManager, actorAccessToken: string): void;
|
|
52
|
+
/**
|
|
53
|
+
* Restore the admin's pre-impersonation access token (if any) and clear
|
|
54
|
+
* the stash. Returns `true` when a prior session was restored, `false`
|
|
55
|
+
* when none was found (caller should then `signOut` instead).
|
|
56
|
+
*/
|
|
57
|
+
declare function exitImpersonation(manager: SessionManager): boolean;
|
|
58
|
+
type ReverificationLevel = "password" | "mfa";
|
|
59
|
+
interface ReverifyInput {
|
|
60
|
+
level: ReverificationLevel;
|
|
61
|
+
password?: string;
|
|
62
|
+
totp?: string;
|
|
63
|
+
method?: "totp" | "sms" | "email";
|
|
64
|
+
ttlSeconds?: number;
|
|
65
|
+
}
|
|
66
|
+
interface ReverifyResult {
|
|
67
|
+
token: string;
|
|
68
|
+
level: ReverificationLevel;
|
|
69
|
+
expiresAt: Date;
|
|
70
|
+
}
|
|
71
|
+
declare function reverify(manager: SessionManager, input: ReverifyInput, options?: {
|
|
72
|
+
path?: string;
|
|
73
|
+
}): Promise<ReverifyResult>;
|
|
74
|
+
/**
|
|
75
|
+
* Convenience wrapper: returns a `fetch`-compatible function that
|
|
76
|
+
* automatically attaches `X-Reverification-Token` on the next call,
|
|
77
|
+
* then forgets it (single-use mirror of the server semantics).
|
|
78
|
+
*/
|
|
79
|
+
declare function withReverification(manager: SessionManager, token: string): (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
80
|
+
|
|
81
|
+
export { PRIOR_SESSION_STORAGE_KEY, type ReverificationLevel, type ReverifyInput, type ReverifyResult, SessionManager, createPkcePair, enterImpersonation, exitImpersonation, randomUrlSafe, reverify, s256Challenge, withReverification };
|