@iqauth/sdk 2.6.4 → 2.8.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 +173 -1
- package/dist/browser-session.d.mts +4 -4
- package/dist/browser-session.d.ts +4 -4
- package/dist/browser-session.js +212 -46
- package/dist/browser-session.mjs +3 -3
- package/dist/browser.d.mts +5 -5
- package/dist/browser.d.ts +5 -5
- package/dist/browser.js +293 -34
- package/dist/browser.mjs +5 -5
- package/dist/{chunk-BVV54LPI.mjs → chunk-25SSYDIP.mjs} +10 -4
- package/dist/{chunk-XAWYUPMO.mjs → chunk-4V7FKOTG.mjs} +242 -22
- package/dist/{chunk-6I6RM4MN.mjs → chunk-6PJRLRB4.mjs} +33 -3
- package/dist/{chunk-SL3KRS4W.mjs → chunk-CIJORODR.mjs} +23 -1
- package/dist/{chunk-LIZYFXH7.mjs → chunk-DFWHSDYQ.mjs} +1 -1
- package/dist/chunk-GLXSIGVS.mjs +66 -0
- package/dist/{chunk-DJIBN2N7.mjs → chunk-GN37E64I.mjs} +29 -7
- package/dist/{chunk-WQWBJSSS.mjs → chunk-HVHNYPDC.mjs} +6 -6
- package/dist/chunk-JRDVUWAL.mjs +46 -0
- package/dist/{chunk-UNYDG2L4.mjs → chunk-NUO2I65G.mjs} +56 -23
- package/dist/{chunk-5T7GHBX6.mjs → chunk-TLET552H.mjs} +36 -0
- package/dist/chunk-VYQ3ETCK.mjs +244 -0
- package/dist/{chunk-3JULWS6F.mjs → chunk-WCELYTJ3.mjs} +3 -3
- package/dist/chunk-WHT6WKTY.mjs +3180 -0
- package/dist/{chunk-MKKZULZR.mjs → chunk-WIFG74IK.mjs} +1 -1
- package/dist/chunk-WSH4SW7F.mjs +490 -0
- package/dist/{chunk-W3F4JYGP.mjs → chunk-ZLJPABB7.mjs} +139 -23
- package/dist/cli/index.js +2 -2
- package/dist/cli/index.mjs +2 -2
- package/dist/{client-BNQe3AgF.d.ts → client-D8L-PaWr.d.mts} +59 -6
- package/dist/{client-kYlJFgPv.d.mts → client-DkPL0EPZ.d.ts} +59 -6
- package/dist/{doctor-YYNHNMLD.mjs → doctor-JAFXWU3X.mjs} +2 -2
- package/dist/errors-Jl1Jtm-6.d.mts +107 -0
- package/dist/errors-Jl1Jtm-6.d.ts +107 -0
- package/dist/{express-CHpfa7D_.d.ts → express-Budysq4h.d.ts} +2 -2
- package/dist/{express-B6_1vBYZ.d.mts → express-DDTA3qV1.d.mts} +2 -2
- package/dist/express.d.mts +7 -6
- package/dist/express.d.ts +7 -6
- package/dist/express.js +563 -85
- package/dist/express.mjs +73 -34
- package/dist/fastify.d.mts +10 -0
- package/dist/fastify.d.ts +10 -0
- package/dist/fastify.js +589 -65
- package/dist/fastify.mjs +101 -11
- package/dist/hono.d.mts +10 -0
- package/dist/hono.d.ts +10 -0
- package/dist/hono.js +566 -65
- package/dist/hono.mjs +78 -11
- package/dist/index-Cko-d5po.d.mts +1848 -0
- package/dist/index-RNqwEcmY.d.ts +1848 -0
- package/dist/index.d.mts +56 -8
- package/dist/index.d.ts +56 -8
- package/dist/index.js +694 -75
- package/dist/index.mjs +30 -10
- package/dist/{keys-NLWFAOEM.mjs → keys-6Y776TG2.mjs} +2 -2
- package/dist/locales.d.mts +1 -1
- package/dist/locales.d.ts +1 -1
- package/dist/locales.js +36 -0
- package/dist/locales.mjs +1 -1
- package/dist/mobile.d.mts +77 -7
- package/dist/mobile.d.ts +77 -7
- package/dist/mobile.js +307 -46
- package/dist/mobile.mjs +98 -3
- package/dist/next.d.mts +10 -1
- package/dist/next.d.ts +10 -1
- package/dist/next.js +596 -205
- package/dist/next.mjs +83 -10
- package/dist/{provisioningBridge-88xjOS2n.d.mts → provisioningBridge-BXPMZCLe.d.ts} +30 -2
- package/dist/{provisioningBridge-DnTfzdZK.d.ts → provisioningBridge-IEycmsgb.d.mts} +30 -2
- package/dist/{publishableKey-BaR0HoAH.d.ts → publishableKey-f2kq-rKw.d.mts} +1 -1
- package/dist/{publishableKey-BaR0HoAH.d.mts → publishableKey-f2kq-rKw.d.ts} +1 -1
- package/dist/react-permissions.d.mts +52 -0
- package/dist/react-permissions.d.ts +52 -0
- package/dist/react-permissions.js +239 -0
- package/dist/react-permissions.mjs +98 -0
- package/dist/react.d.mts +9 -1624
- package/dist/react.d.ts +9 -1624
- package/dist/react.js +882 -73
- package/dist/react.mjs +71 -2631
- package/dist/{reverify-4UEJXUS6.mjs → reverify-C64QXKJO.mjs} +2 -2
- package/dist/server/handlers.d.mts +200 -4
- package/dist/server/handlers.d.ts +200 -4
- package/dist/server/handlers.js +530 -16
- package/dist/server/handlers.mjs +14 -3
- package/dist/server.d.mts +171 -8
- package/dist/server.d.ts +171 -8
- package/dist/server.js +579 -61
- package/dist/server.mjs +99 -12
- package/dist/service.d.mts +4 -4
- package/dist/service.d.ts +4 -4
- package/dist/service.js +212 -46
- package/dist/service.mjs +3 -3
- package/dist/{signIn-CiIBTJIh.d.mts → signIn-CReqfXsh.d.mts} +95 -3
- package/dist/{signIn-OCr88Zf8.d.ts → signIn-Cfa1GTpO.d.ts} +95 -3
- package/dist/{signIn-4OKLDEIH.mjs → signIn-SHBW6Z4T.mjs} +1 -1
- package/dist/test.mjs +3 -3
- package/dist/{tokens-DCyzzn8L.d.mts → tokens-9F6ETrzk.d.ts} +9 -2
- package/dist/{tokens-aHiGFr_E.d.ts → tokens-B06VtvUi.d.mts} +9 -2
- package/dist/{types-DZAflmmq.d.mts → types-Bn8O-OEd.d.mts} +164 -11
- package/dist/{types-DZAflmmq.d.ts → types-Bn8O-OEd.d.ts} +164 -11
- package/dist/{types-6bNdxesb.d.ts → types-DnU2LhXR.d.mts} +7 -1
- package/dist/{types-6bNdxesb.d.mts → types-DnU2LhXR.d.ts} +7 -1
- package/dist/webhooks.d.mts +113 -17
- package/dist/webhooks.d.ts +113 -17
- package/dist/webhooks.js +179 -15
- package/dist/webhooks.mjs +7 -1
- package/dist/ws.d.mts +2 -2
- package/dist/ws.d.ts +2 -2
- package/dist/ws.js +80 -30
- package/dist/ws.mjs +4 -4
- package/docs/error-handling.md +101 -0
- package/docs/guides/effective-permissions.md +171 -0
- package/docs/guides/invitations.md +65 -0
- package/package.json +19 -4
- package/dist/chunk-6TDJJER7.mjs +0 -217
- package/dist/chunk-UKZLOHZG.mjs +0 -83
- package/dist/errors-CDdl24MP.d.mts +0 -52
- package/dist/errors-CDdl24MP.d.ts +0 -52
package/dist/server/handlers.mjs
CHANGED
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
import {
|
|
2
|
+
__resetSignoutMarkersForTests,
|
|
3
|
+
__resetSignoutRegistryWarningForTests,
|
|
4
|
+
buildUserinfoResponse,
|
|
5
|
+
createInMemorySignoutRegistry,
|
|
2
6
|
handleCallback,
|
|
3
7
|
handleRefresh,
|
|
4
8
|
handleSignout,
|
|
9
|
+
handleUserinfo,
|
|
5
10
|
serializeCookie
|
|
6
|
-
} from "../chunk-
|
|
7
|
-
import "../chunk-
|
|
8
|
-
import "../chunk-
|
|
11
|
+
} from "../chunk-WSH4SW7F.mjs";
|
|
12
|
+
import "../chunk-HVHNYPDC.mjs";
|
|
13
|
+
import "../chunk-NUO2I65G.mjs";
|
|
14
|
+
import "../chunk-6PJRLRB4.mjs";
|
|
9
15
|
import "../chunk-Y6FXYEAI.mjs";
|
|
10
16
|
export {
|
|
17
|
+
__resetSignoutMarkersForTests,
|
|
18
|
+
__resetSignoutRegistryWarningForTests,
|
|
19
|
+
buildUserinfoResponse,
|
|
20
|
+
createInMemorySignoutRegistry,
|
|
11
21
|
handleCallback,
|
|
12
22
|
handleRefresh,
|
|
13
23
|
handleSignout,
|
|
24
|
+
handleUserinfo,
|
|
14
25
|
serializeCookie
|
|
15
26
|
};
|
package/dist/server.d.mts
CHANGED
|
@@ -1,10 +1,173 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { I as IQAuthClient } from './client-
|
|
3
|
-
export { E as ErrorCodes, I as IQAuthError } from './errors-
|
|
4
|
-
export { C as CookieAwareMiddlewareOptions, D as DEFAULT_ACCESS_COOKIE, a as DEFAULT_REFRESH_COOKIE, i as iqAuthMiddleware } from './express-
|
|
5
|
-
export { HandlerResponse, IQAuthHelperConfig, SetCookieDirective, handleCallback, handleRefresh, handleSignout, serializeCookie } from './server/handlers.mjs';
|
|
6
|
-
export { P as ProvisioningBridge, a as ProvisioningBridgeOptions, d as ProvisioningContext, b as ProvisioningStorage, c as createProvisioningBridge } from './provisioningBridge-
|
|
7
|
-
import './tokens-
|
|
1
|
+
import { J as JwtClaims, f as IQAuthTokenClientConfig, X as ExpressMiddlewareOptions, a as IQAuthRequestLike, b as IQAuthResponseLike, c as IQAuthNextFunction } from './types-Bn8O-OEd.mjs';
|
|
2
|
+
import { I as IQAuthClient } from './client-D8L-PaWr.mjs';
|
|
3
|
+
export { E as ErrorCodes, I as IQAuthError } from './errors-Jl1Jtm-6.mjs';
|
|
4
|
+
export { C as CookieAwareMiddlewareOptions, D as DEFAULT_ACCESS_COOKIE, a as DEFAULT_REFRESH_COOKIE, i as iqAuthMiddleware } from './express-DDTA3qV1.mjs';
|
|
5
|
+
export { HandlerResponse, IQAuthHelperConfig, SetCookieDirective, UserinfoResponse, buildUserinfoResponse, handleCallback, handleRefresh, handleSignout, handleUserinfo, serializeCookie } from './server/handlers.mjs';
|
|
6
|
+
export { P as ProvisioningBridge, a as ProvisioningBridgeOptions, d as ProvisioningContext, e as ProvisioningError, b as ProvisioningStorage, c as createProvisioningBridge } from './provisioningBridge-IEycmsgb.mjs';
|
|
7
|
+
import './tokens-B06VtvUi.mjs';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* linkLocalUserToIqAuthSub — first-time-migration helper.
|
|
11
|
+
*
|
|
12
|
+
* Use case: an app already had local users before adopting IQAuth. After a
|
|
13
|
+
* user signs in via IQAuth, the JWT's `sub` won't match any local row. The
|
|
14
|
+
* conventional self-heal is "look up by email, write the sub onto the matching
|
|
15
|
+
* row". Doing this safely requires:
|
|
16
|
+
*
|
|
17
|
+
* 1. An atomic transaction (find + write) so two concurrent first-time
|
|
18
|
+
* sign-ins for the same user can't both succeed.
|
|
19
|
+
* 2. Email matching that's case-insensitive by default (since email
|
|
20
|
+
* providers fold case in delivery).
|
|
21
|
+
* 3. A refusal to link when the candidate row already has a different
|
|
22
|
+
* non-null `iqauthSub` (i.e. that local row belongs to someone else).
|
|
23
|
+
* 4. A refusal to link when more than one local row matches the email
|
|
24
|
+
* (duplicate-email collision in the legacy table).
|
|
25
|
+
*
|
|
26
|
+
* The helper is ORM-agnostic. Pass any adapter that implements
|
|
27
|
+
* `LinkAdapter`. A reference Drizzle adapter is exported as
|
|
28
|
+
* `createDrizzleLinkAdapter`.
|
|
29
|
+
*
|
|
30
|
+
* Result codes:
|
|
31
|
+
* - 'linked' — wrote claims.sub onto the matched local row.
|
|
32
|
+
* - 'already_linked' — a row was already keyed by claims.sub. No write.
|
|
33
|
+
* - 'conflict' — matched row already has a different non-null sub,
|
|
34
|
+
* OR more than one local row matches the email,
|
|
35
|
+
* OR the email is unverified and adoption is gated
|
|
36
|
+
* (reason 'unverified_email' — see H-4 below).
|
|
37
|
+
* - 'not_found' — no local row matched any of the provided lookupBy
|
|
38
|
+
* keys. Caller should provision a new row separately.
|
|
39
|
+
*
|
|
40
|
+
* Security (H-4): writing the IQAuth `sub` onto a pre-existing local row matched
|
|
41
|
+
* by email is a takeover of that account. It is only performed when the claims
|
|
42
|
+
* assert a verified email (`claims.email_verified === true`). When the email is
|
|
43
|
+
* unverified and `allowUnverifiedEmail` is not set, the helper fails closed with
|
|
44
|
+
* `{ status: 'conflict', reason: 'unverified_email' }` instead of linking.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
type LinkLookupBy = "email";
|
|
48
|
+
type LinkResult = {
|
|
49
|
+
status: "linked";
|
|
50
|
+
userId: string;
|
|
51
|
+
} | {
|
|
52
|
+
status: "already_linked";
|
|
53
|
+
userId: string;
|
|
54
|
+
} | {
|
|
55
|
+
status: "conflict";
|
|
56
|
+
userId?: string;
|
|
57
|
+
reason: "different_sub" | "duplicate_email" | "unverified_email";
|
|
58
|
+
} | {
|
|
59
|
+
status: "not_found";
|
|
60
|
+
};
|
|
61
|
+
interface LinkCandidate {
|
|
62
|
+
id: string;
|
|
63
|
+
email: string | null;
|
|
64
|
+
iqauthSub: string | null;
|
|
65
|
+
}
|
|
66
|
+
interface LinkAdapterTx {
|
|
67
|
+
findByIqAuthSub(sub: string): Promise<LinkCandidate | null>;
|
|
68
|
+
findByEmail(email: string, opts: {
|
|
69
|
+
caseInsensitive: boolean;
|
|
70
|
+
}): Promise<LinkCandidate[]>;
|
|
71
|
+
/**
|
|
72
|
+
* Write the IQAuth `sub` onto the matched row. Implementations SHOULD make
|
|
73
|
+
* this a conditional update (`WHERE id=? AND iqauth_sub IS NULL OR iqauth_sub=?`)
|
|
74
|
+
* and return `false` if no row was updated — that signals a concurrent
|
|
75
|
+
* writer claimed the row first and the helper will surface a `conflict`.
|
|
76
|
+
* For backward-compatibility, returning `void` is treated as "succeeded".
|
|
77
|
+
*/
|
|
78
|
+
setIqAuthSub(userId: string, sub: string): Promise<void | boolean>;
|
|
79
|
+
}
|
|
80
|
+
interface LinkAdapter {
|
|
81
|
+
/**
|
|
82
|
+
* Run `fn` inside a serialized transaction. Implementations MUST guarantee
|
|
83
|
+
* that candidate rows surfaced by `findByIqAuthSub` / `findByEmail` are
|
|
84
|
+
* held under a row-level lock (or equivalent isolation) for the duration
|
|
85
|
+
* of the transaction so the read+write pair is atomic against other
|
|
86
|
+
* concurrent first-time logins for the same user. Concretely:
|
|
87
|
+
* - Postgres / MySQL: implement the read with `SELECT … FOR UPDATE`.
|
|
88
|
+
* - SQLite: relies on the file-level write lock — no extra clause needed.
|
|
89
|
+
* - Otherwise: run the transaction at SERIALIZABLE isolation.
|
|
90
|
+
* The reference `createDrizzleLinkAdapter` does this for you.
|
|
91
|
+
*/
|
|
92
|
+
withTransaction<T>(fn: (tx: LinkAdapterTx) => Promise<T>): Promise<T>;
|
|
93
|
+
}
|
|
94
|
+
interface LinkLocalUserOptions {
|
|
95
|
+
adapter: LinkAdapter;
|
|
96
|
+
claims: Pick<JwtClaims, "sub" | "email" | "email_verified">;
|
|
97
|
+
/**
|
|
98
|
+
* Lookup keys to try, in order. Currently only `'email'` is supported.
|
|
99
|
+
* Defaults to `['email']`.
|
|
100
|
+
*/
|
|
101
|
+
lookupBy?: LinkLookupBy[];
|
|
102
|
+
/**
|
|
103
|
+
* Email matching. Default true — most legacy stores fold case in delivery
|
|
104
|
+
* but preserve the cased original at registration time.
|
|
105
|
+
*/
|
|
106
|
+
caseInsensitiveEmail?: boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Security gate (H-4): by default the email→sub link only proceeds when the
|
|
109
|
+
* claims assert a verified email (`claims.email_verified === true`). When the
|
|
110
|
+
* email is unverified and this flag is left `false`, the helper fails closed
|
|
111
|
+
* with `{ status: 'conflict', reason: 'unverified_email' }` rather than
|
|
112
|
+
* letting an unverified email take over a pre-existing local account.
|
|
113
|
+
*
|
|
114
|
+
* Set `true` ONLY when your issuer is trusted to never emit an unverified
|
|
115
|
+
* email for linking (or you have a compensating control). Defaults to
|
|
116
|
+
* `false` (secure).
|
|
117
|
+
*/
|
|
118
|
+
allowUnverifiedEmail?: boolean;
|
|
119
|
+
}
|
|
120
|
+
declare function linkLocalUserToIqAuthSub(options: LinkLocalUserOptions): Promise<LinkResult>;
|
|
121
|
+
interface DrizzleLikeDb {
|
|
122
|
+
transaction<T>(fn: (tx: unknown) => Promise<T>): Promise<T>;
|
|
123
|
+
}
|
|
124
|
+
interface DrizzleLinkAdapterDeps {
|
|
125
|
+
/** Drizzle DB instance with `.transaction()`. */
|
|
126
|
+
db: DrizzleLikeDb;
|
|
127
|
+
/** Drizzle table object (e.g. `users`). */
|
|
128
|
+
table: unknown;
|
|
129
|
+
/** Column refs on the table — `id`, `email`, and the iqauth sub column. */
|
|
130
|
+
columns: {
|
|
131
|
+
id: unknown;
|
|
132
|
+
email: unknown;
|
|
133
|
+
iqauthSub: unknown;
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* Optional override for the schema property keys used by `.set(...)` on
|
|
137
|
+
* UPDATE. Drizzle's `.set()` is keyed by the table's TypeScript property
|
|
138
|
+
* names (the keys of your schema object), which need not match the
|
|
139
|
+
* underlying column name. Defaults to `{ iqauthSub: 'iqauthSub' }`.
|
|
140
|
+
*/
|
|
141
|
+
columnNames?: {
|
|
142
|
+
iqauthSub?: string;
|
|
143
|
+
};
|
|
144
|
+
/** Drizzle `eq` operator import (`drizzle-orm`). */
|
|
145
|
+
eq: (a: unknown, b: unknown) => unknown;
|
|
146
|
+
/**
|
|
147
|
+
* Drizzle `sql` template tag (`drizzle-orm`). Used for case-insensitive
|
|
148
|
+
* email comparison via `lower(email) = lower($1)`.
|
|
149
|
+
*/
|
|
150
|
+
sql: (strings: TemplateStringsArray, ...values: unknown[]) => unknown;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Reference adapter for Drizzle ORM (Postgres / SQLite / MySQL). The host
|
|
154
|
+
* app passes its own `db`, `table`, column refs, and `eq` / `sql` imports
|
|
155
|
+
* so this SDK has no hard dependency on `drizzle-orm`.
|
|
156
|
+
*
|
|
157
|
+
* Example:
|
|
158
|
+
*
|
|
159
|
+
* import { eq, sql } from 'drizzle-orm';
|
|
160
|
+
* import { db } from './db';
|
|
161
|
+
* import { users } from './schema';
|
|
162
|
+
* import { createDrizzleLinkAdapter, linkLocalUserToIqAuthSub } from '@iqauth/sdk/server';
|
|
163
|
+
*
|
|
164
|
+
* const adapter = createDrizzleLinkAdapter({
|
|
165
|
+
* db, table: users, eq, sql,
|
|
166
|
+
* columns: { id: users.id, email: users.email, iqauthSub: users.iqauthSub },
|
|
167
|
+
* });
|
|
168
|
+
* const result = await linkLocalUserToIqAuthSub({ adapter, claims: req.auth });
|
|
169
|
+
*/
|
|
170
|
+
declare function createDrizzleLinkAdapter(deps: DrizzleLinkAdapterDeps): LinkAdapter;
|
|
8
171
|
|
|
9
172
|
declare class ServerIQAuthClient extends IQAuthClient {
|
|
10
173
|
constructor(config: IQAuthTokenClientConfig);
|
|
@@ -12,4 +175,4 @@ declare class ServerIQAuthClient extends IQAuthClient {
|
|
|
12
175
|
}
|
|
13
176
|
declare function createServerClient(config: IQAuthTokenClientConfig): ServerIQAuthClient;
|
|
14
177
|
|
|
15
|
-
export { ExpressMiddlewareOptions, IQAuthClient, IQAuthTokenClientConfig, ServerIQAuthClient, createServerClient };
|
|
178
|
+
export { type DrizzleLinkAdapterDeps, ExpressMiddlewareOptions, IQAuthClient, IQAuthTokenClientConfig, type LinkAdapter, type LinkAdapterTx, type LinkCandidate, type LinkLocalUserOptions, type LinkLookupBy, type LinkResult, ServerIQAuthClient, createDrizzleLinkAdapter, createServerClient, linkLocalUserToIqAuthSub };
|
package/dist/server.d.ts
CHANGED
|
@@ -1,10 +1,173 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { I as IQAuthClient } from './client-
|
|
3
|
-
export { E as ErrorCodes, I as IQAuthError } from './errors-
|
|
4
|
-
export { C as CookieAwareMiddlewareOptions, D as DEFAULT_ACCESS_COOKIE, a as DEFAULT_REFRESH_COOKIE, i as iqAuthMiddleware } from './express-
|
|
5
|
-
export { HandlerResponse, IQAuthHelperConfig, SetCookieDirective, handleCallback, handleRefresh, handleSignout, serializeCookie } from './server/handlers.js';
|
|
6
|
-
export { P as ProvisioningBridge, a as ProvisioningBridgeOptions, d as ProvisioningContext, b as ProvisioningStorage, c as createProvisioningBridge } from './provisioningBridge-
|
|
7
|
-
import './tokens-
|
|
1
|
+
import { J as JwtClaims, f as IQAuthTokenClientConfig, X as ExpressMiddlewareOptions, a as IQAuthRequestLike, b as IQAuthResponseLike, c as IQAuthNextFunction } from './types-Bn8O-OEd.js';
|
|
2
|
+
import { I as IQAuthClient } from './client-DkPL0EPZ.js';
|
|
3
|
+
export { E as ErrorCodes, I as IQAuthError } from './errors-Jl1Jtm-6.js';
|
|
4
|
+
export { C as CookieAwareMiddlewareOptions, D as DEFAULT_ACCESS_COOKIE, a as DEFAULT_REFRESH_COOKIE, i as iqAuthMiddleware } from './express-Budysq4h.js';
|
|
5
|
+
export { HandlerResponse, IQAuthHelperConfig, SetCookieDirective, UserinfoResponse, buildUserinfoResponse, handleCallback, handleRefresh, handleSignout, handleUserinfo, serializeCookie } from './server/handlers.js';
|
|
6
|
+
export { P as ProvisioningBridge, a as ProvisioningBridgeOptions, d as ProvisioningContext, e as ProvisioningError, b as ProvisioningStorage, c as createProvisioningBridge } from './provisioningBridge-BXPMZCLe.js';
|
|
7
|
+
import './tokens-9F6ETrzk.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* linkLocalUserToIqAuthSub — first-time-migration helper.
|
|
11
|
+
*
|
|
12
|
+
* Use case: an app already had local users before adopting IQAuth. After a
|
|
13
|
+
* user signs in via IQAuth, the JWT's `sub` won't match any local row. The
|
|
14
|
+
* conventional self-heal is "look up by email, write the sub onto the matching
|
|
15
|
+
* row". Doing this safely requires:
|
|
16
|
+
*
|
|
17
|
+
* 1. An atomic transaction (find + write) so two concurrent first-time
|
|
18
|
+
* sign-ins for the same user can't both succeed.
|
|
19
|
+
* 2. Email matching that's case-insensitive by default (since email
|
|
20
|
+
* providers fold case in delivery).
|
|
21
|
+
* 3. A refusal to link when the candidate row already has a different
|
|
22
|
+
* non-null `iqauthSub` (i.e. that local row belongs to someone else).
|
|
23
|
+
* 4. A refusal to link when more than one local row matches the email
|
|
24
|
+
* (duplicate-email collision in the legacy table).
|
|
25
|
+
*
|
|
26
|
+
* The helper is ORM-agnostic. Pass any adapter that implements
|
|
27
|
+
* `LinkAdapter`. A reference Drizzle adapter is exported as
|
|
28
|
+
* `createDrizzleLinkAdapter`.
|
|
29
|
+
*
|
|
30
|
+
* Result codes:
|
|
31
|
+
* - 'linked' — wrote claims.sub onto the matched local row.
|
|
32
|
+
* - 'already_linked' — a row was already keyed by claims.sub. No write.
|
|
33
|
+
* - 'conflict' — matched row already has a different non-null sub,
|
|
34
|
+
* OR more than one local row matches the email,
|
|
35
|
+
* OR the email is unverified and adoption is gated
|
|
36
|
+
* (reason 'unverified_email' — see H-4 below).
|
|
37
|
+
* - 'not_found' — no local row matched any of the provided lookupBy
|
|
38
|
+
* keys. Caller should provision a new row separately.
|
|
39
|
+
*
|
|
40
|
+
* Security (H-4): writing the IQAuth `sub` onto a pre-existing local row matched
|
|
41
|
+
* by email is a takeover of that account. It is only performed when the claims
|
|
42
|
+
* assert a verified email (`claims.email_verified === true`). When the email is
|
|
43
|
+
* unverified and `allowUnverifiedEmail` is not set, the helper fails closed with
|
|
44
|
+
* `{ status: 'conflict', reason: 'unverified_email' }` instead of linking.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
type LinkLookupBy = "email";
|
|
48
|
+
type LinkResult = {
|
|
49
|
+
status: "linked";
|
|
50
|
+
userId: string;
|
|
51
|
+
} | {
|
|
52
|
+
status: "already_linked";
|
|
53
|
+
userId: string;
|
|
54
|
+
} | {
|
|
55
|
+
status: "conflict";
|
|
56
|
+
userId?: string;
|
|
57
|
+
reason: "different_sub" | "duplicate_email" | "unverified_email";
|
|
58
|
+
} | {
|
|
59
|
+
status: "not_found";
|
|
60
|
+
};
|
|
61
|
+
interface LinkCandidate {
|
|
62
|
+
id: string;
|
|
63
|
+
email: string | null;
|
|
64
|
+
iqauthSub: string | null;
|
|
65
|
+
}
|
|
66
|
+
interface LinkAdapterTx {
|
|
67
|
+
findByIqAuthSub(sub: string): Promise<LinkCandidate | null>;
|
|
68
|
+
findByEmail(email: string, opts: {
|
|
69
|
+
caseInsensitive: boolean;
|
|
70
|
+
}): Promise<LinkCandidate[]>;
|
|
71
|
+
/**
|
|
72
|
+
* Write the IQAuth `sub` onto the matched row. Implementations SHOULD make
|
|
73
|
+
* this a conditional update (`WHERE id=? AND iqauth_sub IS NULL OR iqauth_sub=?`)
|
|
74
|
+
* and return `false` if no row was updated — that signals a concurrent
|
|
75
|
+
* writer claimed the row first and the helper will surface a `conflict`.
|
|
76
|
+
* For backward-compatibility, returning `void` is treated as "succeeded".
|
|
77
|
+
*/
|
|
78
|
+
setIqAuthSub(userId: string, sub: string): Promise<void | boolean>;
|
|
79
|
+
}
|
|
80
|
+
interface LinkAdapter {
|
|
81
|
+
/**
|
|
82
|
+
* Run `fn` inside a serialized transaction. Implementations MUST guarantee
|
|
83
|
+
* that candidate rows surfaced by `findByIqAuthSub` / `findByEmail` are
|
|
84
|
+
* held under a row-level lock (or equivalent isolation) for the duration
|
|
85
|
+
* of the transaction so the read+write pair is atomic against other
|
|
86
|
+
* concurrent first-time logins for the same user. Concretely:
|
|
87
|
+
* - Postgres / MySQL: implement the read with `SELECT … FOR UPDATE`.
|
|
88
|
+
* - SQLite: relies on the file-level write lock — no extra clause needed.
|
|
89
|
+
* - Otherwise: run the transaction at SERIALIZABLE isolation.
|
|
90
|
+
* The reference `createDrizzleLinkAdapter` does this for you.
|
|
91
|
+
*/
|
|
92
|
+
withTransaction<T>(fn: (tx: LinkAdapterTx) => Promise<T>): Promise<T>;
|
|
93
|
+
}
|
|
94
|
+
interface LinkLocalUserOptions {
|
|
95
|
+
adapter: LinkAdapter;
|
|
96
|
+
claims: Pick<JwtClaims, "sub" | "email" | "email_verified">;
|
|
97
|
+
/**
|
|
98
|
+
* Lookup keys to try, in order. Currently only `'email'` is supported.
|
|
99
|
+
* Defaults to `['email']`.
|
|
100
|
+
*/
|
|
101
|
+
lookupBy?: LinkLookupBy[];
|
|
102
|
+
/**
|
|
103
|
+
* Email matching. Default true — most legacy stores fold case in delivery
|
|
104
|
+
* but preserve the cased original at registration time.
|
|
105
|
+
*/
|
|
106
|
+
caseInsensitiveEmail?: boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Security gate (H-4): by default the email→sub link only proceeds when the
|
|
109
|
+
* claims assert a verified email (`claims.email_verified === true`). When the
|
|
110
|
+
* email is unverified and this flag is left `false`, the helper fails closed
|
|
111
|
+
* with `{ status: 'conflict', reason: 'unverified_email' }` rather than
|
|
112
|
+
* letting an unverified email take over a pre-existing local account.
|
|
113
|
+
*
|
|
114
|
+
* Set `true` ONLY when your issuer is trusted to never emit an unverified
|
|
115
|
+
* email for linking (or you have a compensating control). Defaults to
|
|
116
|
+
* `false` (secure).
|
|
117
|
+
*/
|
|
118
|
+
allowUnverifiedEmail?: boolean;
|
|
119
|
+
}
|
|
120
|
+
declare function linkLocalUserToIqAuthSub(options: LinkLocalUserOptions): Promise<LinkResult>;
|
|
121
|
+
interface DrizzleLikeDb {
|
|
122
|
+
transaction<T>(fn: (tx: unknown) => Promise<T>): Promise<T>;
|
|
123
|
+
}
|
|
124
|
+
interface DrizzleLinkAdapterDeps {
|
|
125
|
+
/** Drizzle DB instance with `.transaction()`. */
|
|
126
|
+
db: DrizzleLikeDb;
|
|
127
|
+
/** Drizzle table object (e.g. `users`). */
|
|
128
|
+
table: unknown;
|
|
129
|
+
/** Column refs on the table — `id`, `email`, and the iqauth sub column. */
|
|
130
|
+
columns: {
|
|
131
|
+
id: unknown;
|
|
132
|
+
email: unknown;
|
|
133
|
+
iqauthSub: unknown;
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* Optional override for the schema property keys used by `.set(...)` on
|
|
137
|
+
* UPDATE. Drizzle's `.set()` is keyed by the table's TypeScript property
|
|
138
|
+
* names (the keys of your schema object), which need not match the
|
|
139
|
+
* underlying column name. Defaults to `{ iqauthSub: 'iqauthSub' }`.
|
|
140
|
+
*/
|
|
141
|
+
columnNames?: {
|
|
142
|
+
iqauthSub?: string;
|
|
143
|
+
};
|
|
144
|
+
/** Drizzle `eq` operator import (`drizzle-orm`). */
|
|
145
|
+
eq: (a: unknown, b: unknown) => unknown;
|
|
146
|
+
/**
|
|
147
|
+
* Drizzle `sql` template tag (`drizzle-orm`). Used for case-insensitive
|
|
148
|
+
* email comparison via `lower(email) = lower($1)`.
|
|
149
|
+
*/
|
|
150
|
+
sql: (strings: TemplateStringsArray, ...values: unknown[]) => unknown;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Reference adapter for Drizzle ORM (Postgres / SQLite / MySQL). The host
|
|
154
|
+
* app passes its own `db`, `table`, column refs, and `eq` / `sql` imports
|
|
155
|
+
* so this SDK has no hard dependency on `drizzle-orm`.
|
|
156
|
+
*
|
|
157
|
+
* Example:
|
|
158
|
+
*
|
|
159
|
+
* import { eq, sql } from 'drizzle-orm';
|
|
160
|
+
* import { db } from './db';
|
|
161
|
+
* import { users } from './schema';
|
|
162
|
+
* import { createDrizzleLinkAdapter, linkLocalUserToIqAuthSub } from '@iqauth/sdk/server';
|
|
163
|
+
*
|
|
164
|
+
* const adapter = createDrizzleLinkAdapter({
|
|
165
|
+
* db, table: users, eq, sql,
|
|
166
|
+
* columns: { id: users.id, email: users.email, iqauthSub: users.iqauthSub },
|
|
167
|
+
* });
|
|
168
|
+
* const result = await linkLocalUserToIqAuthSub({ adapter, claims: req.auth });
|
|
169
|
+
*/
|
|
170
|
+
declare function createDrizzleLinkAdapter(deps: DrizzleLinkAdapterDeps): LinkAdapter;
|
|
8
171
|
|
|
9
172
|
declare class ServerIQAuthClient extends IQAuthClient {
|
|
10
173
|
constructor(config: IQAuthTokenClientConfig);
|
|
@@ -12,4 +175,4 @@ declare class ServerIQAuthClient extends IQAuthClient {
|
|
|
12
175
|
}
|
|
13
176
|
declare function createServerClient(config: IQAuthTokenClientConfig): ServerIQAuthClient;
|
|
14
177
|
|
|
15
|
-
export { ExpressMiddlewareOptions, IQAuthClient, IQAuthTokenClientConfig, ServerIQAuthClient, createServerClient };
|
|
178
|
+
export { type DrizzleLinkAdapterDeps, ExpressMiddlewareOptions, IQAuthClient, IQAuthTokenClientConfig, type LinkAdapter, type LinkAdapterTx, type LinkCandidate, type LinkLocalUserOptions, type LinkLookupBy, type LinkResult, ServerIQAuthClient, createDrizzleLinkAdapter, createServerClient, linkLocalUserToIqAuthSub };
|