@camstack/addon-auth 1.1.1 → 1.1.3

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.
Files changed (21) hide show
  1. package/dist/{dist-JmuFs-U5.mjs → dist-mmJDfwXE.js} +335 -8
  2. package/dist/{dist-CXCR7sEk.js → dist-suALViLT.mjs} +276 -37
  3. package/dist/magic-link/auth-magic-link.addon.js +104 -30
  4. package/dist/magic-link/auth-magic-link.addon.mjs +104 -30
  5. package/dist/oidc/auth-oidc.addon.js +36 -8
  6. package/dist/oidc/auth-oidc.addon.mjs +36 -8
  7. package/dist/webauthn/_stub.js +568 -0
  8. package/dist/webauthn/_virtual_mf-localSharedImportMap___mfe_internal__addon_auth_webauthn_widgets-Bg_T1-iY.mjs +156 -0
  9. package/dist/webauthn/_virtual_mf___mfe_internal__addon_auth_webauthn_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-EnxrfqY5.mjs +26 -0
  10. package/dist/webauthn/_virtual_mf___mfe_internal__addon_auth_webauthn_widgets__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-CQ-aEQ9b.mjs +26 -0
  11. package/dist/webauthn/_virtual_mf___mfe_internal__addon_auth_webauthn_widgets__loadShare__react__loadShare__.js-BIIa6vDX.mjs +26 -0
  12. package/dist/webauthn/_virtual_mf___mfe_internal__addon_auth_webauthn_widgets__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-BL2etuqg.mjs +26 -0
  13. package/dist/webauthn/auth-webauthn.addon.js +141 -5
  14. package/dist/webauthn/auth-webauthn.addon.mjs +136 -37
  15. package/dist/webauthn/dist-CYZr2fwk.mjs +2726 -0
  16. package/dist/webauthn/hostInit-Boe91Eeg.mjs +129 -0
  17. package/dist/webauthn/remoteEntry.js +134 -0
  18. package/dist/webauthn/remoteEntry.ssr.js +33 -0
  19. package/dist/webauthn/virtualExposes-Ckj56FAf.mjs +27 -0
  20. package/dist/webauthn/virtual_mf-exposes-ssr___mfe_internal__addon_auth_webauthn_widgets__remoteEntry_js-C5AzEzK5.mjs +10 -0
  21. package/package.json +30 -5
@@ -1,4 +1,4 @@
1
- import { a as BaseAddon, i as errMsg, n as authProviderCapability, t as addonRoutesCapability } from "../dist-JmuFs-U5.mjs";
1
+ import { i as loginMethodCapability, o as errMsg, r as authProviderCapability, s as BaseAddon, t as addonRoutesCapability } from "../dist-suALViLT.mjs";
2
2
  //#region src/magic-link/auth-magic-link.addon.ts
