@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
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Catalog of all localizable strings used by the IQAuth SDK UI components and
|
|
3
|
+
* hosted auth pages. The shape is defined as a strict interface so missing
|
|
4
|
+
* keys in a contributed locale produce a TypeScript compile error.
|
|
5
|
+
*
|
|
6
|
+
* Strings may contain `{placeholder}` tokens that are interpolated at render
|
|
7
|
+
* time via `t(bundle, key, vars)`.
|
|
8
|
+
*/
|
|
9
|
+
interface IQAuthLocaleBundle {
|
|
10
|
+
/** BCP-47 tag for this bundle (e.g. "en-US", "fr-FR"). */
|
|
11
|
+
locale: string;
|
|
12
|
+
"common.loading": string;
|
|
13
|
+
"common.submitting": string;
|
|
14
|
+
"common.cancel": string;
|
|
15
|
+
"common.continue": string;
|
|
16
|
+
"common.back": string;
|
|
17
|
+
"common.close": string;
|
|
18
|
+
"common.save": string;
|
|
19
|
+
"common.saving": string;
|
|
20
|
+
"common.delete": string;
|
|
21
|
+
"common.confirm": string;
|
|
22
|
+
"common.email": string;
|
|
23
|
+
"common.password": string;
|
|
24
|
+
"common.name": string;
|
|
25
|
+
"common.or": string;
|
|
26
|
+
"common.required": string;
|
|
27
|
+
"common.optional": string;
|
|
28
|
+
"common.retry": string;
|
|
29
|
+
"signIn.title": string;
|
|
30
|
+
"signIn.subtitle": string;
|
|
31
|
+
"signIn.titleWithApp": string;
|
|
32
|
+
"signIn.emailLabel": string;
|
|
33
|
+
"signIn.emailPlaceholder": string;
|
|
34
|
+
"signIn.passwordLabel": string;
|
|
35
|
+
"signIn.passwordPlaceholder": string;
|
|
36
|
+
"signIn.submit": string;
|
|
37
|
+
"signIn.submitting": string;
|
|
38
|
+
"signIn.continueWithGoogle": string;
|
|
39
|
+
"signIn.continueWithMagicLink": string;
|
|
40
|
+
"signIn.continueWithPasskey": string;
|
|
41
|
+
"signIn.forgotPassword": string;
|
|
42
|
+
"signIn.noAccount": string;
|
|
43
|
+
"signIn.signUp": string;
|
|
44
|
+
"signIn.resumingSession": string;
|
|
45
|
+
"signIn.useDifferentAccount": string;
|
|
46
|
+
"signIn.selectTenant": string;
|
|
47
|
+
"signIn.selectTenantSubtitle": string;
|
|
48
|
+
"signIn.dividerOr": string;
|
|
49
|
+
"signIn.preparingExperience": string;
|
|
50
|
+
"signIn.applicationUnavailable": string;
|
|
51
|
+
"signIn.applicationNotFound": string;
|
|
52
|
+
"signIn.invalidRedirect": string;
|
|
53
|
+
"signIn.returnUrlNotRegistered": string;
|
|
54
|
+
"signIn.welcomeBackName": string;
|
|
55
|
+
"signIn.oneMomentResume": string;
|
|
56
|
+
"signIn.notYouUseDifferent": string;
|
|
57
|
+
"signIn.chooseWorkspace": string;
|
|
58
|
+
"signIn.pickTenantToSignIn": string;
|
|
59
|
+
"signIn.subtitleHosted": string;
|
|
60
|
+
"signIn.createAccount": string;
|
|
61
|
+
"signIn.couldntResume": string;
|
|
62
|
+
"signUp.title": string;
|
|
63
|
+
"signUp.subtitle": string;
|
|
64
|
+
"signUp.nameLabel": string;
|
|
65
|
+
"signUp.namePlaceholder": string;
|
|
66
|
+
"signUp.emailLabel": string;
|
|
67
|
+
"signUp.passwordLabel": string;
|
|
68
|
+
"signUp.passwordHint": string;
|
|
69
|
+
"signUp.submit": string;
|
|
70
|
+
"signUp.submitting": string;
|
|
71
|
+
"signUp.haveAccount": string;
|
|
72
|
+
"signUp.signIn": string;
|
|
73
|
+
"signUp.tenantNameLabel": string;
|
|
74
|
+
"signUp.tenantNamePlaceholder": string;
|
|
75
|
+
"signUp.legal": string;
|
|
76
|
+
"forgotPassword.title": string;
|
|
77
|
+
"forgotPassword.subtitle": string;
|
|
78
|
+
"forgotPassword.submit": string;
|
|
79
|
+
"forgotPassword.submitting": string;
|
|
80
|
+
"forgotPassword.sent": string;
|
|
81
|
+
"forgotPassword.backToSignIn": string;
|
|
82
|
+
"resetPassword.title": string;
|
|
83
|
+
"resetPassword.newPasswordLabel": string;
|
|
84
|
+
"resetPassword.confirmPasswordLabel": string;
|
|
85
|
+
"resetPassword.submit": string;
|
|
86
|
+
"resetPassword.submitting": string;
|
|
87
|
+
"resetPassword.success": string;
|
|
88
|
+
"resetPassword.mismatch": string;
|
|
89
|
+
"mfa.title": string;
|
|
90
|
+
"mfa.subtitle": string;
|
|
91
|
+
"mfa.totpLabel": string;
|
|
92
|
+
"mfa.totpPlaceholder": string;
|
|
93
|
+
"mfa.smsLabel": string;
|
|
94
|
+
"mfa.emailLabel": string;
|
|
95
|
+
"mfa.submit": string;
|
|
96
|
+
"mfa.submitting": string;
|
|
97
|
+
"mfa.useBackupCode": string;
|
|
98
|
+
"mfa.useAuthenticator": string;
|
|
99
|
+
"mfa.useSms": string;
|
|
100
|
+
"mfa.useEmail": string;
|
|
101
|
+
"mfa.resend": string;
|
|
102
|
+
"mfa.resent": string;
|
|
103
|
+
"mfa.backupCodeLabel": string;
|
|
104
|
+
"userButton.signedInAs": string;
|
|
105
|
+
"userButton.manageAccount": string;
|
|
106
|
+
"userButton.switchOrg": string;
|
|
107
|
+
"userButton.signOut": string;
|
|
108
|
+
"userProfile.title": string;
|
|
109
|
+
"userProfile.profileTab": string;
|
|
110
|
+
"userProfile.securityTab": string;
|
|
111
|
+
"userProfile.sessionsTab": string;
|
|
112
|
+
"userProfile.linkedAccountsTab": string;
|
|
113
|
+
"userProfile.changeName": string;
|
|
114
|
+
"userProfile.changeEmail": string;
|
|
115
|
+
"userProfile.changePassword": string;
|
|
116
|
+
"userProfile.currentPassword": string;
|
|
117
|
+
"userProfile.newPassword": string;
|
|
118
|
+
"userProfile.confirmPassword": string;
|
|
119
|
+
"userProfile.passwordUpdated": string;
|
|
120
|
+
"userProfile.mfaSection": string;
|
|
121
|
+
"userProfile.mfaEnable": string;
|
|
122
|
+
"userProfile.mfaDisable": string;
|
|
123
|
+
"userProfile.sessionsSection": string;
|
|
124
|
+
"userProfile.revokeSession": string;
|
|
125
|
+
"userProfile.revokeAllOthers": string;
|
|
126
|
+
"userProfile.thisDevice": string;
|
|
127
|
+
"userProfile.sessionsEmpty": string;
|
|
128
|
+
"userProfile.linkedAccountsEmpty": string;
|
|
129
|
+
"userProfile.connectGoogle": string;
|
|
130
|
+
"userProfile.disconnect": string;
|
|
131
|
+
"orgSwitcher.label": string;
|
|
132
|
+
"orgSwitcher.personal": string;
|
|
133
|
+
"orgSwitcher.createNew": string;
|
|
134
|
+
"orgSwitcher.manage": string;
|
|
135
|
+
"orgSwitcher.noOrgs": string;
|
|
136
|
+
"orgProfile.title": string;
|
|
137
|
+
"orgProfile.generalTab": string;
|
|
138
|
+
"orgProfile.membersTab": string;
|
|
139
|
+
"orgProfile.invitationsTab": string;
|
|
140
|
+
"orgProfile.dangerTab": string;
|
|
141
|
+
"orgProfile.invite": string;
|
|
142
|
+
"orgProfile.inviteEmailLabel": string;
|
|
143
|
+
"orgProfile.inviteRoleLabel": string;
|
|
144
|
+
"orgProfile.inviteSend": string;
|
|
145
|
+
"orgProfile.inviteSent": string;
|
|
146
|
+
"orgProfile.removeMember": string;
|
|
147
|
+
"orgProfile.deleteOrg": string;
|
|
148
|
+
"orgProfile.deleteOrgConfirm": string;
|
|
149
|
+
"createOrg.title": string;
|
|
150
|
+
"createOrg.nameLabel": string;
|
|
151
|
+
"createOrg.submit": string;
|
|
152
|
+
"createOrg.submitting": string;
|
|
153
|
+
"waitlist.title": string;
|
|
154
|
+
"waitlist.subtitle": string;
|
|
155
|
+
"waitlist.submit": string;
|
|
156
|
+
"waitlist.submitting": string;
|
|
157
|
+
"waitlist.success": string;
|
|
158
|
+
"impersonation.banner": string;
|
|
159
|
+
"impersonation.exit": string;
|
|
160
|
+
"magicLink.title": string;
|
|
161
|
+
"magicLink.subtitle": string;
|
|
162
|
+
"magicLink.resend": string;
|
|
163
|
+
"magicLink.changeEmail": string;
|
|
164
|
+
"errors.generic": string;
|
|
165
|
+
"errors.network": string;
|
|
166
|
+
"errors.invalidCredentials": string;
|
|
167
|
+
"errors.userNotFound": string;
|
|
168
|
+
"errors.emailInUse": string;
|
|
169
|
+
"errors.weakPassword": string;
|
|
170
|
+
"errors.mfaInvalid": string;
|
|
171
|
+
"errors.mfaExpired": string;
|
|
172
|
+
"errors.tooManyAttempts": string;
|
|
173
|
+
"errors.sessionExpired": string;
|
|
174
|
+
"errors.permissionDenied": string;
|
|
175
|
+
"errors.notFound": string;
|
|
176
|
+
"errors.serverError": string;
|
|
177
|
+
"errors.invalidEmail": string;
|
|
178
|
+
"errors.passwordTooShort": string;
|
|
179
|
+
"errors.required": string;
|
|
180
|
+
"errors.invitationInvalid": string;
|
|
181
|
+
"validation.emailRequired": string;
|
|
182
|
+
"validation.emailInvalid": string;
|
|
183
|
+
"validation.passwordRequired": string;
|
|
184
|
+
"validation.nameRequired": string;
|
|
185
|
+
"validation.codeRequired": string;
|
|
186
|
+
"validation.codeInvalid": string;
|
|
187
|
+
}
|
|
188
|
+
type IQAuthLocaleKey = keyof Omit<IQAuthLocaleBundle, "locale">;
|
|
189
|
+
/**
|
|
190
|
+
* A partial bundle (used for the `localization` prop on IQAuthProvider) that
|
|
191
|
+
* may override a subset of keys; missing keys fall back to the default
|
|
192
|
+
* (en-US) bundle.
|
|
193
|
+
*/
|
|
194
|
+
type IQAuthLocaleOverride = Partial<IQAuthLocaleBundle>;
|
|
195
|
+
|
|
196
|
+
export type { IQAuthLocaleBundle as I, IQAuthLocaleOverride as a, IQAuthLocaleKey as b };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @iqauth/sdk/webhooks — webhook signature verification.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors Stripe's `constructEvent` shape. Verifies the `X-IQAuth-Signature`
|
|
5
|
+
* header (format `t=<unix>,v1=<hex hmac sha256>`) emitted by IQAuth's
|
|
6
|
+
* webhook fan-out (src/services/webhook.service.ts). Constant-time compare;
|
|
7
|
+
* tolerance window in seconds (default 300).
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* import { verifyWebhookSignature } from "@iqauth/sdk/webhooks";
|
|
11
|
+
* app.post("/webhooks/iqauth", express.raw({ type: "application/json" }), (req, res) => {
|
|
12
|
+
* try {
|
|
13
|
+
* const evt = verifyWebhookSignature({
|
|
14
|
+
* payload: req.body, // Buffer or string of the RAW request body
|
|
15
|
+
* header: req.header("X-IQAuth-Signature"),
|
|
16
|
+
* secret: process.env.IQAUTH_WEBHOOK_SECRET!,
|
|
17
|
+
* });
|
|
18
|
+
* // evt is the parsed JSON event
|
|
19
|
+
* } catch (err) {
|
|
20
|
+
* return res.status(400).send(err.message);
|
|
21
|
+
* }
|
|
22
|
+
* });
|
|
23
|
+
*/
|
|
24
|
+
interface VerifyWebhookOptions {
|
|
25
|
+
/** Raw HTTP request body (Buffer, Uint8Array, or string). DO NOT use a
|
|
26
|
+
* pre-parsed JSON object — the signature is over the exact bytes. */
|
|
27
|
+
payload: Buffer | Uint8Array | string;
|
|
28
|
+
/** Value of the `X-IQAuth-Signature` header. */
|
|
29
|
+
header: string | string[] | null | undefined;
|
|
30
|
+
/** The endpoint signing secret returned when the endpoint was created. */
|
|
31
|
+
secret: string;
|
|
32
|
+
/** How many seconds old a signature may be (default 300). */
|
|
33
|
+
toleranceSeconds?: number;
|
|
34
|
+
/** Override the current time (seconds since epoch) — for tests. */
|
|
35
|
+
nowSeconds?: number;
|
|
36
|
+
}
|
|
37
|
+
interface IQAuthWebhookEvent {
|
|
38
|
+
id?: string;
|
|
39
|
+
type?: string;
|
|
40
|
+
tenantId?: string;
|
|
41
|
+
data?: Record<string, unknown>;
|
|
42
|
+
createdAt?: number | string;
|
|
43
|
+
[key: string]: unknown;
|
|
44
|
+
}
|
|
45
|
+
declare class WebhookSignatureError extends Error {
|
|
46
|
+
code: string;
|
|
47
|
+
constructor(code: string, message: string);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Verify the signature on a webhook payload and return the parsed event.
|
|
51
|
+
* Throws `WebhookSignatureError` on any verification failure.
|
|
52
|
+
*/
|
|
53
|
+
declare function verifyWebhookSignature(opts: VerifyWebhookOptions): IQAuthWebhookEvent;
|
|
54
|
+
/**
|
|
55
|
+
* Boolean variant — never throws; returns `true` only if the signature is
|
|
56
|
+
* valid and the timestamp is within tolerance. Useful when you want to
|
|
57
|
+
* branch on validity rather than catch.
|
|
58
|
+
*/
|
|
59
|
+
declare function isValidWebhookSignature(opts: VerifyWebhookOptions): boolean;
|
|
60
|
+
|
|
61
|
+
export { type IQAuthWebhookEvent, type VerifyWebhookOptions, WebhookSignatureError, isValidWebhookSignature, verifyWebhookSignature };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @iqauth/sdk/webhooks — webhook signature verification.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors Stripe's `constructEvent` shape. Verifies the `X-IQAuth-Signature`
|
|
5
|
+
* header (format `t=<unix>,v1=<hex hmac sha256>`) emitted by IQAuth's
|
|
6
|
+
* webhook fan-out (src/services/webhook.service.ts). Constant-time compare;
|
|
7
|
+
* tolerance window in seconds (default 300).
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* import { verifyWebhookSignature } from "@iqauth/sdk/webhooks";
|
|
11
|
+
* app.post("/webhooks/iqauth", express.raw({ type: "application/json" }), (req, res) => {
|
|
12
|
+
* try {
|
|
13
|
+
* const evt = verifyWebhookSignature({
|
|
14
|
+
* payload: req.body, // Buffer or string of the RAW request body
|
|
15
|
+
* header: req.header("X-IQAuth-Signature"),
|
|
16
|
+
* secret: process.env.IQAUTH_WEBHOOK_SECRET!,
|
|
17
|
+
* });
|
|
18
|
+
* // evt is the parsed JSON event
|
|
19
|
+
* } catch (err) {
|
|
20
|
+
* return res.status(400).send(err.message);
|
|
21
|
+
* }
|
|
22
|
+
* });
|
|
23
|
+
*/
|
|
24
|
+
interface VerifyWebhookOptions {
|
|
25
|
+
/** Raw HTTP request body (Buffer, Uint8Array, or string). DO NOT use a
|
|
26
|
+
* pre-parsed JSON object — the signature is over the exact bytes. */
|
|
27
|
+
payload: Buffer | Uint8Array | string;
|
|
28
|
+
/** Value of the `X-IQAuth-Signature` header. */
|
|
29
|
+
header: string | string[] | null | undefined;
|
|
30
|
+
/** The endpoint signing secret returned when the endpoint was created. */
|
|
31
|
+
secret: string;
|
|
32
|
+
/** How many seconds old a signature may be (default 300). */
|
|
33
|
+
toleranceSeconds?: number;
|
|
34
|
+
/** Override the current time (seconds since epoch) — for tests. */
|
|
35
|
+
nowSeconds?: number;
|
|
36
|
+
}
|
|
37
|
+
interface IQAuthWebhookEvent {
|
|
38
|
+
id?: string;
|
|
39
|
+
type?: string;
|
|
40
|
+
tenantId?: string;
|
|
41
|
+
data?: Record<string, unknown>;
|
|
42
|
+
createdAt?: number | string;
|
|
43
|
+
[key: string]: unknown;
|
|
44
|
+
}
|
|
45
|
+
declare class WebhookSignatureError extends Error {
|
|
46
|
+
code: string;
|
|
47
|
+
constructor(code: string, message: string);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Verify the signature on a webhook payload and return the parsed event.
|
|
51
|
+
* Throws `WebhookSignatureError` on any verification failure.
|
|
52
|
+
*/
|
|
53
|
+
declare function verifyWebhookSignature(opts: VerifyWebhookOptions): IQAuthWebhookEvent;
|
|
54
|
+
/**
|
|
55
|
+
* Boolean variant — never throws; returns `true` only if the signature is
|
|
56
|
+
* valid and the timestamp is within tolerance. Useful when you want to
|
|
57
|
+
* branch on validity rather than catch.
|
|
58
|
+
*/
|
|
59
|
+
declare function isValidWebhookSignature(opts: VerifyWebhookOptions): boolean;
|
|
60
|
+
|
|
61
|
+
export { type IQAuthWebhookEvent, type VerifyWebhookOptions, WebhookSignatureError, isValidWebhookSignature, verifyWebhookSignature };
|
package/dist/webhooks.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/webhooks.ts
|
|
31
|
+
var webhooks_exports = {};
|
|
32
|
+
__export(webhooks_exports, {
|
|
33
|
+
WebhookSignatureError: () => WebhookSignatureError,
|
|
34
|
+
isValidWebhookSignature: () => isValidWebhookSignature,
|
|
35
|
+
verifyWebhookSignature: () => verifyWebhookSignature
|
|
36
|
+
});
|
|
37
|
+
module.exports = __toCommonJS(webhooks_exports);
|
|
38
|
+
var import_crypto = __toESM(require("crypto"));
|
|
39
|
+
var WebhookSignatureError = class extends Error {
|
|
40
|
+
constructor(code, message) {
|
|
41
|
+
super(message);
|
|
42
|
+
this.name = "WebhookSignatureError";
|
|
43
|
+
this.code = code;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
function toBuffer(p) {
|
|
47
|
+
if (typeof p === "string") return Buffer.from(p, "utf8");
|
|
48
|
+
if (Buffer.isBuffer(p)) return p;
|
|
49
|
+
return Buffer.from(p);
|
|
50
|
+
}
|
|
51
|
+
function parseHeader(header) {
|
|
52
|
+
let t = NaN;
|
|
53
|
+
const v1 = [];
|
|
54
|
+
for (const part of header.split(",")) {
|
|
55
|
+
const [k, v] = part.split("=", 2);
|
|
56
|
+
if (!k || v === void 0) continue;
|
|
57
|
+
const key = k.trim();
|
|
58
|
+
const value = v.trim();
|
|
59
|
+
if (key === "t") t = Number(value);
|
|
60
|
+
else if (key === "v1") v1.push(value);
|
|
61
|
+
}
|
|
62
|
+
return { t, v1 };
|
|
63
|
+
}
|
|
64
|
+
function timingSafeEqualHex(a, b) {
|
|
65
|
+
if (a.length !== b.length) return false;
|
|
66
|
+
try {
|
|
67
|
+
return import_crypto.default.timingSafeEqual(Buffer.from(a, "hex"), Buffer.from(b, "hex"));
|
|
68
|
+
} catch {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function verifyWebhookSignature(opts) {
|
|
73
|
+
const headerRaw = Array.isArray(opts.header) ? opts.header[0] : opts.header;
|
|
74
|
+
if (!headerRaw || typeof headerRaw !== "string") {
|
|
75
|
+
throw new WebhookSignatureError("MISSING_HEADER", "Missing X-IQAuth-Signature header");
|
|
76
|
+
}
|
|
77
|
+
if (!opts.secret) {
|
|
78
|
+
throw new WebhookSignatureError("MISSING_SECRET", "secret is required");
|
|
79
|
+
}
|
|
80
|
+
const { t, v1 } = parseHeader(headerRaw);
|
|
81
|
+
if (!Number.isFinite(t) || v1.length === 0) {
|
|
82
|
+
throw new WebhookSignatureError("MALFORMED_HEADER", `Could not parse signature header: ${headerRaw}`);
|
|
83
|
+
}
|
|
84
|
+
const tolerance = opts.toleranceSeconds ?? 300;
|
|
85
|
+
const now = opts.nowSeconds ?? Math.floor(Date.now() / 1e3);
|
|
86
|
+
if (Math.abs(now - t) > tolerance) {
|
|
87
|
+
throw new WebhookSignatureError(
|
|
88
|
+
"TIMESTAMP_OUT_OF_TOLERANCE",
|
|
89
|
+
`Signature timestamp ${t} is outside the ${tolerance}s tolerance window (now=${now})`
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
const body = toBuffer(opts.payload);
|
|
93
|
+
const expected = import_crypto.default.createHmac("sha256", opts.secret).update(`${t}.`).update(body).digest("hex");
|
|
94
|
+
const matched = v1.some((sig) => timingSafeEqualHex(sig, expected));
|
|
95
|
+
if (!matched) {
|
|
96
|
+
throw new WebhookSignatureError("SIGNATURE_MISMATCH", "Webhook signature does not match expected value");
|
|
97
|
+
}
|
|
98
|
+
let parsed;
|
|
99
|
+
try {
|
|
100
|
+
parsed = JSON.parse(body.toString("utf8"));
|
|
101
|
+
} catch {
|
|
102
|
+
throw new WebhookSignatureError("MALFORMED_BODY", "Webhook body is not valid JSON");
|
|
103
|
+
}
|
|
104
|
+
return parsed;
|
|
105
|
+
}
|
|
106
|
+
function isValidWebhookSignature(opts) {
|
|
107
|
+
try {
|
|
108
|
+
verifyWebhookSignature(opts);
|
|
109
|
+
return true;
|
|
110
|
+
} catch {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
115
|
+
0 && (module.exports = {
|
|
116
|
+
WebhookSignatureError,
|
|
117
|
+
isValidWebhookSignature,
|
|
118
|
+
verifyWebhookSignature
|
|
119
|
+
});
|
package/dist/ws.d.mts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { c as TokenVerifyOptions } from './tokens-DCyzzn8L.mjs';
|
|
2
|
+
import { J as JwtClaims } from './types-DZAflmmq.mjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @iqauth/sdk/ws — WebSocket upgrade auth helper.
|
|
6
|
+
*
|
|
7
|
+
* Realtime apps don't fit the HTTP framework-adapter shape: a Node WS server
|
|
8
|
+
* sees the original `IncomingMessage` on `upgrade` and must decide accept-or-
|
|
9
|
+
* reject before swapping protocols. This helper takes the same options shape
|
|
10
|
+
* as the framework middlewares (`publishableKey`, `audience`, `issuer`,
|
|
11
|
+
* `clockTolerance`, cookie name) and verifies a token from one of:
|
|
12
|
+
*
|
|
13
|
+
* 1. `Authorization: Bearer <jwt>` header on the upgrade request.
|
|
14
|
+
* 2. The configured access cookie (default `iqauth_at`) on the `Cookie`
|
|
15
|
+
* header.
|
|
16
|
+
* 3. The `Sec-WebSocket-Protocol` subprotocol header. Browser `WebSocket`
|
|
17
|
+
* can't set custom headers, so the convention is to publish the token
|
|
18
|
+
* as a subprotocol value alongside the real subprotocol — e.g.
|
|
19
|
+
* `new WebSocket(url, ["iqauth.bearer.<jwt>", "graphql-transport-ws"])`.
|
|
20
|
+
* Any value matching `iqauth.bearer.<jwt>` (or a bare JWT-shaped value)
|
|
21
|
+
* is treated as the bearer.
|
|
22
|
+
*
|
|
23
|
+
* Returns `{ claims }` on success, `null` on missing/invalid/expired token.
|
|
24
|
+
* Throws only on programmer error (no publishable key, etc).
|
|
25
|
+
*
|
|
26
|
+
* import { verifyWsUpgrade } from "@iqauth/sdk/ws";
|
|
27
|
+
* wss.on("upgrade", async (req, socket, head) => {
|
|
28
|
+
* const result = await verifyWsUpgrade(req, { publishableKey });
|
|
29
|
+
* if (!result) { socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n"); socket.destroy(); return; }
|
|
30
|
+
* wss.handleUpgrade(req, socket, head, ws => wss.emit("connection", ws, req, result.claims));
|
|
31
|
+
* });
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
interface VerifyWsUpgradeOptions extends TokenVerifyOptions {
|
|
35
|
+
/** A `pk_test_…` / `pk_live_…` publishable key. Issuer + JWKS auto-discovered. */
|
|
36
|
+
publishableKey: string;
|
|
37
|
+
/**
|
|
38
|
+
* Cookie name carrying an httpOnly access token. Defaults to `iqauth_at`.
|
|
39
|
+
* Set to `null` to disable cookie-based extraction.
|
|
40
|
+
*/
|
|
41
|
+
cookieName?: string | null;
|
|
42
|
+
/**
|
|
43
|
+
* Subprotocol prefix recognized as a bearer token in
|
|
44
|
+
* `Sec-WebSocket-Protocol`. Defaults to `iqauth.bearer.`. Set to `null`
|
|
45
|
+
* to disable subprotocol-based extraction.
|
|
46
|
+
*/
|
|
47
|
+
subprotocolPrefix?: string | null;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The minimum shape we read off a Node `IncomingMessage`. Accepting just
|
|
51
|
+
* `{ headers }` keeps the helper testable and framework-agnostic.
|
|
52
|
+
*/
|
|
53
|
+
interface WsUpgradeRequestLike {
|
|
54
|
+
headers: Record<string, string | string[] | undefined>;
|
|
55
|
+
}
|
|
56
|
+
interface VerifyWsUpgradeResult {
|
|
57
|
+
claims: JwtClaims;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Verify a WebSocket upgrade request. Returns `{ claims }` on success or
|
|
61
|
+
* `null` when the request lacks a token, the token is malformed, expired,
|
|
62
|
+
* or fails verification. Throws only when `options.publishableKey` is
|
|
63
|
+
* missing or malformed (programmer error).
|
|
64
|
+
*/
|
|
65
|
+
declare function verifyWsUpgrade(req: WsUpgradeRequestLike | {
|
|
66
|
+
cookie?: string;
|
|
67
|
+
secWebSocketProtocol?: string;
|
|
68
|
+
authorization?: string;
|
|
69
|
+
}, options: VerifyWsUpgradeOptions): Promise<VerifyWsUpgradeResult | null>;
|
|
70
|
+
/** @internal Test helper — clear the per-issuer TokensModule cache. */
|
|
71
|
+
declare function _resetWsVerifierCache(): void;
|
|
72
|
+
|
|
73
|
+
export { type VerifyWsUpgradeOptions, type VerifyWsUpgradeResult, type WsUpgradeRequestLike, _resetWsVerifierCache, verifyWsUpgrade };
|
package/dist/ws.d.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { c as TokenVerifyOptions } from './tokens-aHiGFr_E.js';
|
|
2
|
+
import { J as JwtClaims } from './types-DZAflmmq.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @iqauth/sdk/ws — WebSocket upgrade auth helper.
|
|
6
|
+
*
|
|
7
|
+
* Realtime apps don't fit the HTTP framework-adapter shape: a Node WS server
|
|
8
|
+
* sees the original `IncomingMessage` on `upgrade` and must decide accept-or-
|
|
9
|
+
* reject before swapping protocols. This helper takes the same options shape
|
|
10
|
+
* as the framework middlewares (`publishableKey`, `audience`, `issuer`,
|
|
11
|
+
* `clockTolerance`, cookie name) and verifies a token from one of:
|
|
12
|
+
*
|
|
13
|
+
* 1. `Authorization: Bearer <jwt>` header on the upgrade request.
|
|
14
|
+
* 2. The configured access cookie (default `iqauth_at`) on the `Cookie`
|
|
15
|
+
* header.
|
|
16
|
+
* 3. The `Sec-WebSocket-Protocol` subprotocol header. Browser `WebSocket`
|
|
17
|
+
* can't set custom headers, so the convention is to publish the token
|
|
18
|
+
* as a subprotocol value alongside the real subprotocol — e.g.
|
|
19
|
+
* `new WebSocket(url, ["iqauth.bearer.<jwt>", "graphql-transport-ws"])`.
|
|
20
|
+
* Any value matching `iqauth.bearer.<jwt>` (or a bare JWT-shaped value)
|
|
21
|
+
* is treated as the bearer.
|
|
22
|
+
*
|
|
23
|
+
* Returns `{ claims }` on success, `null` on missing/invalid/expired token.
|
|
24
|
+
* Throws only on programmer error (no publishable key, etc).
|
|
25
|
+
*
|
|
26
|
+
* import { verifyWsUpgrade } from "@iqauth/sdk/ws";
|
|
27
|
+
* wss.on("upgrade", async (req, socket, head) => {
|
|
28
|
+
* const result = await verifyWsUpgrade(req, { publishableKey });
|
|
29
|
+
* if (!result) { socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n"); socket.destroy(); return; }
|
|
30
|
+
* wss.handleUpgrade(req, socket, head, ws => wss.emit("connection", ws, req, result.claims));
|
|
31
|
+
* });
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
interface VerifyWsUpgradeOptions extends TokenVerifyOptions {
|
|
35
|
+
/** A `pk_test_…` / `pk_live_…` publishable key. Issuer + JWKS auto-discovered. */
|
|
36
|
+
publishableKey: string;
|
|
37
|
+
/**
|
|
38
|
+
* Cookie name carrying an httpOnly access token. Defaults to `iqauth_at`.
|
|
39
|
+
* Set to `null` to disable cookie-based extraction.
|
|
40
|
+
*/
|
|
41
|
+
cookieName?: string | null;
|
|
42
|
+
/**
|
|
43
|
+
* Subprotocol prefix recognized as a bearer token in
|
|
44
|
+
* `Sec-WebSocket-Protocol`. Defaults to `iqauth.bearer.`. Set to `null`
|
|
45
|
+
* to disable subprotocol-based extraction.
|
|
46
|
+
*/
|
|
47
|
+
subprotocolPrefix?: string | null;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The minimum shape we read off a Node `IncomingMessage`. Accepting just
|
|
51
|
+
* `{ headers }` keeps the helper testable and framework-agnostic.
|
|
52
|
+
*/
|
|
53
|
+
interface WsUpgradeRequestLike {
|
|
54
|
+
headers: Record<string, string | string[] | undefined>;
|
|
55
|
+
}
|
|
56
|
+
interface VerifyWsUpgradeResult {
|
|
57
|
+
claims: JwtClaims;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Verify a WebSocket upgrade request. Returns `{ claims }` on success or
|
|
61
|
+
* `null` when the request lacks a token, the token is malformed, expired,
|
|
62
|
+
* or fails verification. Throws only when `options.publishableKey` is
|
|
63
|
+
* missing or malformed (programmer error).
|
|
64
|
+
*/
|
|
65
|
+
declare function verifyWsUpgrade(req: WsUpgradeRequestLike | {
|
|
66
|
+
cookie?: string;
|
|
67
|
+
secWebSocketProtocol?: string;
|
|
68
|
+
authorization?: string;
|
|
69
|
+
}, options: VerifyWsUpgradeOptions): Promise<VerifyWsUpgradeResult | null>;
|
|
70
|
+
/** @internal Test helper — clear the per-issuer TokensModule cache. */
|
|
71
|
+
declare function _resetWsVerifierCache(): void;
|
|
72
|
+
|
|
73
|
+
export { type VerifyWsUpgradeOptions, type VerifyWsUpgradeResult, type WsUpgradeRequestLike, _resetWsVerifierCache, verifyWsUpgrade };
|