@luminaryworks/auth-react 0.4.5 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/dist/index.css +229 -4
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +197 -7
- package/dist/index.js +757 -14
- package/dist/index.js.map +1 -1
- package/dist/style.css +229 -4
- package/dist/style.css.map +1 -1
- package/package.json +4 -3
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: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\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 width: 100%;\n min-width: 0;\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\n/* src/LoginLanguageSwitcher.module.scss */\n.LoginLanguageSwitcher_module_switch {\n position: relative;\n display: inline-flex;\n}\n.LoginLanguageSwitcher_module_trigger {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 32px;\n padding: 0 10px 0 8px;\n border: 1px solid rgba(15, 23, 42, 0.12);\n border-radius: 999px;\n background: rgba(255, 255, 255, 0.86);\n color: #243044;\n font: inherit;\n font-size: 13px;\n line-height: 1;\n cursor: pointer;\n backdrop-filter: blur(8px);\n}\n.LoginLanguageSwitcher_module_trigger:hover,\n.LoginLanguageSwitcher_module_trigger[aria-expanded=true] {\n border-color: var(--lw-login-accent, #1677ff);\n color: #0f172a;\n}\n.LoginLanguageSwitcher_module_icon {\n width: 14px;\n height: 14px;\n display: block;\n}\n.LoginLanguageSwitcher_module_caret {\n width: 10px;\n height: 10px;\n opacity: 0.65;\n}\n.LoginLanguageSwitcher_module_menu {\n position: absolute;\n top: calc(100% + 6px);\n right: 0;\n z-index: 20;\n min-width: 168px;\n margin: 0;\n padding: 6px;\n list-style: none;\n border-radius: 12px;\n background: #fff;\n box-shadow: 0 12px 40px rgba(15, 23, 42, 0.16);\n}\n.LoginLanguageSwitcher_module_item {\n display: block;\n width: 100%;\n border: 0;\n border-radius: 8px;\n padding: 8px 10px;\n background: transparent;\n color: #1f2937;\n font: inherit;\n font-size: 14px;\n text-align: left;\n cursor: pointer;\n}\n.LoginLanguageSwitcher_module_item:hover {\n background: #f3f6fb;\n}\n.LoginLanguageSwitcher_module_itemActive {\n background: color-mix(in srgb, var(--lw-login-accent, #1677ff) 14%, #fff);\n color: var(--lw-login-accent, #1677ff);\n font-weight: 650;\n}\n.LoginLanguageSwitcher_module_onDark {\n border-color: rgba(255, 255, 255, 0.22);\n background: rgba(15, 23, 42, 0.28);\n color: rgba(255, 255, 255, 0.92);\n}\n.LoginLanguageSwitcher_module_onDark:hover,\n.LoginLanguageSwitcher_module_onDark[aria-expanded=true] {\n border-color: rgba(255, 255, 255, 0.55);\n color: #fff;\n}\n\n/* src/LoginCanvas.module.scss */\n.LoginCanvas_module_canvas {\n --lw-login-accent: #1677ff;\n position: relative;\n min-height: 100dvh;\n display: grid;\n place-items: center;\n padding: 56px 20px 24px;\n overflow: hidden;\n background:\n radial-gradient(\n 900px 480px at 8% -8%,\n color-mix(in srgb, var(--lw-login-accent) 28%, transparent),\n transparent 58%),\n radial-gradient(\n 720px 420px at 100% 12%,\n color-mix(in srgb, var(--lw-login-accent) 16%, transparent),\n transparent 52%),\n linear-gradient(\n 165deg,\n var(--lw-login-bg-a, #f4f7fb) 0%,\n var(--lw-login-bg-b, #e7eef6) 100%);\n color: var(--lw-login-ink, #122033);\n}\n.LoginCanvas_module_canvas[data-tone=console] {\n --lw-login-accent: #7eb6ff;\n --lw-login-bg-a: #0e1726;\n --lw-login-bg-b: #1a2740;\n --lw-login-ink: #e8eef8;\n --lw-login-grid: rgba(255, 255, 255, 0.05);\n}\n.LoginCanvas_module_canvas[data-tone=vista] {\n --lw-login-accent: #1677ff;\n --lw-login-bg-a: #f4f7fb;\n --lw-login-bg-b: #dde7f0;\n --lw-login-grid: rgba(11, 18, 32, 0.045);\n}\n.LoginCanvas_module_canvas[data-tone=data] {\n --lw-login-accent: #5aa2ff;\n --lw-login-bg-a: #070b14;\n --lw-login-bg-b: #12203a;\n --lw-login-ink: #e8eefc;\n --lw-login-grid: rgba(255, 255, 255, 0.045);\n}\n.LoginCanvas_module_canvas[data-tone=edu] {\n --lw-login-accent: #f59e0b;\n --lw-login-bg-a: #fff7ed;\n --lw-login-bg-b: #e0e7ff;\n --lw-login-grid: rgba(67, 56, 202, 0.06);\n}\n.LoginCanvas_module_grid {\n position: absolute;\n inset: 0;\n pointer-events: none;\n background-image:\n linear-gradient(var(--lw-login-grid, rgba(15, 23, 42, 0.04)) 1px, transparent 1px),\n linear-gradient(\n 90deg,\n var(--lw-login-grid, rgba(15, 23, 42, 0.04)) 1px,\n transparent 1px);\n background-size: 48px 48px;\n mask-image:\n radial-gradient(\n ellipse at center,\n #000 18%,\n transparent 74%);\n}\n.LoginCanvas_module_lang {\n position: fixed;\n top: 14px;\n right: 16px;\n z-index: 5;\n}\n.LoginCanvas_module_decor {\n position: absolute;\n inset: 0;\n pointer-events: none;\n z-index: 0;\n}\n.LoginCanvas_module_slot {\n position: relative;\n z-index: 1;\n width: min(440px, 100%);\n}\n.LoginCanvas_module_slotFill {\n width: min(1120px, 100%);\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({
|
|
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());
|
|
@@ -669,6 +920,138 @@ function useLuminaryAuth() {
|
|
|
669
920
|
return ctx;
|
|
670
921
|
}
|
|
671
922
|
|
|
923
|
+
// src/login-labels.ts
|
|
924
|
+
var DEFAULT_LOGIN_LOCALES = [
|
|
925
|
+
{ code: "en", label: "English", short: "EN" },
|
|
926
|
+
{ code: "zh-CN", label: "\u7B80\u4F53\u4E2D\u6587", short: "\u7B80" },
|
|
927
|
+
{ code: "zh-TW", label: "\u7E41\u9AD4\u4E2D\u6587", short: "\u7E41" },
|
|
928
|
+
{ code: "es", label: "Espa\xF1ol", short: "ES" }
|
|
929
|
+
];
|
|
930
|
+
var ZH_CN = {
|
|
931
|
+
title: "\u767B\u5F55",
|
|
932
|
+
subtitle: "\u4F7F\u7528 LuminaryWorks \u7EDF\u4E00\u8D26\u53F7",
|
|
933
|
+
identifierPlaceholder: "\u90AE\u7BB1\u6216\u7528\u6237\u540D",
|
|
934
|
+
registerIdentifierPlaceholder: "\u7528\u6237\u540D",
|
|
935
|
+
passwordPlaceholder: "\u5BC6\u7801",
|
|
936
|
+
confirmPasswordPlaceholder: "\u786E\u8BA4\u5BC6\u7801",
|
|
937
|
+
submitPassword: "\u5BC6\u7801\u767B\u5F55",
|
|
938
|
+
submitRegister: "\u521B\u5EFA\u8D26\u53F7",
|
|
939
|
+
submitSso: "\u4F7F\u7528\u7EDF\u4E00\u8D26\u53F7\u7EE7\u7EED",
|
|
940
|
+
submitGoogle: "Google",
|
|
941
|
+
submitGithub: "GitHub",
|
|
942
|
+
socialDivider: "\u6216",
|
|
943
|
+
hint: "\u793E\u4EA4\u8D26\u53F7\u4F1A\u76F4\u63A5\u6253\u5F00\u5BF9\u5E94\u670D\u52A1\u3002\u5BC6\u7801\u767B\u5F55\u4F7F\u7528\u4F60\u7684 LuminaryWorks \u8D26\u53F7\u3002",
|
|
944
|
+
registerHint: "\u90AE\u7BB1\u6CE8\u518C\u9700\u8981\u9A8C\u8BC1\u7801\u3002\u4E5F\u53EF\u4EE5\u7528\u7528\u6237\u540D\u6CE8\u518C\uFF0C\u6216\u6539\u7528\u793E\u4EA4\u8D26\u53F7\u4E00\u952E\u767B\u5F55\u3002",
|
|
945
|
+
cancel: "\u53D6\u6D88",
|
|
946
|
+
experienceUnavailable: "\u5BC6\u7801\u767B\u5F55\u6682\u4E0D\u53EF\u7528\uFF0C\u8BF7\u6539\u7528\u793E\u4EA4\u8D26\u53F7\u3002",
|
|
947
|
+
showPassword: "\u663E\u793A\u5BC6\u7801",
|
|
948
|
+
hidePassword: "\u9690\u85CF\u5BC6\u7801",
|
|
949
|
+
registerLink: "\u521B\u5EFA\u8D26\u53F7",
|
|
950
|
+
loginLink: "\u5DF2\u6709\u8D26\u53F7\uFF1F\u53BB\u767B\u5F55",
|
|
951
|
+
passwordMismatch: "\u4E24\u6B21\u5BC6\u7801\u4E0D\u4E00\u81F4",
|
|
952
|
+
registerTitle: "\u521B\u5EFA\u8D26\u53F7",
|
|
953
|
+
registerSubtitle: "\u521B\u5EFA LuminaryWorks \u7EDF\u4E00\u8D26\u53F7",
|
|
954
|
+
registerWithEmail: "\u90AE\u7BB1",
|
|
955
|
+
registerWithUsername: "\u7528\u6237\u540D",
|
|
956
|
+
emailPlaceholder: "\u90AE\u7BB1",
|
|
957
|
+
verificationCodePlaceholder: "\u9A8C\u8BC1\u7801",
|
|
958
|
+
sendCode: "\u53D1\u9001\u9A8C\u8BC1\u7801",
|
|
959
|
+
resendCode: "\u91CD\u65B0\u53D1\u9001",
|
|
960
|
+
allowedDomainsPrefix: "\u5141\u8BB8\u7684\u90AE\u7BB1\u57DF\u540D",
|
|
961
|
+
allowedDomainsOpen: "\u5927\u591A\u6570\u90AE\u7BB1\u90FD\u53EF\u4EE5\u6CE8\u518C\u3002",
|
|
962
|
+
allowedDomainsBlocklist: "\u5927\u591A\u6570\u90AE\u7BB1\u53EF\u7528\u3002\u4E34\u65F6\u90AE\u7BB1\u4F1A\u88AB\u62D2\u7EDD\u3002",
|
|
963
|
+
waitingExternalBrowser: "\u8BF7\u5728\u6D4F\u89C8\u5668\u4E2D\u5B8C\u6210\u767B\u5F55\uFF0C\u7136\u540E\u56DE\u5230\u672C\u5E94\u7528\u3002"
|
|
964
|
+
};
|
|
965
|
+
var ZH_TW = {
|
|
966
|
+
title: "\u767B\u5165",
|
|
967
|
+
subtitle: "\u4F7F\u7528 LuminaryWorks \u7D71\u4E00\u5E33\u865F",
|
|
968
|
+
identifierPlaceholder: "\u96FB\u5B50\u90F5\u4EF6\u6216\u4F7F\u7528\u8005\u540D\u7A31",
|
|
969
|
+
registerIdentifierPlaceholder: "\u4F7F\u7528\u8005\u540D\u7A31",
|
|
970
|
+
passwordPlaceholder: "\u5BC6\u78BC",
|
|
971
|
+
confirmPasswordPlaceholder: "\u78BA\u8A8D\u5BC6\u78BC",
|
|
972
|
+
submitPassword: "\u5BC6\u78BC\u767B\u5165",
|
|
973
|
+
submitRegister: "\u5EFA\u7ACB\u5E33\u865F",
|
|
974
|
+
submitSso: "\u4F7F\u7528\u7D71\u4E00\u5E33\u865F\u7E7C\u7E8C",
|
|
975
|
+
submitGoogle: "Google",
|
|
976
|
+
submitGithub: "GitHub",
|
|
977
|
+
socialDivider: "\u6216",
|
|
978
|
+
hint: "\u793E\u4EA4\u5E33\u865F\u6703\u76F4\u63A5\u958B\u555F\u5C0D\u61C9\u670D\u52D9\u3002\u5BC6\u78BC\u767B\u5165\u4F7F\u7528\u4F60\u7684 LuminaryWorks \u5E33\u865F\u3002",
|
|
979
|
+
registerHint: "\u96FB\u5B50\u90F5\u4EF6\u8A3B\u518A\u9700\u8981\u9A57\u8B49\u78BC\u3002\u4E5F\u53EF\u4EE5\u7528\u4F7F\u7528\u8005\u540D\u7A31\u8A3B\u518A\uFF0C\u6216\u6539\u7528\u793E\u4EA4\u5E33\u865F\u3002",
|
|
980
|
+
cancel: "\u53D6\u6D88",
|
|
981
|
+
experienceUnavailable: "\u5BC6\u78BC\u767B\u5165\u66AB\u4E0D\u53EF\u7528\uFF0C\u8ACB\u6539\u7528\u793E\u4EA4\u5E33\u865F\u3002",
|
|
982
|
+
showPassword: "\u986F\u793A\u5BC6\u78BC",
|
|
983
|
+
hidePassword: "\u96B1\u85CF\u5BC6\u78BC",
|
|
984
|
+
registerLink: "\u5EFA\u7ACB\u5E33\u865F",
|
|
985
|
+
loginLink: "\u5DF2\u6709\u5E33\u865F\uFF1F\u53BB\u767B\u5165",
|
|
986
|
+
passwordMismatch: "\u5169\u6B21\u5BC6\u78BC\u4E0D\u4E00\u81F4",
|
|
987
|
+
registerTitle: "\u5EFA\u7ACB\u5E33\u865F",
|
|
988
|
+
registerSubtitle: "\u5EFA\u7ACB LuminaryWorks \u7D71\u4E00\u5E33\u865F",
|
|
989
|
+
registerWithEmail: "\u96FB\u5B50\u90F5\u4EF6",
|
|
990
|
+
registerWithUsername: "\u4F7F\u7528\u8005\u540D\u7A31",
|
|
991
|
+
emailPlaceholder: "\u96FB\u5B50\u90F5\u4EF6",
|
|
992
|
+
verificationCodePlaceholder: "\u9A57\u8B49\u78BC",
|
|
993
|
+
sendCode: "\u767C\u9001\u9A57\u8B49\u78BC",
|
|
994
|
+
resendCode: "\u91CD\u65B0\u767C\u9001",
|
|
995
|
+
allowedDomainsPrefix: "\u5141\u8A31\u7684\u96FB\u5B50\u90F5\u4EF6\u7DB2\u57DF",
|
|
996
|
+
allowedDomainsOpen: "\u5927\u591A\u6578\u96FB\u5B50\u90F5\u4EF6\u90FD\u53EF\u4EE5\u8A3B\u518A\u3002",
|
|
997
|
+
allowedDomainsBlocklist: "\u5927\u591A\u6578\u96FB\u5B50\u90F5\u4EF6\u53EF\u7528\u3002\u81E8\u6642\u4FE1\u7BB1\u6703\u88AB\u62D2\u7D55\u3002",
|
|
998
|
+
waitingExternalBrowser: "\u8ACB\u5728\u700F\u89BD\u5668\u4E2D\u5B8C\u6210\u767B\u5165\uFF0C\u7136\u5F8C\u56DE\u5230\u672C\u61C9\u7528\u3002"
|
|
999
|
+
};
|
|
1000
|
+
var ES = {
|
|
1001
|
+
title: "Iniciar sesi\xF3n",
|
|
1002
|
+
subtitle: "Usa tu cuenta unificada de LuminaryWorks",
|
|
1003
|
+
identifierPlaceholder: "Correo o usuario",
|
|
1004
|
+
registerIdentifierPlaceholder: "Usuario",
|
|
1005
|
+
passwordPlaceholder: "Contrase\xF1a",
|
|
1006
|
+
confirmPasswordPlaceholder: "Confirmar contrase\xF1a",
|
|
1007
|
+
submitPassword: "Entrar con contrase\xF1a",
|
|
1008
|
+
submitRegister: "Crear cuenta",
|
|
1009
|
+
submitSso: "Continuar con la cuenta unificada",
|
|
1010
|
+
submitGoogle: "Google",
|
|
1011
|
+
submitGithub: "GitHub",
|
|
1012
|
+
socialDivider: "o",
|
|
1013
|
+
hint: "Las redes abren el proveedor directamente. La contrase\xF1a usa tu cuenta LuminaryWorks.",
|
|
1014
|
+
registerHint: "El registro por correo pide un c\xF3digo. Tambi\xE9n puedes usar un nombre de usuario o una red social.",
|
|
1015
|
+
cancel: "Cancelar",
|
|
1016
|
+
experienceUnavailable: "El inicio con contrase\xF1a no est\xE1 disponible. Usa una red social.",
|
|
1017
|
+
showPassword: "Mostrar contrase\xF1a",
|
|
1018
|
+
hidePassword: "Ocultar contrase\xF1a",
|
|
1019
|
+
registerLink: "Crear una cuenta",
|
|
1020
|
+
loginLink: "\xBFYa tienes cuenta? Inicia sesi\xF3n",
|
|
1021
|
+
passwordMismatch: "Las contrase\xF1as no coinciden",
|
|
1022
|
+
registerTitle: "Crear cuenta",
|
|
1023
|
+
registerSubtitle: "Crea tu cuenta unificada de LuminaryWorks",
|
|
1024
|
+
registerWithEmail: "Correo",
|
|
1025
|
+
registerWithUsername: "Usuario",
|
|
1026
|
+
emailPlaceholder: "Correo",
|
|
1027
|
+
verificationCodePlaceholder: "C\xF3digo",
|
|
1028
|
+
sendCode: "Enviar c\xF3digo",
|
|
1029
|
+
resendCode: "Reenviar",
|
|
1030
|
+
allowedDomainsPrefix: "Dominios de correo permitidos",
|
|
1031
|
+
allowedDomainsOpen: "El registro por correo est\xE1 abierto para la mayor\xEDa de proveedores.",
|
|
1032
|
+
allowedDomainsBlocklist: "Se aceptan la mayor\xEDa de correos. Se bloquean direcciones temporales.",
|
|
1033
|
+
waitingExternalBrowser: "Termina el inicio de sesi\xF3n en el navegador y vuelve a esta app."
|
|
1034
|
+
};
|
|
1035
|
+
var BY_LOCALE = {
|
|
1036
|
+
en: void 0,
|
|
1037
|
+
"zh-CN": ZH_CN,
|
|
1038
|
+
"zh-TW": ZH_TW,
|
|
1039
|
+
es: ES
|
|
1040
|
+
};
|
|
1041
|
+
function normalizeLoginLocale(locale) {
|
|
1042
|
+
const raw = (locale ?? "en").trim().toLowerCase();
|
|
1043
|
+
if (raw === "zh" || raw === "zh-cn" || raw === "zh-hans" || raw.startsWith("zh-cn")) {
|
|
1044
|
+
return "zh-CN";
|
|
1045
|
+
}
|
|
1046
|
+
if (raw === "zh-tw" || raw === "zh-hant" || raw.startsWith("zh-tw")) return "zh-TW";
|
|
1047
|
+
if (raw === "es" || raw.startsWith("es-")) return "es";
|
|
1048
|
+
if (raw === "en" || raw.startsWith("en-")) return "en";
|
|
1049
|
+
return "en";
|
|
1050
|
+
}
|
|
1051
|
+
function loginLabels(locale) {
|
|
1052
|
+
return BY_LOCALE[normalizeLoginLocale(locale)] ?? {};
|
|
1053
|
+
}
|
|
1054
|
+
|
|
672
1055
|
// src/HeadlessLoginPanel.module.scss
|
|
673
1056
|
var HeadlessLoginPanel_module_default = {
|
|
674
1057
|
panel: "HeadlessLoginPanel_module_panel",
|
|
@@ -678,6 +1061,12 @@ var HeadlessLoginPanel_module_default = {
|
|
|
678
1061
|
title: "HeadlessLoginPanel_module_title",
|
|
679
1062
|
subtitle: "HeadlessLoginPanel_module_subtitle",
|
|
680
1063
|
form: "HeadlessLoginPanel_module_form",
|
|
1064
|
+
channelTabs: "HeadlessLoginPanel_module_channelTabs",
|
|
1065
|
+
channelTab: "HeadlessLoginPanel_module_channelTab",
|
|
1066
|
+
channelTabActive: "HeadlessLoginPanel_module_channelTabActive",
|
|
1067
|
+
codeRow: "HeadlessLoginPanel_module_codeRow",
|
|
1068
|
+
codeInput: "HeadlessLoginPanel_module_codeInput",
|
|
1069
|
+
secondaryBtn: "HeadlessLoginPanel_module_secondaryBtn",
|
|
681
1070
|
socialStack: "HeadlessLoginPanel_module_socialStack",
|
|
682
1071
|
input: "HeadlessLoginPanel_module_input",
|
|
683
1072
|
passwordField: "HeadlessLoginPanel_module_passwordField",
|
|
@@ -692,6 +1081,7 @@ var HeadlessLoginPanel_module_default = {
|
|
|
692
1081
|
dividerLine: "HeadlessLoginPanel_module_dividerLine",
|
|
693
1082
|
dividerText: "HeadlessLoginPanel_module_dividerText",
|
|
694
1083
|
hint: "HeadlessLoginPanel_module_hint",
|
|
1084
|
+
domainsHint: "HeadlessLoginPanel_module_domainsHint",
|
|
695
1085
|
switchMode: "HeadlessLoginPanel_module_switchMode",
|
|
696
1086
|
switchModeBtn: "HeadlessLoginPanel_module_switchModeBtn",
|
|
697
1087
|
error: "HeadlessLoginPanel_module_error",
|
|
@@ -712,7 +1102,7 @@ var defaults = {
|
|
|
712
1102
|
submitGithub: "GitHub",
|
|
713
1103
|
socialDivider: "or",
|
|
714
1104
|
hint: "Social providers open directly. Password uses your LuminaryWorks account.",
|
|
715
|
-
registerHint: "
|
|
1105
|
+
registerHint: "Email register needs a code (Gmail / Outlook / QQ / 163 / iCloud \u2026). Username is an alternative. Prefer one-click? Use a social provider.",
|
|
716
1106
|
cancel: "Cancel",
|
|
717
1107
|
experienceUnavailable: "Password sign-in is unavailable; use a social provider instead.",
|
|
718
1108
|
showPassword: "Show password",
|
|
@@ -721,7 +1111,17 @@ var defaults = {
|
|
|
721
1111
|
loginLink: "Already have an account? Sign in",
|
|
722
1112
|
passwordMismatch: "Passwords do not match",
|
|
723
1113
|
registerTitle: "Create account",
|
|
724
|
-
registerSubtitle: "Create your LuminaryWorks unified account"
|
|
1114
|
+
registerSubtitle: "Create your LuminaryWorks unified account",
|
|
1115
|
+
registerWithEmail: "Email",
|
|
1116
|
+
registerWithUsername: "Username",
|
|
1117
|
+
emailPlaceholder: "Email",
|
|
1118
|
+
verificationCodePlaceholder: "Verification code",
|
|
1119
|
+
sendCode: "Send code",
|
|
1120
|
+
resendCode: "Resend code",
|
|
1121
|
+
allowedDomainsPrefix: "Allowed email domains",
|
|
1122
|
+
allowedDomainsOpen: "Email registration is open for most providers.",
|
|
1123
|
+
allowedDomainsBlocklist: "Most email providers are accepted. Temporary / disposable addresses are blocked.",
|
|
1124
|
+
waitingExternalBrowser: "Finish signing in with your browser, then return to this app."
|
|
725
1125
|
};
|
|
726
1126
|
function resolveSieBase(config) {
|
|
727
1127
|
const explicit = config.experienceApiBase?.trim();
|
|
@@ -748,10 +1148,15 @@ function HeadlessLoginPanel({
|
|
|
748
1148
|
productName,
|
|
749
1149
|
logoSrc,
|
|
750
1150
|
labels: labelsProp,
|
|
1151
|
+
locale,
|
|
751
1152
|
returnUrl,
|
|
752
1153
|
mode = "popup",
|
|
1154
|
+
openExternalUrl,
|
|
753
1155
|
showSocialConnectors = true,
|
|
754
1156
|
showRegister = true,
|
|
1157
|
+
registerEmailPolicy: registerEmailPolicyProp,
|
|
1158
|
+
fetchRegisterPolicy = true,
|
|
1159
|
+
getCaptchaToken,
|
|
755
1160
|
socialProviders = "auto",
|
|
756
1161
|
showCancel,
|
|
757
1162
|
onCancel,
|
|
@@ -766,12 +1171,50 @@ function HeadlessLoginPanel({
|
|
|
766
1171
|
experienceAdapterProp,
|
|
767
1172
|
config.iamProvider
|
|
768
1173
|
);
|
|
769
|
-
const
|
|
1174
|
+
const [emailPolicy, setEmailPolicy] = react.useState(
|
|
1175
|
+
() => registerEmailPolicyProp ?? resolveRegisterEmailPolicy()
|
|
1176
|
+
);
|
|
1177
|
+
react.useEffect(() => {
|
|
1178
|
+
if (registerEmailPolicyProp) {
|
|
1179
|
+
setEmailPolicy(registerEmailPolicyProp);
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1182
|
+
if (!fetchRegisterPolicy || !showRegister) {
|
|
1183
|
+
setEmailPolicy(resolveRegisterEmailPolicy());
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1186
|
+
const base = config.experienceApiBase?.trim();
|
|
1187
|
+
if (!base) {
|
|
1188
|
+
setEmailPolicy(resolveRegisterEmailPolicy());
|
|
1189
|
+
return;
|
|
1190
|
+
}
|
|
1191
|
+
let cancelled = false;
|
|
1192
|
+
void fetchRegisterEmailPolicy(base).then((policy) => {
|
|
1193
|
+
if (!cancelled) setEmailPolicy(policy);
|
|
1194
|
+
});
|
|
1195
|
+
return () => {
|
|
1196
|
+
cancelled = true;
|
|
1197
|
+
};
|
|
1198
|
+
}, [
|
|
1199
|
+
config.experienceApiBase,
|
|
1200
|
+
fetchRegisterPolicy,
|
|
1201
|
+
registerEmailPolicyProp,
|
|
1202
|
+
showRegister
|
|
1203
|
+
]);
|
|
1204
|
+
react.useEffect(() => {
|
|
1205
|
+
if (experienceAdapter instanceof LogtoExperienceAdapter) {
|
|
1206
|
+
experienceAdapter.emailPolicy = emailPolicy;
|
|
1207
|
+
}
|
|
1208
|
+
}, [emailPolicy, experienceAdapter]);
|
|
1209
|
+
const labels = { ...defaults, ...loginLabels(locale), ...labelsProp };
|
|
770
1210
|
const configured = isIdpConfigured(config);
|
|
771
1211
|
const [panelMode, setPanelMode] = react.useState("sign-in");
|
|
1212
|
+
const [registerChannel, setRegisterChannel] = react.useState("email");
|
|
772
1213
|
const [identifier, setIdentifier] = react.useState("");
|
|
773
1214
|
const [password, setPassword] = react.useState("");
|
|
774
1215
|
const [confirmPassword, setConfirmPassword] = react.useState("");
|
|
1216
|
+
const [verificationCode, setVerificationCode] = react.useState("");
|
|
1217
|
+
const [emailVerificationId, setEmailVerificationId] = react.useState(null);
|
|
775
1218
|
const [passwordVisible, setPasswordVisible] = react.useState(false);
|
|
776
1219
|
const panelStyle = {
|
|
777
1220
|
...style,
|
|
@@ -779,13 +1222,20 @@ function HeadlessLoginPanel({
|
|
|
779
1222
|
};
|
|
780
1223
|
const [loading, setLoading] = react.useState(null);
|
|
781
1224
|
const [error, setError] = react.useState("");
|
|
1225
|
+
const [awaitingExternal, setAwaitingExternal] = react.useState(false);
|
|
782
1226
|
const [connectors, setConnectors] = react.useState([]);
|
|
783
1227
|
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
1228
|
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";
|
|
1229
|
+
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");
|
|
1230
|
+
const emailRegisterEnabled = experienceAdapter.capabilities.includes(LOGIN_EXPERIENCE_CAPABILITIES.emailCodeSignUp) && typeof experienceAdapter.sendRegisterEmailCode === "function" && typeof experienceAdapter.experienceEmailPasswordSignUp === "function";
|
|
1231
|
+
const usernameRegisterEnabled = experienceAdapter.capabilities.includes(LOGIN_EXPERIENCE_CAPABILITIES.passwordSignUp) && typeof experienceAdapter.experiencePasswordSignUp === "function";
|
|
786
1232
|
const isRegister = panelMode === "register" && registerEnabled;
|
|
1233
|
+
const useEmailRegister = isRegister && registerChannel === "email" && emailRegisterEnabled;
|
|
787
1234
|
react.useEffect(() => {
|
|
788
|
-
const resetBusy = () =>
|
|
1235
|
+
const resetBusy = () => {
|
|
1236
|
+
setLoading(null);
|
|
1237
|
+
setAwaitingExternal(false);
|
|
1238
|
+
};
|
|
789
1239
|
window.addEventListener("pageshow", resetBusy);
|
|
790
1240
|
window.addEventListener("popstate", resetBusy);
|
|
791
1241
|
return () => {
|
|
@@ -828,6 +1278,19 @@ function HeadlessLoginPanel({
|
|
|
828
1278
|
]);
|
|
829
1279
|
const runOidc = async (experienceRequest) => {
|
|
830
1280
|
if (!configured) throw new Error("IdP not configured");
|
|
1281
|
+
if (mode === "external") {
|
|
1282
|
+
if (!openExternalUrl) {
|
|
1283
|
+
throw new Error('openExternalUrl is required when mode is "external"');
|
|
1284
|
+
}
|
|
1285
|
+
const url = await prepareSignInRequestUrl(config, {
|
|
1286
|
+
returnUrl,
|
|
1287
|
+
...experienceRequest
|
|
1288
|
+
});
|
|
1289
|
+
setAwaitingExternal(true);
|
|
1290
|
+
await openExternalUrl(url);
|
|
1291
|
+
setLoading(null);
|
|
1292
|
+
return;
|
|
1293
|
+
}
|
|
831
1294
|
if (mode === "redirect") {
|
|
832
1295
|
await signInRedirect(config, { returnUrl, ...experienceRequest });
|
|
833
1296
|
return;
|
|
@@ -851,6 +1314,7 @@ function HeadlessLoginPanel({
|
|
|
851
1314
|
} catch (e) {
|
|
852
1315
|
setError(e instanceof Error ? e.message : String(e));
|
|
853
1316
|
setLoading(null);
|
|
1317
|
+
setAwaitingExternal(false);
|
|
854
1318
|
}
|
|
855
1319
|
};
|
|
856
1320
|
const runHostedOidc = async () => {
|
|
@@ -861,6 +1325,7 @@ function HeadlessLoginPanel({
|
|
|
861
1325
|
} catch (e) {
|
|
862
1326
|
setError(e instanceof Error ? e.message : String(e));
|
|
863
1327
|
setLoading(null);
|
|
1328
|
+
setAwaitingExternal(false);
|
|
864
1329
|
}
|
|
865
1330
|
};
|
|
866
1331
|
const followExperienceRedirect = (redirectTo) => {
|
|
@@ -880,17 +1345,46 @@ function HeadlessLoginPanel({
|
|
|
880
1345
|
if (!apiBase) {
|
|
881
1346
|
throw new Error(labels.experienceUnavailable);
|
|
882
1347
|
}
|
|
1348
|
+
const captchaToken = getCaptchaToken ? await getCaptchaToken() : void 0;
|
|
883
1349
|
if (isRegister) {
|
|
884
|
-
if (!registerEnabled || !experienceAdapter.experiencePasswordSignUp) {
|
|
885
|
-
throw new Error(labels.experienceUnavailable);
|
|
886
|
-
}
|
|
887
1350
|
if (password !== confirmPassword) {
|
|
888
1351
|
throw new Error(labels.passwordMismatch);
|
|
889
1352
|
}
|
|
1353
|
+
if (useEmailRegister) {
|
|
1354
|
+
if (!experienceAdapter.experienceEmailPasswordSignUp || !emailVerificationId) {
|
|
1355
|
+
throw new Error("Send and enter the email verification code first");
|
|
1356
|
+
}
|
|
1357
|
+
const decision = evaluateRegisterEmail(identifier.trim(), emailPolicy);
|
|
1358
|
+
if (!decision.ok) throw new Error(registerEmailRejectionMessage(decision));
|
|
1359
|
+
const result3 = await experienceAdapter.experienceEmailPasswordSignUp({
|
|
1360
|
+
apiBase,
|
|
1361
|
+
identifier: identifier.trim(),
|
|
1362
|
+
email: identifier.trim(),
|
|
1363
|
+
password,
|
|
1364
|
+
code: verificationCode,
|
|
1365
|
+
verificationId: emailVerificationId,
|
|
1366
|
+
captchaToken,
|
|
1367
|
+
issuer: config.issuer,
|
|
1368
|
+
clientId: config.clientId,
|
|
1369
|
+
redirectUri: config.redirectUri,
|
|
1370
|
+
audience: config.audience,
|
|
1371
|
+
scopes: config.scopes,
|
|
1372
|
+
returnUrl
|
|
1373
|
+
});
|
|
1374
|
+
if (result3.redirectTo) {
|
|
1375
|
+
followExperienceRedirect(result3.redirectTo);
|
|
1376
|
+
return;
|
|
1377
|
+
}
|
|
1378
|
+
throw new Error("Experience API did not return redirectTo");
|
|
1379
|
+
}
|
|
1380
|
+
if (!usernameRegisterEnabled || !experienceAdapter.experiencePasswordSignUp) {
|
|
1381
|
+
throw new Error(labels.experienceUnavailable);
|
|
1382
|
+
}
|
|
890
1383
|
const result2 = await experienceAdapter.experiencePasswordSignUp({
|
|
891
1384
|
apiBase,
|
|
892
1385
|
identifier: identifier.trim(),
|
|
893
1386
|
password,
|
|
1387
|
+
captchaToken,
|
|
894
1388
|
issuer: config.issuer,
|
|
895
1389
|
clientId: config.clientId,
|
|
896
1390
|
redirectUri: config.redirectUri,
|
|
@@ -930,20 +1424,59 @@ function HeadlessLoginPanel({
|
|
|
930
1424
|
setLoading(null);
|
|
931
1425
|
}
|
|
932
1426
|
};
|
|
1427
|
+
const runSendCode = async () => {
|
|
1428
|
+
if (!configured || !useEmailRegister) return;
|
|
1429
|
+
setError("");
|
|
1430
|
+
setLoading("send-code");
|
|
1431
|
+
try {
|
|
1432
|
+
const apiBase = config.experienceApiBase?.trim();
|
|
1433
|
+
if (!apiBase || !experienceAdapter.sendRegisterEmailCode) {
|
|
1434
|
+
throw new Error(labels.experienceUnavailable);
|
|
1435
|
+
}
|
|
1436
|
+
const email = identifier.trim();
|
|
1437
|
+
const decision = evaluateRegisterEmail(email, emailPolicy);
|
|
1438
|
+
if (!decision.ok) throw new Error(registerEmailRejectionMessage(decision));
|
|
1439
|
+
const captchaToken = getCaptchaToken ? await getCaptchaToken() : void 0;
|
|
1440
|
+
const sent = await experienceAdapter.sendRegisterEmailCode({
|
|
1441
|
+
apiBase,
|
|
1442
|
+
identifier: email,
|
|
1443
|
+
email,
|
|
1444
|
+
captchaToken,
|
|
1445
|
+
issuer: config.issuer,
|
|
1446
|
+
clientId: config.clientId,
|
|
1447
|
+
redirectUri: config.redirectUri,
|
|
1448
|
+
audience: config.audience,
|
|
1449
|
+
scopes: config.scopes,
|
|
1450
|
+
returnUrl
|
|
1451
|
+
});
|
|
1452
|
+
setEmailVerificationId(sent.verificationId);
|
|
1453
|
+
} catch (e) {
|
|
1454
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
1455
|
+
} finally {
|
|
1456
|
+
setLoading(null);
|
|
1457
|
+
}
|
|
1458
|
+
};
|
|
933
1459
|
const switchPanelMode = (next) => {
|
|
934
1460
|
setPanelMode(next);
|
|
935
1461
|
setError("");
|
|
936
1462
|
setConfirmPassword("");
|
|
1463
|
+
setVerificationCode("");
|
|
1464
|
+
setEmailVerificationId(null);
|
|
937
1465
|
};
|
|
938
1466
|
const busy = loading !== null;
|
|
939
1467
|
const showSocial = socialEnabled && connectors.length > 0;
|
|
940
1468
|
const showHint = Boolean(labelsProp?.hint) || showSocial || isRegister;
|
|
941
|
-
const formReady = Boolean(identifier.trim()) && Boolean(password) && (!isRegister || Boolean(confirmPassword));
|
|
1469
|
+
const formReady = Boolean(identifier.trim()) && Boolean(password) && (!isRegister || Boolean(confirmPassword)) && (!useEmailRegister || Boolean(verificationCode.trim()) && Boolean(emailVerificationId));
|
|
942
1470
|
const titleText = isRegister ? labels.registerTitle : labels.title;
|
|
943
1471
|
const subtitleText = isRegister ? labels.registerSubtitle : labels.subtitle;
|
|
944
|
-
const identifierPlaceholder = isRegister ? labels.registerIdentifierPlaceholder : labels.identifierPlaceholder;
|
|
1472
|
+
const identifierPlaceholder = isRegister ? useEmailRegister ? labels.emailPlaceholder : labels.registerIdentifierPlaceholder : labels.identifierPlaceholder;
|
|
945
1473
|
const submitLabel = isRegister ? labels.submitRegister : labels.submitPassword;
|
|
946
1474
|
const hintText = isRegister ? labels.registerHint : labels.hint;
|
|
1475
|
+
const domainsHint = useEmailRegister ? registerEmailDomainsHint(emailPolicy, {
|
|
1476
|
+
allowedDomainsPrefix: labels.allowedDomainsPrefix,
|
|
1477
|
+
allowedDomainsOpen: labels.allowedDomainsOpen,
|
|
1478
|
+
allowedDomainsBlocklist: labels.allowedDomainsBlocklist
|
|
1479
|
+
}) : null;
|
|
947
1480
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cx(HeadlessLoginPanel_module_default.panel, className), style: panelStyle, children: [
|
|
948
1481
|
/* @__PURE__ */ jsxRuntime.jsxs("header", { className: HeadlessLoginPanel_module_default.brand, children: [
|
|
949
1482
|
logoSrc ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: logoSrc, alt: "", width: 48, height: 48, className: HeadlessLoginPanel_module_default.logo }) : null,
|
|
@@ -955,19 +1488,93 @@ function HeadlessLoginPanel({
|
|
|
955
1488
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: HeadlessLoginPanel_module_default.subtitle, children: subtitleText }),
|
|
956
1489
|
configured ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
957
1490
|
passwordEnabled ? /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: (e) => void runPassword(e), className: HeadlessLoginPanel_module_default.form, children: [
|
|
1491
|
+
isRegister && emailRegisterEnabled && usernameRegisterEnabled ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: HeadlessLoginPanel_module_default.channelTabs, role: "tablist", "aria-label": "Register method", children: [
|
|
1492
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1493
|
+
"button",
|
|
1494
|
+
{
|
|
1495
|
+
type: "button",
|
|
1496
|
+
role: "tab",
|
|
1497
|
+
className: cx(
|
|
1498
|
+
HeadlessLoginPanel_module_default.channelTab,
|
|
1499
|
+
registerChannel === "email" && HeadlessLoginPanel_module_default.channelTabActive
|
|
1500
|
+
),
|
|
1501
|
+
"aria-selected": registerChannel === "email",
|
|
1502
|
+
disabled: busy,
|
|
1503
|
+
onClick: () => {
|
|
1504
|
+
setRegisterChannel("email");
|
|
1505
|
+
setEmailVerificationId(null);
|
|
1506
|
+
setVerificationCode("");
|
|
1507
|
+
setError("");
|
|
1508
|
+
},
|
|
1509
|
+
children: labels.registerWithEmail
|
|
1510
|
+
}
|
|
1511
|
+
),
|
|
1512
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1513
|
+
"button",
|
|
1514
|
+
{
|
|
1515
|
+
type: "button",
|
|
1516
|
+
role: "tab",
|
|
1517
|
+
className: cx(
|
|
1518
|
+
HeadlessLoginPanel_module_default.channelTab,
|
|
1519
|
+
registerChannel === "username" && HeadlessLoginPanel_module_default.channelTabActive
|
|
1520
|
+
),
|
|
1521
|
+
"aria-selected": registerChannel === "username",
|
|
1522
|
+
disabled: busy,
|
|
1523
|
+
onClick: () => {
|
|
1524
|
+
setRegisterChannel("username");
|
|
1525
|
+
setEmailVerificationId(null);
|
|
1526
|
+
setVerificationCode("");
|
|
1527
|
+
setError("");
|
|
1528
|
+
},
|
|
1529
|
+
children: labels.registerWithUsername
|
|
1530
|
+
}
|
|
1531
|
+
)
|
|
1532
|
+
] }) : null,
|
|
958
1533
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
959
1534
|
"input",
|
|
960
1535
|
{
|
|
961
|
-
type: "text",
|
|
1536
|
+
type: useEmailRegister ? "email" : "text",
|
|
962
1537
|
name: "identifier",
|
|
963
1538
|
autoComplete: isRegister ? "username" : "username",
|
|
964
1539
|
placeholder: identifierPlaceholder,
|
|
965
1540
|
value: identifier,
|
|
966
|
-
onChange: (e) =>
|
|
1541
|
+
onChange: (e) => {
|
|
1542
|
+
setIdentifier(e.target.value);
|
|
1543
|
+
if (useEmailRegister) {
|
|
1544
|
+
setEmailVerificationId(null);
|
|
1545
|
+
}
|
|
1546
|
+
},
|
|
967
1547
|
className: HeadlessLoginPanel_module_default.input,
|
|
968
1548
|
disabled: busy
|
|
969
1549
|
}
|
|
970
1550
|
),
|
|
1551
|
+
domainsHint ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: HeadlessLoginPanel_module_default.domainsHint, "data-testid": "register-allowed-domains", children: domainsHint }) : null,
|
|
1552
|
+
useEmailRegister ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: HeadlessLoginPanel_module_default.codeRow, children: [
|
|
1553
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1554
|
+
"input",
|
|
1555
|
+
{
|
|
1556
|
+
type: "text",
|
|
1557
|
+
name: "verificationCode",
|
|
1558
|
+
inputMode: "numeric",
|
|
1559
|
+
autoComplete: "one-time-code",
|
|
1560
|
+
placeholder: labels.verificationCodePlaceholder,
|
|
1561
|
+
value: verificationCode,
|
|
1562
|
+
onChange: (e) => setVerificationCode(e.target.value),
|
|
1563
|
+
className: cx(HeadlessLoginPanel_module_default.input, HeadlessLoginPanel_module_default.codeInput),
|
|
1564
|
+
disabled: busy
|
|
1565
|
+
}
|
|
1566
|
+
),
|
|
1567
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1568
|
+
"button",
|
|
1569
|
+
{
|
|
1570
|
+
type: "button",
|
|
1571
|
+
className: HeadlessLoginPanel_module_default.secondaryBtn,
|
|
1572
|
+
disabled: busy || !identifier.trim(),
|
|
1573
|
+
onClick: () => void runSendCode(),
|
|
1574
|
+
children: loading === "send-code" ? "\u2026" : emailVerificationId ? labels.resendCode : labels.sendCode
|
|
1575
|
+
}
|
|
1576
|
+
)
|
|
1577
|
+
] }) : null,
|
|
971
1578
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: HeadlessLoginPanel_module_default.passwordField, children: [
|
|
972
1579
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
973
1580
|
"input",
|
|
@@ -1030,6 +1637,7 @@ function HeadlessLoginPanel({
|
|
|
1030
1637
|
children: isRegister ? labels.loginLink : labels.registerLink
|
|
1031
1638
|
}
|
|
1032
1639
|
) }) : null,
|
|
1640
|
+
awaitingExternal ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: HeadlessLoginPanel_module_default.hint, role: "status", children: labels.waitingExternalBrowser }) : null,
|
|
1033
1641
|
showSocial ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: HeadlessLoginPanel_module_default.divider, "aria-hidden": true, children: [
|
|
1034
1642
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: HeadlessLoginPanel_module_default.dividerLine }),
|
|
1035
1643
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: HeadlessLoginPanel_module_default.dividerText, children: labels.socialDivider }),
|
|
@@ -1203,6 +1811,124 @@ function useAuthGateOptional() {
|
|
|
1203
1811
|
return react.useContext(AuthGateContext);
|
|
1204
1812
|
}
|
|
1205
1813
|
|
|
1814
|
+
// src/LoginLanguageSwitcher.module.scss
|
|
1815
|
+
var LoginLanguageSwitcher_module_default = {
|
|
1816
|
+
switch: "LoginLanguageSwitcher_module_switch",
|
|
1817
|
+
trigger: "LoginLanguageSwitcher_module_trigger",
|
|
1818
|
+
icon: "LoginLanguageSwitcher_module_icon",
|
|
1819
|
+
caret: "LoginLanguageSwitcher_module_caret",
|
|
1820
|
+
menu: "LoginLanguageSwitcher_module_menu",
|
|
1821
|
+
item: "LoginLanguageSwitcher_module_item",
|
|
1822
|
+
itemActive: "LoginLanguageSwitcher_module_itemActive",
|
|
1823
|
+
onDark: "LoginLanguageSwitcher_module_onDark"
|
|
1824
|
+
};
|
|
1825
|
+
function GlobeIcon() {
|
|
1826
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { className: LoginLanguageSwitcher_module_default.icon, viewBox: "0 0 24 24", "aria-hidden": "true", children: [
|
|
1827
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "9", fill: "none", stroke: "currentColor", strokeWidth: "1.6" }),
|
|
1828
|
+
/* @__PURE__ */ jsxRuntime.jsx("ellipse", { cx: "12", cy: "12", rx: "4", ry: "9", fill: "none", stroke: "currentColor", strokeWidth: "1.6" }),
|
|
1829
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 12h18M5 8h14M5 16h14", fill: "none", stroke: "currentColor", strokeWidth: "1.6" })
|
|
1830
|
+
] });
|
|
1831
|
+
}
|
|
1832
|
+
function LoginLanguageSwitcher({
|
|
1833
|
+
locale,
|
|
1834
|
+
onChange,
|
|
1835
|
+
locales = DEFAULT_LOGIN_LOCALES,
|
|
1836
|
+
label = "Language",
|
|
1837
|
+
variant = "default"
|
|
1838
|
+
}) {
|
|
1839
|
+
const [open, setOpen] = react.useState(false);
|
|
1840
|
+
const rootRef = react.useRef(null);
|
|
1841
|
+
const menuId = react.useId();
|
|
1842
|
+
const current = locales.find((item) => item.code === locale) ?? locales.find((item) => normalizeLoginLocale(item.code) === normalizeLoginLocale(locale)) ?? locales[0];
|
|
1843
|
+
react.useEffect(() => {
|
|
1844
|
+
if (!open) return;
|
|
1845
|
+
const onPointer = (event) => {
|
|
1846
|
+
if (!rootRef.current?.contains(event.target)) setOpen(false);
|
|
1847
|
+
};
|
|
1848
|
+
const onKey = (event) => {
|
|
1849
|
+
if (event.key === "Escape") setOpen(false);
|
|
1850
|
+
};
|
|
1851
|
+
document.addEventListener("mousedown", onPointer);
|
|
1852
|
+
document.addEventListener("keydown", onKey);
|
|
1853
|
+
return () => {
|
|
1854
|
+
document.removeEventListener("mousedown", onPointer);
|
|
1855
|
+
document.removeEventListener("keydown", onKey);
|
|
1856
|
+
};
|
|
1857
|
+
}, [open]);
|
|
1858
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: LoginLanguageSwitcher_module_default.switch, ref: rootRef, children: [
|
|
1859
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1860
|
+
"button",
|
|
1861
|
+
{
|
|
1862
|
+
type: "button",
|
|
1863
|
+
className: variant === "onDark" ? `${LoginLanguageSwitcher_module_default.trigger} ${LoginLanguageSwitcher_module_default.onDark}` : LoginLanguageSwitcher_module_default.trigger,
|
|
1864
|
+
"aria-label": label,
|
|
1865
|
+
"aria-haspopup": "listbox",
|
|
1866
|
+
"aria-expanded": open,
|
|
1867
|
+
"aria-controls": menuId,
|
|
1868
|
+
onClick: () => setOpen((value) => !value),
|
|
1869
|
+
children: [
|
|
1870
|
+
/* @__PURE__ */ jsxRuntime.jsx(GlobeIcon, {}),
|
|
1871
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: current?.short ?? "EN" }),
|
|
1872
|
+
/* @__PURE__ */ jsxRuntime.jsx("svg", { className: LoginLanguageSwitcher_module_default.caret, viewBox: "0 0 12 12", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2.5 4.5 6 8l3.5-3.5", fill: "none", stroke: "currentColor", strokeWidth: "1.4" }) })
|
|
1873
|
+
]
|
|
1874
|
+
}
|
|
1875
|
+
),
|
|
1876
|
+
open ? /* @__PURE__ */ jsxRuntime.jsx("ul", { className: LoginLanguageSwitcher_module_default.menu, id: menuId, role: "listbox", "aria-label": label, children: locales.map((item) => {
|
|
1877
|
+
const active = item.code === current?.code;
|
|
1878
|
+
return /* @__PURE__ */ jsxRuntime.jsx("li", { role: "presentation", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1879
|
+
"button",
|
|
1880
|
+
{
|
|
1881
|
+
type: "button",
|
|
1882
|
+
role: "option",
|
|
1883
|
+
"aria-selected": active,
|
|
1884
|
+
className: active ? `${LoginLanguageSwitcher_module_default.item} ${LoginLanguageSwitcher_module_default.itemActive}` : LoginLanguageSwitcher_module_default.item,
|
|
1885
|
+
onClick: () => {
|
|
1886
|
+
onChange(item.code);
|
|
1887
|
+
setOpen(false);
|
|
1888
|
+
},
|
|
1889
|
+
children: item.label
|
|
1890
|
+
}
|
|
1891
|
+
) }, item.code);
|
|
1892
|
+
}) }) : null
|
|
1893
|
+
] });
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
// src/LoginCanvas.module.scss
|
|
1897
|
+
var LoginCanvas_module_default = {
|
|
1898
|
+
canvas: "LoginCanvas_module_canvas",
|
|
1899
|
+
grid: "LoginCanvas_module_grid",
|
|
1900
|
+
lang: "LoginCanvas_module_lang",
|
|
1901
|
+
decor: "LoginCanvas_module_decor",
|
|
1902
|
+
slot: "LoginCanvas_module_slot",
|
|
1903
|
+
slotFill: "LoginCanvas_module_slotFill"
|
|
1904
|
+
};
|
|
1905
|
+
function LoginCanvas({
|
|
1906
|
+
tone,
|
|
1907
|
+
locale,
|
|
1908
|
+
onLocaleChange,
|
|
1909
|
+
locales,
|
|
1910
|
+
languageLabel,
|
|
1911
|
+
fill = false,
|
|
1912
|
+
decor,
|
|
1913
|
+
children
|
|
1914
|
+
}) {
|
|
1915
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: LoginCanvas_module_default.canvas, "data-tone": tone, children: [
|
|
1916
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: LoginCanvas_module_default.grid, "aria-hidden": "true" }),
|
|
1917
|
+
decor ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: LoginCanvas_module_default.decor, children: decor }) : null,
|
|
1918
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: LoginCanvas_module_default.lang, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1919
|
+
LoginLanguageSwitcher,
|
|
1920
|
+
{
|
|
1921
|
+
locale,
|
|
1922
|
+
onChange: onLocaleChange,
|
|
1923
|
+
locales,
|
|
1924
|
+
label: languageLabel,
|
|
1925
|
+
variant: tone === "console" || tone === "data" ? "onDark" : "default"
|
|
1926
|
+
}
|
|
1927
|
+
) }),
|
|
1928
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: fill ? `${LoginCanvas_module_default.slot} ${LoginCanvas_module_default.slotFill}` : LoginCanvas_module_default.slot, children })
|
|
1929
|
+
] });
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1206
1932
|
// src/post-login-path.ts
|
|
1207
1933
|
var DEFAULT_UNSAFE = ["/login", "/auth", "/exception"];
|
|
1208
1934
|
function normalizeAppPath(path, fallback) {
|
|
@@ -1254,10 +1980,15 @@ function createPostLoginPathHelpers(options) {
|
|
|
1254
1980
|
}
|
|
1255
1981
|
|
|
1256
1982
|
exports.AuthGateProvider = AuthGateProvider;
|
|
1983
|
+
exports.DEFAULT_DISPOSABLE_EMAIL_BLOCKLIST = DEFAULT_DISPOSABLE_EMAIL_BLOCKLIST;
|
|
1984
|
+
exports.DEFAULT_LOGIN_LOCALES = DEFAULT_LOGIN_LOCALES;
|
|
1257
1985
|
exports.DEFAULT_LOGIN_THEME_COLOR = DEFAULT_LOGIN_THEME_COLOR;
|
|
1986
|
+
exports.DEFAULT_REGISTER_EMAIL_ALLOWLIST = DEFAULT_REGISTER_EMAIL_ALLOWLIST;
|
|
1258
1987
|
exports.HeadlessLoginPanel = HeadlessLoginPanel;
|
|
1259
1988
|
exports.HostedOidcExperienceAdapter = HostedOidcExperienceAdapter;
|
|
1260
1989
|
exports.LOGIN_EXPERIENCE_CAPABILITIES = LOGIN_EXPERIENCE_CAPABILITIES;
|
|
1990
|
+
exports.LoginCanvas = LoginCanvas;
|
|
1991
|
+
exports.LoginLanguageSwitcher = LoginLanguageSwitcher;
|
|
1261
1992
|
exports.LogtoExperienceAdapter = LogtoExperienceAdapter;
|
|
1262
1993
|
exports.LuminaryAuthProvider = LuminaryAuthProvider;
|
|
1263
1994
|
exports.ReauthOverlay = ReauthOverlay;
|
|
@@ -1267,8 +1998,12 @@ exports.createLoginExperienceAdapter = createLoginExperienceAdapter;
|
|
|
1267
1998
|
exports.createLogtoDirectSignInRequest = createLogtoDirectSignInRequest;
|
|
1268
1999
|
exports.createPostLoginPathHelpers = createPostLoginPathHelpers;
|
|
1269
2000
|
exports.createUserManager = createUserManager;
|
|
2001
|
+
exports.emailDomain = emailDomain;
|
|
2002
|
+
exports.evaluateRegisterEmail = evaluateRegisterEmail;
|
|
1270
2003
|
exports.experiencePasswordSignIn = experiencePasswordSignIn;
|
|
2004
|
+
exports.fetchRegisterEmailPolicy = fetchRegisterEmailPolicy;
|
|
1271
2005
|
exports.fetchSocialConnectors = fetchSocialConnectors;
|
|
2006
|
+
exports.formatAllowedEmailDomains = formatAllowedEmailDomains;
|
|
1272
2007
|
exports.getCurrentUser = getCurrentUser;
|
|
1273
2008
|
exports.getStoredAccessToken = getStoredAccessToken;
|
|
1274
2009
|
exports.handleSignInCallback = handleSignInCallback;
|
|
@@ -1276,13 +2011,21 @@ exports.handleSignInPopupCallback = handleSignInPopupCallback;
|
|
|
1276
2011
|
exports.isIdpConfigured = isIdpConfigured;
|
|
1277
2012
|
exports.isLogtoRegisterUsername = isLogtoRegisterUsername;
|
|
1278
2013
|
exports.isOidcPopupWindow = isOidcPopupWindow;
|
|
2014
|
+
exports.isValidEmailShape = isValidEmailShape;
|
|
2015
|
+
exports.loginLabels = loginLabels;
|
|
2016
|
+
exports.normalizeLoginLocale = normalizeLoginLocale;
|
|
2017
|
+
exports.normalizeRegisterEmailMode = normalizeRegisterEmailMode;
|
|
1279
2018
|
exports.prepareSignInRequestUrl = prepareSignInRequestUrl;
|
|
1280
2019
|
exports.readIdpConfigFromEnv = readIdpConfigFromEnv;
|
|
2020
|
+
exports.readRegisterEmailPolicyFromEnv = readRegisterEmailPolicyFromEnv;
|
|
2021
|
+
exports.registerEmailDomainsHint = registerEmailDomainsHint;
|
|
2022
|
+
exports.registerEmailRejectionMessage = registerEmailRejectionMessage;
|
|
1281
2023
|
exports.resetUserManager = resetUserManager;
|
|
1282
2024
|
exports.resolveExperienceApiBase = resolveExperienceApiBase;
|
|
1283
2025
|
exports.resolveIssuer = resolveIssuer;
|
|
1284
2026
|
exports.resolveLoginExperienceAdapter = resolveLoginExperienceAdapter;
|
|
1285
2027
|
exports.resolveLoginExperienceProviderFromEnv = resolveLoginExperienceProviderFromEnv;
|
|
2028
|
+
exports.resolveRegisterEmailPolicy = resolveRegisterEmailPolicy;
|
|
1286
2029
|
exports.sameOriginAuthorizeUrl = sameOriginAuthorizeUrl;
|
|
1287
2030
|
exports.signInPopup = signInPopup;
|
|
1288
2031
|
exports.signInRedirect = signInRedirect;
|