@camstack/addon-auth 1.1.3 → 1.1.5
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/dist/{dist-suALViLT.mjs → dist-BopfLZ9P.mjs} +156 -1
- package/dist/{dist-mmJDfwXE.js → dist-biwV9TkV.js} +161 -0
- package/dist/magic-link/auth-magic-link.addon.js +12 -7
- package/dist/magic-link/auth-magic-link.addon.mjs +12 -7
- package/dist/oidc/auth-oidc.addon.js +3 -7
- package/dist/oidc/auth-oidc.addon.mjs +3 -7
- package/dist/webauthn/_stub.js +69 -3
- package/dist/webauthn/{_virtual_mf-localSharedImportMap___mfe_internal__addon_auth_webauthn_widgets-Bg_T1-iY.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_auth_webauthn_widgets-DW5sgIHW.mjs} +2 -2
- package/dist/webauthn/auth-webauthn.addon.js +205 -54
- package/dist/webauthn/auth-webauthn.addon.mjs +205 -54
- package/dist/webauthn/{hostInit-Boe91Eeg.mjs → hostInit-DKNSGMxo.mjs} +2 -2
- package/dist/webauthn/remoteEntry.js +1 -1
- package/package.json +1 -1
|
@@ -7052,6 +7052,123 @@ var ConvertResultSchema = object({
|
|
|
7052
7052
|
})).readonly()
|
|
7053
7053
|
});
|
|
7054
7054
|
/**
|
|
7055
|
+
* Build an `IAddonRouteProvider` from a list of routes. Implements
|
|
7056
|
+
* both the operator-facing `getRoutes` (returning route descriptors
|
|
7057
|
+
* minus the handlers, which can't cross JSON) and the framework-
|
|
7058
|
+
* private `invoke` method that the hub calls when this provider lives
|
|
7059
|
+
* in a forked worker.
|
|
7060
|
+
*
|
|
7061
|
+
* Co-located addons use the returned `getRoutes` directly because
|
|
7062
|
+
* their handlers don't need to cross any wire. The `invoke` method
|
|
7063
|
+
* is present anyway so the bridge code on the hub is uniform — it
|
|
7064
|
+
* doesn't need to switch on "local vs remote provider" at the call
|
|
7065
|
+
* site.
|
|
7066
|
+
*
|
|
7067
|
+
* Example:
|
|
7068
|
+
* const routes: IAddonHttpRoute[] = [
|
|
7069
|
+
* { method: 'GET', path: '/start', access: 'public', handler: this.handleStart },
|
|
7070
|
+
* ]
|
|
7071
|
+
* return [
|
|
7072
|
+
* {
|
|
7073
|
+
* capability: addonRoutesCapability,
|
|
7074
|
+
* provider: buildAddonRouteProvider('auth-oidc', routes),
|
|
7075
|
+
* },
|
|
7076
|
+
* ]
|
|
7077
|
+
*/
|
|
7078
|
+
function buildAddonRouteProvider(id, routes) {
|
|
7079
|
+
return {
|
|
7080
|
+
id,
|
|
7081
|
+
getRoutes: () => routes,
|
|
7082
|
+
invoke: async (input) => {
|
|
7083
|
+
const match = matchRoute(routes, input.method, input.path);
|
|
7084
|
+
if (!match) return {
|
|
7085
|
+
status: 404,
|
|
7086
|
+
headers: {},
|
|
7087
|
+
redirectUrl: null,
|
|
7088
|
+
body: { error: `No route matches ${input.method} ${input.path}` }
|
|
7089
|
+
};
|
|
7090
|
+
const envelope = {
|
|
7091
|
+
status: 200,
|
|
7092
|
+
headers: {},
|
|
7093
|
+
redirectUrl: null
|
|
7094
|
+
};
|
|
7095
|
+
const reply = buildCapturingReply(envelope);
|
|
7096
|
+
const request = {
|
|
7097
|
+
params: {
|
|
7098
|
+
...input.params,
|
|
7099
|
+
...match.params
|
|
7100
|
+
},
|
|
7101
|
+
query: input.query,
|
|
7102
|
+
body: input.body,
|
|
7103
|
+
headers: input.headers,
|
|
7104
|
+
...input.user ? { user: input.user } : {},
|
|
7105
|
+
...input.scopedToken !== void 0 ? { scopedToken: input.scopedToken } : {}
|
|
7106
|
+
};
|
|
7107
|
+
await match.route.handler(request, reply);
|
|
7108
|
+
return envelope;
|
|
7109
|
+
}
|
|
7110
|
+
};
|
|
7111
|
+
}
|
|
7112
|
+
/**
|
|
7113
|
+
* Pattern matcher: same semantics as `AddonRouteRegistry.matchRoute`
|
|
7114
|
+
* but operating on a flat list and bypassing the `/addon/<id>/` prefix
|
|
7115
|
+
* — the bridge sends the post-prefix path directly so we don't need
|
|
7116
|
+
* to round-trip it through normalization.
|
|
7117
|
+
*/
|
|
7118
|
+
function matchRoute(routes, method, path) {
|
|
7119
|
+
const normalizedMethod = method.toUpperCase();
|
|
7120
|
+
for (const route of routes) {
|
|
7121
|
+
if (route.method !== normalizedMethod) continue;
|
|
7122
|
+
const params = matchPath(route.path, path);
|
|
7123
|
+
if (params !== null) return {
|
|
7124
|
+
route,
|
|
7125
|
+
params
|
|
7126
|
+
};
|
|
7127
|
+
}
|
|
7128
|
+
return null;
|
|
7129
|
+
}
|
|
7130
|
+
function matchPath(pattern, p) {
|
|
7131
|
+
const patternParts = pattern.split("/").filter(Boolean);
|
|
7132
|
+
const pathParts = p.split("/").filter(Boolean);
|
|
7133
|
+
if (patternParts.length !== pathParts.length) return null;
|
|
7134
|
+
const params = {};
|
|
7135
|
+
for (let i = 0; i < patternParts.length; i++) {
|
|
7136
|
+
const a = patternParts[i];
|
|
7137
|
+
const b = pathParts[i];
|
|
7138
|
+
if (a.startsWith(":")) params[a.slice(1)] = b;
|
|
7139
|
+
else if (a !== b) return null;
|
|
7140
|
+
}
|
|
7141
|
+
return params;
|
|
7142
|
+
}
|
|
7143
|
+
function buildCapturingReply(envelope) {
|
|
7144
|
+
const wrapper = {
|
|
7145
|
+
status(code) {
|
|
7146
|
+
envelope.status = code;
|
|
7147
|
+
return wrapper;
|
|
7148
|
+
},
|
|
7149
|
+
code(code) {
|
|
7150
|
+
envelope.status = code;
|
|
7151
|
+
return wrapper;
|
|
7152
|
+
},
|
|
7153
|
+
send(data) {
|
|
7154
|
+
envelope.body = data;
|
|
7155
|
+
},
|
|
7156
|
+
redirect(url) {
|
|
7157
|
+
envelope.redirectUrl = url;
|
|
7158
|
+
if (envelope.status === 200) envelope.status = 302;
|
|
7159
|
+
},
|
|
7160
|
+
header(name, value) {
|
|
7161
|
+
envelope.headers[name.toLowerCase()] = value;
|
|
7162
|
+
return wrapper;
|
|
7163
|
+
},
|
|
7164
|
+
type(mime) {
|
|
7165
|
+
envelope.contentType = mime;
|
|
7166
|
+
return wrapper;
|
|
7167
|
+
}
|
|
7168
|
+
};
|
|
7169
|
+
return wrapper;
|
|
7170
|
+
}
|
|
7171
|
+
/**
|
|
7055
7172
|
* Numeric day-of-week: 0 = Sunday … 6 = Saturday (matches `Date.getDay`).
|
|
7056
7173
|
* Named `RecordingWeekday` to avoid collision with the string-union
|
|
7057
7174
|
* `Weekday` exported from `interfaces/timezones.ts`.
|
|
@@ -16284,6 +16401,19 @@ method(_void(), array(TurnServerSchema).readonly());
|
|
|
16284
16401
|
* b. `finishAuthentication({userId, response})` → server verifies
|
|
16285
16402
|
* the assertion, bumps the credential counter, returns ok.
|
|
16286
16403
|
*
|
|
16404
|
+
* 2b. Usernameless (discoverable-credential) authentication — the
|
|
16405
|
+
* passkey IS the primary factor, no password leg:
|
|
16406
|
+
* a. `beginDiscoverableAuthentication({})` → assertion options with
|
|
16407
|
+
* EMPTY `allowCredentials` (the browser offers every resident
|
|
16408
|
+
* passkey it holds for this RP) + `userVerification: 'required'`
|
|
16409
|
+
* (the passkey replaces both factors, so UV is mandatory).
|
|
16410
|
+
* The challenge is stored server-side, NOT bound to any user.
|
|
16411
|
+
* b. `finishDiscoverableAuthentication({response})` → the provider
|
|
16412
|
+
* resolves the credential by the response's credential id,
|
|
16413
|
+
* verifies the assertion against the stored challenge + that
|
|
16414
|
+
* credential's public key/counter, and returns the OWNING
|
|
16415
|
+
* `userId` — the caller (core auth router) mints the session.
|
|
16416
|
+
*
|
|
16287
16417
|
* 3. Management:
|
|
16288
16418
|
* - `listPasskeys({userId})` — enumerate user's enrolled credentials.
|
|
16289
16419
|
* - `removePasskey({userId, credentialId})` — revoke one credential.
|
|
@@ -16340,6 +16470,19 @@ var userPasskeysCapability = {
|
|
|
16340
16470
|
kind: "mutation",
|
|
16341
16471
|
access: "view"
|
|
16342
16472
|
}),
|
|
16473
|
+
beginDiscoverableAuthentication: method(object({}), object({ optionsJSON: record(string(), unknown()) }), {
|
|
16474
|
+
kind: "mutation",
|
|
16475
|
+
access: "view"
|
|
16476
|
+
}),
|
|
16477
|
+
finishDiscoverableAuthentication: method(object({
|
|
16478
|
+
/** AuthenticationResponseJSON from the browser. */
|
|
16479
|
+
response: record(string(), unknown()) }), object({
|
|
16480
|
+
verified: boolean(),
|
|
16481
|
+
userId: string().nullable()
|
|
16482
|
+
}), {
|
|
16483
|
+
kind: "mutation",
|
|
16484
|
+
access: "view"
|
|
16485
|
+
}),
|
|
16343
16486
|
listPasskeys: method(object({ userId: string() }), array(PasskeySummarySchema), { auth: "admin" }),
|
|
16344
16487
|
removePasskey: method(object({
|
|
16345
16488
|
userId: string(),
|
|
@@ -22842,6 +22985,12 @@ Object.freeze({
|
|
|
22842
22985
|
addonId: null,
|
|
22843
22986
|
access: "view"
|
|
22844
22987
|
},
|
|
22988
|
+
"userPasskeys.beginDiscoverableAuthentication": {
|
|
22989
|
+
capName: "user-passkeys",
|
|
22990
|
+
capScope: "system",
|
|
22991
|
+
addonId: null,
|
|
22992
|
+
access: "view"
|
|
22993
|
+
},
|
|
22845
22994
|
"userPasskeys.beginRegistration": {
|
|
22846
22995
|
capName: "user-passkeys",
|
|
22847
22996
|
capScope: "system",
|
|
@@ -22854,6 +23003,12 @@ Object.freeze({
|
|
|
22854
23003
|
addonId: null,
|
|
22855
23004
|
access: "view"
|
|
22856
23005
|
},
|
|
23006
|
+
"userPasskeys.finishDiscoverableAuthentication": {
|
|
23007
|
+
capName: "user-passkeys",
|
|
23008
|
+
capScope: "system",
|
|
23009
|
+
addonId: null,
|
|
23010
|
+
access: "view"
|
|
23011
|
+
},
|
|
22857
23012
|
"userPasskeys.finishRegistration": {
|
|
22858
23013
|
capName: "user-passkeys",
|
|
22859
23014
|
capScope: "system",
|
|
@@ -23124,4 +23279,4 @@ object({
|
|
|
23124
23279
|
schemaVersion: literal(1)
|
|
23125
23280
|
});
|
|
23126
23281
|
//#endregion
|
|
23127
|
-
export {
|
|
23282
|
+
export { loginMethodCapability as a, BaseAddon as c, string as d, buildAddonRouteProvider as i, array as l, addonWidgetsSourceCapability as n, userPasskeysCapability as o, authProviderCapability as r, errMsg as s, addonRoutesCapability as t, object as u };
|
|
@@ -7052,6 +7052,123 @@ var ConvertResultSchema = object({
|
|
|
7052
7052
|
})).readonly()
|
|
7053
7053
|
});
|
|
7054
7054
|
/**
|
|
7055
|
+
* Build an `IAddonRouteProvider` from a list of routes. Implements
|
|
7056
|
+
* both the operator-facing `getRoutes` (returning route descriptors
|
|
7057
|
+
* minus the handlers, which can't cross JSON) and the framework-
|
|
7058
|
+
* private `invoke` method that the hub calls when this provider lives
|
|
7059
|
+
* in a forked worker.
|
|
7060
|
+
*
|
|
7061
|
+
* Co-located addons use the returned `getRoutes` directly because
|
|
7062
|
+
* their handlers don't need to cross any wire. The `invoke` method
|
|
7063
|
+
* is present anyway so the bridge code on the hub is uniform — it
|
|
7064
|
+
* doesn't need to switch on "local vs remote provider" at the call
|
|
7065
|
+
* site.
|
|
7066
|
+
*
|
|
7067
|
+
* Example:
|
|
7068
|
+
* const routes: IAddonHttpRoute[] = [
|
|
7069
|
+
* { method: 'GET', path: '/start', access: 'public', handler: this.handleStart },
|
|
7070
|
+
* ]
|
|
7071
|
+
* return [
|
|
7072
|
+
* {
|
|
7073
|
+
* capability: addonRoutesCapability,
|
|
7074
|
+
* provider: buildAddonRouteProvider('auth-oidc', routes),
|
|
7075
|
+
* },
|
|
7076
|
+
* ]
|
|
7077
|
+
*/
|
|
7078
|
+
function buildAddonRouteProvider(id, routes) {
|
|
7079
|
+
return {
|
|
7080
|
+
id,
|
|
7081
|
+
getRoutes: () => routes,
|
|
7082
|
+
invoke: async (input) => {
|
|
7083
|
+
const match = matchRoute(routes, input.method, input.path);
|
|
7084
|
+
if (!match) return {
|
|
7085
|
+
status: 404,
|
|
7086
|
+
headers: {},
|
|
7087
|
+
redirectUrl: null,
|
|
7088
|
+
body: { error: `No route matches ${input.method} ${input.path}` }
|
|
7089
|
+
};
|
|
7090
|
+
const envelope = {
|
|
7091
|
+
status: 200,
|
|
7092
|
+
headers: {},
|
|
7093
|
+
redirectUrl: null
|
|
7094
|
+
};
|
|
7095
|
+
const reply = buildCapturingReply(envelope);
|
|
7096
|
+
const request = {
|
|
7097
|
+
params: {
|
|
7098
|
+
...input.params,
|
|
7099
|
+
...match.params
|
|
7100
|
+
},
|
|
7101
|
+
query: input.query,
|
|
7102
|
+
body: input.body,
|
|
7103
|
+
headers: input.headers,
|
|
7104
|
+
...input.user ? { user: input.user } : {},
|
|
7105
|
+
...input.scopedToken !== void 0 ? { scopedToken: input.scopedToken } : {}
|
|
7106
|
+
};
|
|
7107
|
+
await match.route.handler(request, reply);
|
|
7108
|
+
return envelope;
|
|
7109
|
+
}
|
|
7110
|
+
};
|
|
7111
|
+
}
|
|
7112
|
+
/**
|
|
7113
|
+
* Pattern matcher: same semantics as `AddonRouteRegistry.matchRoute`
|
|
7114
|
+
* but operating on a flat list and bypassing the `/addon/<id>/` prefix
|
|
7115
|
+
* — the bridge sends the post-prefix path directly so we don't need
|
|
7116
|
+
* to round-trip it through normalization.
|
|
7117
|
+
*/
|
|
7118
|
+
function matchRoute(routes, method, path) {
|
|
7119
|
+
const normalizedMethod = method.toUpperCase();
|
|
7120
|
+
for (const route of routes) {
|
|
7121
|
+
if (route.method !== normalizedMethod) continue;
|
|
7122
|
+
const params = matchPath(route.path, path);
|
|
7123
|
+
if (params !== null) return {
|
|
7124
|
+
route,
|
|
7125
|
+
params
|
|
7126
|
+
};
|
|
7127
|
+
}
|
|
7128
|
+
return null;
|
|
7129
|
+
}
|
|
7130
|
+
function matchPath(pattern, p) {
|
|
7131
|
+
const patternParts = pattern.split("/").filter(Boolean);
|
|
7132
|
+
const pathParts = p.split("/").filter(Boolean);
|
|
7133
|
+
if (patternParts.length !== pathParts.length) return null;
|
|
7134
|
+
const params = {};
|
|
7135
|
+
for (let i = 0; i < patternParts.length; i++) {
|
|
7136
|
+
const a = patternParts[i];
|
|
7137
|
+
const b = pathParts[i];
|
|
7138
|
+
if (a.startsWith(":")) params[a.slice(1)] = b;
|
|
7139
|
+
else if (a !== b) return null;
|
|
7140
|
+
}
|
|
7141
|
+
return params;
|
|
7142
|
+
}
|
|
7143
|
+
function buildCapturingReply(envelope) {
|
|
7144
|
+
const wrapper = {
|
|
7145
|
+
status(code) {
|
|
7146
|
+
envelope.status = code;
|
|
7147
|
+
return wrapper;
|
|
7148
|
+
},
|
|
7149
|
+
code(code) {
|
|
7150
|
+
envelope.status = code;
|
|
7151
|
+
return wrapper;
|
|
7152
|
+
},
|
|
7153
|
+
send(data) {
|
|
7154
|
+
envelope.body = data;
|
|
7155
|
+
},
|
|
7156
|
+
redirect(url) {
|
|
7157
|
+
envelope.redirectUrl = url;
|
|
7158
|
+
if (envelope.status === 200) envelope.status = 302;
|
|
7159
|
+
},
|
|
7160
|
+
header(name, value) {
|
|
7161
|
+
envelope.headers[name.toLowerCase()] = value;
|
|
7162
|
+
return wrapper;
|
|
7163
|
+
},
|
|
7164
|
+
type(mime) {
|
|
7165
|
+
envelope.contentType = mime;
|
|
7166
|
+
return wrapper;
|
|
7167
|
+
}
|
|
7168
|
+
};
|
|
7169
|
+
return wrapper;
|
|
7170
|
+
}
|
|
7171
|
+
/**
|
|
7055
7172
|
* Numeric day-of-week: 0 = Sunday … 6 = Saturday (matches `Date.getDay`).
|
|
7056
7173
|
* Named `RecordingWeekday` to avoid collision with the string-union
|
|
7057
7174
|
* `Weekday` exported from `interfaces/timezones.ts`.
|
|
@@ -16284,6 +16401,19 @@ method(_void(), array(TurnServerSchema).readonly());
|
|
|
16284
16401
|
* b. `finishAuthentication({userId, response})` → server verifies
|
|
16285
16402
|
* the assertion, bumps the credential counter, returns ok.
|
|
16286
16403
|
*
|
|
16404
|
+
* 2b. Usernameless (discoverable-credential) authentication — the
|
|
16405
|
+
* passkey IS the primary factor, no password leg:
|
|
16406
|
+
* a. `beginDiscoverableAuthentication({})` → assertion options with
|
|
16407
|
+
* EMPTY `allowCredentials` (the browser offers every resident
|
|
16408
|
+
* passkey it holds for this RP) + `userVerification: 'required'`
|
|
16409
|
+
* (the passkey replaces both factors, so UV is mandatory).
|
|
16410
|
+
* The challenge is stored server-side, NOT bound to any user.
|
|
16411
|
+
* b. `finishDiscoverableAuthentication({response})` → the provider
|
|
16412
|
+
* resolves the credential by the response's credential id,
|
|
16413
|
+
* verifies the assertion against the stored challenge + that
|
|
16414
|
+
* credential's public key/counter, and returns the OWNING
|
|
16415
|
+
* `userId` — the caller (core auth router) mints the session.
|
|
16416
|
+
*
|
|
16287
16417
|
* 3. Management:
|
|
16288
16418
|
* - `listPasskeys({userId})` — enumerate user's enrolled credentials.
|
|
16289
16419
|
* - `removePasskey({userId, credentialId})` — revoke one credential.
|
|
@@ -16340,6 +16470,19 @@ var userPasskeysCapability = {
|
|
|
16340
16470
|
kind: "mutation",
|
|
16341
16471
|
access: "view"
|
|
16342
16472
|
}),
|
|
16473
|
+
beginDiscoverableAuthentication: method(object({}), object({ optionsJSON: record(string(), unknown()) }), {
|
|
16474
|
+
kind: "mutation",
|
|
16475
|
+
access: "view"
|
|
16476
|
+
}),
|
|
16477
|
+
finishDiscoverableAuthentication: method(object({
|
|
16478
|
+
/** AuthenticationResponseJSON from the browser. */
|
|
16479
|
+
response: record(string(), unknown()) }), object({
|
|
16480
|
+
verified: boolean(),
|
|
16481
|
+
userId: string().nullable()
|
|
16482
|
+
}), {
|
|
16483
|
+
kind: "mutation",
|
|
16484
|
+
access: "view"
|
|
16485
|
+
}),
|
|
16343
16486
|
listPasskeys: method(object({ userId: string() }), array(PasskeySummarySchema), { auth: "admin" }),
|
|
16344
16487
|
removePasskey: method(object({
|
|
16345
16488
|
userId: string(),
|
|
@@ -22842,6 +22985,12 @@ Object.freeze({
|
|
|
22842
22985
|
addonId: null,
|
|
22843
22986
|
access: "view"
|
|
22844
22987
|
},
|
|
22988
|
+
"userPasskeys.beginDiscoverableAuthentication": {
|
|
22989
|
+
capName: "user-passkeys",
|
|
22990
|
+
capScope: "system",
|
|
22991
|
+
addonId: null,
|
|
22992
|
+
access: "view"
|
|
22993
|
+
},
|
|
22845
22994
|
"userPasskeys.beginRegistration": {
|
|
22846
22995
|
capName: "user-passkeys",
|
|
22847
22996
|
capScope: "system",
|
|
@@ -22854,6 +23003,12 @@ Object.freeze({
|
|
|
22854
23003
|
addonId: null,
|
|
22855
23004
|
access: "view"
|
|
22856
23005
|
},
|
|
23006
|
+
"userPasskeys.finishDiscoverableAuthentication": {
|
|
23007
|
+
capName: "user-passkeys",
|
|
23008
|
+
capScope: "system",
|
|
23009
|
+
addonId: null,
|
|
23010
|
+
access: "view"
|
|
23011
|
+
},
|
|
22857
23012
|
"userPasskeys.finishRegistration": {
|
|
22858
23013
|
capName: "user-passkeys",
|
|
22859
23014
|
capScope: "system",
|
|
@@ -23154,6 +23309,12 @@ Object.defineProperty(exports, "authProviderCapability", {
|
|
|
23154
23309
|
return authProviderCapability;
|
|
23155
23310
|
}
|
|
23156
23311
|
});
|
|
23312
|
+
Object.defineProperty(exports, "buildAddonRouteProvider", {
|
|
23313
|
+
enumerable: true,
|
|
23314
|
+
get: function() {
|
|
23315
|
+
return buildAddonRouteProvider;
|
|
23316
|
+
}
|
|
23317
|
+
});
|
|
23157
23318
|
Object.defineProperty(exports, "errMsg", {
|
|
23158
23319
|
enumerable: true,
|
|
23159
23320
|
get: function() {
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
require("../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
6
|
+
const require_dist = require("../dist-biwV9TkV.js");
|
|
7
7
|
//#region src/magic-link/auth-magic-link.addon.ts
|
|
8
8
|
/**
|
|
9
9
|
* Magic-link authentication addon.
|
|
@@ -30,6 +30,7 @@ const require_dist = require("../dist-mmJDfwXE.js");
|
|
|
30
30
|
* - Auto-provision unknown emails (currently rejected for safety).
|
|
31
31
|
*/
|
|
32
32
|
var DEFAULT_CONFIG = {
|
|
33
|
+
enabled: false,
|
|
33
34
|
displayName: "Email magic link",
|
|
34
35
|
icon: "mail",
|
|
35
36
|
publicOrigin: "",
|
|
@@ -52,7 +53,7 @@ var AuthMagicLinkAddon = class extends require_dist.BaseAddon {
|
|
|
52
53
|
throw new Error("Magic link does not use callback — the email link redirects to /api/auth/sso/finish directly");
|
|
53
54
|
}
|
|
54
55
|
};
|
|
55
|
-
const
|
|
56
|
+
const routeProvider = require_dist.buildAddonRouteProvider("auth-magic-link", [
|
|
56
57
|
{
|
|
57
58
|
method: "GET",
|
|
58
59
|
path: "/start",
|
|
@@ -74,11 +75,7 @@ var AuthMagicLinkAddon = class extends require_dist.BaseAddon {
|
|
|
74
75
|
description: "Click target embedded in the magic-link email",
|
|
75
76
|
handler: async (req, reply) => this.handleLogin(req, reply)
|
|
76
77
|
}
|
|
77
|
-
];
|
|
78
|
-
const routeProvider = {
|
|
79
|
-
id: "auth-magic-link",
|
|
80
|
-
getRoutes: () => routes
|
|
81
|
-
};
|
|
78
|
+
]);
|
|
82
79
|
const loginMethodProvider = { getLoginMethods: async () => this.buildLoginMethods() };
|
|
83
80
|
this.ctx.logger.info("Magic-link auth provider initialized", { meta: { deliveryMode: this.config.deliveryMode } });
|
|
84
81
|
return [
|
|
@@ -97,6 +94,7 @@ var AuthMagicLinkAddon = class extends require_dist.BaseAddon {
|
|
|
97
94
|
];
|
|
98
95
|
}
|
|
99
96
|
buildLoginMethods() {
|
|
97
|
+
if (!this.config.enabled) return [];
|
|
100
98
|
return [{
|
|
101
99
|
kind: "redirect",
|
|
102
100
|
id: "auth-magic-link",
|
|
@@ -113,6 +111,13 @@ var AuthMagicLinkAddon = class extends require_dist.BaseAddon {
|
|
|
113
111
|
description: "Passwordless email login. The user types an email; the hub mails them a one-time link valid for the configured TTL.",
|
|
114
112
|
columns: 1,
|
|
115
113
|
fields: [
|
|
114
|
+
this.field({
|
|
115
|
+
type: "boolean",
|
|
116
|
+
key: "enabled",
|
|
117
|
+
label: "Enabled",
|
|
118
|
+
description: "Offer the magic-link button on the login page. Leave off until email delivery is configured.",
|
|
119
|
+
default: DEFAULT_CONFIG.enabled
|
|
120
|
+
}),
|
|
116
121
|
this.field({
|
|
117
122
|
type: "text",
|
|
118
123
|
key: "displayName",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as loginMethodCapability, c as BaseAddon, i as buildAddonRouteProvider, r as authProviderCapability, s as errMsg, t as addonRoutesCapability } from "../dist-BopfLZ9P.mjs";
|
|
2
2
|
//#region src/magic-link/auth-magic-link.addon.ts
|
|
3
3
|
/**
|
|
4
4
|
* Magic-link authentication addon.
|
|
@@ -25,6 +25,7 @@ import { i as loginMethodCapability, o as errMsg, r as authProviderCapability, s
|
|
|
25
25
|
* - Auto-provision unknown emails (currently rejected for safety).
|
|
26
26
|
*/
|
|
27
27
|
var DEFAULT_CONFIG = {
|
|
28
|
+
enabled: false,
|
|
28
29
|
displayName: "Email magic link",
|
|
29
30
|
icon: "mail",
|
|
30
31
|
publicOrigin: "",
|
|
@@ -47,7 +48,7 @@ var AuthMagicLinkAddon = class extends BaseAddon {
|
|
|
47
48
|
throw new Error("Magic link does not use callback — the email link redirects to /api/auth/sso/finish directly");
|
|
48
49
|
}
|
|
49
50
|
};
|
|
50
|
-
const
|
|
51
|
+
const routeProvider = buildAddonRouteProvider("auth-magic-link", [
|
|
51
52
|
{
|
|
52
53
|
method: "GET",
|
|
53
54
|
path: "/start",
|
|
@@ -69,11 +70,7 @@ var AuthMagicLinkAddon = class extends BaseAddon {
|
|
|
69
70
|
description: "Click target embedded in the magic-link email",
|
|
70
71
|
handler: async (req, reply) => this.handleLogin(req, reply)
|
|
71
72
|
}
|
|
72
|
-
];
|
|
73
|
-
const routeProvider = {
|
|
74
|
-
id: "auth-magic-link",
|
|
75
|
-
getRoutes: () => routes
|
|
76
|
-
};
|
|
73
|
+
]);
|
|
77
74
|
const loginMethodProvider = { getLoginMethods: async () => this.buildLoginMethods() };
|
|
78
75
|
this.ctx.logger.info("Magic-link auth provider initialized", { meta: { deliveryMode: this.config.deliveryMode } });
|
|
79
76
|
return [
|
|
@@ -92,6 +89,7 @@ var AuthMagicLinkAddon = class extends BaseAddon {
|
|
|
92
89
|
];
|
|
93
90
|
}
|
|
94
91
|
buildLoginMethods() {
|
|
92
|
+
if (!this.config.enabled) return [];
|
|
95
93
|
return [{
|
|
96
94
|
kind: "redirect",
|
|
97
95
|
id: "auth-magic-link",
|
|
@@ -108,6 +106,13 @@ var AuthMagicLinkAddon = class extends BaseAddon {
|
|
|
108
106
|
description: "Passwordless email login. The user types an email; the hub mails them a one-time link valid for the configured TTL.",
|
|
109
107
|
columns: 1,
|
|
110
108
|
fields: [
|
|
109
|
+
this.field({
|
|
110
|
+
type: "boolean",
|
|
111
|
+
key: "enabled",
|
|
112
|
+
label: "Enabled",
|
|
113
|
+
description: "Offer the magic-link button on the login page. Leave off until email delivery is configured.",
|
|
114
|
+
default: DEFAULT_CONFIG.enabled
|
|
115
|
+
}),
|
|
111
116
|
this.field({
|
|
112
117
|
type: "text",
|
|
113
118
|
key: "displayName",
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
6
|
+
const require_dist = require("../dist-biwV9TkV.js");
|
|
7
7
|
let node_crypto = require("node:crypto");
|
|
8
8
|
node_crypto = require_chunk.__toESM(node_crypto);
|
|
9
9
|
//#region node_modules/jose/dist/webapi/lib/buffer_utils.js
|
|
@@ -1237,7 +1237,7 @@ var AuthOidcAddon = class extends require_dist.BaseAddon {
|
|
|
1237
1237
|
return this.handleCallback(code, state);
|
|
1238
1238
|
}
|
|
1239
1239
|
};
|
|
1240
|
-
const
|
|
1240
|
+
const routeProvider = require_dist.buildAddonRouteProvider("auth-oidc", [{
|
|
1241
1241
|
method: "GET",
|
|
1242
1242
|
path: "/:providerId/start",
|
|
1243
1243
|
access: "public",
|
|
@@ -1249,11 +1249,7 @@ var AuthOidcAddon = class extends require_dist.BaseAddon {
|
|
|
1249
1249
|
access: "public",
|
|
1250
1250
|
description: "OIDC redirect callback for a configured provider",
|
|
1251
1251
|
handler: async (req, reply) => this.handleCallbackRoute(req, reply)
|
|
1252
|
-
}];
|
|
1253
|
-
const routeProvider = {
|
|
1254
|
-
id: "auth-oidc",
|
|
1255
|
-
getRoutes: () => routes
|
|
1256
|
-
};
|
|
1252
|
+
}]);
|
|
1257
1253
|
const loginMethodProvider = { getLoginMethods: async () => this.buildLoginMethods() };
|
|
1258
1254
|
this.ctx.logger.info("OIDC auth provider initialized", { meta: { providers: [...this.instances.keys()] } });
|
|
1259
1255
|
return [
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as loginMethodCapability, c as BaseAddon, i as buildAddonRouteProvider, r as authProviderCapability, s as errMsg, t as addonRoutesCapability } from "../dist-BopfLZ9P.mjs";
|
|
2
2
|
import * as crypto$1 from "node:crypto";
|
|
3
3
|
//#region node_modules/jose/dist/webapi/lib/buffer_utils.js
|
|
4
4
|
var encoder = new TextEncoder();
|
|
@@ -1231,7 +1231,7 @@ var AuthOidcAddon = class extends BaseAddon {
|
|
|
1231
1231
|
return this.handleCallback(code, state);
|
|
1232
1232
|
}
|
|
1233
1233
|
};
|
|
1234
|
-
const
|
|
1234
|
+
const routeProvider = buildAddonRouteProvider("auth-oidc", [{
|
|
1235
1235
|
method: "GET",
|
|
1236
1236
|
path: "/:providerId/start",
|
|
1237
1237
|
access: "public",
|
|
@@ -1243,11 +1243,7 @@ var AuthOidcAddon = class extends BaseAddon {
|
|
|
1243
1243
|
access: "public",
|
|
1244
1244
|
description: "OIDC redirect callback for a configured provider",
|
|
1245
1245
|
handler: async (req, reply) => this.handleCallbackRoute(req, reply)
|
|
1246
|
-
}];
|
|
1247
|
-
const routeProvider = {
|
|
1248
|
-
id: "auth-oidc",
|
|
1249
|
-
getRoutes: () => routes
|
|
1250
|
-
};
|
|
1246
|
+
}]);
|
|
1251
1247
|
const loginMethodProvider = { getLoginMethods: async () => this.buildLoginMethods() };
|
|
1252
1248
|
this.ctx.logger.info("OIDC auth provider initialized", { meta: { providers: [...this.instances.keys()] } });
|
|
1253
1249
|
return [
|
package/dist/webauthn/_stub.js
CHANGED
|
@@ -559,10 +559,76 @@ function H(e) {
|
|
|
559
559
|
});
|
|
560
560
|
}
|
|
561
561
|
//#endregion
|
|
562
|
-
//#region src/webauthn/widgets/
|
|
562
|
+
//#region src/webauthn/widgets/direct-login-i18n.ts
|
|
563
563
|
var U = {
|
|
564
|
+
en: {
|
|
565
|
+
signInWithPasskey: "Sign in with a passkey",
|
|
566
|
+
unsupported: "This browser does not support passkeys.",
|
|
567
|
+
failed: "Passkey sign-in failed"
|
|
568
|
+
},
|
|
569
|
+
it: {
|
|
570
|
+
signInWithPasskey: "Accedi con una passkey",
|
|
571
|
+
unsupported: "Questo browser non supporta le passkey.",
|
|
572
|
+
failed: "Accesso con passkey non riuscito"
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
function W(e) {
|
|
576
|
+
return (e ?? globalThis.navigator?.language ?? "en").toLowerCase().split("-")[0] === "it" ? "it" : "en";
|
|
577
|
+
}
|
|
578
|
+
function G(e, t) {
|
|
579
|
+
return U[e][t];
|
|
580
|
+
}
|
|
581
|
+
//#endregion
|
|
582
|
+
//#region src/webauthn/widgets/PasskeyDirectLogin.tsx
|
|
583
|
+
function K(e) {
|
|
584
|
+
if (!e) return null;
|
|
585
|
+
let t = e.onComplete, n = e.onError, r = e.locale;
|
|
586
|
+
return typeof t == "function" ? {
|
|
587
|
+
onComplete: t,
|
|
588
|
+
onError: typeof n == "function" ? n : void 0,
|
|
589
|
+
locale: typeof r == "string" ? r : void 0
|
|
590
|
+
} : null;
|
|
591
|
+
}
|
|
592
|
+
function q(e) {
|
|
593
|
+
let n = a(), r = K(e.config), [i, c] = t(!1), [l, u] = t(null), d = W(r?.locale);
|
|
594
|
+
return r ? L() ? /* @__PURE__ */ s("div", {
|
|
595
|
+
className: "space-y-2",
|
|
596
|
+
children: [/* @__PURE__ */ s("button", {
|
|
597
|
+
type: "button",
|
|
598
|
+
"data-testid": "login-passkey-direct",
|
|
599
|
+
onClick: async () => {
|
|
600
|
+
u(null), c(!0);
|
|
601
|
+
try {
|
|
602
|
+
let e = await R((await n.passkeyLoginBegin()).optionsJSON), t = await n.passkeyLoginFinish(e);
|
|
603
|
+
r.onComplete(t.token);
|
|
604
|
+
} catch (e) {
|
|
605
|
+
let t = e instanceof Error ? e.message : G(d, "failed");
|
|
606
|
+
u(t), r.onError?.(t);
|
|
607
|
+
} finally {
|
|
608
|
+
c(!1);
|
|
609
|
+
}
|
|
610
|
+
},
|
|
611
|
+
disabled: i,
|
|
612
|
+
className: "w-full flex items-center justify-center gap-2 rounded-lg border border-border bg-surface px-4 py-2.5 text-sm font-medium text-foreground hover:bg-surface-hover disabled:opacity-50 transition-colors",
|
|
613
|
+
children: [i ? /* @__PURE__ */ o(b, { className: "h-4 w-4 animate-spin text-primary" }) : /* @__PURE__ */ o(v, { className: "h-4 w-4 text-primary" }), G(d, "signInWithPasskey")]
|
|
614
|
+
}), l && /* @__PURE__ */ o("div", {
|
|
615
|
+
className: "rounded-md bg-danger/10 border border-danger/20 px-3 py-2 text-xs text-danger",
|
|
616
|
+
children: l
|
|
617
|
+
})]
|
|
618
|
+
}) : /* @__PURE__ */ o("div", {
|
|
619
|
+
className: "rounded-lg border border-border bg-surface px-3 py-2 text-xs text-foreground-subtle",
|
|
620
|
+
children: G(d, "unsupported")
|
|
621
|
+
}) : /* @__PURE__ */ o("div", {
|
|
622
|
+
className: "rounded-lg border border-warning/30 bg-warning/10 px-3 py-2 text-xs text-warning",
|
|
623
|
+
children: "Passkey direct-login widget requires an onComplete callback."
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
//#endregion
|
|
627
|
+
//#region src/webauthn/widgets/index.tsx
|
|
628
|
+
var J = {
|
|
564
629
|
"passkey-enrollment": B,
|
|
565
|
-
"passkey-login": H
|
|
630
|
+
"passkey-login": H,
|
|
631
|
+
"passkey-direct-login": q
|
|
566
632
|
};
|
|
567
633
|
//#endregion
|
|
568
|
-
export {
|
|
634
|
+
export { J as default };
|
|
@@ -3,7 +3,7 @@ import "./dist-CYZr2fwk.mjs";
|
|
|
3
3
|
var e = {
|
|
4
4
|
"@camstack/sdk": {
|
|
5
5
|
name: "@camstack/sdk",
|
|
6
|
-
version: "1.1.
|
|
6
|
+
version: "1.1.21",
|
|
7
7
|
scope: ["default"],
|
|
8
8
|
loaded: !1,
|
|
9
9
|
from: "addon_auth_webauthn_widgets",
|
|
@@ -18,7 +18,7 @@ var e = {
|
|
|
18
18
|
},
|
|
19
19
|
"@camstack/types": {
|
|
20
20
|
name: "@camstack/types",
|
|
21
|
-
version: "1.1.
|
|
21
|
+
version: "1.1.37",
|
|
22
22
|
scope: ["default"],
|
|
23
23
|
loaded: !1,
|
|
24
24
|
from: "addon_auth_webauthn_widgets",
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
6
|
+
const require_dist = require("../dist-biwV9TkV.js");
|
|
7
7
|
let stream = require("stream");
|
|
8
8
|
stream = require_chunk.__toESM(stream, 1);
|
|
9
9
|
let http = require("http");
|
|
@@ -12257,61 +12257,97 @@ var AUTH_WEBAUTHN_REMOTE_NAME = "addon_auth_webauthn_widgets";
|
|
|
12257
12257
|
var AUTH_WEBAUTHN_BUNDLE = "remoteEntry.js";
|
|
12258
12258
|
/** Manifest addon id — the public bundle namespace + login-method addonId. */
|
|
12259
12259
|
var AUTH_WEBAUTHN_ADDON_ID = "auth-webauthn";
|
|
12260
|
-
var authWebauthnWidgets = [
|
|
12261
|
-
|
|
12262
|
-
|
|
12263
|
-
|
|
12264
|
-
|
|
12265
|
-
|
|
12266
|
-
|
|
12267
|
-
|
|
12268
|
-
|
|
12269
|
-
|
|
12270
|
-
|
|
12271
|
-
|
|
12272
|
-
|
|
12273
|
-
|
|
12274
|
-
|
|
12275
|
-
|
|
12276
|
-
|
|
12277
|
-
|
|
12278
|
-
|
|
12279
|
-
|
|
12280
|
-
|
|
12281
|
-
|
|
12282
|
-
|
|
12283
|
-
|
|
12284
|
-
|
|
12285
|
-
|
|
12286
|
-
|
|
12287
|
-
|
|
12288
|
-
tab: "dashboard",
|
|
12289
|
-
label: "Passkey login",
|
|
12290
|
-
kind: "remote",
|
|
12291
|
-
remote: {
|
|
12292
|
-
remoteName: AUTH_WEBAUTHN_REMOTE_NAME,
|
|
12293
|
-
exposedModule: "./widgets",
|
|
12294
|
-
componentKey: "passkey-login"
|
|
12260
|
+
var authWebauthnWidgets = [
|
|
12261
|
+
{
|
|
12262
|
+
tab: "dashboard",
|
|
12263
|
+
label: "Passkeys",
|
|
12264
|
+
kind: "remote",
|
|
12265
|
+
remote: {
|
|
12266
|
+
remoteName: AUTH_WEBAUTHN_REMOTE_NAME,
|
|
12267
|
+
exposedModule: "./widgets",
|
|
12268
|
+
componentKey: "passkey-enrollment"
|
|
12269
|
+
},
|
|
12270
|
+
stableId: "passkey-enrollment",
|
|
12271
|
+
description: "Enroll and manage passkeys (WebAuthn) for your account.",
|
|
12272
|
+
icon: "fingerprint",
|
|
12273
|
+
bundle: AUTH_WEBAUTHN_BUNDLE,
|
|
12274
|
+
hosts: ["dashboard"],
|
|
12275
|
+
requires: {
|
|
12276
|
+
deviceContext: false,
|
|
12277
|
+
integrationContext: false
|
|
12278
|
+
},
|
|
12279
|
+
defaultSize: "lg",
|
|
12280
|
+
allowedSizes: [
|
|
12281
|
+
"md",
|
|
12282
|
+
"lg",
|
|
12283
|
+
"xl"
|
|
12284
|
+
],
|
|
12285
|
+
defaultColumns: 12,
|
|
12286
|
+
defaultRows: 3,
|
|
12287
|
+
preAuth: false
|
|
12295
12288
|
},
|
|
12296
|
-
|
|
12297
|
-
|
|
12298
|
-
|
|
12299
|
-
|
|
12300
|
-
|
|
12301
|
-
|
|
12302
|
-
|
|
12303
|
-
|
|
12289
|
+
{
|
|
12290
|
+
tab: "dashboard",
|
|
12291
|
+
label: "Passkey login",
|
|
12292
|
+
kind: "remote",
|
|
12293
|
+
remote: {
|
|
12294
|
+
remoteName: AUTH_WEBAUTHN_REMOTE_NAME,
|
|
12295
|
+
exposedModule: "./widgets",
|
|
12296
|
+
componentKey: "passkey-login"
|
|
12297
|
+
},
|
|
12298
|
+
stableId: "passkey-login",
|
|
12299
|
+
description: "Login-page passkey second-factor ceremony.",
|
|
12300
|
+
icon: "fingerprint",
|
|
12301
|
+
bundle: AUTH_WEBAUTHN_BUNDLE,
|
|
12302
|
+
hosts: ["dashboard"],
|
|
12303
|
+
requires: {
|
|
12304
|
+
deviceContext: false,
|
|
12305
|
+
integrationContext: false
|
|
12306
|
+
},
|
|
12307
|
+
defaultSize: "sm",
|
|
12308
|
+
allowedSizes: ["sm"],
|
|
12309
|
+
defaultColumns: 4,
|
|
12310
|
+
defaultRows: 1,
|
|
12311
|
+
preAuth: true
|
|
12304
12312
|
},
|
|
12305
|
-
|
|
12306
|
-
|
|
12307
|
-
|
|
12308
|
-
|
|
12309
|
-
|
|
12310
|
-
|
|
12313
|
+
{
|
|
12314
|
+
tab: "dashboard",
|
|
12315
|
+
label: "Passkey sign-in",
|
|
12316
|
+
kind: "remote",
|
|
12317
|
+
remote: {
|
|
12318
|
+
remoteName: AUTH_WEBAUTHN_REMOTE_NAME,
|
|
12319
|
+
exposedModule: "./widgets",
|
|
12320
|
+
componentKey: "passkey-direct-login"
|
|
12321
|
+
},
|
|
12322
|
+
stableId: "passkey-direct-login",
|
|
12323
|
+
description: "Login-page usernameless (discoverable-credential) passkey sign-in.",
|
|
12324
|
+
icon: "fingerprint",
|
|
12325
|
+
bundle: AUTH_WEBAUTHN_BUNDLE,
|
|
12326
|
+
hosts: ["dashboard"],
|
|
12327
|
+
requires: {
|
|
12328
|
+
deviceContext: false,
|
|
12329
|
+
integrationContext: false
|
|
12330
|
+
},
|
|
12331
|
+
defaultSize: "sm",
|
|
12332
|
+
allowedSizes: ["sm"],
|
|
12333
|
+
defaultColumns: 4,
|
|
12334
|
+
defaultRows: 1,
|
|
12335
|
+
preAuth: true
|
|
12336
|
+
}
|
|
12337
|
+
];
|
|
12311
12338
|
/**
|
|
12312
|
-
* Login-method
|
|
12313
|
-
*
|
|
12314
|
-
*
|
|
12339
|
+
* Login-method contributions — aggregated by the public
|
|
12340
|
+
* `auth.listLoginMethods` procedure, which stamps a public `bundleUrl`
|
|
12341
|
+
* from `addonId` + `bundle`.
|
|
12342
|
+
*
|
|
12343
|
+
* - `passkey-login` (second-factor) — the post-password 2FA ceremony,
|
|
12344
|
+
* mounted only when the challenged user has the `passkey` factor.
|
|
12345
|
+
* - `passkey-direct-login` (primary) — usernameless sign-in via the
|
|
12346
|
+
* standard WebAuthn discoverable-credentials flow. Contributed
|
|
12347
|
+
* unconditionally while the addon is registered: the browser's own
|
|
12348
|
+
* passkey picker is the honest gate (it simply offers nothing when
|
|
12349
|
+
* no resident credential matches the RP) — probing the server for
|
|
12350
|
+
* "does anyone have a passkey" pre-auth would leak enrollment state.
|
|
12315
12351
|
*/
|
|
12316
12352
|
var authWebauthnLoginMethods = [{
|
|
12317
12353
|
kind: "widget",
|
|
@@ -12324,6 +12360,17 @@ var authWebauthnLoginMethods = [{
|
|
|
12324
12360
|
componentKey: "passkey-login"
|
|
12325
12361
|
},
|
|
12326
12362
|
stage: "second-factor"
|
|
12363
|
+
}, {
|
|
12364
|
+
kind: "widget",
|
|
12365
|
+
id: `${AUTH_WEBAUTHN_ADDON_ID}/passkey-direct-login`,
|
|
12366
|
+
addonId: AUTH_WEBAUTHN_ADDON_ID,
|
|
12367
|
+
bundle: AUTH_WEBAUTHN_BUNDLE,
|
|
12368
|
+
remote: {
|
|
12369
|
+
remoteName: AUTH_WEBAUTHN_REMOTE_NAME,
|
|
12370
|
+
exposedModule: "./widgets",
|
|
12371
|
+
componentKey: "passkey-direct-login"
|
|
12372
|
+
},
|
|
12373
|
+
stage: "primary"
|
|
12327
12374
|
}];
|
|
12328
12375
|
//#endregion
|
|
12329
12376
|
//#region src/webauthn/auth-webauthn.addon.ts
|
|
@@ -12388,6 +12435,8 @@ var AuthWebauthnAddon = class extends require_dist.BaseAddon {
|
|
|
12388
12435
|
finishRegistration: async ({ userId, response, label }) => this.finishRegistration(userId, response, label),
|
|
12389
12436
|
beginAuthentication: async ({ userId }) => this.beginAuthentication(userId),
|
|
12390
12437
|
finishAuthentication: async ({ userId, response }) => this.finishAuthentication(userId, response),
|
|
12438
|
+
beginDiscoverableAuthentication: async () => this.beginDiscoverableAuthentication(),
|
|
12439
|
+
finishDiscoverableAuthentication: async ({ response }) => this.finishDiscoverableAuthentication(response),
|
|
12391
12440
|
listPasskeys: async ({ userId }) => this.listPasskeys(userId),
|
|
12392
12441
|
removePasskey: async ({ userId, credentialId }) => this.removePasskey(userId, credentialId)
|
|
12393
12442
|
};
|
|
@@ -12505,9 +12554,12 @@ var AuthWebauthnAddon = class extends require_dist.BaseAddon {
|
|
|
12505
12554
|
if (this.config.origin && this.config.origin.trim()) return this.config.origin.trim().replace(/\/+$/, "");
|
|
12506
12555
|
return (process.env["CAMSTACK_PUBLIC_ORIGIN"] || "http://localhost:4443").replace(/\/+$/, "");
|
|
12507
12556
|
}
|
|
12557
|
+
challengeTtlMs() {
|
|
12558
|
+
return (this.config.challengeTtlSec || DEFAULT_CONFIG.challengeTtlSec) * 1e3;
|
|
12559
|
+
}
|
|
12508
12560
|
pruneExpiredChallenges() {
|
|
12509
12561
|
const now = Date.now();
|
|
12510
|
-
const ttlMs =
|
|
12562
|
+
const ttlMs = this.challengeTtlMs();
|
|
12511
12563
|
for (const [k, v] of this.pending.entries()) if (now - v.createdAt > ttlMs) this.pending.delete(k);
|
|
12512
12564
|
}
|
|
12513
12565
|
base64urlEncode(bytes) {
|
|
@@ -12633,6 +12685,105 @@ var AuthWebauthnAddon = class extends require_dist.BaseAddon {
|
|
|
12633
12685
|
await this.store.updateCounter(credentialId, verification.authenticationInfo.newCounter);
|
|
12634
12686
|
return { verified: true };
|
|
12635
12687
|
}
|
|
12688
|
+
/**
|
|
12689
|
+
* Usernameless (discoverable-credential) login — leg 1. Issues
|
|
12690
|
+
* assertion options with an EMPTY `allowCredentials` list so the
|
|
12691
|
+
* browser offers every resident passkey it holds for this RP, and
|
|
12692
|
+
* `userVerification: 'required'` because the passkey is the ONLY
|
|
12693
|
+
* factor (it replaces both username/password and the 2FA leg).
|
|
12694
|
+
* The challenge is stored server-side keyed by its own value and is
|
|
12695
|
+
* NOT bound to any user — `finishDiscoverableAuthentication` resolves
|
|
12696
|
+
* the owner from the asserted credential id.
|
|
12697
|
+
*/
|
|
12698
|
+
async beginDiscoverableAuthentication() {
|
|
12699
|
+
if (!this.store) throw new Error("passkey store unavailable");
|
|
12700
|
+
this.pruneExpiredChallenges();
|
|
12701
|
+
const options = await generateAuthenticationOptions({
|
|
12702
|
+
rpID: this.resolveRpID(),
|
|
12703
|
+
allowCredentials: [],
|
|
12704
|
+
userVerification: "required"
|
|
12705
|
+
});
|
|
12706
|
+
this.pending.set(options.challenge, {
|
|
12707
|
+
challenge: options.challenge,
|
|
12708
|
+
userId: "",
|
|
12709
|
+
kind: "discoverable-authentication",
|
|
12710
|
+
createdAt: Date.now()
|
|
12711
|
+
});
|
|
12712
|
+
return { optionsJSON: options };
|
|
12713
|
+
}
|
|
12714
|
+
/**
|
|
12715
|
+
* Usernameless login — leg 2. Resolves the credential by the
|
|
12716
|
+
* response's credential id, verifies the assertion against the stored
|
|
12717
|
+
* challenge + that credential's public key/counter, and returns the
|
|
12718
|
+
* OWNING userId. Fail-closed: every anomaly returns
|
|
12719
|
+
* `{ verified: false, userId: null }`.
|
|
12720
|
+
*
|
|
12721
|
+
* Replay protection: the pending challenge is consumed (deleted)
|
|
12722
|
+
* BEFORE verification, so an assertion can be spent exactly once.
|
|
12723
|
+
* The TTL is enforced here too — pruning alone only runs on begin
|
|
12724
|
+
* legs, which an attacker controls the timing of.
|
|
12725
|
+
*/
|
|
12726
|
+
async finishDiscoverableAuthentication(response) {
|
|
12727
|
+
if (!this.store) return {
|
|
12728
|
+
verified: false,
|
|
12729
|
+
userId: null
|
|
12730
|
+
};
|
|
12731
|
+
const challenge = this.decodeClientData(response)?.challenge;
|
|
12732
|
+
if (!challenge) return {
|
|
12733
|
+
verified: false,
|
|
12734
|
+
userId: null
|
|
12735
|
+
};
|
|
12736
|
+
const pending = this.pending.get(challenge);
|
|
12737
|
+
if (!pending || pending.kind !== "discoverable-authentication") return {
|
|
12738
|
+
verified: false,
|
|
12739
|
+
userId: null
|
|
12740
|
+
};
|
|
12741
|
+
this.pending.delete(challenge);
|
|
12742
|
+
if (Date.now() - pending.createdAt > this.challengeTtlMs()) return {
|
|
12743
|
+
verified: false,
|
|
12744
|
+
userId: null
|
|
12745
|
+
};
|
|
12746
|
+
const credentialId = typeof response["id"] === "string" ? response["id"] : "";
|
|
12747
|
+
const stored = await this.store.findByCredentialId(credentialId);
|
|
12748
|
+
if (!stored) return {
|
|
12749
|
+
verified: false,
|
|
12750
|
+
userId: null
|
|
12751
|
+
};
|
|
12752
|
+
let verification;
|
|
12753
|
+
try {
|
|
12754
|
+
verification = await verifyAuthenticationResponse({
|
|
12755
|
+
response,
|
|
12756
|
+
expectedChallenge: challenge,
|
|
12757
|
+
expectedRPID: this.resolveRpID(),
|
|
12758
|
+
expectedOrigin: this.resolveOrigin(),
|
|
12759
|
+
requireUserVerification: true,
|
|
12760
|
+
credential: {
|
|
12761
|
+
id: stored.credentialId,
|
|
12762
|
+
publicKey: this.base64urlDecode(stored.publicKey),
|
|
12763
|
+
counter: stored.counter,
|
|
12764
|
+
transports: stored.transports
|
|
12765
|
+
}
|
|
12766
|
+
});
|
|
12767
|
+
} catch (err) {
|
|
12768
|
+
this.ctx.logger.warn("Discoverable passkey verification threw", { meta: {
|
|
12769
|
+
credentialId,
|
|
12770
|
+
err: err instanceof Error ? err.message : String(err)
|
|
12771
|
+
} });
|
|
12772
|
+
return {
|
|
12773
|
+
verified: false,
|
|
12774
|
+
userId: null
|
|
12775
|
+
};
|
|
12776
|
+
}
|
|
12777
|
+
if (!verification.verified || !verification.authenticationInfo) return {
|
|
12778
|
+
verified: false,
|
|
12779
|
+
userId: null
|
|
12780
|
+
};
|
|
12781
|
+
await this.store.updateCounter(credentialId, verification.authenticationInfo.newCounter);
|
|
12782
|
+
return {
|
|
12783
|
+
verified: true,
|
|
12784
|
+
userId: stored.userId
|
|
12785
|
+
};
|
|
12786
|
+
}
|
|
12636
12787
|
async listPasskeys(userId) {
|
|
12637
12788
|
if (!this.store) return [];
|
|
12638
12789
|
return (await this.store.findByUserId(userId)).map((r) => ({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as loginMethodCapability, c as BaseAddon, d as string, l as array, n as addonWidgetsSourceCapability, o as userPasskeysCapability, u as object } from "../dist-BopfLZ9P.mjs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import Stream from "stream";
|
|
4
4
|
import http from "http";
|
|
@@ -12283,61 +12283,97 @@ var AUTH_WEBAUTHN_REMOTE_NAME = "addon_auth_webauthn_widgets";
|
|
|
12283
12283
|
var AUTH_WEBAUTHN_BUNDLE = "remoteEntry.js";
|
|
12284
12284
|
/** Manifest addon id — the public bundle namespace + login-method addonId. */
|
|
12285
12285
|
var AUTH_WEBAUTHN_ADDON_ID = "auth-webauthn";
|
|
12286
|
-
var authWebauthnWidgets = [
|
|
12287
|
-
|
|
12288
|
-
|
|
12289
|
-
|
|
12290
|
-
|
|
12291
|
-
|
|
12292
|
-
|
|
12293
|
-
|
|
12294
|
-
|
|
12295
|
-
|
|
12296
|
-
|
|
12297
|
-
|
|
12298
|
-
|
|
12299
|
-
|
|
12300
|
-
|
|
12301
|
-
|
|
12302
|
-
|
|
12303
|
-
|
|
12304
|
-
|
|
12305
|
-
|
|
12306
|
-
|
|
12307
|
-
|
|
12308
|
-
|
|
12309
|
-
|
|
12310
|
-
|
|
12311
|
-
|
|
12312
|
-
|
|
12313
|
-
|
|
12314
|
-
tab: "dashboard",
|
|
12315
|
-
label: "Passkey login",
|
|
12316
|
-
kind: "remote",
|
|
12317
|
-
remote: {
|
|
12318
|
-
remoteName: AUTH_WEBAUTHN_REMOTE_NAME,
|
|
12319
|
-
exposedModule: "./widgets",
|
|
12320
|
-
componentKey: "passkey-login"
|
|
12286
|
+
var authWebauthnWidgets = [
|
|
12287
|
+
{
|
|
12288
|
+
tab: "dashboard",
|
|
12289
|
+
label: "Passkeys",
|
|
12290
|
+
kind: "remote",
|
|
12291
|
+
remote: {
|
|
12292
|
+
remoteName: AUTH_WEBAUTHN_REMOTE_NAME,
|
|
12293
|
+
exposedModule: "./widgets",
|
|
12294
|
+
componentKey: "passkey-enrollment"
|
|
12295
|
+
},
|
|
12296
|
+
stableId: "passkey-enrollment",
|
|
12297
|
+
description: "Enroll and manage passkeys (WebAuthn) for your account.",
|
|
12298
|
+
icon: "fingerprint",
|
|
12299
|
+
bundle: AUTH_WEBAUTHN_BUNDLE,
|
|
12300
|
+
hosts: ["dashboard"],
|
|
12301
|
+
requires: {
|
|
12302
|
+
deviceContext: false,
|
|
12303
|
+
integrationContext: false
|
|
12304
|
+
},
|
|
12305
|
+
defaultSize: "lg",
|
|
12306
|
+
allowedSizes: [
|
|
12307
|
+
"md",
|
|
12308
|
+
"lg",
|
|
12309
|
+
"xl"
|
|
12310
|
+
],
|
|
12311
|
+
defaultColumns: 12,
|
|
12312
|
+
defaultRows: 3,
|
|
12313
|
+
preAuth: false
|
|
12321
12314
|
},
|
|
12322
|
-
|
|
12323
|
-
|
|
12324
|
-
|
|
12325
|
-
|
|
12326
|
-
|
|
12327
|
-
|
|
12328
|
-
|
|
12329
|
-
|
|
12315
|
+
{
|
|
12316
|
+
tab: "dashboard",
|
|
12317
|
+
label: "Passkey login",
|
|
12318
|
+
kind: "remote",
|
|
12319
|
+
remote: {
|
|
12320
|
+
remoteName: AUTH_WEBAUTHN_REMOTE_NAME,
|
|
12321
|
+
exposedModule: "./widgets",
|
|
12322
|
+
componentKey: "passkey-login"
|
|
12323
|
+
},
|
|
12324
|
+
stableId: "passkey-login",
|
|
12325
|
+
description: "Login-page passkey second-factor ceremony.",
|
|
12326
|
+
icon: "fingerprint",
|
|
12327
|
+
bundle: AUTH_WEBAUTHN_BUNDLE,
|
|
12328
|
+
hosts: ["dashboard"],
|
|
12329
|
+
requires: {
|
|
12330
|
+
deviceContext: false,
|
|
12331
|
+
integrationContext: false
|
|
12332
|
+
},
|
|
12333
|
+
defaultSize: "sm",
|
|
12334
|
+
allowedSizes: ["sm"],
|
|
12335
|
+
defaultColumns: 4,
|
|
12336
|
+
defaultRows: 1,
|
|
12337
|
+
preAuth: true
|
|
12330
12338
|
},
|
|
12331
|
-
|
|
12332
|
-
|
|
12333
|
-
|
|
12334
|
-
|
|
12335
|
-
|
|
12336
|
-
|
|
12339
|
+
{
|
|
12340
|
+
tab: "dashboard",
|
|
12341
|
+
label: "Passkey sign-in",
|
|
12342
|
+
kind: "remote",
|
|
12343
|
+
remote: {
|
|
12344
|
+
remoteName: AUTH_WEBAUTHN_REMOTE_NAME,
|
|
12345
|
+
exposedModule: "./widgets",
|
|
12346
|
+
componentKey: "passkey-direct-login"
|
|
12347
|
+
},
|
|
12348
|
+
stableId: "passkey-direct-login",
|
|
12349
|
+
description: "Login-page usernameless (discoverable-credential) passkey sign-in.",
|
|
12350
|
+
icon: "fingerprint",
|
|
12351
|
+
bundle: AUTH_WEBAUTHN_BUNDLE,
|
|
12352
|
+
hosts: ["dashboard"],
|
|
12353
|
+
requires: {
|
|
12354
|
+
deviceContext: false,
|
|
12355
|
+
integrationContext: false
|
|
12356
|
+
},
|
|
12357
|
+
defaultSize: "sm",
|
|
12358
|
+
allowedSizes: ["sm"],
|
|
12359
|
+
defaultColumns: 4,
|
|
12360
|
+
defaultRows: 1,
|
|
12361
|
+
preAuth: true
|
|
12362
|
+
}
|
|
12363
|
+
];
|
|
12337
12364
|
/**
|
|
12338
|
-
* Login-method
|
|
12339
|
-
*
|
|
12340
|
-
*
|
|
12365
|
+
* Login-method contributions — aggregated by the public
|
|
12366
|
+
* `auth.listLoginMethods` procedure, which stamps a public `bundleUrl`
|
|
12367
|
+
* from `addonId` + `bundle`.
|
|
12368
|
+
*
|
|
12369
|
+
* - `passkey-login` (second-factor) — the post-password 2FA ceremony,
|
|
12370
|
+
* mounted only when the challenged user has the `passkey` factor.
|
|
12371
|
+
* - `passkey-direct-login` (primary) — usernameless sign-in via the
|
|
12372
|
+
* standard WebAuthn discoverable-credentials flow. Contributed
|
|
12373
|
+
* unconditionally while the addon is registered: the browser's own
|
|
12374
|
+
* passkey picker is the honest gate (it simply offers nothing when
|
|
12375
|
+
* no resident credential matches the RP) — probing the server for
|
|
12376
|
+
* "does anyone have a passkey" pre-auth would leak enrollment state.
|
|
12341
12377
|
*/
|
|
12342
12378
|
var authWebauthnLoginMethods = [{
|
|
12343
12379
|
kind: "widget",
|
|
@@ -12350,6 +12386,17 @@ var authWebauthnLoginMethods = [{
|
|
|
12350
12386
|
componentKey: "passkey-login"
|
|
12351
12387
|
},
|
|
12352
12388
|
stage: "second-factor"
|
|
12389
|
+
}, {
|
|
12390
|
+
kind: "widget",
|
|
12391
|
+
id: `${AUTH_WEBAUTHN_ADDON_ID}/passkey-direct-login`,
|
|
12392
|
+
addonId: AUTH_WEBAUTHN_ADDON_ID,
|
|
12393
|
+
bundle: AUTH_WEBAUTHN_BUNDLE,
|
|
12394
|
+
remote: {
|
|
12395
|
+
remoteName: AUTH_WEBAUTHN_REMOTE_NAME,
|
|
12396
|
+
exposedModule: "./widgets",
|
|
12397
|
+
componentKey: "passkey-direct-login"
|
|
12398
|
+
},
|
|
12399
|
+
stage: "primary"
|
|
12353
12400
|
}];
|
|
12354
12401
|
var EndpointsSchema = array(object({
|
|
12355
12402
|
hostname: string(),
|
|
@@ -12377,6 +12424,8 @@ var AuthWebauthnAddon = class extends BaseAddon {
|
|
|
12377
12424
|
finishRegistration: async ({ userId, response, label }) => this.finishRegistration(userId, response, label),
|
|
12378
12425
|
beginAuthentication: async ({ userId }) => this.beginAuthentication(userId),
|
|
12379
12426
|
finishAuthentication: async ({ userId, response }) => this.finishAuthentication(userId, response),
|
|
12427
|
+
beginDiscoverableAuthentication: async () => this.beginDiscoverableAuthentication(),
|
|
12428
|
+
finishDiscoverableAuthentication: async ({ response }) => this.finishDiscoverableAuthentication(response),
|
|
12380
12429
|
listPasskeys: async ({ userId }) => this.listPasskeys(userId),
|
|
12381
12430
|
removePasskey: async ({ userId, credentialId }) => this.removePasskey(userId, credentialId)
|
|
12382
12431
|
};
|
|
@@ -12494,9 +12543,12 @@ var AuthWebauthnAddon = class extends BaseAddon {
|
|
|
12494
12543
|
if (this.config.origin && this.config.origin.trim()) return this.config.origin.trim().replace(/\/+$/, "");
|
|
12495
12544
|
return (process.env["CAMSTACK_PUBLIC_ORIGIN"] || "http://localhost:4443").replace(/\/+$/, "");
|
|
12496
12545
|
}
|
|
12546
|
+
challengeTtlMs() {
|
|
12547
|
+
return (this.config.challengeTtlSec || DEFAULT_CONFIG.challengeTtlSec) * 1e3;
|
|
12548
|
+
}
|
|
12497
12549
|
pruneExpiredChallenges() {
|
|
12498
12550
|
const now = Date.now();
|
|
12499
|
-
const ttlMs =
|
|
12551
|
+
const ttlMs = this.challengeTtlMs();
|
|
12500
12552
|
for (const [k, v] of this.pending.entries()) if (now - v.createdAt > ttlMs) this.pending.delete(k);
|
|
12501
12553
|
}
|
|
12502
12554
|
base64urlEncode(bytes) {
|
|
@@ -12622,6 +12674,105 @@ var AuthWebauthnAddon = class extends BaseAddon {
|
|
|
12622
12674
|
await this.store.updateCounter(credentialId, verification.authenticationInfo.newCounter);
|
|
12623
12675
|
return { verified: true };
|
|
12624
12676
|
}
|
|
12677
|
+
/**
|
|
12678
|
+
* Usernameless (discoverable-credential) login — leg 1. Issues
|
|
12679
|
+
* assertion options with an EMPTY `allowCredentials` list so the
|
|
12680
|
+
* browser offers every resident passkey it holds for this RP, and
|
|
12681
|
+
* `userVerification: 'required'` because the passkey is the ONLY
|
|
12682
|
+
* factor (it replaces both username/password and the 2FA leg).
|
|
12683
|
+
* The challenge is stored server-side keyed by its own value and is
|
|
12684
|
+
* NOT bound to any user — `finishDiscoverableAuthentication` resolves
|
|
12685
|
+
* the owner from the asserted credential id.
|
|
12686
|
+
*/
|
|
12687
|
+
async beginDiscoverableAuthentication() {
|
|
12688
|
+
if (!this.store) throw new Error("passkey store unavailable");
|
|
12689
|
+
this.pruneExpiredChallenges();
|
|
12690
|
+
const options = await generateAuthenticationOptions({
|
|
12691
|
+
rpID: this.resolveRpID(),
|
|
12692
|
+
allowCredentials: [],
|
|
12693
|
+
userVerification: "required"
|
|
12694
|
+
});
|
|
12695
|
+
this.pending.set(options.challenge, {
|
|
12696
|
+
challenge: options.challenge,
|
|
12697
|
+
userId: "",
|
|
12698
|
+
kind: "discoverable-authentication",
|
|
12699
|
+
createdAt: Date.now()
|
|
12700
|
+
});
|
|
12701
|
+
return { optionsJSON: options };
|
|
12702
|
+
}
|
|
12703
|
+
/**
|
|
12704
|
+
* Usernameless login — leg 2. Resolves the credential by the
|
|
12705
|
+
* response's credential id, verifies the assertion against the stored
|
|
12706
|
+
* challenge + that credential's public key/counter, and returns the
|
|
12707
|
+
* OWNING userId. Fail-closed: every anomaly returns
|
|
12708
|
+
* `{ verified: false, userId: null }`.
|
|
12709
|
+
*
|
|
12710
|
+
* Replay protection: the pending challenge is consumed (deleted)
|
|
12711
|
+
* BEFORE verification, so an assertion can be spent exactly once.
|
|
12712
|
+
* The TTL is enforced here too — pruning alone only runs on begin
|
|
12713
|
+
* legs, which an attacker controls the timing of.
|
|
12714
|
+
*/
|
|
12715
|
+
async finishDiscoverableAuthentication(response) {
|
|
12716
|
+
if (!this.store) return {
|
|
12717
|
+
verified: false,
|
|
12718
|
+
userId: null
|
|
12719
|
+
};
|
|
12720
|
+
const challenge = this.decodeClientData(response)?.challenge;
|
|
12721
|
+
if (!challenge) return {
|
|
12722
|
+
verified: false,
|
|
12723
|
+
userId: null
|
|
12724
|
+
};
|
|
12725
|
+
const pending = this.pending.get(challenge);
|
|
12726
|
+
if (!pending || pending.kind !== "discoverable-authentication") return {
|
|
12727
|
+
verified: false,
|
|
12728
|
+
userId: null
|
|
12729
|
+
};
|
|
12730
|
+
this.pending.delete(challenge);
|
|
12731
|
+
if (Date.now() - pending.createdAt > this.challengeTtlMs()) return {
|
|
12732
|
+
verified: false,
|
|
12733
|
+
userId: null
|
|
12734
|
+
};
|
|
12735
|
+
const credentialId = typeof response["id"] === "string" ? response["id"] : "";
|
|
12736
|
+
const stored = await this.store.findByCredentialId(credentialId);
|
|
12737
|
+
if (!stored) return {
|
|
12738
|
+
verified: false,
|
|
12739
|
+
userId: null
|
|
12740
|
+
};
|
|
12741
|
+
let verification;
|
|
12742
|
+
try {
|
|
12743
|
+
verification = await verifyAuthenticationResponse({
|
|
12744
|
+
response,
|
|
12745
|
+
expectedChallenge: challenge,
|
|
12746
|
+
expectedRPID: this.resolveRpID(),
|
|
12747
|
+
expectedOrigin: this.resolveOrigin(),
|
|
12748
|
+
requireUserVerification: true,
|
|
12749
|
+
credential: {
|
|
12750
|
+
id: stored.credentialId,
|
|
12751
|
+
publicKey: this.base64urlDecode(stored.publicKey),
|
|
12752
|
+
counter: stored.counter,
|
|
12753
|
+
transports: stored.transports
|
|
12754
|
+
}
|
|
12755
|
+
});
|
|
12756
|
+
} catch (err) {
|
|
12757
|
+
this.ctx.logger.warn("Discoverable passkey verification threw", { meta: {
|
|
12758
|
+
credentialId,
|
|
12759
|
+
err: err instanceof Error ? err.message : String(err)
|
|
12760
|
+
} });
|
|
12761
|
+
return {
|
|
12762
|
+
verified: false,
|
|
12763
|
+
userId: null
|
|
12764
|
+
};
|
|
12765
|
+
}
|
|
12766
|
+
if (!verification.verified || !verification.authenticationInfo) return {
|
|
12767
|
+
verified: false,
|
|
12768
|
+
userId: null
|
|
12769
|
+
};
|
|
12770
|
+
await this.store.updateCounter(credentialId, verification.authenticationInfo.newCounter);
|
|
12771
|
+
return {
|
|
12772
|
+
verified: true,
|
|
12773
|
+
userId: stored.userId
|
|
12774
|
+
};
|
|
12775
|
+
}
|
|
12625
12776
|
async listPasskeys(userId) {
|
|
12626
12777
|
if (!this.store) return [];
|
|
12627
12778
|
return (await this.store.findByUserId(userId)).map((r) => ({
|
|
@@ -36,7 +36,7 @@ async function r() {
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"@camstack/types": {
|
|
39
|
-
version: "1.1.
|
|
39
|
+
version: "1.1.37",
|
|
40
40
|
scope: "default",
|
|
41
41
|
shareConfig: {
|
|
42
42
|
singleton: !0,
|
|
@@ -45,7 +45,7 @@ async function r() {
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
"@camstack/sdk": {
|
|
48
|
-
version: "1.1.
|
|
48
|
+
version: "1.1.21",
|
|
49
49
|
scope: "default",
|
|
50
50
|
shareConfig: {
|
|
51
51
|
singleton: !0,
|
|
@@ -30,7 +30,7 @@ async function d(e) {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
async function f() {
|
|
33
|
-
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_auth_webauthn_widgets-
|
|
33
|
+
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_auth_webauthn_widgets-DW5sgIHW.mjs")).catch((e) => {
|
|
34
34
|
throw l = void 0, e;
|
|
35
35
|
}), l;
|
|
36
36
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-auth",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
4
4
|
"description": "CamStack authentication bundle — magic-link, OIDC, and WebAuthn/passkey. Multi-entry npm package shipping 3 addons under a single bundle; each addon keeps its own id, capabilities, and runner.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|