@luminaryworks/auth-react 0.4.5 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- (function(){if(typeof document==="undefined")return;var id="lw-auth-react-headless-login";if(document.getElementById(id))return;var s=document.createElement("style");s.id=id;s.textContent="/* src/HeadlessLoginPanel.module.scss */\n.HeadlessLoginPanel_module_panel {\n --lw-auth-theme: #1677ff;\n --lw-auth-theme-deep: color-mix(in srgb, var(--lw-auth-theme) 72%, #000);\n width: min(100%, 400px);\n padding: 28px 28px 22px;\n border-radius: 16px;\n background: rgba(255, 255, 255, 0.96);\n color: #14212b;\n box-shadow: 0 24px 64px rgba(15, 23, 42, 0.28);\n font-family:\n \"Segoe UI\",\n \"PingFang SC\",\n \"Noto Sans SC\",\n sans-serif;\n}\n.HeadlessLoginPanel_module_brand {\n display: flex;\n gap: 14px;\n align-items: center;\n margin-bottom: 12px;\n}\n.HeadlessLoginPanel_module_logo {\n border-radius: 12px;\n display: block;\n}\n.HeadlessLoginPanel_module_product {\n margin: 0;\n font-size: 13px;\n font-weight: 650;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n color: var(--lw-auth-theme);\n}\n.HeadlessLoginPanel_module_title {\n margin: 2px 0 0;\n font-size: 22px;\n font-weight: 700;\n line-height: 1.25;\n}\n.HeadlessLoginPanel_module_subtitle {\n margin: 0 0 18px;\n font-size: 14px;\n line-height: 1.5;\n color: #4b5c68;\n}\n.HeadlessLoginPanel_module_form {\n display: grid;\n gap: 10px;\n}\n.HeadlessLoginPanel_module_socialStack {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n font-size: 12px;\n}\n.HeadlessLoginPanel_module_input {\n width: 100%;\n box-sizing: border-box;\n border: 1px solid #c9d5de;\n border-radius: 10px;\n padding: 10px 12px;\n font-size: 14px;\n outline: none;\n background: #f7fafc;\n}\n.HeadlessLoginPanel_module_input:focus {\n border-color: var(--lw-auth-theme);\n}\n.HeadlessLoginPanel_module_input:disabled {\n opacity: 0.65;\n cursor: not-allowed;\n}\n.HeadlessLoginPanel_module_passwordField {\n position: relative;\n display: flex;\n align-items: center;\n}\n.HeadlessLoginPanel_module_passwordInput {\n padding-right: 42px;\n}\n.HeadlessLoginPanel_module_passwordToggle {\n position: absolute;\n right: 4px;\n top: 50%;\n transform: translateY(-50%);\n z-index: 1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n border: none;\n border-radius: 4px;\n padding: 0;\n color: #5a6b76;\n background: transparent;\n cursor: pointer;\n}\n.HeadlessLoginPanel_module_passwordToggle:hover:not(:disabled) {\n color: var(--lw-auth-theme);\n background: color-mix(in srgb, var(--lw-auth-theme) 12%, transparent);\n}\n.HeadlessLoginPanel_module_passwordToggle:focus-visible {\n outline: 2px solid var(--lw-auth-theme);\n outline-offset: 1px;\n}\n.HeadlessLoginPanel_module_passwordToggle:disabled {\n opacity: 0.65;\n cursor: not-allowed;\n}\n.HeadlessLoginPanel_module_primaryBtn {\n border: none;\n border-radius: 10px;\n padding: 11px 14px;\n font-size: 14px;\n font-weight: 650;\n cursor: pointer;\n color: #fff;\n background:\n linear-gradient(\n 135deg,\n var(--lw-auth-theme) 0%,\n var(--lw-auth-theme-deep) 100%);\n}\n.HeadlessLoginPanel_module_primaryBtn:disabled {\n opacity: 0.65;\n cursor: not-allowed;\n}\n.HeadlessLoginPanel_module_socialBtn {\n flex: 1 1 calc(50% - 5px);\n min-width: min(140px, 100%);\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n border: 1px solid #b7c9d4;\n border-radius: 10px;\n padding: 11px 10px;\n font-size: 14px;\n font-weight: 650;\n cursor: pointer;\n color: #14212b;\n background: #fff;\n}\n.HeadlessLoginPanel_module_socialBtn:disabled {\n opacity: 0.65;\n cursor: not-allowed;\n}\n.HeadlessLoginPanel_module_socialLogo {\n flex-shrink: 0;\n display: block;\n}\n.HeadlessLoginPanel_module_logoFallback {\n width: 18px;\n height: 18px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--lw-auth-theme) 14%, #fff);\n color: var(--lw-auth-theme-deep);\n font-size: 11px;\n font-weight: 700;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n.HeadlessLoginPanel_module_cancelBtn {\n margin-top: 10px;\n width: 100%;\n border: none;\n background: transparent;\n color: #5a6b76;\n font-size: 13px;\n cursor: pointer;\n padding: 8px;\n}\n.HeadlessLoginPanel_module_cancelBtn:disabled {\n opacity: 0.65;\n cursor: not-allowed;\n}\n.HeadlessLoginPanel_module_divider {\n display: flex;\n align-items: center;\n gap: 10px;\n margin: 14px 0;\n color: #90a0ab;\n}\n.HeadlessLoginPanel_module_dividerLine {\n flex: 1;\n height: 1px;\n background: #d5e0e7;\n}\n.HeadlessLoginPanel_module_dividerText {\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n}\n.HeadlessLoginPanel_module_hint {\n margin: 10px 0 0;\n font-size: 12px;\n color: #6b7c87;\n line-height: 1.45;\n}\n.HeadlessLoginPanel_module_switchMode {\n margin: 12px 0 0;\n text-align: center;\n}\n.HeadlessLoginPanel_module_switchModeBtn {\n border: none;\n background: transparent;\n color: var(--lw-auth-theme);\n font-size: 13px;\n font-weight: 650;\n cursor: pointer;\n padding: 4px 8px;\n text-decoration: underline;\n text-underline-offset: 3px;\n}\n.HeadlessLoginPanel_module_switchModeBtn:disabled {\n opacity: 0.65;\n cursor: not-allowed;\n}\n.HeadlessLoginPanel_module_error {\n margin: 12px 0 0;\n font-size: 13px;\n color: #b42318;\n line-height: 1.4;\n}\n.HeadlessLoginPanel_module_footer {\n margin-top: 18px;\n padding-top: 12px;\n border-top: 1px solid #e4ebf0;\n font-size: 12px;\n color: #6b7c87;\n display: flex;\n gap: 8px;\n justify-content: center;\n}\n/*# sourceMappingURL=index.css.map */";document.head.appendChild(s);})();
1
+ (function(){if(typeof document==="undefined")return;var id="lw-auth-react-headless-login";if(document.getElementById(id))return;var s=document.createElement("style");s.id=id;s.textContent="/* src/HeadlessLoginPanel.module.scss */\n.HeadlessLoginPanel_module_panel {\n --lw-auth-theme: #1677ff;\n --lw-auth-theme-deep: color-mix(in srgb, var(--lw-auth-theme) 72%, #000);\n width: min(100%, 400px);\n padding: 28px 28px 22px;\n border-radius: 16px;\n background: rgba(255, 255, 255, 0.96);\n color: #14212b;\n box-shadow: 0 24px 64px rgba(15, 23, 42, 0.28);\n font-family:\n \"Segoe UI\",\n \"PingFang SC\",\n \"Noto Sans SC\",\n sans-serif;\n}\n.HeadlessLoginPanel_module_brand {\n display: flex;\n gap: 14px;\n align-items: center;\n margin-bottom: 12px;\n}\n.HeadlessLoginPanel_module_logo {\n border-radius: 12px;\n display: block;\n}\n.HeadlessLoginPanel_module_product {\n margin: 0;\n font-size: 13px;\n font-weight: 650;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n color: var(--lw-auth-theme);\n}\n.HeadlessLoginPanel_module_title {\n margin: 2px 0 0;\n font-size: 22px;\n font-weight: 700;\n line-height: 1.25;\n}\n.HeadlessLoginPanel_module_subtitle {\n margin: 0 0 18px;\n font-size: 14px;\n line-height: 1.5;\n color: #4b5c68;\n}\n.HeadlessLoginPanel_module_form {\n display: grid;\n gap: 10px;\n}\n.HeadlessLoginPanel_module_channelTabs {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 6px;\n margin-bottom: 2px;\n}\n.HeadlessLoginPanel_module_channelTab {\n border: 1px solid #c9d5de;\n border-radius: 10px;\n padding: 8px 10px;\n font-size: 13px;\n font-weight: 650;\n cursor: pointer;\n color: #4b5c68;\n background: #f7fafc;\n}\n.HeadlessLoginPanel_module_channelTab:disabled {\n opacity: 0.65;\n cursor: not-allowed;\n}\n.HeadlessLoginPanel_module_channelTabActive {\n color: var(--lw-auth-theme-deep);\n border-color: var(--lw-auth-theme);\n background: color-mix(in srgb, var(--lw-auth-theme) 10%, #fff);\n}\n.HeadlessLoginPanel_module_codeRow {\n display: grid;\n grid-template-columns: 1fr auto;\n gap: 8px;\n align-items: stretch;\n}\n.HeadlessLoginPanel_module_codeInput {\n min-width: 0;\n}\n.HeadlessLoginPanel_module_secondaryBtn {\n border: 1px solid #b7c9d4;\n border-radius: 10px;\n padding: 0 12px;\n font-size: 13px;\n font-weight: 650;\n cursor: pointer;\n color: #14212b;\n background: #fff;\n white-space: nowrap;\n}\n.HeadlessLoginPanel_module_secondaryBtn:disabled {\n opacity: 0.65;\n cursor: not-allowed;\n}\n.HeadlessLoginPanel_module_socialStack {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n font-size: 12px;\n}\n.HeadlessLoginPanel_module_input {\n width: 100%;\n box-sizing: border-box;\n border: 1px solid #c9d5de;\n border-radius: 10px;\n padding: 10px 12px;\n font-size: 14px;\n outline: none;\n background: #f7fafc;\n}\n.HeadlessLoginPanel_module_input:focus {\n border-color: var(--lw-auth-theme);\n}\n.HeadlessLoginPanel_module_input:disabled {\n opacity: 0.65;\n cursor: not-allowed;\n}\n.HeadlessLoginPanel_module_passwordField {\n position: relative;\n display: flex;\n align-items: center;\n}\n.HeadlessLoginPanel_module_passwordInput {\n padding-right: 42px;\n}\n.HeadlessLoginPanel_module_passwordToggle {\n position: absolute;\n right: 4px;\n top: 50%;\n transform: translateY(-50%);\n z-index: 1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n border: none;\n border-radius: 4px;\n padding: 0;\n color: #5a6b76;\n background: transparent;\n cursor: pointer;\n}\n.HeadlessLoginPanel_module_passwordToggle:hover:not(:disabled) {\n color: var(--lw-auth-theme);\n background: color-mix(in srgb, var(--lw-auth-theme) 12%, transparent);\n}\n.HeadlessLoginPanel_module_passwordToggle:focus-visible {\n outline: 2px solid var(--lw-auth-theme);\n outline-offset: 1px;\n}\n.HeadlessLoginPanel_module_passwordToggle:disabled {\n opacity: 0.65;\n cursor: not-allowed;\n}\n.HeadlessLoginPanel_module_primaryBtn {\n border: none;\n border-radius: 10px;\n padding: 11px 14px;\n font-size: 14px;\n font-weight: 650;\n cursor: pointer;\n color: #fff;\n background:\n linear-gradient(\n 135deg,\n var(--lw-auth-theme) 0%,\n var(--lw-auth-theme-deep) 100%);\n}\n.HeadlessLoginPanel_module_primaryBtn:disabled {\n opacity: 0.65;\n cursor: not-allowed;\n}\n.HeadlessLoginPanel_module_socialBtn {\n flex: 1 1 calc(50% - 5px);\n min-width: min(140px, 100%);\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n border: 1px solid #b7c9d4;\n border-radius: 10px;\n padding: 11px 10px;\n font-size: 14px;\n font-weight: 650;\n cursor: pointer;\n color: #14212b;\n background: #fff;\n}\n.HeadlessLoginPanel_module_socialBtn:disabled {\n opacity: 0.65;\n cursor: not-allowed;\n}\n.HeadlessLoginPanel_module_socialLogo {\n flex-shrink: 0;\n display: block;\n}\n.HeadlessLoginPanel_module_logoFallback {\n width: 18px;\n height: 18px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--lw-auth-theme) 14%, #fff);\n color: var(--lw-auth-theme-deep);\n font-size: 11px;\n font-weight: 700;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n.HeadlessLoginPanel_module_cancelBtn {\n margin-top: 10px;\n width: 100%;\n border: none;\n background: transparent;\n color: #5a6b76;\n font-size: 13px;\n cursor: pointer;\n padding: 8px;\n}\n.HeadlessLoginPanel_module_cancelBtn:disabled {\n opacity: 0.65;\n cursor: not-allowed;\n}\n.HeadlessLoginPanel_module_divider {\n display: flex;\n align-items: center;\n gap: 10px;\n margin: 14px 0;\n color: #90a0ab;\n}\n.HeadlessLoginPanel_module_dividerLine {\n flex: 1;\n height: 1px;\n background: #d5e0e7;\n}\n.HeadlessLoginPanel_module_dividerText {\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n}\n.HeadlessLoginPanel_module_hint {\n margin: 10px 0 0;\n font-size: 12px;\n color: #6b7c87;\n line-height: 1.45;\n}\n.HeadlessLoginPanel_module_domainsHint {\n margin: -2px 0 0;\n font-size: 12px;\n line-height: 1.45;\n color: #4b5c68;\n word-break: break-word;\n}\n.HeadlessLoginPanel_module_switchMode {\n margin: 12px 0 0;\n text-align: center;\n}\n.HeadlessLoginPanel_module_switchModeBtn {\n border: none;\n background: transparent;\n color: var(--lw-auth-theme);\n font-size: 13px;\n font-weight: 650;\n cursor: pointer;\n padding: 4px 8px;\n text-decoration: underline;\n text-underline-offset: 3px;\n}\n.HeadlessLoginPanel_module_switchModeBtn:disabled {\n opacity: 0.65;\n cursor: not-allowed;\n}\n.HeadlessLoginPanel_module_error {\n margin: 12px 0 0;\n font-size: 13px;\n color: #b42318;\n line-height: 1.4;\n}\n.HeadlessLoginPanel_module_footer {\n margin-top: 18px;\n padding-top: 12px;\n border-top: 1px solid #e4ebf0;\n font-size: 12px;\n color: #6b7c87;\n display: flex;\n gap: 8px;\n justify-content: center;\n}\n/*# sourceMappingURL=index.css.map */";document.head.appendChild(s);})();
2
2
  'use strict';
