@iqauth/sdk 2.3.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 +110 -0
- package/dist/browser-session.d.mts +3 -2
- package/dist/browser-session.d.ts +3 -2
- package/dist/browser.d.mts +64 -29
- package/dist/browser.d.ts +64 -29
- package/dist/browser.js +782 -38
- package/dist/browser.mjs +43 -3
- package/dist/bundle-LUKDQYVQ.mjs +374 -0
- package/dist/chunk-3JULWS6F.mjs +106 -0
- package/dist/chunk-5T7GHBX6.mjs +1165 -0
- package/dist/{chunk-KGEPDXHU.mjs → chunk-6TDJJER7.mjs} +2 -2
- package/dist/{chunk-RACIPVLD.mjs → chunk-76W5TLQQ.mjs} +262 -220
- package/dist/{chunk-EKTNEZIH.mjs → chunk-BVV54LPI.mjs} +37 -5
- 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/cli/index.js +144 -36
- package/dist/cli/index.mjs +1 -1
- package/dist/{client-DTX4hNdS.d.ts → client-BNQe3AgF.d.ts} +3 -62
- package/dist/{client-vdh2a9fJ.d.mts → client-kYlJFgPv.d.mts} +3 -62
- package/dist/doctor-YYNHNMLD.mjs +198 -0
- package/dist/{express-A0-dWEMy.d.mts → express-B6_1vBYZ.d.mts} +23 -2
- package/dist/{express-Bo_pJKHN.d.ts → express-CHpfa7D_.d.ts} +23 -2
- package/dist/express.d.mts +5 -4
- package/dist/express.d.ts +5 -4
- package/dist/express.js +36 -4
- package/dist/express.mjs +8 -8
- package/dist/fastify.js +2 -2
- package/dist/fastify.mjs +4 -4
- package/dist/hono.js +2 -2
- package/dist/hono.mjs +4 -4
- package/dist/index.d.mts +8 -3
- package/dist/index.d.ts +8 -3
- package/dist/index.js +500 -4
- package/dist/index.mjs +29 -9
- 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 -2
- package/dist/mobile.d.ts +3 -2
- package/dist/next.d.mts +1 -1
- package/dist/next.d.ts +1 -1
- package/dist/next.js +2 -2
- package/dist/next.mjs +1 -1
- 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 +2985 -567
- package/dist/react.mjs +1517 -94
- package/dist/reverify-4UEJXUS6.mjs +16 -0
- package/dist/server/handlers.d.mts +10 -1
- package/dist/server/handlers.d.ts +10 -1
- package/dist/server/handlers.js +2 -2
- package/dist/server/handlers.mjs +1 -1
- package/dist/server.d.mts +5 -3
- package/dist/server.d.ts +5 -3
- package/dist/server.js +89 -4
- package/dist/server.mjs +12 -8
- package/dist/service.d.mts +3 -2
- package/dist/service.d.ts +3 -2
- package/dist/signIn-CCY4JE5G.mjs +15 -0
- package/dist/{signIn-Cd0P4y9d.d.mts → signIn-CiIBTJIh.d.mts} +224 -4
- package/dist/{signIn-DKakyzeu.d.ts → signIn-OCr88Zf8.d.ts} +224 -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.mts → types-DZAflmmq.d.mts} +6 -0
- package/dist/{types-Cxl3bQHt.d.ts → 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 +22 -2
- package/dist/doctor-A5E7LSFW.mjs +0 -90
package/dist/browser.js
CHANGED
|
@@ -3,6 +3,9 @@ var __defProp = Object.defineProperty;
|
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __esm = (fn, res) => function __init() {
|
|
7
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
|
+
};
|
|
6
9
|
var __export = (target, all) => {
|
|
7
10
|
for (var name in all)
|
|
8
11
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -17,28 +20,426 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
20
|
};
|
|
18
21
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
22
|
|
|
23
|
+
// ../../node_modules/@simplewebauthn/browser/dist/bundle/index.js
|
|
24
|
+
var bundle_exports = {};
|
|
25
|
+
__export(bundle_exports, {
|
|
26
|
+
WebAuthnAbortService: () => WebAuthnAbortService,
|
|
27
|
+
WebAuthnError: () => WebAuthnError,
|
|
28
|
+
base64URLStringToBuffer: () => base64URLStringToBuffer,
|
|
29
|
+
browserSupportsWebAuthn: () => browserSupportsWebAuthn,
|
|
30
|
+
browserSupportsWebAuthnAutofill: () => browserSupportsWebAuthnAutofill,
|
|
31
|
+
bufferToBase64URLString: () => bufferToBase64URLString,
|
|
32
|
+
platformAuthenticatorIsAvailable: () => platformAuthenticatorIsAvailable,
|
|
33
|
+
startAuthentication: () => startAuthentication,
|
|
34
|
+
startRegistration: () => startRegistration
|
|
35
|
+
});
|
|
36
|
+
function bufferToBase64URLString(buffer) {
|
|
37
|
+
const bytes = new Uint8Array(buffer);
|
|
38
|
+
let str = "";
|
|
39
|
+
for (const charCode of bytes) {
|
|
40
|
+
str += String.fromCharCode(charCode);
|
|
41
|
+
}
|
|
42
|
+
const base64String = btoa(str);
|
|
43
|
+
return base64String.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
44
|
+
}
|
|
45
|
+
function base64URLStringToBuffer(base64URLString) {
|
|
46
|
+
const base64 = base64URLString.replace(/-/g, "+").replace(/_/g, "/");
|
|
47
|
+
const padLength = (4 - base64.length % 4) % 4;
|
|
48
|
+
const padded = base64.padEnd(base64.length + padLength, "=");
|
|
49
|
+
const binary = atob(padded);
|
|
50
|
+
const buffer = new ArrayBuffer(binary.length);
|
|
51
|
+
const bytes = new Uint8Array(buffer);
|
|
52
|
+
for (let i = 0; i < binary.length; i++) {
|
|
53
|
+
bytes[i] = binary.charCodeAt(i);
|
|
54
|
+
}
|
|
55
|
+
return buffer;
|
|
56
|
+
}
|
|
57
|
+
function browserSupportsWebAuthn() {
|
|
58
|
+
return window?.PublicKeyCredential !== void 0 && typeof window.PublicKeyCredential === "function";
|
|
59
|
+
}
|
|
60
|
+
function toPublicKeyCredentialDescriptor(descriptor) {
|
|
61
|
+
const { id } = descriptor;
|
|
62
|
+
return {
|
|
63
|
+
...descriptor,
|
|
64
|
+
id: base64URLStringToBuffer(id),
|
|
65
|
+
transports: descriptor.transports
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function isValidDomain(hostname) {
|
|
69
|
+
return hostname === "localhost" || /^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/i.test(hostname);
|
|
70
|
+
}
|
|
71
|
+
function identifyRegistrationError({ error, options }) {
|
|
72
|
+
const { publicKey } = options;
|
|
73
|
+
if (!publicKey) {
|
|
74
|
+
throw Error("options was missing required publicKey property");
|
|
75
|
+
}
|
|
76
|
+
if (error.name === "AbortError") {
|
|
77
|
+
if (options.signal instanceof AbortSignal) {
|
|
78
|
+
return new WebAuthnError({
|
|
79
|
+
message: "Registration ceremony was sent an abort signal",
|
|
80
|
+
code: "ERROR_CEREMONY_ABORTED",
|
|
81
|
+
cause: error
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
} else if (error.name === "ConstraintError") {
|
|
85
|
+
if (publicKey.authenticatorSelection?.requireResidentKey === true) {
|
|
86
|
+
return new WebAuthnError({
|
|
87
|
+
message: "Discoverable credentials were required but no available authenticator supported it",
|
|
88
|
+
code: "ERROR_AUTHENTICATOR_MISSING_DISCOVERABLE_CREDENTIAL_SUPPORT",
|
|
89
|
+
cause: error
|
|
90
|
+
});
|
|
91
|
+
} else if (options.mediation === "conditional" && publicKey.authenticatorSelection?.userVerification === "required") {
|
|
92
|
+
return new WebAuthnError({
|
|
93
|
+
message: "User verification was required during automatic registration but it could not be performed",
|
|
94
|
+
code: "ERROR_AUTO_REGISTER_USER_VERIFICATION_FAILURE",
|
|
95
|
+
cause: error
|
|
96
|
+
});
|
|
97
|
+
} else if (publicKey.authenticatorSelection?.userVerification === "required") {
|
|
98
|
+
return new WebAuthnError({
|
|
99
|
+
message: "User verification was required but no available authenticator supported it",
|
|
100
|
+
code: "ERROR_AUTHENTICATOR_MISSING_USER_VERIFICATION_SUPPORT",
|
|
101
|
+
cause: error
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
} else if (error.name === "InvalidStateError") {
|
|
105
|
+
return new WebAuthnError({
|
|
106
|
+
message: "The authenticator was previously registered",
|
|
107
|
+
code: "ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED",
|
|
108
|
+
cause: error
|
|
109
|
+
});
|
|
110
|
+
} else if (error.name === "NotAllowedError") {
|
|
111
|
+
return new WebAuthnError({
|
|
112
|
+
message: error.message,
|
|
113
|
+
code: "ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",
|
|
114
|
+
cause: error
|
|
115
|
+
});
|
|
116
|
+
} else if (error.name === "NotSupportedError") {
|
|
117
|
+
const validPubKeyCredParams = publicKey.pubKeyCredParams.filter((param) => param.type === "public-key");
|
|
118
|
+
if (validPubKeyCredParams.length === 0) {
|
|
119
|
+
return new WebAuthnError({
|
|
120
|
+
message: 'No entry in pubKeyCredParams was of type "public-key"',
|
|
121
|
+
code: "ERROR_MALFORMED_PUBKEYCREDPARAMS",
|
|
122
|
+
cause: error
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
return new WebAuthnError({
|
|
126
|
+
message: "No available authenticator supported any of the specified pubKeyCredParams algorithms",
|
|
127
|
+
code: "ERROR_AUTHENTICATOR_NO_SUPPORTED_PUBKEYCREDPARAMS_ALG",
|
|
128
|
+
cause: error
|
|
129
|
+
});
|
|
130
|
+
} else if (error.name === "SecurityError") {
|
|
131
|
+
const effectiveDomain = window.location.hostname;
|
|
132
|
+
if (!isValidDomain(effectiveDomain)) {
|
|
133
|
+
return new WebAuthnError({
|
|
134
|
+
message: `${window.location.hostname} is an invalid domain`,
|
|
135
|
+
code: "ERROR_INVALID_DOMAIN",
|
|
136
|
+
cause: error
|
|
137
|
+
});
|
|
138
|
+
} else if (publicKey.rp.id !== effectiveDomain) {
|
|
139
|
+
return new WebAuthnError({
|
|
140
|
+
message: `The RP ID "${publicKey.rp.id}" is invalid for this domain`,
|
|
141
|
+
code: "ERROR_INVALID_RP_ID",
|
|
142
|
+
cause: error
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
} else if (error.name === "TypeError") {
|
|
146
|
+
if (publicKey.user.id.byteLength < 1 || publicKey.user.id.byteLength > 64) {
|
|
147
|
+
return new WebAuthnError({
|
|
148
|
+
message: "User ID was not between 1 and 64 characters",
|
|
149
|
+
code: "ERROR_INVALID_USER_ID_LENGTH",
|
|
150
|
+
cause: error
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
} else if (error.name === "UnknownError") {
|
|
154
|
+
return new WebAuthnError({
|
|
155
|
+
message: "The authenticator was unable to process the specified options, or could not create a new credential",
|
|
156
|
+
code: "ERROR_AUTHENTICATOR_GENERAL_ERROR",
|
|
157
|
+
cause: error
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
return error;
|
|
161
|
+
}
|
|
162
|
+
function toAuthenticatorAttachment(attachment) {
|
|
163
|
+
if (!attachment) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (attachments.indexOf(attachment) < 0) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
return attachment;
|
|
170
|
+
}
|
|
171
|
+
async function startRegistration(options) {
|
|
172
|
+
const { optionsJSON, useAutoRegister = false } = options;
|
|
173
|
+
if (!browserSupportsWebAuthn()) {
|
|
174
|
+
throw new Error("WebAuthn is not supported in this browser");
|
|
175
|
+
}
|
|
176
|
+
const publicKey = {
|
|
177
|
+
...optionsJSON,
|
|
178
|
+
challenge: base64URLStringToBuffer(optionsJSON.challenge),
|
|
179
|
+
user: {
|
|
180
|
+
...optionsJSON.user,
|
|
181
|
+
id: base64URLStringToBuffer(optionsJSON.user.id)
|
|
182
|
+
},
|
|
183
|
+
excludeCredentials: optionsJSON.excludeCredentials?.map(toPublicKeyCredentialDescriptor)
|
|
184
|
+
};
|
|
185
|
+
const createOptions = {};
|
|
186
|
+
if (useAutoRegister) {
|
|
187
|
+
createOptions.mediation = "conditional";
|
|
188
|
+
}
|
|
189
|
+
createOptions.publicKey = publicKey;
|
|
190
|
+
createOptions.signal = WebAuthnAbortService.createNewAbortSignal();
|
|
191
|
+
let credential;
|
|
192
|
+
try {
|
|
193
|
+
credential = await navigator.credentials.create(createOptions);
|
|
194
|
+
} catch (err) {
|
|
195
|
+
throw identifyRegistrationError({ error: err, options: createOptions });
|
|
196
|
+
}
|
|
197
|
+
if (!credential) {
|
|
198
|
+
throw new Error("Registration was not completed");
|
|
199
|
+
}
|
|
200
|
+
const { id, rawId, response, type } = credential;
|
|
201
|
+
let transports = void 0;
|
|
202
|
+
if (typeof response.getTransports === "function") {
|
|
203
|
+
transports = response.getTransports();
|
|
204
|
+
}
|
|
205
|
+
let responsePublicKeyAlgorithm = void 0;
|
|
206
|
+
if (typeof response.getPublicKeyAlgorithm === "function") {
|
|
207
|
+
try {
|
|
208
|
+
responsePublicKeyAlgorithm = response.getPublicKeyAlgorithm();
|
|
209
|
+
} catch (error) {
|
|
210
|
+
warnOnBrokenImplementation("getPublicKeyAlgorithm()", error);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
let responsePublicKey = void 0;
|
|
214
|
+
if (typeof response.getPublicKey === "function") {
|
|
215
|
+
try {
|
|
216
|
+
const _publicKey = response.getPublicKey();
|
|
217
|
+
if (_publicKey !== null) {
|
|
218
|
+
responsePublicKey = bufferToBase64URLString(_publicKey);
|
|
219
|
+
}
|
|
220
|
+
} catch (error) {
|
|
221
|
+
warnOnBrokenImplementation("getPublicKey()", error);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
let responseAuthenticatorData;
|
|
225
|
+
if (typeof response.getAuthenticatorData === "function") {
|
|
226
|
+
try {
|
|
227
|
+
responseAuthenticatorData = bufferToBase64URLString(response.getAuthenticatorData());
|
|
228
|
+
} catch (error) {
|
|
229
|
+
warnOnBrokenImplementation("getAuthenticatorData()", error);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
id,
|
|
234
|
+
rawId: bufferToBase64URLString(rawId),
|
|
235
|
+
response: {
|
|
236
|
+
attestationObject: bufferToBase64URLString(response.attestationObject),
|
|
237
|
+
clientDataJSON: bufferToBase64URLString(response.clientDataJSON),
|
|
238
|
+
transports,
|
|
239
|
+
publicKeyAlgorithm: responsePublicKeyAlgorithm,
|
|
240
|
+
publicKey: responsePublicKey,
|
|
241
|
+
authenticatorData: responseAuthenticatorData
|
|
242
|
+
},
|
|
243
|
+
type,
|
|
244
|
+
clientExtensionResults: credential.getClientExtensionResults(),
|
|
245
|
+
authenticatorAttachment: toAuthenticatorAttachment(credential.authenticatorAttachment)
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
function warnOnBrokenImplementation(methodName, cause) {
|
|
249
|
+
console.warn(`The browser extension that intercepted this WebAuthn API call incorrectly implemented ${methodName}. You should report this error to them.
|
|
250
|
+
`, cause);
|
|
251
|
+
}
|
|
252
|
+
function browserSupportsWebAuthnAutofill() {
|
|
253
|
+
if (!browserSupportsWebAuthn()) {
|
|
254
|
+
return new Promise((resolve) => resolve(false));
|
|
255
|
+
}
|
|
256
|
+
const globalPublicKeyCredential = window.PublicKeyCredential;
|
|
257
|
+
if (globalPublicKeyCredential.isConditionalMediationAvailable === void 0) {
|
|
258
|
+
return new Promise((resolve) => resolve(false));
|
|
259
|
+
}
|
|
260
|
+
return globalPublicKeyCredential.isConditionalMediationAvailable();
|
|
261
|
+
}
|
|
262
|
+
function identifyAuthenticationError({ error, options }) {
|
|
263
|
+
const { publicKey } = options;
|
|
264
|
+
if (!publicKey) {
|
|
265
|
+
throw Error("options was missing required publicKey property");
|
|
266
|
+
}
|
|
267
|
+
if (error.name === "AbortError") {
|
|
268
|
+
if (options.signal instanceof AbortSignal) {
|
|
269
|
+
return new WebAuthnError({
|
|
270
|
+
message: "Authentication ceremony was sent an abort signal",
|
|
271
|
+
code: "ERROR_CEREMONY_ABORTED",
|
|
272
|
+
cause: error
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
} else if (error.name === "NotAllowedError") {
|
|
276
|
+
return new WebAuthnError({
|
|
277
|
+
message: error.message,
|
|
278
|
+
code: "ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",
|
|
279
|
+
cause: error
|
|
280
|
+
});
|
|
281
|
+
} else if (error.name === "SecurityError") {
|
|
282
|
+
const effectiveDomain = window.location.hostname;
|
|
283
|
+
if (!isValidDomain(effectiveDomain)) {
|
|
284
|
+
return new WebAuthnError({
|
|
285
|
+
message: `${window.location.hostname} is an invalid domain`,
|
|
286
|
+
code: "ERROR_INVALID_DOMAIN",
|
|
287
|
+
cause: error
|
|
288
|
+
});
|
|
289
|
+
} else if (publicKey.rpId !== effectiveDomain) {
|
|
290
|
+
return new WebAuthnError({
|
|
291
|
+
message: `The RP ID "${publicKey.rpId}" is invalid for this domain`,
|
|
292
|
+
code: "ERROR_INVALID_RP_ID",
|
|
293
|
+
cause: error
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
} else if (error.name === "UnknownError") {
|
|
297
|
+
return new WebAuthnError({
|
|
298
|
+
message: "The authenticator was unable to process the specified options, or could not create a new assertion signature",
|
|
299
|
+
code: "ERROR_AUTHENTICATOR_GENERAL_ERROR",
|
|
300
|
+
cause: error
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
return error;
|
|
304
|
+
}
|
|
305
|
+
async function startAuthentication(options) {
|
|
306
|
+
const { optionsJSON, useBrowserAutofill = false, verifyBrowserAutofillInput = true } = options;
|
|
307
|
+
if (!browserSupportsWebAuthn()) {
|
|
308
|
+
throw new Error("WebAuthn is not supported in this browser");
|
|
309
|
+
}
|
|
310
|
+
let allowCredentials;
|
|
311
|
+
if (optionsJSON.allowCredentials?.length !== 0) {
|
|
312
|
+
allowCredentials = optionsJSON.allowCredentials?.map(toPublicKeyCredentialDescriptor);
|
|
313
|
+
}
|
|
314
|
+
const publicKey = {
|
|
315
|
+
...optionsJSON,
|
|
316
|
+
challenge: base64URLStringToBuffer(optionsJSON.challenge),
|
|
317
|
+
allowCredentials
|
|
318
|
+
};
|
|
319
|
+
const getOptions = {};
|
|
320
|
+
if (useBrowserAutofill) {
|
|
321
|
+
if (!await browserSupportsWebAuthnAutofill()) {
|
|
322
|
+
throw Error("Browser does not support WebAuthn autofill");
|
|
323
|
+
}
|
|
324
|
+
const eligibleInputs = document.querySelectorAll("input[autocomplete$='webauthn']");
|
|
325
|
+
if (eligibleInputs.length < 1 && verifyBrowserAutofillInput) {
|
|
326
|
+
throw Error('No <input> with "webauthn" as the only or last value in its `autocomplete` attribute was detected');
|
|
327
|
+
}
|
|
328
|
+
getOptions.mediation = "conditional";
|
|
329
|
+
publicKey.allowCredentials = [];
|
|
330
|
+
}
|
|
331
|
+
getOptions.publicKey = publicKey;
|
|
332
|
+
getOptions.signal = WebAuthnAbortService.createNewAbortSignal();
|
|
333
|
+
let credential;
|
|
334
|
+
try {
|
|
335
|
+
credential = await navigator.credentials.get(getOptions);
|
|
336
|
+
} catch (err) {
|
|
337
|
+
throw identifyAuthenticationError({ error: err, options: getOptions });
|
|
338
|
+
}
|
|
339
|
+
if (!credential) {
|
|
340
|
+
throw new Error("Authentication was not completed");
|
|
341
|
+
}
|
|
342
|
+
const { id, rawId, response, type } = credential;
|
|
343
|
+
let userHandle = void 0;
|
|
344
|
+
if (response.userHandle) {
|
|
345
|
+
userHandle = bufferToBase64URLString(response.userHandle);
|
|
346
|
+
}
|
|
347
|
+
return {
|
|
348
|
+
id,
|
|
349
|
+
rawId: bufferToBase64URLString(rawId),
|
|
350
|
+
response: {
|
|
351
|
+
authenticatorData: bufferToBase64URLString(response.authenticatorData),
|
|
352
|
+
clientDataJSON: bufferToBase64URLString(response.clientDataJSON),
|
|
353
|
+
signature: bufferToBase64URLString(response.signature),
|
|
354
|
+
userHandle
|
|
355
|
+
},
|
|
356
|
+
type,
|
|
357
|
+
clientExtensionResults: credential.getClientExtensionResults(),
|
|
358
|
+
authenticatorAttachment: toAuthenticatorAttachment(credential.authenticatorAttachment)
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
function platformAuthenticatorIsAvailable() {
|
|
362
|
+
if (!browserSupportsWebAuthn()) {
|
|
363
|
+
return new Promise((resolve) => resolve(false));
|
|
364
|
+
}
|
|
365
|
+
return PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
|
|
366
|
+
}
|
|
367
|
+
var WebAuthnError, BaseWebAuthnAbortService, WebAuthnAbortService, attachments;
|
|
368
|
+
var init_bundle = __esm({
|
|
369
|
+
"../../node_modules/@simplewebauthn/browser/dist/bundle/index.js"() {
|
|
370
|
+
"use strict";
|
|
371
|
+
WebAuthnError = class extends Error {
|
|
372
|
+
constructor({ message, code, cause, name }) {
|
|
373
|
+
super(message, { cause });
|
|
374
|
+
this.name = name ?? cause.name;
|
|
375
|
+
this.code = code;
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
BaseWebAuthnAbortService = class {
|
|
379
|
+
createNewAbortSignal() {
|
|
380
|
+
if (this.controller) {
|
|
381
|
+
const abortError = new Error("Cancelling existing WebAuthn API call for new one");
|
|
382
|
+
abortError.name = "AbortError";
|
|
383
|
+
this.controller.abort(abortError);
|
|
384
|
+
}
|
|
385
|
+
const newController = new AbortController();
|
|
386
|
+
this.controller = newController;
|
|
387
|
+
return newController.signal;
|
|
388
|
+
}
|
|
389
|
+
cancelCeremony() {
|
|
390
|
+
if (this.controller) {
|
|
391
|
+
const abortError = new Error("Manually cancelling existing WebAuthn API call");
|
|
392
|
+
abortError.name = "AbortError";
|
|
393
|
+
this.controller.abort(abortError);
|
|
394
|
+
this.controller = void 0;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
WebAuthnAbortService = new BaseWebAuthnAbortService();
|
|
399
|
+
attachments = ["cross-platform", "platform"];
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
|
|
20
403
|
// src/browser.ts
|
|
21
404
|
var browser_exports = {};
|
|
22
405
|
__export(browser_exports, {
|
|
406
|
+
AccountRegistry: () => AccountRegistry,
|
|
23
407
|
ErrorCodes: () => ErrorCodes,
|
|
24
408
|
IQAuthError: () => IQAuthError,
|
|
409
|
+
MultiAccountTokenStore: () => MultiAccountTokenStore,
|
|
410
|
+
PRIOR_SESSION_STORAGE_KEY: () => PRIOR_SESSION_STORAGE_KEY,
|
|
25
411
|
REFRESH_COOKIE: () => REFRESH_COOKIE,
|
|
26
412
|
SessionManager: () => SessionManager,
|
|
413
|
+
beginPasskeyAuthentication: () => beginPasskeyAuthentication,
|
|
414
|
+
beginPasskeyRegistration: () => beginPasskeyRegistration,
|
|
27
415
|
buildSignInUrl: () => buildSignInUrl,
|
|
28
416
|
clearCookie: () => clearCookie,
|
|
29
417
|
createPkcePair: () => createPkcePair,
|
|
30
418
|
encodePublishableKey: () => encodePublishableKey,
|
|
419
|
+
enrollPasskey: () => enrollPasskey,
|
|
420
|
+
enterImpersonation: () => enterImpersonation,
|
|
421
|
+
exitImpersonation: () => exitImpersonation,
|
|
422
|
+
finishPasskeyAuthentication: () => finishPasskeyAuthentication,
|
|
423
|
+
finishPasskeyRegistration: () => finishPasskeyRegistration,
|
|
31
424
|
getCookie: () => getCookie,
|
|
32
425
|
handleAuthCallback: () => handleAuthCallback,
|
|
33
426
|
isPublishableKey: () => isPublishableKey,
|
|
34
427
|
isSecretKey: () => isSecretKey,
|
|
428
|
+
linkProvider: () => linkProvider,
|
|
429
|
+
listLinkedIdentities: () => listLinkedIdentities,
|
|
35
430
|
parsePublishableKey: () => parsePublishableKey,
|
|
36
431
|
randomUrlSafe: () => randomUrlSafe,
|
|
37
432
|
redirectToSignIn: () => redirectToSignIn,
|
|
433
|
+
requestMagicLink: () => requestMagicLink,
|
|
434
|
+
reverify: () => reverify,
|
|
38
435
|
s256Challenge: () => s256Challenge,
|
|
39
436
|
setCookie: () => setCookie,
|
|
40
437
|
signIn: () => signIn,
|
|
41
|
-
|
|
438
|
+
signInWithPasskey: () => signInWithPasskey,
|
|
439
|
+
signOut: () => signOut,
|
|
440
|
+
unlinkProvider: () => unlinkProvider,
|
|
441
|
+
verifyMagicLink: () => verifyMagicLink,
|
|
442
|
+
withReverification: () => withReverification
|
|
42
443
|
});
|
|
43
444
|
module.exports = __toCommonJS(browser_exports);
|
|
44
445
|
|
|
@@ -257,6 +658,20 @@ function clearPkce(state) {
|
|
|
257
658
|
// src/browser/sessionManager.ts
|
|
258
659
|
var DEFAULT_REFRESH_PATH = "/api/v1/auth/refresh";
|
|
259
660
|
var DEFAULT_USERINFO_PATH = "/api/v1/auth/me";
|
|
661
|
+
async function readAuthErrorCode(res) {
|
|
662
|
+
try {
|
|
663
|
+
const cloned = res.clone();
|
|
664
|
+
const data = await cloned.json().catch(() => null);
|
|
665
|
+
if (!data || typeof data !== "object") return null;
|
|
666
|
+
const err = data.error;
|
|
667
|
+
if (err && typeof err.code === "string") {
|
|
668
|
+
return { code: err.code, message: typeof err.message === "string" ? err.message : void 0 };
|
|
669
|
+
}
|
|
670
|
+
return null;
|
|
671
|
+
} catch {
|
|
672
|
+
return null;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
260
675
|
function decodeClaims(token) {
|
|
261
676
|
try {
|
|
262
677
|
const parts = token.split(".");
|
|
@@ -290,11 +705,11 @@ var EMPTY = {
|
|
|
290
705
|
error: null,
|
|
291
706
|
version: 0
|
|
292
707
|
};
|
|
293
|
-
function defaultCookieStore() {
|
|
708
|
+
function defaultCookieStore(name = REFRESH_COOKIE) {
|
|
294
709
|
return {
|
|
295
|
-
read: () => getCookie(
|
|
296
|
-
write: (token) => setCookie(
|
|
297
|
-
clear: () => clearCookie(
|
|
710
|
+
read: () => getCookie(name),
|
|
711
|
+
write: (token) => setCookie(name, token, { maxAgeSeconds: 60 * 60 * 24 * 30 }),
|
|
712
|
+
clear: () => clearCookie(name)
|
|
298
713
|
};
|
|
299
714
|
}
|
|
300
715
|
var NO_OP_STORE = {
|
|
@@ -323,7 +738,8 @@ var SessionManager = class {
|
|
|
323
738
|
this.useCookies = options.useCookies ?? true;
|
|
324
739
|
this.serverManagedSession = options.serverManagedSession ?? false;
|
|
325
740
|
this.proactiveRefresh = this.serverManagedSession ? false : options.proactiveRefresh ?? true;
|
|
326
|
-
this.
|
|
741
|
+
this.refreshCookieName = options.cookieNames?.refresh ?? REFRESH_COOKIE;
|
|
742
|
+
this.tokenStore = options.tokenStore ?? (this.serverManagedSession ? NO_OP_STORE : this.useCookies ? defaultCookieStore(this.refreshCookieName) : NO_OP_STORE);
|
|
327
743
|
this.crossTabLockTimeoutMs = options.crossTabLockTimeoutMs ?? 4e3;
|
|
328
744
|
this.fetchImpl = options.fetchImpl ?? (typeof fetch !== "undefined" ? fetch.bind(globalThis) : (() => {
|
|
329
745
|
throw new Error("global fetch is not available; pass fetchImpl");
|
|
@@ -351,6 +767,10 @@ var SessionManager = class {
|
|
|
351
767
|
get issuerUrl() {
|
|
352
768
|
return this.issuer;
|
|
353
769
|
}
|
|
770
|
+
/** Cookie name the SDK uses for the refresh token (overridable via `cookieNames.refresh`). */
|
|
771
|
+
get refreshCookie() {
|
|
772
|
+
return this.refreshCookieName;
|
|
773
|
+
}
|
|
354
774
|
getSnapshot() {
|
|
355
775
|
return this.snapshot;
|
|
356
776
|
}
|
|
@@ -457,7 +877,7 @@ var SessionManager = class {
|
|
|
457
877
|
return false;
|
|
458
878
|
}
|
|
459
879
|
if (data.refreshToken) {
|
|
460
|
-
await Promise.resolve(this.tokenStore.write(data.refreshToken));
|
|
880
|
+
await Promise.resolve(this.tokenStore.write(data.refreshToken, { claims: decodeClaims(data.accessToken) }));
|
|
461
881
|
}
|
|
462
882
|
this.applyAccessToken(data.accessToken);
|
|
463
883
|
this.broadcast("session:refresh");
|
|
@@ -501,7 +921,7 @@ var SessionManager = class {
|
|
|
501
921
|
const claims = decodeClaims(accessToken);
|
|
502
922
|
const user = claimsToSessionUser(claims);
|
|
503
923
|
if (refreshToken) {
|
|
504
|
-
void Promise.resolve(this.tokenStore.write(refreshToken));
|
|
924
|
+
void Promise.resolve(this.tokenStore.write(refreshToken, { claims }));
|
|
505
925
|
}
|
|
506
926
|
this.update({
|
|
507
927
|
status: user ? "authenticated" : "unauthenticated",
|
|
@@ -535,33 +955,39 @@ var SessionManager = class {
|
|
|
535
955
|
*/
|
|
536
956
|
async fetch(input, init = {}) {
|
|
537
957
|
const exec = async (token2) => {
|
|
538
|
-
const
|
|
539
|
-
if (token2)
|
|
958
|
+
const headers2 = new Headers(init.headers || {});
|
|
959
|
+
if (token2) headers2.set("Authorization", `Bearer ${token2}`);
|
|
540
960
|
return this.fetchImpl(input, {
|
|
541
961
|
...init,
|
|
542
|
-
headers,
|
|
962
|
+
headers: headers2,
|
|
543
963
|
credentials: init.credentials ?? "include"
|
|
544
964
|
});
|
|
545
965
|
};
|
|
546
966
|
let token = await this.getToken();
|
|
547
967
|
let res = await exec(token);
|
|
548
968
|
if (res.status !== 401) return res;
|
|
969
|
+
const initialErr = await readAuthErrorCode(res);
|
|
970
|
+
if (initialErr && (initialErr.code === "SESSION_EXPIRED_INACTIVITY" || initialErr.code === "SESSION_EXPIRED_MAX_DURATION" || initialErr.code === "SESSION_INVALID")) {
|
|
971
|
+
this.signOutLocal("unauthenticated");
|
|
972
|
+
throw new IQAuthError(initialErr.code, initialErr.message || "Session expired", 401);
|
|
973
|
+
}
|
|
549
974
|
const refreshed = await this.refresh();
|
|
550
975
|
if (!refreshed) {
|
|
551
976
|
this.signOutLocal("unauthenticated");
|
|
552
977
|
throw new IQAuthError(
|
|
553
|
-
"TOKEN_EXPIRED",
|
|
554
|
-
"Session refresh failed; user must sign in again",
|
|
978
|
+
initialErr?.code || "TOKEN_EXPIRED",
|
|
979
|
+
initialErr?.message || "Session refresh failed; user must sign in again",
|
|
555
980
|
401
|
|
556
981
|
);
|
|
557
982
|
}
|
|
558
983
|
token = this.snapshot.accessToken;
|
|
559
984
|
res = await exec(token);
|
|
560
985
|
if (res.status === 401) {
|
|
986
|
+
const secondErr = await readAuthErrorCode(res);
|
|
561
987
|
this.signOutLocal("unauthenticated");
|
|
562
988
|
throw new IQAuthError(
|
|
563
|
-
"TOKEN_EXPIRED",
|
|
564
|
-
"Authenticated request failed twice with 401; aborting",
|
|
989
|
+
secondErr?.code || "TOKEN_EXPIRED",
|
|
990
|
+
secondErr?.message || "Authenticated request failed twice with 401; aborting",
|
|
565
991
|
401
|
|
566
992
|
);
|
|
567
993
|
}
|
|
@@ -589,6 +1015,14 @@ var SessionManager = class {
|
|
|
589
1015
|
});
|
|
590
1016
|
this.broadcast("session:signout");
|
|
591
1017
|
}
|
|
1018
|
+
/**
|
|
1019
|
+
* Replace the refresh-token store at runtime. Used by the F22
|
|
1020
|
+
* `<MultisessionAppSupport>` wrapper to swap in a `MultiAccountTokenStore`
|
|
1021
|
+
* after the manager has already been constructed by `<IQAuthProvider>`.
|
|
1022
|
+
*/
|
|
1023
|
+
setTokenStore(store) {
|
|
1024
|
+
this.tokenStore = store;
|
|
1025
|
+
}
|
|
592
1026
|
destroy() {
|
|
593
1027
|
if (this.proactiveTimer) clearTimeout(this.proactiveTimer);
|
|
594
1028
|
this.proactiveTimer = null;
|
|
@@ -681,6 +1115,217 @@ var SessionManager = class {
|
|
|
681
1115
|
}
|
|
682
1116
|
};
|
|
683
1117
|
|
|
1118
|
+
// src/browser/passwordless.ts
|
|
1119
|
+
function url(base, path) {
|
|
1120
|
+
return `${base.replace(/\/+$/, "")}${path}`;
|
|
1121
|
+
}
|
|
1122
|
+
function headers(o) {
|
|
1123
|
+
const h = { "Content-Type": "application/json" };
|
|
1124
|
+
if (o.cookieSession !== false) h["x-iqauth-session"] = "cookie";
|
|
1125
|
+
return h;
|
|
1126
|
+
}
|
|
1127
|
+
async function postJson(u, body, h) {
|
|
1128
|
+
const r = await fetch(u, {
|
|
1129
|
+
method: "POST",
|
|
1130
|
+
credentials: "include",
|
|
1131
|
+
headers: h,
|
|
1132
|
+
body: JSON.stringify(body)
|
|
1133
|
+
});
|
|
1134
|
+
const p = await r.json().catch(() => ({}));
|
|
1135
|
+
if (!r.ok) {
|
|
1136
|
+
const msg = p?.error?.message || `Request failed (${r.status})`;
|
|
1137
|
+
throw new Error(msg);
|
|
1138
|
+
}
|
|
1139
|
+
return p.data;
|
|
1140
|
+
}
|
|
1141
|
+
async function requestMagicLink(opts, input) {
|
|
1142
|
+
await postJson(url(opts.iqAuthBaseUrl, "/api/v1/auth/magic-link/request"), input, headers(opts));
|
|
1143
|
+
return { ok: true };
|
|
1144
|
+
}
|
|
1145
|
+
async function verifyMagicLink(opts, token) {
|
|
1146
|
+
return postJson(url(opts.iqAuthBaseUrl, "/api/v1/auth/magic-link/verify"), { token }, headers(opts));
|
|
1147
|
+
}
|
|
1148
|
+
async function beginPasskeyAuthentication(opts, input = {}) {
|
|
1149
|
+
return postJson(url(opts.iqAuthBaseUrl, "/api/v1/auth/passkeys/authentication/options"), input, headers(opts));
|
|
1150
|
+
}
|
|
1151
|
+
async function finishPasskeyAuthentication(opts, response) {
|
|
1152
|
+
return postJson(url(opts.iqAuthBaseUrl, "/api/v1/auth/passkeys/authentication/verify"), { response }, headers(opts));
|
|
1153
|
+
}
|
|
1154
|
+
async function beginPasskeyRegistration(opts) {
|
|
1155
|
+
return postJson(url(opts.iqAuthBaseUrl, "/api/v1/auth/passkeys/registration/options"), {}, headers(opts));
|
|
1156
|
+
}
|
|
1157
|
+
async function finishPasskeyRegistration(opts, response, name) {
|
|
1158
|
+
return postJson(url(opts.iqAuthBaseUrl, "/api/v1/auth/passkeys/registration/verify"), { response, name }, headers(opts));
|
|
1159
|
+
}
|
|
1160
|
+
async function signInWithPasskey(opts, input = {}) {
|
|
1161
|
+
const mod = await Promise.resolve().then(() => (init_bundle(), bundle_exports));
|
|
1162
|
+
const options = await beginPasskeyAuthentication(opts, input);
|
|
1163
|
+
const response = await mod.startAuthentication({ optionsJSON: options });
|
|
1164
|
+
return finishPasskeyAuthentication(opts, response);
|
|
1165
|
+
}
|
|
1166
|
+
async function enrollPasskey(opts, name) {
|
|
1167
|
+
const mod = await Promise.resolve().then(() => (init_bundle(), bundle_exports));
|
|
1168
|
+
const options = await beginPasskeyRegistration(opts);
|
|
1169
|
+
const response = await mod.startRegistration({ optionsJSON: options });
|
|
1170
|
+
return finishPasskeyRegistration(opts, response, name);
|
|
1171
|
+
}
|
|
1172
|
+
async function listLinkedIdentities(opts) {
|
|
1173
|
+
const r = await fetch(url(opts.iqAuthBaseUrl, "/api/v1/auth/identities"), { credentials: "include" });
|
|
1174
|
+
const p = await r.json().catch(() => ({}));
|
|
1175
|
+
if (!r.ok) throw new Error(p?.error?.message || `Request failed (${r.status})`);
|
|
1176
|
+
return p?.data?.identities ?? [];
|
|
1177
|
+
}
|
|
1178
|
+
async function linkProvider(opts, input) {
|
|
1179
|
+
await postJson(url(opts.iqAuthBaseUrl, "/api/v1/auth/identities/link"), input, headers(opts));
|
|
1180
|
+
return { ok: true };
|
|
1181
|
+
}
|
|
1182
|
+
async function unlinkProvider(opts, input) {
|
|
1183
|
+
await postJson(url(opts.iqAuthBaseUrl, "/api/v1/auth/identities/unlink"), input, headers(opts));
|
|
1184
|
+
return { ok: true };
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
// src/browser/accountRegistry.ts
|
|
1188
|
+
var STORAGE_PREFIX = "iqauth.accounts.";
|
|
1189
|
+
var COOKIE_PREFIX = "iqauth_rt_";
|
|
1190
|
+
var COOKIE_MAX_AGE = 60 * 60 * 24 * 30;
|
|
1191
|
+
function isBrowser2() {
|
|
1192
|
+
return typeof window !== "undefined" && typeof localStorage !== "undefined";
|
|
1193
|
+
}
|
|
1194
|
+
function storageKey(appId) {
|
|
1195
|
+
return STORAGE_PREFIX + appId;
|
|
1196
|
+
}
|
|
1197
|
+
function readState(appId) {
|
|
1198
|
+
if (!isBrowser2()) return { accounts: [], activeAccountId: null };
|
|
1199
|
+
try {
|
|
1200
|
+
const raw = localStorage.getItem(storageKey(appId));
|
|
1201
|
+
if (!raw) return { accounts: [], activeAccountId: null };
|
|
1202
|
+
const parsed = JSON.parse(raw);
|
|
1203
|
+
return {
|
|
1204
|
+
accounts: Array.isArray(parsed.accounts) ? parsed.accounts : [],
|
|
1205
|
+
activeAccountId: parsed.activeAccountId ?? null
|
|
1206
|
+
};
|
|
1207
|
+
} catch {
|
|
1208
|
+
return { accounts: [], activeAccountId: null };
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
function writeState(appId, state) {
|
|
1212
|
+
if (!isBrowser2()) return;
|
|
1213
|
+
try {
|
|
1214
|
+
localStorage.setItem(storageKey(appId), JSON.stringify(state));
|
|
1215
|
+
} catch {
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
var AccountRegistry = class {
|
|
1219
|
+
constructor(appId) {
|
|
1220
|
+
this.appId = appId;
|
|
1221
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
1222
|
+
this.storageHandler = null;
|
|
1223
|
+
if (isBrowser2()) {
|
|
1224
|
+
this.storageHandler = (ev) => {
|
|
1225
|
+
if (ev.key === storageKey(this.appId)) {
|
|
1226
|
+
for (const l of this.listeners) l();
|
|
1227
|
+
}
|
|
1228
|
+
};
|
|
1229
|
+
window.addEventListener("storage", this.storageHandler);
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
destroy() {
|
|
1233
|
+
if (this.storageHandler && isBrowser2()) {
|
|
1234
|
+
window.removeEventListener("storage", this.storageHandler);
|
|
1235
|
+
}
|
|
1236
|
+
this.listeners.clear();
|
|
1237
|
+
}
|
|
1238
|
+
list() {
|
|
1239
|
+
return readState(this.appId).accounts.slice();
|
|
1240
|
+
}
|
|
1241
|
+
active() {
|
|
1242
|
+
return readState(this.appId).activeAccountId;
|
|
1243
|
+
}
|
|
1244
|
+
get(accountId) {
|
|
1245
|
+
return readState(this.appId).accounts.find((a) => a.accountId === accountId) ?? null;
|
|
1246
|
+
}
|
|
1247
|
+
upsert(rec) {
|
|
1248
|
+
const state = readState(this.appId);
|
|
1249
|
+
const idx = state.accounts.findIndex((a) => a.accountId === rec.accountId);
|
|
1250
|
+
if (idx >= 0) state.accounts[idx] = { ...state.accounts[idx], ...rec };
|
|
1251
|
+
else state.accounts.push(rec);
|
|
1252
|
+
writeState(this.appId, state);
|
|
1253
|
+
this.notify();
|
|
1254
|
+
}
|
|
1255
|
+
setActive(accountId) {
|
|
1256
|
+
const state = readState(this.appId);
|
|
1257
|
+
state.activeAccountId = accountId;
|
|
1258
|
+
writeState(this.appId, state);
|
|
1259
|
+
this.notify();
|
|
1260
|
+
}
|
|
1261
|
+
remove(accountId) {
|
|
1262
|
+
const state = readState(this.appId);
|
|
1263
|
+
state.accounts = state.accounts.filter((a) => a.accountId !== accountId);
|
|
1264
|
+
if (state.activeAccountId === accountId) state.activeAccountId = state.accounts[0]?.accountId ?? null;
|
|
1265
|
+
writeState(this.appId, state);
|
|
1266
|
+
clearCookie(COOKIE_PREFIX + accountId);
|
|
1267
|
+
this.notify();
|
|
1268
|
+
}
|
|
1269
|
+
subscribe(listener) {
|
|
1270
|
+
this.listeners.add(listener);
|
|
1271
|
+
return () => this.listeners.delete(listener);
|
|
1272
|
+
}
|
|
1273
|
+
/**
|
|
1274
|
+
* Read the refresh token for a specific account from its per-account
|
|
1275
|
+
* cookie. Used by `MultiAccountTokenStore.read()`.
|
|
1276
|
+
*/
|
|
1277
|
+
readRefreshToken(accountId) {
|
|
1278
|
+
return getCookie(COOKIE_PREFIX + accountId);
|
|
1279
|
+
}
|
|
1280
|
+
/**
|
|
1281
|
+
* Persist a refresh token to the per-account cookie. Caller is the
|
|
1282
|
+
* SessionManager via `MultiAccountTokenStore.write()`.
|
|
1283
|
+
*/
|
|
1284
|
+
writeRefreshToken(accountId, token, opts = {}) {
|
|
1285
|
+
setCookie(COOKIE_PREFIX + accountId, token, { maxAgeSeconds: COOKIE_MAX_AGE, ...opts });
|
|
1286
|
+
}
|
|
1287
|
+
clearRefreshToken(accountId) {
|
|
1288
|
+
clearCookie(COOKIE_PREFIX + accountId);
|
|
1289
|
+
}
|
|
1290
|
+
notify() {
|
|
1291
|
+
for (const l of this.listeners) l();
|
|
1292
|
+
}
|
|
1293
|
+
};
|
|
1294
|
+
var MultiAccountTokenStore = class {
|
|
1295
|
+
constructor(registry, fallback) {
|
|
1296
|
+
this.registry = registry;
|
|
1297
|
+
this.fallback = fallback;
|
|
1298
|
+
}
|
|
1299
|
+
read() {
|
|
1300
|
+
const id = this.registry.active();
|
|
1301
|
+
if (id) return this.registry.readRefreshToken(id);
|
|
1302
|
+
return this.fallback.read();
|
|
1303
|
+
}
|
|
1304
|
+
write(token, ctx) {
|
|
1305
|
+
let id = this.registry.active();
|
|
1306
|
+
if (!id) {
|
|
1307
|
+
const sub = ctx?.claims?.sub;
|
|
1308
|
+
if (sub) {
|
|
1309
|
+
id = sub;
|
|
1310
|
+
this.registry.setActive(sub);
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
if (id) {
|
|
1314
|
+
this.registry.writeRefreshToken(id, token);
|
|
1315
|
+
return;
|
|
1316
|
+
}
|
|
1317
|
+
return this.fallback.write(token);
|
|
1318
|
+
}
|
|
1319
|
+
clear() {
|
|
1320
|
+
const id = this.registry.active();
|
|
1321
|
+
if (id) {
|
|
1322
|
+
this.registry.clearRefreshToken(id);
|
|
1323
|
+
return;
|
|
1324
|
+
}
|
|
1325
|
+
return this.fallback.clear();
|
|
1326
|
+
}
|
|
1327
|
+
};
|
|
1328
|
+
|
|
684
1329
|
// src/browser/pkce.ts
|
|
685
1330
|
function getCrypto() {
|
|
686
1331
|
if (typeof globalThis !== "undefined" && globalThis.crypto) {
|
|
@@ -741,34 +1386,35 @@ async function buildSignInUrl(manager, opts = {}) {
|
|
|
741
1386
|
returnTo,
|
|
742
1387
|
createdAt: Date.now()
|
|
743
1388
|
});
|
|
744
|
-
const
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
1389
|
+
const url2 = new URL(opts.signInPath ?? DEFAULT_SIGN_IN_PATH, manager.issuerUrl);
|
|
1390
|
+
url2.searchParams.set("response_type", "code");
|
|
1391
|
+
url2.searchParams.set("app", manager.appKey);
|
|
1392
|
+
url2.searchParams.set("publishable_key", manager.publishableKey.raw);
|
|
1393
|
+
url2.searchParams.set("redirect_uri", redirectUri);
|
|
1394
|
+
url2.searchParams.set("state", pkce.state);
|
|
1395
|
+
url2.searchParams.set("nonce", pkce.nonce);
|
|
1396
|
+
url2.searchParams.set("code_challenge", pkce.codeChallenge);
|
|
1397
|
+
url2.searchParams.set("code_challenge_method", "S256");
|
|
1398
|
+
url2.searchParams.set("scope", opts.scope ?? "openid profile email");
|
|
1399
|
+
url2.searchParams.set("return_to", returnTo);
|
|
1400
|
+
if (opts.prompt) url2.searchParams.set("prompt", opts.prompt);
|
|
1401
|
+
return url2.toString();
|
|
756
1402
|
}
|
|
757
1403
|
async function redirectToSignIn(manager, opts = {}) {
|
|
758
|
-
const
|
|
1404
|
+
const url2 = await buildSignInUrl(manager, opts);
|
|
759
1405
|
if (typeof window === "undefined") {
|
|
760
1406
|
throw new Error("redirectToSignIn requires a browser environment");
|
|
761
1407
|
}
|
|
762
|
-
window.location.assign(
|
|
1408
|
+
window.location.assign(url2);
|
|
763
1409
|
}
|
|
764
1410
|
async function signIn(manager, opts = {}) {
|
|
765
1411
|
return redirectToSignIn(manager, opts);
|
|
766
1412
|
}
|
|
767
1413
|
async function handleAuthCallback(manager, options = {}) {
|
|
768
|
-
const
|
|
769
|
-
const code =
|
|
770
|
-
const state =
|
|
771
|
-
const errorParam =
|
|
1414
|
+
const url2 = new URL(options.url ?? (typeof window !== "undefined" ? window.location.href : ""));
|
|
1415
|
+
const code = url2.searchParams.get("code");
|
|
1416
|
+
const state = url2.searchParams.get("state");
|
|
1417
|
+
const errorParam = url2.searchParams.get("error");
|
|
772
1418
|
if (errorParam) {
|
|
773
1419
|
return { ok: false, returnTo: "/", error: errorParam };
|
|
774
1420
|
}
|
|
@@ -807,7 +1453,8 @@ async function handleAuthCallback(manager, options = {}) {
|
|
|
807
1453
|
return { ok: false, returnTo: record.returnTo, error: "missing_access_token" };
|
|
808
1454
|
}
|
|
809
1455
|
if (tokens.refresh_token) {
|
|
810
|
-
|
|
1456
|
+
const cookieName = options.cookieNames?.refresh ?? manager.refreshCookie ?? REFRESH_COOKIE;
|
|
1457
|
+
setCookie(cookieName, tokens.refresh_token, { maxAgeSeconds: 60 * 60 * 24 * 30 });
|
|
811
1458
|
}
|
|
812
1459
|
manager.applyAccessToken(tokens.access_token, tokens.refresh_token);
|
|
813
1460
|
return { ok: true, returnTo: record.returnTo };
|
|
@@ -816,8 +1463,8 @@ async function signOut(manager, opts = {}) {
|
|
|
816
1463
|
if (!opts.localOnly) {
|
|
817
1464
|
const issuer = manager.issuerUrl.replace(/\/$/, "");
|
|
818
1465
|
try {
|
|
819
|
-
const
|
|
820
|
-
await manager.fetch(
|
|
1466
|
+
const url2 = `${issuer}${opts.logoutPath ?? DEFAULT_LOGOUT_PATH}`;
|
|
1467
|
+
await manager.fetch(url2, { method: "POST" }).catch(() => void 0);
|
|
821
1468
|
} catch {
|
|
822
1469
|
}
|
|
823
1470
|
if (opts.endSsoSession !== false) {
|
|
@@ -830,31 +1477,128 @@ async function signOut(manager, opts = {}) {
|
|
|
830
1477
|
}
|
|
831
1478
|
}
|
|
832
1479
|
}
|
|
833
|
-
clearCookie(REFRESH_COOKIE);
|
|
1480
|
+
clearCookie(manager.refreshCookie ?? REFRESH_COOKIE);
|
|
834
1481
|
manager.signOutLocal();
|
|
835
1482
|
if (opts.returnTo && typeof window !== "undefined") {
|
|
836
1483
|
window.location.assign(opts.returnTo);
|
|
837
1484
|
}
|
|
838
1485
|
}
|
|
1486
|
+
|
|
1487
|
+
// src/browser/reverify.ts
|
|
1488
|
+
var PRIOR_SESSION_STORAGE_KEY = "iqauth_prior_admin_session";
|
|
1489
|
+
function enterImpersonation(manager, actorAccessToken) {
|
|
1490
|
+
if (typeof window === "undefined") return;
|
|
1491
|
+
const current = manager.getSnapshot().accessToken;
|
|
1492
|
+
if (current) {
|
|
1493
|
+
const envelope = { accessToken: current, savedAt: Date.now() };
|
|
1494
|
+
try {
|
|
1495
|
+
window.sessionStorage.setItem(PRIOR_SESSION_STORAGE_KEY, JSON.stringify(envelope));
|
|
1496
|
+
} catch {
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
manager.applyAccessToken(actorAccessToken);
|
|
1500
|
+
}
|
|
1501
|
+
function exitImpersonation(manager) {
|
|
1502
|
+
if (typeof window === "undefined") return false;
|
|
1503
|
+
let raw = null;
|
|
1504
|
+
try {
|
|
1505
|
+
raw = window.sessionStorage.getItem(PRIOR_SESSION_STORAGE_KEY);
|
|
1506
|
+
} catch {
|
|
1507
|
+
return false;
|
|
1508
|
+
}
|
|
1509
|
+
if (!raw) return false;
|
|
1510
|
+
try {
|
|
1511
|
+
const envelope = JSON.parse(raw);
|
|
1512
|
+
if (!envelope?.accessToken) return false;
|
|
1513
|
+
manager.applyAccessToken(envelope.accessToken);
|
|
1514
|
+
return true;
|
|
1515
|
+
} catch {
|
|
1516
|
+
return false;
|
|
1517
|
+
} finally {
|
|
1518
|
+
try {
|
|
1519
|
+
window.sessionStorage.removeItem(PRIOR_SESSION_STORAGE_KEY);
|
|
1520
|
+
} catch {
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
var DEFAULT_REVERIFY_PATH = "/api/v1/auth/reverify";
|
|
1525
|
+
async function reverify(manager, input, options = {}) {
|
|
1526
|
+
if (input.level === "password" && !input.password) {
|
|
1527
|
+
throw new IQAuthError("MISSING_PASSWORD", "password is required for level=password");
|
|
1528
|
+
}
|
|
1529
|
+
if (input.level === "mfa" && !input.totp) {
|
|
1530
|
+
throw new IQAuthError("MISSING_CODE", "totp code is required for level=mfa");
|
|
1531
|
+
}
|
|
1532
|
+
const issuer = manager.issuerUrl.replace(/\/$/, "");
|
|
1533
|
+
const url2 = `${issuer}${options.path ?? DEFAULT_REVERIFY_PATH}`;
|
|
1534
|
+
const res = await manager.fetch(url2, {
|
|
1535
|
+
method: "POST",
|
|
1536
|
+
headers: { "Content-Type": "application/json" },
|
|
1537
|
+
body: JSON.stringify(input)
|
|
1538
|
+
});
|
|
1539
|
+
let payload = null;
|
|
1540
|
+
try {
|
|
1541
|
+
payload = await res.json();
|
|
1542
|
+
} catch {
|
|
1543
|
+
}
|
|
1544
|
+
if (!res.ok || !payload?.success) {
|
|
1545
|
+
const code = payload?.error?.code ?? "REVERIFICATION_FAILED";
|
|
1546
|
+
const message = payload?.error?.message ?? `reverification failed (${res.status})`;
|
|
1547
|
+
throw new IQAuthError(code, message);
|
|
1548
|
+
}
|
|
1549
|
+
return {
|
|
1550
|
+
token: payload.data.reverificationToken,
|
|
1551
|
+
level: payload.data.level,
|
|
1552
|
+
expiresAt: new Date(payload.data.expiresAt)
|
|
1553
|
+
};
|
|
1554
|
+
}
|
|
1555
|
+
function withReverification(manager, token) {
|
|
1556
|
+
let used = false;
|
|
1557
|
+
return async (input, init = {}) => {
|
|
1558
|
+
if (used) throw new IQAuthError("REVERIFICATION_USED", "Reverification token already consumed");
|
|
1559
|
+
used = true;
|
|
1560
|
+
const headers2 = new Headers(init.headers);
|
|
1561
|
+
headers2.set("X-Reverification-Token", token);
|
|
1562
|
+
return manager.fetch(input, { ...init, headers: headers2 });
|
|
1563
|
+
};
|
|
1564
|
+
}
|
|
839
1565
|
// Annotate the CommonJS export names for ESM import in node:
|
|
840
1566
|
0 && (module.exports = {
|
|
1567
|
+
AccountRegistry,
|
|
841
1568
|
ErrorCodes,
|
|
842
1569
|
IQAuthError,
|
|
1570
|
+
MultiAccountTokenStore,
|
|
1571
|
+
PRIOR_SESSION_STORAGE_KEY,
|
|
843
1572
|
REFRESH_COOKIE,
|
|
844
1573
|
SessionManager,
|
|
1574
|
+
beginPasskeyAuthentication,
|
|
1575
|
+
beginPasskeyRegistration,
|
|
845
1576
|
buildSignInUrl,
|
|
846
1577
|
clearCookie,
|
|
847
1578
|
createPkcePair,
|
|
848
1579
|
encodePublishableKey,
|
|
1580
|
+
enrollPasskey,
|
|
1581
|
+
enterImpersonation,
|
|
1582
|
+
exitImpersonation,
|
|
1583
|
+
finishPasskeyAuthentication,
|
|
1584
|
+
finishPasskeyRegistration,
|
|
849
1585
|
getCookie,
|
|
850
1586
|
handleAuthCallback,
|
|
851
1587
|
isPublishableKey,
|
|
852
1588
|
isSecretKey,
|
|
1589
|
+
linkProvider,
|
|
1590
|
+
listLinkedIdentities,
|
|
853
1591
|
parsePublishableKey,
|
|
854
1592
|
randomUrlSafe,
|
|
855
1593
|
redirectToSignIn,
|
|
1594
|
+
requestMagicLink,
|
|
1595
|
+
reverify,
|
|
856
1596
|
s256Challenge,
|
|
857
1597
|
setCookie,
|
|
858
1598
|
signIn,
|
|
859
|
-
|
|
1599
|
+
signInWithPasskey,
|
|
1600
|
+
signOut,
|
|
1601
|
+
unlinkProvider,
|
|
1602
|
+
verifyMagicLink,
|
|
1603
|
+
withReverification
|
|
860
1604
|
});
|