3
3
  /**
4
4
  * Magic-link authentication addon.
@@ -33,21 +33,10 @@ var DEFAULT_CONFIG = {
33
33
  ttlSec: 600
34
34
  };
35
35
  var AuthMagicLinkAddon = class extends BaseAddon {
36
- kind = "magic-link";
37
- hasRedirectFlow = false;
38
- hasCredentialFlow = true;
39
- displayName;
40
- icon;
41
36
  constructor() {
42
37
  super({ ...DEFAULT_CONFIG });
43
- this.displayName = DEFAULT_CONFIG.displayName;
44
- this.icon = DEFAULT_CONFIG.icon;
45
38
  }
46
39
  async onInitialize() {
47
- Object.assign(this, {
48
- displayName: this.config.displayName || DEFAULT_CONFIG.displayName,
49
- icon: this.config.icon || DEFAULT_CONFIG.icon
50
- });
51
40
  const authProvider = {
52
41
  validateCredentials: async () => null,
53
42
  validateToken: async () => null,
@@ -58,30 +47,58 @@ var AuthMagicLinkAddon = class extends BaseAddon {
58
47
  throw new Error("Magic link does not use callback — the email link redirects to /api/auth/sso/finish directly");
59
48
  }
60
49
  };
61
- const routes = [{
62
- method: "POST",
63
- path: "/request",
64
- access: "public",
65
- description: "Request a magic link be delivered to the given email",
66
- handler: async (req, reply) => this.handleRequest(req, reply)
67
- }, {
68
- method: "GET",
69
- path: "/login",
70
- access: "public",
71
- description: "Click target embedded in the magic-link email",
72
- handler: async (req, reply) => this.handleLogin(req, reply)
73
- }];
50
+ const routes = [
51
+ {
52
+ method: "GET",
53
+ path: "/start",
54
+ access: "public",
55
+ description: "Login-page landing: email-entry form that POSTs to /request",
56
+ handler: async (req, reply) => this.handleStart(req, reply)
57
+ },
58
+ {
59
+ method: "POST",
60
+ path: "/request",
61
+ access: "public",
62
+ description: "Request a magic link be delivered to the given email",
63
+ handler: async (req, reply) => this.handleRequest(req, reply)
64
+ },
65
+ {
66
+ method: "GET",
67
+ path: "/login",
68
+ access: "public",
69
+ description: "Click target embedded in the magic-link email",
70
+ handler: async (req, reply) => this.handleLogin(req, reply)
71
+ }
72
+ ];
74
73
  const routeProvider = {
75
74
  id: "auth-magic-link",
76
75
  getRoutes: () => routes
77
76
  };
77
+ const loginMethodProvider = { getLoginMethods: async () => this.buildLoginMethods() };
78
78
  this.ctx.logger.info("Magic-link auth provider initialized", { meta: { deliveryMode: this.config.deliveryMode } });
79
+ return [
80
+ {
81
+ capability: authProviderCapability,
82
+ provider: authProvider
83
+ },
84
+ {
85
+ capability: addonRoutesCapability,
86
+ provider: routeProvider
87
+ },
88
+ {
89
+ capability: loginMethodCapability,
90
+ provider: loginMethodProvider
91
+ }
92
+ ];
93
+ }
94
+ buildLoginMethods() {
79
95
  return [{
80
- capability: authProviderCapability,
81
- provider: authProvider
82
- }, {
83
- capability: addonRoutesCapability,
84
- provider: routeProvider
96
+ kind: "redirect",
97
+ id: "auth-magic-link",
98
+ label: this.config.displayName || DEFAULT_CONFIG.displayName,
99
+ icon: this.config.icon || DEFAULT_CONFIG.icon,
100
+ startUrl: "/addon/auth-magic-link/start",
101
+ stage: "primary"
85
102
  }];
86
103
  }
87
104
  globalSettingsSchema() {
@@ -164,6 +181,59 @@ var AuthMagicLinkAddon = class extends BaseAddon {
164
181
  for (const u of users) if (u.username.toLowerCase() === needle) return u;
165
182
  return null;
166
183
  }
184
+ /**
185
+ * Login-page landing for the magic-link `redirect` login method. Serves
186
+ * a self-contained email-entry page that POSTs to `/request`; the tiny
187
+ * inline script lives in the ADDON's served HTML (not the admin shell),
188
+ * keeping the login page free of any magic-link-specific JS. Always
189
+ * responds 200 (account-enumeration-safe, mirroring `/request`).
190
+ */
191
+ async handleStart(_req, reply) {
192
+ const title = this.config.displayName || DEFAULT_CONFIG.displayName;
193
+ const html = `<!doctype html>
194
+ <html lang="en"><head><meta charset="utf-8">
195
+ <meta name="viewport" content="width=device-width, initial-scale=1">
196
+ <title>${escapeHtml(title)}</title>
197
+ <style>
198
+ :root { color-scheme: dark; }
199
+ body { margin:0; min-height:100vh; display:flex; align-items:center; justify-content:center;
200
+ background:#0b0d12; color:#e5e7eb; font:14px/1.5 system-ui,-apple-system,sans-serif; }
201
+ .card { width:100%; max-width:22rem; padding:1.5rem; border:1px solid #232733; border-radius:0.75rem;
202
+ background:#12151c; box-shadow:0 10px 30px rgba(0,0,0,.35); }
203
+ h1 { font-size:1rem; margin:0 0 1rem; }
204
+ label { font-size:.75rem; color:#9ca3af; display:block; margin-bottom:.35rem; }
205
+ input { width:100%; box-sizing:border-box; padding:.6rem .7rem; border-radius:.5rem;
206
+ border:1px solid #232733; background:#0b0d12; color:#e5e7eb; outline:none; }
207
+ input:focus { border-color:#6366f1; }
208
+ button { margin-top:1rem; width:100%; padding:.6rem; border:0; border-radius:.5rem; cursor:pointer;
209
+ background:#6366f1; color:#fff; font-weight:600; }
210
+ button:disabled { opacity:.5; cursor:default; }
211
+ .msg { margin-top:1rem; font-size:.8rem; color:#34d399; }
212
+ .err { color:#f87171; }
213
+ a { color:#9ca3af; font-size:.75rem; }
214
+ </style></head>
215
+ <body><form class="card" id="f">
216
+ <h1>${escapeHtml(title)}</h1>
217
+ <label for="email">Email address</label>
218
+ <input id="email" name="email" type="email" autocomplete="email" required autofocus placeholder="you@example.com">
219
+ <button id="b" type="submit">Send login link</button>
220
+ <div class="msg" id="m" hidden></div>
221
+ <p style="margin-top:1rem"><a href="/admin/login">Back to sign in</a></p>
222
+ </form>
223
+ <script>
224
+ var f=document.getElementById('f'),b=document.getElementById('b'),m=document.getElementById('m');
225
+ f.addEventListener('submit',function(e){e.preventDefault();b.disabled=true;m.hidden=true;m.className='msg';
226
+ fetch('/addon/auth-magic-link/request',{method:'POST',headers:{'content-type':'application/json'},
227
+ body:JSON.stringify({email:document.getElementById('email').value})})
228
+ .then(function(){m.textContent='If that email matches an account, a login link is on its way.';m.hidden=false;})
229
+ .catch(function(){m.className='msg err';m.textContent='Something went wrong. Please try again.';m.hidden=false;})
230
+ .finally(function(){b.disabled=false;});});
231
+ <\/script>
232
+ </body></html>`;
233
+ reply.code(200);
234
+ reply.header("Content-Type", "text/html; charset=utf-8");
235
+ reply.send(html);
236
+ }
167
237
  async handleRequest(req, reply) {
168
238
  try {
169
239
  const body = req.body ?? {};
@@ -230,5 +300,9 @@ var AuthMagicLinkAddon = class extends BaseAddon {
230
300
  reply.send("");
231
301
  }
232
302
  };
303
+ /** Minimal HTML-attribute/text escaper for the interpolated page title. */
304
+ function escapeHtml(value) {
305
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
306
+ }
233
307
  //#endregion
234
308
  export { AuthMagicLinkAddon, AuthMagicLinkAddon as default };
@@ -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-CXCR7sEk.js");
6
+ const require_dist = require("../dist-mmJDfwXE.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
@@ -1254,14 +1254,42 @@ var AuthOidcAddon = class extends require_dist.BaseAddon {
1254
1254
  id: "auth-oidc",
1255
1255
  getRoutes: () => routes
1256
1256
  };
1257
+ const loginMethodProvider = { getLoginMethods: async () => this.buildLoginMethods() };
1257
1258
  this.ctx.logger.info("OIDC auth provider initialized", { meta: { providers: [...this.instances.keys()] } });
1258
- return [{
1259
- capability: require_dist.authProviderCapability,
1260
- provider: authProvider
1261
- }, {
1262
- capability: require_dist.addonRoutesCapability,
1263
- provider: routeProvider
1264
- }];
1259
+ return [
1260
+ {
1261
+ capability: require_dist.authProviderCapability,
1262
+ provider: authProvider
1263
+ },
1264
+ {
1265
+ capability: require_dist.addonRoutesCapability,
1266
+ provider: routeProvider
1267
+ },
1268
+ {
1269
+ capability: require_dist.loginMethodCapability,
1270
+ provider: loginMethodProvider
1271
+ }
1272
+ ];
1273
+ }
1274
+ /**
1275
+ * Build one `redirect` login-method per configured provider instance.
1276
+ * `startUrl` is the addon's public `/start` route for that instance;
1277
+ * the login page renders a generic button navigating to it.
1278
+ */
1279
+ buildLoginMethods() {
1280
+ const out = [];
1281
+ for (const inst of this.instances.values()) {
1282
+ const id = inst.config.id;
1283
+ out.push({
1284
+ kind: "redirect",
1285
+ id: `auth-oidc/${id}`,
1286
+ label: inst.config.displayName || DEFAULT_PROVIDER.displayName,
1287
+ icon: inst.config.icon || DEFAULT_PROVIDER.icon,
1288
+ startUrl: `/addon/auth-oidc/${id}/start`,
1289
+ stage: "primary"
1290
+ });
1291
+ }
1292
+ return out;
1265
1293
  }
1266
1294
  globalSettingsSchema() {
1267
1295
  return this.schema({ sections: [{
@@ -1,4 +1,4 @@
1
- import { a as BaseAddon, i as errMsg, n as authProviderCapability, t as addonRoutesCapability } from "../dist-JmuFs-U5.mjs";
1
+ import { i as loginMethodCapability, o as errMsg, r as authProviderCapability, s as BaseAddon, t as addonRoutesCapability } from "../dist-suALViLT.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();
@@ -1248,14 +1248,42 @@ var AuthOidcAddon = class extends BaseAddon {
1248
1248
  id: "auth-oidc",
1249
1249
  getRoutes: () => routes
1250
1250
  };
1251
+ const loginMethodProvider = { getLoginMethods: async () => this.buildLoginMethods() };
1251
1252
  this.ctx.logger.info("OIDC auth provider initialized", { meta: { providers: [...this.instances.keys()] } });
1252
- return [{
1253
- capability: authProviderCapability,
1254
- provider: authProvider
1255
- }, {
1256
- capability: addonRoutesCapability,
1257
- provider: routeProvider
1258
- }];
1253
+ return [
1254
+ {
1255
+ capability: authProviderCapability,
1256
+ provider: authProvider
1257
+ },
1258
+ {
1259
+ capability: addonRoutesCapability,
1260
+ provider: routeProvider
1261
+ },
1262
+ {
1263
+ capability: loginMethodCapability,
1264
+ provider: loginMethodProvider
1265
+ }
1266
+ ];
1267
+ }
1268
+ /**
1269
+ * Build one `redirect` login-method per configured provider instance.
1270
+ * `startUrl` is the addon's public `/start` route for that instance;
1271
+ * the login page renders a generic button navigating to it.
1272
+ */
1273
+ buildLoginMethods() {
1274
+ const out = [];
1275
+ for (const inst of this.instances.values()) {
1276
+ const id = inst.config.id;
1277
+ out.push({
1278
+ kind: "redirect",
1279
+ id: `auth-oidc/${id}`,
1280
+ label: inst.config.displayName || DEFAULT_PROVIDER.displayName,
1281
+ icon: inst.config.icon || DEFAULT_PROVIDER.icon,
1282
+ startUrl: `/addon/auth-oidc/${id}/start`,
1283
+ stage: "primary"
1284
+ });
1285
+ }
1286
+ return out;
1259
1287
  }
1260
1288
  globalSettingsSchema() {
1261
1289
  return this.schema({ sections: [{