3
3
 
4
4
  var oidcClientTs = require('oidc-client-ts');
@@ -55,6 +55,8 @@ var LOGIN_EXPERIENCE_CAPABILITIES = {
55
55
  passwordSignIn: "password-sign-in",
56
56
  /** Username + password self-register via Experience (Logto NewPasswordIdentity). */
57
57
  passwordSignUp: "password-sign-up",
58
+ /** Email + verification code + password self-register. */
59
+ emailCodeSignUp: "email-code-sign-up",
58
60
  socialConnectors: "social-connectors",
59
61
  socialDirectSignIn: "social-direct-sign-in"
60
62
  };
@@ -86,6 +88,160 @@ function resolveLoginExperienceProviderFromEnv(env = {}) {
86
88
  return env.VITE_IAM_PROVIDER ?? env.PUBLIC_IAM_PROVIDER ?? env.IAM_PROVIDER ?? env.IDP_MODE ?? "logto";
87
89
  }
88
90
 
91
+ // src/register-policy.ts
92
+ var DEFAULT_REGISTER_EMAIL_ALLOWLIST = [
93
+ // Google
94
+ "gmail.com",
95
+ "googlemail.com",
96
+ // Tencent / QQ
97
+ "qq.com",
98
+ "foxmail.com",
99
+ // NetEase
100
+ "163.com",
101
+ "126.com",
102
+ "yeah.net",
103
+ // Microsoft
104
+ "outlook.com",
105
+ "hotmail.com",
106
+ "live.com",
107
+ "msn.com",
108
+ // Apple
109
+ "icloud.com",
110
+ "me.com",
111
+ "mac.com",
112
+ // Yahoo
113
+ "yahoo.com",
114
+ "yahoo.co.jp",
115
+ // Proton
116
+ "proton.me",
117
+ "protonmail.com",
118
+ // Others commonly used by real users
119
+ "sina.com",
120
+ "sina.cn",
121
+ "aliyun.com",
122
+ "139.com"
123
+ ];
124
+ var DEFAULT_DISPOSABLE_EMAIL_BLOCKLIST = [
125
+ "mailinator.com",
126
+ "guerrillamail.com",
127
+ "guerrillamail.net",
128
+ "10minutemail.com",
129
+ "tempmail.com",
130
+ "temp-mail.org",
131
+ "yopmail.com",
132
+ "trashmail.com",
133
+ "sharklasers.com",
134
+ "getnada.com",
135
+ "maildrop.cc",
136
+ "discard.email",
137
+ "mailnesia.com"
138
+ ];
139
+ function parseList(raw) {
140
+ if (!raw) return [];
141
+ const parts = Array.isArray(raw) ? raw : String(raw).split(/[\s,;]+/);
142
+ return [
143
+ ...new Set(
144
+ parts.map((s) => s.trim().toLowerCase().replace(/^@/, "")).filter(Boolean)
145
+ )
146
+ ];
147
+ }
148
+ function normalizeRegisterEmailMode(raw) {
149
+ const key = (raw || "allowlist").trim().toLowerCase();
150
+ if (key === "off" || key === "false" || key === "0" || key === "disabled") return "off";
151
+ if (key === "blocklist" || key === "deny" || key === "blacklist") return "blocklist";
152
+ return "allowlist";
153
+ }
154
+ function resolveRegisterEmailPolicy(input = {}) {
155
+ const mode = normalizeRegisterEmailMode(input.mode);
156
+ const allowlist = parseList(input.allowlist);
157
+ const blocklist = parseList(input.blocklist);
158
+ return {
159
+ mode,
160
+ allowlist: allowlist.length ? allowlist : DEFAULT_REGISTER_EMAIL_ALLOWLIST,
161
+ blocklist: blocklist.length ? blocklist : DEFAULT_DISPOSABLE_EMAIL_BLOCKLIST
162
+ };
163
+ }
164
+ function readRegisterEmailPolicyFromEnv(env = {}) {
165
+ return resolveRegisterEmailPolicy({
166
+ mode: env.VITE_AUTH_REGISTER_EMAIL_MODE || env.PUBLIC_AUTH_REGISTER_EMAIL_MODE || env.NEXT_PUBLIC_AUTH_REGISTER_EMAIL_MODE || env.AUTH_REGISTER_EMAIL_MODE,
167
+ allowlist: env.VITE_AUTH_REGISTER_EMAIL_ALLOWLIST || env.PUBLIC_AUTH_REGISTER_EMAIL_ALLOWLIST || env.NEXT_PUBLIC_AUTH_REGISTER_EMAIL_ALLOWLIST || env.AUTH_REGISTER_EMAIL_ALLOWLIST,
168
+ blocklist: env.VITE_AUTH_REGISTER_EMAIL_BLOCKLIST || env.PUBLIC_AUTH_REGISTER_EMAIL_BLOCKLIST || env.NEXT_PUBLIC_AUTH_REGISTER_EMAIL_BLOCKLIST || env.AUTH_REGISTER_EMAIL_BLOCKLIST
169
+ });
170
+ }
171
+ function emailDomain(email) {
172
+ const trimmed = email.trim().toLowerCase();
173
+ const at = trimmed.lastIndexOf("@");
174
+ if (at <= 0 || at === trimmed.length - 1) return null;
175
+ if (trimmed.includes(" ") || trimmed.indexOf("@") !== at) return null;
176
+ return trimmed.slice(at + 1);
177
+ }
178
+ function isValidEmailShape(email) {
179
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.trim());
180
+ }
181
+ function evaluateRegisterEmail(email, policy = resolveRegisterEmailPolicy()) {
182
+ if (!isValidEmailShape(email)) return { ok: false, reason: "invalid" };
183
+ const domain = emailDomain(email);
184
+ if (!domain) return { ok: false, reason: "invalid" };
185
+ if (policy.blocklist.includes(domain)) {
186
+ return { ok: false, reason: "disposable", domain };
187
+ }
188
+ if (policy.mode === "off") return { ok: true, domain };
189
+ if (policy.mode === "blocklist") return { ok: true, domain };
190
+ if (!policy.allowlist.includes(domain)) {
191
+ return { ok: false, reason: "not_allowed", domain };
192
+ }
193
+ return { ok: true, domain };
194
+ }
195
+ function registerEmailRejectionMessage(decision) {
196
+ if (decision.ok) return "";
197
+ if (decision.reason === "invalid") return "Enter a valid email address";
198
+ if (decision.reason === "disposable") {
199
+ return "Temporary or disposable email addresses are not allowed";
200
+ }
201
+ return "This email provider is not accepted for self-registration. Use Gmail, Outlook, QQ, 163, iCloud, or another allowed provider \u2014 or sign in with Google / GitHub.";
202
+ }
203
+ function formatAllowedEmailDomains(policy, options) {
204
+ if (policy.mode !== "allowlist") return null;
205
+ const list = [...policy.allowlist].map((d) => d.toLowerCase()).filter(Boolean);
206
+ if (!list.length) return null;
207
+ const max = options?.max ?? 16;
208
+ const shown = list.slice(0, max);
209
+ const rest = list.length - shown.length;
210
+ const body = shown.join(", ");
211
+ return rest > 0 ? `${body}, +${rest} more` : body;
212
+ }
213
+ function registerEmailDomainsHint(policy, labels) {
214
+ const prefix = labels?.allowedDomainsPrefix ?? "Allowed email domains";
215
+ const formatted = formatAllowedEmailDomains(policy);
216
+ if (formatted) return `${prefix}: ${formatted}`;
217
+ if (policy.mode === "blocklist") {
218
+ return labels?.allowedDomainsBlocklist ?? "Most email providers are accepted. Temporary / disposable addresses are blocked.";
219
+ }
220
+ if (policy.mode === "off" && policy.blocklist.length > 0) {
221
+ return labels?.allowedDomainsBlocklist ?? "Most email providers are accepted. Temporary / disposable addresses are blocked.";
222
+ }
223
+ return labels?.allowedDomainsOpen ?? "Email registration is open for most providers.";
224
+ }
225
+ async function fetchRegisterEmailPolicy(apiBase, init) {
226
+ const base = apiBase.replace(/\/$/, "");
227
+ try {
228
+ const res = await fetch(`${base}/api/register-policy`, {
229
+ credentials: "omit",
230
+ headers: { Accept: "application/json" },
231
+ ...init
232
+ });
233
+ if (!res.ok) return resolveRegisterEmailPolicy();
234
+ const data = await res.json();
235
+ return resolveRegisterEmailPolicy({
236
+ mode: data.mode,
237
+ allowlist: data.allowlist,
238
+ blocklist: data.blocklist
239
+ });
240
+ } catch {
241
+ return resolveRegisterEmailPolicy();
242
+ }
243
+ }
244
+
89
245
  // src/logto-experience-adapter.ts
