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