@iqauth/sdk 2.2.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +134 -0
- package/dist/browser-session.d.mts +3 -3
- package/dist/browser-session.d.ts +3 -3
- package/dist/browser-session.js +89 -68
- package/dist/browser-session.mjs +2 -1
- package/dist/browser.d.mts +64 -29
- package/dist/browser.d.ts +64 -29
- package/dist/browser.js +794 -39
- package/dist/browser.mjs +44 -4
- package/dist/bundle-LUKDQYVQ.mjs +374 -0
- package/dist/chunk-3JULWS6F.mjs +106 -0
- package/dist/chunk-5T7GHBX6.mjs +1165 -0
- package/dist/{chunk-M4J6BPK7.mjs → chunk-6TDJJER7.mjs} +12 -3
- package/dist/{chunk-QZB745C2.mjs → chunk-76W5TLQQ.mjs} +264 -211
- package/dist/{chunk-D72UL5HL.mjs → chunk-BVV54LPI.mjs} +36 -4
- package/dist/chunk-LIZYFXH7.mjs +90 -0
- package/dist/chunk-MKKZULZR.mjs +241 -0
- package/dist/chunk-SL3KRS4W.mjs +54 -0
- package/dist/chunk-TKZTCPEK.mjs +232 -0
- package/dist/chunk-UKZLOHZG.mjs +83 -0
- package/dist/chunk-UNYDG2L4.mjs +209 -0
- package/dist/{chunk-MDUHPQMM.mjs → chunk-W3F4JYGP.mjs} +8 -180
- package/dist/{chunk-QEJB7WEQ.mjs → chunk-WQWBJSSS.mjs} +1 -1
- package/dist/cli/index.js +144 -36
- package/dist/cli/index.mjs +1 -1
- package/dist/{client-DXbHb2ul.d.ts → client-BNQe3AgF.d.ts} +3 -67
- package/dist/{client-Dv4v92Mj.d.mts → client-kYlJFgPv.d.mts} +3 -67
- package/dist/doctor-YYNHNMLD.mjs +198 -0
- package/dist/{express-BZmF1llh.d.mts → express-B6_1vBYZ.d.mts} +23 -2
- package/dist/{express-B4o3P8vK.d.ts → express-CHpfa7D_.d.ts} +23 -2
- package/dist/express.d.mts +77 -6
- package/dist/express.d.ts +77 -6
- package/dist/express.js +336 -74
- package/dist/express.mjs +209 -8
- package/dist/fastify.js +103 -72
- package/dist/fastify.mjs +6 -4
- package/dist/hono.js +102 -72
- package/dist/hono.mjs +5 -4
- package/dist/index.d.mts +8 -4
- package/dist/index.d.ts +8 -4
- package/dist/index.js +590 -73
- package/dist/index.mjs +30 -8
- package/dist/locales.d.mts +53 -0
- package/dist/locales.d.ts +53 -0
- package/dist/locales.js +1202 -0
- package/dist/locales.mjs +29 -0
- package/dist/mobile.d.mts +3 -3
- package/dist/mobile.d.ts +3 -3
- package/dist/mobile.js +89 -68
- package/dist/mobile.mjs +2 -1
- package/dist/next.d.mts +10 -1
- package/dist/next.d.ts +10 -1
- package/dist/next.js +101 -1618
- package/dist/next.mjs +9 -9
- package/dist/provisioningBridge-88xjOS2n.d.mts +86 -0
- package/dist/provisioningBridge-DnTfzdZK.d.ts +86 -0
- package/dist/react.d.mts +1349 -10
- package/dist/react.d.ts +1349 -10
- package/dist/react.js +2998 -569
- package/dist/react.mjs +1518 -95
- package/dist/reverify-4UEJXUS6.mjs +16 -0
- package/dist/server/handlers.d.mts +12 -1
- package/dist/server/handlers.d.ts +12 -1
- package/dist/server/handlers.js +12 -3
- package/dist/server/handlers.mjs +2 -2
- package/dist/server.d.mts +5 -4
- package/dist/server.d.ts +5 -4
- package/dist/server.js +188 -73
- package/dist/server.mjs +13 -8
- package/dist/service.d.mts +3 -3
- package/dist/service.d.ts +3 -3
- package/dist/service.js +89 -68
- package/dist/service.mjs +2 -1
- package/dist/signIn-CCY4JE5G.mjs +15 -0
- package/dist/{signIn-D_kP3v-c.d.mts → signIn-CiIBTJIh.d.mts} +232 -4
- package/dist/{signIn-BVDTIA_t.d.ts → signIn-OCr88Zf8.d.ts} +232 -4
- package/dist/test.d.mts +86 -0
- package/dist/test.d.ts +86 -0
- package/dist/test.js +289 -0
- package/dist/test.mjs +9 -0
- package/dist/tokens-DCyzzn8L.d.mts +63 -0
- package/dist/tokens-aHiGFr_E.d.ts +63 -0
- package/dist/types-6bNdxesb.d.mts +196 -0
- package/dist/types-6bNdxesb.d.ts +196 -0
- package/dist/{types-Cxl3bQHt.d.ts → types-DZAflmmq.d.mts} +6 -0
- package/dist/{types-Cxl3bQHt.d.mts → types-DZAflmmq.d.ts} +6 -0
- package/dist/webhooks.d.mts +61 -0
- package/dist/webhooks.d.ts +61 -0
- package/dist/webhooks.js +119 -0
- package/dist/webhooks.mjs +11 -0
- package/dist/ws.d.mts +73 -0
- package/dist/ws.d.ts +73 -0
- package/dist/ws.js +397 -0
- package/dist/ws.mjs +12 -0
- package/package.json +24 -3
- package/dist/doctor-XCI77BQS.mjs +0 -90
package/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
|
|
|
@@ -177,7 +578,7 @@ function assertPublishableKey(raw, opts) {
|
|
|
177
578
|
if (!isValidIssuerUrl(decoded.iss)) {
|
|
178
579
|
throw new IQAuthError(
|
|
179
580
|
"CONFIG_INVALID",
|
|
180
|
-
`${ctx}IQAuth publishable key encodes an invalid issuer (iss=${JSON.stringify(decoded.iss)}). Expected a fully-qualified URL like "https://auth.example.com" (scheme required). Regenerate the key from the IQAuth admin console
|
|
581
|
+
`${ctx}IQAuth publishable key encodes an invalid issuer (iss=${JSON.stringify(decoded.iss)}). Expected a fully-qualified URL like "https://auth.example.com" (scheme required). Regenerate the key from the IQAuth admin console \u2014 the new key will encode a valid issuer URL.`
|
|
181
582
|
);
|
|
182
583
|
}
|
|
183
584
|
return { mode: shapeMatch[1], iss: decoded.iss, appId: decoded.appId, tenantId: decoded.tenantId, kid: decoded.kid, raw };
|
|
@@ -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) {
|
|
@@ -715,6 +1360,7 @@ async function createPkcePair() {
|
|
|
715
1360
|
// src/browser/signIn.ts
|
|
716
1361
|
var DEFAULT_SIGN_IN_PATH = "/sign-in";
|
|
717
1362
|
var DEFAULT_LOGOUT_PATH = "/api/v1/auth/logout";
|
|
1363
|
+
var DEFAULT_SSO_LOGOUT_PATH = "/oidc/sso-logout";
|
|
718
1364
|
var DEFAULT_TOKEN_PATH = "/oidc/token";
|
|
719
1365
|
var DEFAULT_CALLBACK_PATH = "/auth/callback";
|
|
720
1366
|
function defaultRedirectUri() {
|
|
@@ -740,34 +1386,35 @@ async function buildSignInUrl(manager, opts = {}) {
|
|
|
740
1386
|
returnTo,
|
|
741
1387
|
createdAt: Date.now()
|
|
742
1388
|
});
|
|
743
|
-
const
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
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();
|
|
755
1402
|
}
|
|
756
1403
|
async function redirectToSignIn(manager, opts = {}) {
|
|
757
|
-
const
|
|
1404
|
+
const url2 = await buildSignInUrl(manager, opts);
|
|
758
1405
|
if (typeof window === "undefined") {
|
|
759
1406
|
throw new Error("redirectToSignIn requires a browser environment");
|
|
760
1407
|
}
|
|
761
|
-
window.location.assign(
|
|
1408
|
+
window.location.assign(url2);
|
|
762
1409
|
}
|
|
763
1410
|
async function signIn(manager, opts = {}) {
|
|
764
1411
|
return redirectToSignIn(manager, opts);
|
|
765
1412
|
}
|
|
766
1413
|
async function handleAuthCallback(manager, options = {}) {
|
|
767
|
-
const
|
|
768
|
-
const code =
|
|
769
|
-
const state =
|
|
770
|
-
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");
|
|
771
1418
|
if (errorParam) {
|
|
772
1419
|
return { ok: false, returnTo: "/", error: errorParam };
|
|
773
1420
|
}
|
|
@@ -806,44 +1453,152 @@ async function handleAuthCallback(manager, options = {}) {
|
|
|
806
1453
|
return { ok: false, returnTo: record.returnTo, error: "missing_access_token" };
|
|
807
1454
|
}
|
|
808
1455
|
if (tokens.refresh_token) {
|
|
809
|
-
|
|
1456
|
+
const cookieName = options.cookieNames?.refresh ?? manager.refreshCookie ?? REFRESH_COOKIE;
|
|
1457
|
+
setCookie(cookieName, tokens.refresh_token, { maxAgeSeconds: 60 * 60 * 24 * 30 });
|
|
810
1458
|
}
|
|
811
1459
|
manager.applyAccessToken(tokens.access_token, tokens.refresh_token);
|
|
812
1460
|
return { ok: true, returnTo: record.returnTo };
|
|
813
1461
|
}
|
|
814
1462
|
async function signOut(manager, opts = {}) {
|
|
815
1463
|
if (!opts.localOnly) {
|
|
1464
|
+
const issuer = manager.issuerUrl.replace(/\/$/, "");
|
|
816
1465
|
try {
|
|
817
|
-
const
|
|
818
|
-
await manager.fetch(
|
|
1466
|
+
const url2 = `${issuer}${opts.logoutPath ?? DEFAULT_LOGOUT_PATH}`;
|
|
1467
|
+
await manager.fetch(url2, { method: "POST" }).catch(() => void 0);
|
|
819
1468
|
} catch {
|
|
820
1469
|
}
|
|
1470
|
+
if (opts.endSsoSession !== false) {
|
|
1471
|
+
try {
|
|
1472
|
+
await fetch(`${issuer}${DEFAULT_SSO_LOGOUT_PATH}`, {
|
|
1473
|
+
method: "POST",
|
|
1474
|
+
credentials: "include"
|
|
1475
|
+
}).catch(() => void 0);
|
|
1476
|
+
} catch {
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
821
1479
|
}
|
|
822
|
-
clearCookie(REFRESH_COOKIE);
|
|
1480
|
+
clearCookie(manager.refreshCookie ?? REFRESH_COOKIE);
|
|
823
1481
|
manager.signOutLocal();
|
|
824
1482
|
if (opts.returnTo && typeof window !== "undefined") {
|
|
825
1483
|
window.location.assign(opts.returnTo);
|
|
826
1484
|
}
|
|
827
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
|
+
}
|
|
828
1565
|
// Annotate the CommonJS export names for ESM import in node:
|
|
829
1566
|
0 && (module.exports = {
|
|
1567
|
+
AccountRegistry,
|
|
830
1568
|
ErrorCodes,
|
|
831
1569
|
IQAuthError,
|
|
1570
|
+
MultiAccountTokenStore,
|
|
1571
|
+
PRIOR_SESSION_STORAGE_KEY,
|
|
832
1572
|
REFRESH_COOKIE,
|
|
833
1573
|
SessionManager,
|
|
1574
|
+
beginPasskeyAuthentication,
|
|
1575
|
+
beginPasskeyRegistration,
|
|
834
1576
|
buildSignInUrl,
|
|
835
1577
|
clearCookie,
|
|
836
1578
|
createPkcePair,
|
|
837
1579
|
encodePublishableKey,
|
|
1580
|
+
enrollPasskey,
|
|
1581
|
+
enterImpersonation,
|
|
1582
|
+
exitImpersonation,
|
|
1583
|
+
finishPasskeyAuthentication,
|
|
1584
|
+
finishPasskeyRegistration,
|
|
838
1585
|
getCookie,
|
|
839
1586
|
handleAuthCallback,
|
|
840
1587
|
isPublishableKey,
|
|
841
1588
|
isSecretKey,
|
|
1589
|
+
linkProvider,
|
|
1590
|
+
listLinkedIdentities,
|
|
842
1591
|
parsePublishableKey,
|
|
843
1592
|
randomUrlSafe,
|
|
844
1593
|
redirectToSignIn,
|
|
1594
|
+
requestMagicLink,
|
|
1595
|
+
reverify,
|
|
845
1596
|
s256Challenge,
|
|
846
1597
|
setCookie,
|
|
847
1598
|
signIn,
|
|
848
|
-
|
|
1599
|
+
signInWithPasskey,
|
|
1600
|
+
signOut,
|
|
1601
|
+
unlinkProvider,
|
|
1602
|
+
verifyMagicLink,
|
|
1603
|
+
withReverification
|
|
849
1604
|
});
|