90
246
  var LOGTO_USERNAME_RE = /^[A-Za-z_]\w*$/;
91
247
  function isLogtoRegisterUsername(value) {
@@ -173,9 +329,11 @@ var LogtoExperienceAdapter = class {
173
329
  capabilities = [
174
330
  LOGIN_EXPERIENCE_CAPABILITIES.passwordSignIn,
175
331
  LOGIN_EXPERIENCE_CAPABILITIES.passwordSignUp,
332
+ LOGIN_EXPERIENCE_CAPABILITIES.emailCodeSignUp,
176
333
  LOGIN_EXPERIENCE_CAPABILITIES.socialConnectors,
177
334
  LOGIN_EXPERIENCE_CAPABILITIES.socialDirectSignIn
178
335
  ];
336
+ emailPolicy = resolveRegisterEmailPolicy();
179
337
  async experiencePasswordSignIn(input) {
180
338
  const { apiBase, identifier, password } = input;
181
339
  await bootstrapOidcInteraction(input);
@@ -240,7 +398,10 @@ var LogtoExperienceAdapter = class {
240
398
  await bootstrapOidcInteraction(input);
241
399
  await experienceFetch(apiBase, "", {
242
400
  method: "PUT",
243
- body: JSON.stringify({ interactionEvent: "Register" })
401
+ body: JSON.stringify({
402
+ interactionEvent: "Register",
403
+ ...input.captchaToken ? { captchaToken: input.captchaToken } : {}
404
+ })
244
405
  });
245
406
  const verified = await experienceFetch(
246
407
  apiBase,
@@ -267,6 +428,96 @@ var LogtoExperienceAdapter = class {
267
428
  });
268
429
  return { redirectTo: submitted?.redirectTo, raw: submitted };
269
430
  }
431
+ async sendRegisterEmailCode(input) {
432
+ const email = (input.email || input.identifier || "").trim().toLowerCase();
433
+ const decision = evaluateRegisterEmail(email, this.emailPolicy);
434
+ if (!decision.ok) {
435
+ throw new Error(registerEmailRejectionMessage(decision));
436
+ }
437
+ await bootstrapOidcInteraction({
438
+ apiBase: input.apiBase,
439
+ issuer: input.issuer,
440
+ clientId: input.clientId,
441
+ redirectUri: input.redirectUri,
442
+ audience: input.audience,
443
+ scopes: input.scopes,
444
+ returnUrl: input.returnUrl
445
+ });
446
+ await experienceFetch(input.apiBase, "", {
447
+ method: "PUT",
448
+ body: JSON.stringify({
449
+ interactionEvent: "Register",
450
+ ...input.captchaToken ? { captchaToken: input.captchaToken } : {}
451
+ })
452
+ });
453
+ const sent = await experienceFetch(
454
+ input.apiBase,
455
+ "/verification/verification-code",
456
+ {
457
+ method: "POST",
458
+ body: JSON.stringify({
459
+ identifier: { type: "email", value: email },
460
+ interactionEvent: "Register"
461
+ })
462
+ }
463
+ );
464
+ if (!sent?.verificationId) {
465
+ throw new Error("Experience API did not return verificationId for email code");
466
+ }
467
+ return { verificationId: sent.verificationId };
468
+ }
469
+ async experienceEmailPasswordSignUp(input) {
470
+ const email = (input.email || input.identifier || "").trim().toLowerCase();
471
+ const decision = evaluateRegisterEmail(email, this.emailPolicy);
472
+ if (!decision.ok) {
473
+ throw new Error(registerEmailRejectionMessage(decision));
474
+ }
475
+ if (!input.password || input.password.length < 8) {
476
+ throw new Error("Password must be at least 8 characters");
477
+ }
478
+ if (!input.code?.trim() || !input.verificationId?.trim()) {
479
+ throw new Error("Email verification code is required");
480
+ }
481
+ const verified = await experienceFetch(
482
+ input.apiBase,
483
+ "/verification/verification-code/verify",
484
+ {
485
+ method: "POST",
486
+ body: JSON.stringify({
487
+ identifier: { type: "email", value: email },
488
+ verificationId: input.verificationId,
489
+ code: input.code.trim()
490
+ })
491
+ }
492
+ );
493
+ const codeVerificationId = verified?.verificationId || input.verificationId;
494
+ try {
495
+ await experienceFetch(input.apiBase, "/identification", {
496
+ method: "POST",
497
+ body: JSON.stringify({ verificationId: codeVerificationId })
498
+ });
499
+ } catch (error) {
500
+ const msg = error instanceof Error ? error.message : String(error);
501
+ if (!/missing_profile/i.test(msg)) {
502
+ if (!/422|password|profile/i.test(msg)) {
503
+ throw error instanceof Error ? error : new Error(msg);
504
+ }
505
+ }
506
+ }
507
+ await experienceFetch(input.apiBase, "/profile", {
508
+ method: "POST",
509
+ body: JSON.stringify({ type: "password", value: input.password })
510
+ });
511
+ await experienceFetch(input.apiBase, "/identification", {
512
+ method: "POST",
513
+ body: JSON.stringify({})
514
+ });
515
+ const submitted = await experienceFetch(input.apiBase, "/submit", {
516
+ method: "POST",
517
+ body: JSON.stringify({})
518
+ });
519
+ return { redirectTo: submitted?.redirectTo, raw: submitted };
520
+ }
270
521
  async fetchSocialConnectors(input) {
271
522
  const url = new URL(`${input.apiBase.replace(/\/$/, "")}/api/.well-known/sign-in-exp`);
272
523
  if (input.appId?.trim()) url.searchParams.set("appId", input.appId.trim());
@@ -678,6 +929,12 @@ var HeadlessLoginPanel_module_default = {
678
929
  title: "HeadlessLoginPanel_module_title",
679
930
  subtitle: "HeadlessLoginPanel_module_subtitle",
680
931
  form: "HeadlessLoginPanel_module_form",
932
+ channelTabs: "HeadlessLoginPanel_module_channelTabs",
933
+ channelTab: "HeadlessLoginPanel_module_channelTab",
934
+ channelTabActive: "HeadlessLoginPanel_module_channelTabActive",
935
+ codeRow: "HeadlessLoginPanel_module_codeRow",
936
+ codeInput: "HeadlessLoginPanel_module_codeInput",
937
+ secondaryBtn: "HeadlessLoginPanel_module_secondaryBtn",
681
938
  socialStack: "HeadlessLoginPanel_module_socialStack",
682
939
  input: "HeadlessLoginPanel_module_input",
683
940
  passwordField: "HeadlessLoginPanel_module_passwordField",
@@ -692,6 +949,7 @@ var HeadlessLoginPanel_module_default = {
692
949
  dividerLine: "HeadlessLoginPanel_module_dividerLine",
693
950
  dividerText: "HeadlessLoginPanel_module_dividerText",
694
951
  hint: "HeadlessLoginPanel_module_hint",
952
+ domainsHint: "HeadlessLoginPanel_module_domainsHint",
695
953
  switchMode: "HeadlessLoginPanel_module_switchMode",
696
954
  switchModeBtn: "HeadlessLoginPanel_module_switchModeBtn",
697
955
  error: "HeadlessLoginPanel_module_error",
@@ -712,7 +970,7 @@ var defaults = {
712
970
  submitGithub: "GitHub",
713
971
  socialDivider: "or",
714
972
  hint: "Social providers open directly. Password uses your LuminaryWorks account.",
715
- registerHint: "Create a username (start with a letter or _). Prefer email? Use a social provider below.",
973
+ registerHint: "Email register needs a code (Gmail / Outlook / QQ / 163 / iCloud \u2026). Username is an alternative. Prefer one-click? Use a social provider.",
716
974
  cancel: "Cancel",
717
975
  experienceUnavailable: "Password sign-in is unavailable; use a social provider instead.",
718
976
  showPassword: "Show password",
@@ -721,7 +979,17 @@ var defaults = {
721
979
  loginLink: "Already have an account? Sign in",
722
980
  passwordMismatch: "Passwords do not match",
723
981
  registerTitle: "Create account",
724
- registerSubtitle: "Create your LuminaryWorks unified account"
982
+ registerSubtitle: "Create your LuminaryWorks unified account",
983
+ registerWithEmail: "Email",
984
+ registerWithUsername: "Username",
985
+ emailPlaceholder: "Email",
986
+ verificationCodePlaceholder: "Verification code",
987
+ sendCode: "Send code",
988
+ resendCode: "Resend code",
989
+ allowedDomainsPrefix: "Allowed email domains",
990
+ allowedDomainsOpen: "Email registration is open for most providers.",
991
+ allowedDomainsBlocklist: "Most email providers are accepted. Temporary / disposable addresses are blocked.",
992
+ waitingExternalBrowser: "Finish signing in with your browser, then return to this app."
725
993
  };
726
994
  function resolveSieBase(config) {
727
995
  const explicit = config.experienceApiBase?.trim();
@@ -750,8 +1018,12 @@ function HeadlessLoginPanel({
750
1018
  labels: labelsProp,
751
1019
  returnUrl,
752
1020
  mode = "popup",
1021
+ openExternalUrl,
753
1022
  showSocialConnectors = true,
754
1023
  showRegister = true,
1024
+ registerEmailPolicy: registerEmailPolicyProp,
1025
+ fetchRegisterPolicy = true,
1026
+ getCaptchaToken,
755
1027
  socialProviders = "auto",
756
1028
  showCancel,
757
1029
  onCancel,
@@ -766,12 +1038,50 @@ function HeadlessLoginPanel({
766
1038
  experienceAdapterProp,
767
1039
  config.iamProvider
768
1040
  );
1041
+ const [emailPolicy, setEmailPolicy] = react.useState(
1042
+ () => registerEmailPolicyProp ?? resolveRegisterEmailPolicy()
1043
+ );
1044
+ react.useEffect(() => {
1045
+ if (registerEmailPolicyProp) {
1046
+ setEmailPolicy(registerEmailPolicyProp);
1047
+ return;
1048
+ }
1049
+ if (!fetchRegisterPolicy || !showRegister) {
1050
+ setEmailPolicy(resolveRegisterEmailPolicy());
1051
+ return;
1052
+ }
1053
+ const base = config.experienceApiBase?.trim();
1054
+ if (!base) {
1055
+ setEmailPolicy(resolveRegisterEmailPolicy());
1056
+ return;
1057
+ }
1058
+ let cancelled = false;
1059
+ void fetchRegisterEmailPolicy(base).then((policy) => {
1060
+ if (!cancelled) setEmailPolicy(policy);
1061
+ });
1062
+ return () => {
1063
+ cancelled = true;
1064
+ };
1065
+ }, [
1066
+ config.experienceApiBase,
1067
+ fetchRegisterPolicy,
1068
+ registerEmailPolicyProp,
1069
+ showRegister
1070
+ ]);
1071
+ react.useEffect(() => {
1072
+ if (experienceAdapter instanceof LogtoExperienceAdapter) {
1073
+ experienceAdapter.emailPolicy = emailPolicy;
1074
+ }
1075
+ }, [emailPolicy, experienceAdapter]);
769
1076
  const labels = { ...defaults, ...labelsProp };
770
1077
  const configured = isIdpConfigured(config);
771
1078
  const [panelMode, setPanelMode] = react.useState("sign-in");
1079
+ const [registerChannel, setRegisterChannel] = react.useState("email");
772
1080
  const [identifier, setIdentifier] = react.useState("");
773
1081
  const [password, setPassword] = react.useState("");
774
1082
  const [confirmPassword, setConfirmPassword] = react.useState("");
1083
+ const [verificationCode, setVerificationCode] = react.useState("");
1084
+ const [emailVerificationId, setEmailVerificationId] = react.useState(null);
775
1085
  const [passwordVisible, setPasswordVisible] = react.useState(false);
776
1086
  const panelStyle = {
777
1087
  ...style,
@@ -779,13 +1089,20 @@ function HeadlessLoginPanel({
779
1089
  };
780
1090
  const [loading, setLoading] = react.useState(null);
781
1091
  const [error, setError] = react.useState("");
1092
+ const [awaitingExternal, setAwaitingExternal] = react.useState(false);
782
1093
  const [connectors, setConnectors] = react.useState([]);
783
1094
  const socialEnabled = showSocialConnectors !== false && !(Array.isArray(socialProviders) && socialProviders.length === 0) && experienceAdapter.capabilities.includes(LOGIN_EXPERIENCE_CAPABILITIES.socialConnectors) && experienceAdapter.capabilities.includes(LOGIN_EXPERIENCE_CAPABILITIES.socialDirectSignIn) && typeof experienceAdapter.fetchSocialConnectors === "function" && typeof experienceAdapter.createSocialSignInRequest === "function";
784
1095
  const passwordEnabled = experienceAdapter.capabilities.includes(LOGIN_EXPERIENCE_CAPABILITIES.passwordSignIn) && typeof experienceAdapter.experiencePasswordSignIn === "function";
785
- const registerEnabled = showRegister !== false && experienceAdapter.capabilities.includes(LOGIN_EXPERIENCE_CAPABILITIES.passwordSignUp) && typeof experienceAdapter.experiencePasswordSignUp === "function";
1096
+ const registerEnabled = showRegister !== false && (experienceAdapter.capabilities.includes(LOGIN_EXPERIENCE_CAPABILITIES.passwordSignUp) && typeof experienceAdapter.experiencePasswordSignUp === "function" || experienceAdapter.capabilities.includes(LOGIN_EXPERIENCE_CAPABILITIES.emailCodeSignUp) && typeof experienceAdapter.sendRegisterEmailCode === "function" && typeof experienceAdapter.experienceEmailPasswordSignUp === "function");
1097
+ const emailRegisterEnabled = experienceAdapter.capabilities.includes(LOGIN_EXPERIENCE_CAPABILITIES.emailCodeSignUp) && typeof experienceAdapter.sendRegisterEmailCode === "function" && typeof experienceAdapter.experienceEmailPasswordSignUp === "function";
1098
+ const usernameRegisterEnabled = experienceAdapter.capabilities.includes(LOGIN_EXPERIENCE_CAPABILITIES.passwordSignUp) && typeof experienceAdapter.experiencePasswordSignUp === "function";
786
1099
  const isRegister = panelMode === "register" && registerEnabled;
1100
+ const useEmailRegister = isRegister && registerChannel === "email" && emailRegisterEnabled;
787
1101
  react.useEffect(() => {
788
- const resetBusy = () => setLoading(null);
1102
+ const resetBusy = () => {
1103
+ setLoading(null);
1104
+ setAwaitingExternal(false);
1105
+ };
789
1106
  window.addEventListener("pageshow", resetBusy);
790
1107
  window.addEventListener("popstate", resetBusy);
791
1108
  return () => {
@@ -828,6 +1145,19 @@ function HeadlessLoginPanel({
828
1145
  ]);
829
1146
  const runOidc = async (experienceRequest) => {
830
1147
  if (!configured) throw new Error("IdP not configured");
1148
+ if (mode === "external") {
1149
+ if (!openExternalUrl) {
1150
+ throw new Error('openExternalUrl is required when mode is "external"');
1151
+ }
1152
+ const url = await prepareSignInRequestUrl(config, {
1153
+ returnUrl,
1154
+ ...experienceRequest
1155
+ });
1156
+ setAwaitingExternal(true);
1157
+ await openExternalUrl(url);
1158
+ setLoading(null);
1159
+ return;
1160
+ }
831
1161
  if (mode === "redirect") {
832
1162
  await signInRedirect(config, { returnUrl, ...experienceRequest });
833
1163
  return;
@@ -851,6 +1181,7 @@ function HeadlessLoginPanel({
851
1181
  } catch (e) {
852
1182
  setError(e instanceof Error ? e.message : String(e));
853
1183
  setLoading(null);
1184
+ setAwaitingExternal(false);
854
1185
  }
855
1186
  };
856
1187
  const runHostedOidc = async () => {
@@ -861,6 +1192,7 @@ function HeadlessLoginPanel({
861
1192
  } catch (e) {
862
1193
  setError(e instanceof Error ? e.message : String(e));
863
1194
  setLoading(null);
1195
+ setAwaitingExternal(false);
864
1196
  }
865
1197
  };
866
1198
  const followExperienceRedirect = (redirectTo) => {
@@ -880,17 +1212,46 @@ function HeadlessLoginPanel({
880
1212
  if (!apiBase) {
881
1213
  throw new Error(labels.experienceUnavailable);
882
1214
  }
1215
+ const captchaToken = getCaptchaToken ? await getCaptchaToken() : void 0;
883
1216
  if (isRegister) {
884
- if (!registerEnabled || !experienceAdapter.experiencePasswordSignUp) {
885
- throw new Error(labels.experienceUnavailable);
886
- }
887
1217
  if (password !== confirmPassword) {
888
1218
  throw new Error(labels.passwordMismatch);
889
1219
  }
1220
+ if (useEmailRegister) {
1221
+ if (!experienceAdapter.experienceEmailPasswordSignUp || !emailVerificationId) {
1222
+ throw new Error("Send and enter the email verification code first");
1223
+ }
1224
+ const decision = evaluateRegisterEmail(identifier.trim(), emailPolicy);
1225
+ if (!decision.ok) throw new Error(registerEmailRejectionMessage(decision));
1226
+ const result3 = await experienceAdapter.experienceEmailPasswordSignUp({
1227
+ apiBase,
1228
+ identifier: identifier.trim(),
1229
+ email: identifier.trim(),
1230
+ password,
1231
+ code: verificationCode,
1232
+ verificationId: emailVerificationId,
1233
+ captchaToken,
1234
+ issuer: config.issuer,
1235
+ clientId: config.clientId,
1236
+ redirectUri: config.redirectUri,
1237
+ audience: config.audience,
1238
+ scopes: config.scopes,
1239
+ returnUrl
1240
+ });
1241
+ if (result3.redirectTo) {
1242
+ followExperienceRedirect(result3.redirectTo);
1243
+ return;
1244
+ }
1245
+ throw new Error("Experience API did not return redirectTo");
1246
+ }
1247
+ if (!usernameRegisterEnabled || !experienceAdapter.experiencePasswordSignUp) {
1248
+ throw new Error(labels.experienceUnavailable);
1249
+ }
890
1250
  const result2 = await experienceAdapter.experiencePasswordSignUp({
891
1251
  apiBase,
892
1252
  identifier: identifier.trim(),
893
1253
  password,
1254
+ captchaToken,
894
1255
  issuer: config.issuer,
895
1256
  clientId: config.clientId,
896
1257
  redirectUri: config.redirectUri,
@@ -930,20 +1291,59 @@ function HeadlessLoginPanel({
930
1291
  setLoading(null);
931
1292
  }
932
1293
  };
1294
+ const runSendCode = async () => {
1295
+ if (!configured || !useEmailRegister) return;
1296
+ setError("");
1297
+ setLoading("send-code");
1298
+ try {
1299
+ const apiBase = config.experienceApiBase?.trim();
1300
+ if (!apiBase || !experienceAdapter.sendRegisterEmailCode) {
1301
+ throw new Error(labels.experienceUnavailable);
1302
+ }
1303
+ const email = identifier.trim();
1304
+ const decision = evaluateRegisterEmail(email, emailPolicy);
1305
+ if (!decision.ok) throw new Error(registerEmailRejectionMessage(decision));
1306
+ const captchaToken = getCaptchaToken ? await getCaptchaToken() : void 0;
1307
+ const sent = await experienceAdapter.sendRegisterEmailCode({
1308
+ apiBase,
1309
+ identifier: email,
1310
+ email,
1311
+ captchaToken,
1312
+ issuer: config.issuer,
1313
+ clientId: config.clientId,
1314
+ redirectUri: config.redirectUri,
1315
+ audience: config.audience,
1316
+ scopes: config.scopes,
1317
+ returnUrl
1318
+ });
1319
+ setEmailVerificationId(sent.verificationId);
1320
+ } catch (e) {
1321
+ setError(e instanceof Error ? e.message : String(e));
1322
+ } finally {
1323
+ setLoading(null);
1324
+ }
1325
+ };
933
1326
  const switchPanelMode = (next) => {
934
1327
  setPanelMode(next);
935
1328
  setError("");
936
1329
  setConfirmPassword("");
1330
+ setVerificationCode("");
1331
+ setEmailVerificationId(null);
937
1332
  };
938
1333
  const busy = loading !== null;
939
1334
  const showSocial = socialEnabled && connectors.length > 0;
940
1335
  const showHint = Boolean(labelsProp?.hint) || showSocial || isRegister;
941
- const formReady = Boolean(identifier.trim()) && Boolean(password) && (!isRegister || Boolean(confirmPassword));
1336
+ const formReady = Boolean(identifier.trim()) && Boolean(password) && (!isRegister || Boolean(confirmPassword)) && (!useEmailRegister || Boolean(verificationCode.trim()) && Boolean(emailVerificationId));
942
1337
  const titleText = isRegister ? labels.registerTitle : labels.title;
943
1338
  const subtitleText = isRegister ? labels.registerSubtitle : labels.subtitle;
944
- const identifierPlaceholder = isRegister ? labels.registerIdentifierPlaceholder : labels.identifierPlaceholder;
1339
+ const identifierPlaceholder = isRegister ? useEmailRegister ? labels.emailPlaceholder : labels.registerIdentifierPlaceholder : labels.identifierPlaceholder;
945
1340
  const submitLabel = isRegister ? labels.submitRegister : labels.submitPassword;
946
1341
  const hintText = isRegister ? labels.registerHint : labels.hint;
1342
+ const domainsHint = useEmailRegister ? registerEmailDomainsHint(emailPolicy, {
1343
+ allowedDomainsPrefix: labels.allowedDomainsPrefix,
1344
+ allowedDomainsOpen: labels.allowedDomainsOpen,
1345
+ allowedDomainsBlocklist: labels.allowedDomainsBlocklist
1346
+ }) : null;
947
1347
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cx(HeadlessLoginPanel_module_default.panel, className), style: panelStyle, children: [
948
1348
  /* @__PURE__ */ jsxRuntime.jsxs("header", { className: HeadlessLoginPanel_module_default.brand, children: [
949
1349
  logoSrc ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: logoSrc, alt: "", width: 48, height: 48, className: HeadlessLoginPanel_module_default.logo }) : null,
@@ -955,19 +1355,93 @@ function HeadlessLoginPanel({
955
1355
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: HeadlessLoginPanel_module_default.subtitle, children: subtitleText }),
956
1356
  configured ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
957
1357
  passwordEnabled ? /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: (e) => void runPassword(e), className: HeadlessLoginPanel_module_default.form, children: [
1358
+ isRegister && emailRegisterEnabled && usernameRegisterEnabled ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: HeadlessLoginPanel_module_default.channelTabs, role: "tablist", "aria-label": "Register method", children: [
1359
+ /* @__PURE__ */ jsxRuntime.jsx(
1360
+ "button",
1361
+ {
1362
+ type: "button",
1363
+ role: "tab",
1364
+ className: cx(
1365
+ HeadlessLoginPanel_module_default.channelTab,
1366
+ registerChannel === "email" && HeadlessLoginPanel_module_default.channelTabActive
1367
+ ),
1368
+ "aria-selected": registerChannel === "email",
1369
+ disabled: busy,
1370
+ onClick: () => {
1371
+ setRegisterChannel("email");
1372
+ setEmailVerificationId(null);
1373
+ setVerificationCode("");
1374
+ setError("");
1375
+ },
1376
+ children: labels.registerWithEmail
1377
+ }
1378
+ ),
1379
+ /* @__PURE__ */ jsxRuntime.jsx(
1380
+ "button",
1381
+ {
1382
+ type: "button",
1383
+ role: "tab",
1384
+ className: cx(
1385
+ HeadlessLoginPanel_module_default.channelTab,
1386
+ registerChannel === "username" && HeadlessLoginPanel_module_default.channelTabActive
1387
+ ),
1388
+ "aria-selected": registerChannel === "username",
1389
+ disabled: busy,
1390
+ onClick: () => {
1391
+ setRegisterChannel("username");
1392
+ setEmailVerificationId(null);
1393
+ setVerificationCode("");
1394
+ setError("");
1395
+ },
1396
+ children: labels.registerWithUsername
1397
+ }
1398
+ )
1399
+ ] }) : null,
958
1400
  /* @__PURE__ */ jsxRuntime.jsx(
959
1401
  "input",
960
1402
  {
961
- type: "text",
1403
+ type: useEmailRegister ? "email" : "text",
962
1404
  name: "identifier",
963
1405
  autoComplete: isRegister ? "username" : "username",
964
1406
  placeholder: identifierPlaceholder,
965
1407
  value: identifier,
966
- onChange: (e) => setIdentifier(e.target.value),
1408
+ onChange: (e) => {
1409
+ setIdentifier(e.target.value);
1410
+ if (useEmailRegister) {
1411
+ setEmailVerificationId(null);
1412
+ }
1413
+ },
967
1414
  className: HeadlessLoginPanel_module_default.input,
968
1415
  disabled: busy
969
1416
  }
970
1417
  ),
1418
+ domainsHint ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: HeadlessLoginPanel_module_default.domainsHint, "data-testid": "register-allowed-domains", children: domainsHint }) : null,
1419
+ useEmailRegister ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: HeadlessLoginPanel_module_default.codeRow, children: [
1420
+ /* @__PURE__ */ jsxRuntime.jsx(
1421
+ "input",
1422
+ {
1423
+ type: "text",
1424
+ name: "verificationCode",
1425
+ inputMode: "numeric",
1426
+ autoComplete: "one-time-code",
1427
+ placeholder: labels.verificationCodePlaceholder,
1428
+ value: verificationCode,
1429
+ onChange: (e) => setVerificationCode(e.target.value),
1430
+ className: cx(HeadlessLoginPanel_module_default.input, HeadlessLoginPanel_module_default.codeInput),
1431
+ disabled: busy
1432
+ }
1433
+ ),
1434
+ /* @__PURE__ */ jsxRuntime.jsx(
1435
+ "button",
1436
+ {
1437
+ type: "button",
1438
+ className: HeadlessLoginPanel_module_default.secondaryBtn,
1439
+ disabled: busy || !identifier.trim(),
1440
+ onClick: () => void runSendCode(),
1441
+ children: loading === "send-code" ? "\u2026" : emailVerificationId ? labels.resendCode : labels.sendCode
1442
+ }
1443
+ )
1444
+ ] }) : null,
971
1445
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: HeadlessLoginPanel_module_default.passwordField, children: [
972
1446
  /* @__PURE__ */ jsxRuntime.jsx(
973
1447
  "input",
@@ -1030,6 +1504,7 @@ function HeadlessLoginPanel({
1030
1504
  children: isRegister ? labels.loginLink : labels.registerLink
1031
1505
  }
1032
1506
  ) }) : null,
1507
+ awaitingExternal ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: HeadlessLoginPanel_module_default.hint, role: "status", children: labels.waitingExternalBrowser }) : null,
1033
1508
  showSocial ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: HeadlessLoginPanel_module_default.divider, "aria-hidden": true, children: [
1034
1509
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: HeadlessLoginPanel_module_default.dividerLine }),
1035
1510
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: HeadlessLoginPanel_module_default.dividerText, children: labels.socialDivider }),
@@ -1254,7 +1729,9 @@ function createPostLoginPathHelpers(options) {
1254
1729
  }
1255
1730
 
1256
1731
  exports.AuthGateProvider = AuthGateProvider;
1732
+ exports.DEFAULT_DISPOSABLE_EMAIL_BLOCKLIST = DEFAULT_DISPOSABLE_EMAIL_BLOCKLIST;
1257
1733
  exports.DEFAULT_LOGIN_THEME_COLOR = DEFAULT_LOGIN_THEME_COLOR;
1734
+ exports.DEFAULT_REGISTER_EMAIL_ALLOWLIST = DEFAULT_REGISTER_EMAIL_ALLOWLIST;
1258
1735
  exports.HeadlessLoginPanel = HeadlessLoginPanel;
1259
1736
  exports.HostedOidcExperienceAdapter = HostedOidcExperienceAdapter;
1260
1737
  exports.LOGIN_EXPERIENCE_CAPABILITIES = LOGIN_EXPERIENCE_CAPABILITIES;
@@ -1267,8 +1744,12 @@ exports.createLoginExperienceAdapter = createLoginExperienceAdapter;
1267
1744
  exports.createLogtoDirectSignInRequest = createLogtoDirectSignInRequest;
1268
1745
  exports.createPostLoginPathHelpers = createPostLoginPathHelpers;
1269
1746
  exports.createUserManager = createUserManager;
1747
+ exports.emailDomain = emailDomain;
1748
+ exports.evaluateRegisterEmail = evaluateRegisterEmail;
1270
1749
  exports.experiencePasswordSignIn = experiencePasswordSignIn;
1750
+ exports.fetchRegisterEmailPolicy = fetchRegisterEmailPolicy;
1271
1751
  exports.fetchSocialConnectors = fetchSocialConnectors;
1752
+ exports.formatAllowedEmailDomains = formatAllowedEmailDomains;
1272
1753
  exports.getCurrentUser = getCurrentUser;
1273
1754
  exports.getStoredAccessToken = getStoredAccessToken;
1274
1755
  exports.handleSignInCallback = handleSignInCallback;
@@ -1276,13 +1757,19 @@ exports.handleSignInPopupCallback = handleSignInPopupCallback;
1276
1757
  exports.isIdpConfigured = isIdpConfigured;
1277
1758
  exports.isLogtoRegisterUsername = isLogtoRegisterUsername;
1278
1759
  exports.isOidcPopupWindow = isOidcPopupWindow;
1760
+ exports.isValidEmailShape = isValidEmailShape;
1761
+ exports.normalizeRegisterEmailMode = normalizeRegisterEmailMode;
1279
1762
  exports.prepareSignInRequestUrl = prepareSignInRequestUrl;
1280
1763
  exports.readIdpConfigFromEnv = readIdpConfigFromEnv;
1764
+ exports.readRegisterEmailPolicyFromEnv = readRegisterEmailPolicyFromEnv;
1765
+ exports.registerEmailDomainsHint = registerEmailDomainsHint;
1766
+ exports.registerEmailRejectionMessage = registerEmailRejectionMessage;
1281
1767
  exports.resetUserManager = resetUserManager;
1282
1768
  exports.resolveExperienceApiBase = resolveExperienceApiBase;
1283
1769
  exports.resolveIssuer = resolveIssuer;
1284
1770
  exports.resolveLoginExperienceAdapter = resolveLoginExperienceAdapter;
1285
1771
  exports.resolveLoginExperienceProviderFromEnv = resolveLoginExperienceProviderFromEnv;
1772
+ exports.resolveRegisterEmailPolicy = resolveRegisterEmailPolicy;
1286
1773
  exports.sameOriginAuthorizeUrl = sameOriginAuthorizeUrl;
1287
1774
  exports.signInPopup = signInPopup;
1288
1775
  exports.signInRedirect = signInRedirect;