@cedros/login-react 0.0.46 → 0.0.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +32 -2
  2. package/dist/EmailRegisterForm-ByYQ43yL.cjs +1 -0
  3. package/dist/EmailRegisterForm-ByYQ43yL.cjs.map +1 -0
  4. package/dist/EmailRegisterForm-DMUcNQT-.js +781 -0
  5. package/dist/EmailRegisterForm-DMUcNQT-.js.map +1 -0
  6. package/dist/GoogleLoginButton-DEwtBp56.cjs +1 -0
  7. package/dist/GoogleLoginButton-DEwtBp56.cjs.map +1 -0
  8. package/dist/{GoogleLoginButton-C1WNu7W3.js → GoogleLoginButton-DwyxvhnL.js} +82 -80
  9. package/dist/GoogleLoginButton-DwyxvhnL.js.map +1 -0
  10. package/dist/{PermissionsSection-mm9hfp-u.js → PermissionsSection-0oNHPZzL.js} +383 -415
  11. package/dist/PermissionsSection-0oNHPZzL.js.map +1 -0
  12. package/dist/PermissionsSection-CZsJuxo4.cjs +1 -0
  13. package/dist/PermissionsSection-CZsJuxo4.cjs.map +1 -0
  14. package/dist/SolanaLoginButton-2504p6cr.cjs +1 -0
  15. package/dist/SolanaLoginButton-2504p6cr.cjs.map +1 -0
  16. package/dist/SolanaLoginButton-C7Kc_m6n.js +234 -0
  17. package/dist/SolanaLoginButton-C7Kc_m6n.js.map +1 -0
  18. package/dist/{TeamSection-Km7EwLWD.cjs → TeamSection-DN8PEHH3.cjs} +1 -1
  19. package/dist/{TeamSection-Km7EwLWD.cjs.map → TeamSection-DN8PEHH3.cjs.map} +1 -1
  20. package/dist/{TeamSection-C_eODdLU.js → TeamSection-gUyP4YDM.js} +1 -1
  21. package/dist/{TeamSection-C_eODdLU.js.map → TeamSection-gUyP4YDM.js.map} +1 -1
  22. package/dist/{UsersSection-C1Tt0ePx.cjs → UsersSection-8wLuD0fr.cjs} +1 -1
  23. package/dist/{UsersSection-C1Tt0ePx.cjs.map → UsersSection-8wLuD0fr.cjs.map} +1 -1
  24. package/dist/{UsersSection-Ct_E-MBF.js → UsersSection-CnsFrG-6.js} +1 -1
  25. package/dist/{UsersSection-Ct_E-MBF.js.map → UsersSection-CnsFrG-6.js.map} +1 -1
  26. package/dist/admin-only.cjs +1 -1
  27. package/dist/admin-only.js +1 -1
  28. package/dist/email-only.cjs +1 -1
  29. package/dist/email-only.d.ts +10 -2
  30. package/dist/email-only.js +2 -2
  31. package/dist/google-only.cjs +1 -1
  32. package/dist/google-only.d.ts +7 -2
  33. package/dist/google-only.js +2 -2
  34. package/dist/index.cjs +12 -12
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.ts +182 -11
  37. package/dist/index.js +5221 -4657
  38. package/dist/index.js.map +1 -1
  39. package/dist/login-react.css +1 -1
  40. package/dist/{plugin-Ci67QMGG.cjs → plugin-DNFjEfYp.cjs} +1 -1
  41. package/dist/{plugin-Ci67QMGG.cjs.map → plugin-DNFjEfYp.cjs.map} +1 -1
  42. package/dist/{plugin-Cm8Q6O4-.js → plugin-WYMrRNbz.js} +1 -1
  43. package/dist/{plugin-Cm8Q6O4-.js.map → plugin-WYMrRNbz.js.map} +1 -1
  44. package/dist/solana-only.cjs +1 -1
  45. package/dist/solana-only.d.ts +6 -1
  46. package/dist/solana-only.js +2 -2
  47. package/dist/useAuth-2vgrAH-M.cjs +1 -0
  48. package/dist/useAuth-2vgrAH-M.cjs.map +1 -0
  49. package/dist/{useAuth-l-itM5am.js → useAuth-CNflw856.js} +465 -469
  50. package/dist/useAuth-CNflw856.js.map +1 -0
  51. package/dist/useServerFeatures-9_aNPaa6.cjs +1 -0
  52. package/dist/useServerFeatures-9_aNPaa6.cjs.map +1 -0
  53. package/dist/useServerFeatures-DSkYdan-.js +82 -0
  54. package/dist/useServerFeatures-DSkYdan-.js.map +1 -0
  55. package/dist/{useUsersStatsSummary-BGeh3RnI.js → useUsersStatsSummary-B4_RBEYy.js} +504 -442
  56. package/dist/useUsersStatsSummary-B4_RBEYy.js.map +1 -0
  57. package/dist/useUsersStatsSummary-CHRMrlk4.cjs +1 -0
  58. package/dist/useUsersStatsSummary-CHRMrlk4.cjs.map +1 -0
  59. package/package.json +1 -1
  60. package/dist/EmailRegisterForm-p2X5QP58.js +0 -750
  61. package/dist/EmailRegisterForm-p2X5QP58.js.map +0 -1
  62. package/dist/EmailRegisterForm-xFb6MaVA.cjs +0 -1
  63. package/dist/EmailRegisterForm-xFb6MaVA.cjs.map +0 -1
  64. package/dist/GoogleLoginButton-2zNTIKMm.cjs +0 -1
  65. package/dist/GoogleLoginButton-2zNTIKMm.cjs.map +0 -1
  66. package/dist/GoogleLoginButton-C1WNu7W3.js.map +0 -1
  67. package/dist/PermissionsSection-4zcE9Zs9.cjs +0 -1
  68. package/dist/PermissionsSection-4zcE9Zs9.cjs.map +0 -1
  69. package/dist/PermissionsSection-mm9hfp-u.js.map +0 -1
  70. package/dist/SolanaLoginButton-CqdzSSeJ.cjs +0 -1
  71. package/dist/SolanaLoginButton-CqdzSSeJ.cjs.map +0 -1
  72. package/dist/SolanaLoginButton-CyeX35eU.js +0 -232
  73. package/dist/SolanaLoginButton-CyeX35eU.js.map +0 -1
  74. package/dist/sanitization-Bo_tn-L2.cjs +0 -1
  75. package/dist/sanitization-Bo_tn-L2.cjs.map +0 -1
  76. package/dist/sanitization-CQ-H1MSg.js +0 -39
  77. package/dist/sanitization-CQ-H1MSg.js.map +0 -1
  78. package/dist/useAuth-B1yS_YiD.cjs +0 -1
  79. package/dist/useAuth-B1yS_YiD.cjs.map +0 -1
  80. package/dist/useAuth-l-itM5am.js.map +0 -1
  81. package/dist/useUsersStatsSummary-BGeh3RnI.js.map +0 -1
  82. package/dist/useUsersStatsSummary-DnsYtFGX.cjs +0 -1
  83. package/dist/useUsersStatsSummary-DnsYtFGX.cjs.map +0 -1
package/README.md CHANGED
@@ -204,7 +204,8 @@ function AuthStatus() {
204
204
 
205
205
  | Component | Description |
206
206
  |-----------|-------------|
207
- | `CedrosAdminDashboard` | Complete standalone admin panel with sidebar navigation and all sections |
207
+ | `CedrosAdminDashboard` | Complete standalone admin panel with sidebar navigation and all sections. **Auto-detects first run and shows SetupWizard.** |
208
+ | `SetupWizard` | First-run setup wizard — creates the first admin account (email, password, name, org). Shown automatically by `CedrosAdminDashboard` when no admin exists. |
208
209
  | `AdminShell` | Plugin host for combining multiple admin dashboards (cedros-login + cedros-pay) |
209
210
  | `AdminPanel` | Legacy admin dashboard with tabs for members, invites, sessions, system settings |
210
211
  | `SystemSettings` | System settings editor (privacy, withdrawal, rate limits) - system admin only |
@@ -1367,7 +1368,9 @@ function AdminStatsWidget() {
1367
1368
 
1368
1369
  ### Admin Dashboard
1369
1370
 
1370
- The `CedrosAdminDashboard` is a complete, ready-to-use admin panel with sidebar navigation:
1371
+ The `CedrosAdminDashboard` is a complete, ready-to-use admin panel with sidebar navigation.
1372
+
1373
+ **First-run setup is automatic** — when no admin user exists, the dashboard shows a WordPress-style setup wizard instead of the normal admin interface. The wizard prompts for email, password, name, and organization name to create the first admin account. Once created, it reloads into the full dashboard. No env vars or SQL needed.
1371
1374
 
1372
1375
  ```tsx
1373
1376
  import { CedrosAdminDashboard } from '@cedros/login-react';
@@ -1385,16 +1388,43 @@ function AdminPage() {
1385
1388
  }
1386
1389
  ```
1387
1390
 
1391
+ If you need custom setup flow control, use the `SetupWizard` component directly:
1392
+
1393
+ ```tsx
1394
+ import { SetupWizard, useSetup, CedrosAdminDashboard } from '@cedros/login-react';
1395
+
1396
+ function AdminApp() {
1397
+ const { status, isLoading, checkStatus } = useSetup();
1398
+
1399
+ useEffect(() => { checkStatus(); }, [checkStatus]);
1400
+
1401
+ if (isLoading) return <div>Loading...</div>;
1402
+ if (status?.needsSetup) {
1403
+ return <SetupWizard onComplete={() => window.location.reload()} />;
1404
+ }
1405
+ return <CedrosAdminDashboard />;
1406
+ }
1407
+ ```
1408
+
1388
1409
  **Available sections:**
1389
1410
  - `users` - User management with stats (system admin only)
1390
1411
  - `team` - Organization members and pending invites
1412
+ - `referrals` - Referral analytics and payout management
1391
1413
  - `deposits` - Browse all deposits with status filtering
1392
1414
  - `withdrawals` - Process company withdrawal queue
1415
+ - `compliance` - Token gate rule management
1416
+ - `accreditation-queue` - Review accredited investor submissions
1417
+ - `sanctions` - Sanctions screening stats and refresh
1418
+ - `signup-gating` - Access code management and signup stats
1393
1419
  - `settings-auth` - Authentication settings (OAuth, passkeys, etc.)
1394
1420
  - `settings-messaging` - Email/SMTP and webhook configuration
1395
1421
  - `settings-wallet` - User wallet settings
1396
1422
  - `settings-credits` - Credit system and treasury configuration
1423
+ - `settings-compliance` - KYC, accreditation, sanctions, token gating settings
1424
+ - `settings-referrals` - Referral reward configuration
1425
+ - `settings-signup` - Signup volume limits and access code settings
1397
1426
  - `settings-server` - Auth server settings
1427
+ - `settings-images` - Image storage (S3) settings
1398
1428
 
1399
1429
  ### Unified Admin Dashboard (Plugin System)
1400
1430
 
@@ -0,0 +1 @@
1
+ "use strict";const e=require("react/jsx-runtime"),r=require("react"),P=require("./LoadingSpinner-d6sSxgQN.cjs"),z=require("./ErrorMessage-CHbYbVi2.cjs"),N=require("./useCedrosLogin-DtJorrE7.cjs"),M=require("./validation-BuGQrA-K.cjs"),B=require("./useServerFeatures-9_aNPaa6.cjs");function O(l={}){const{maxAttempts:p=5,windowMs:w=6e4,showCountdown:f=!1}=l,u=r.useRef([]),[m,n]=r.useState(!1),[,b]=r.useState(0),h=r.useCallback(()=>{b(o=>o+1)},[]),i=r.useCallback(()=>{const o=Date.now();u.current=u.current.filter(s=>o-s<w)},[w]),v=r.useCallback(()=>{i(),n(o=>u.current.length===0&&o?!1:o)},[i]),a=r.useCallback(()=>(i(),Math.max(0,p-u.current.length)),[i,p]),g=r.useCallback(()=>{if(i(),u.current.length===0)return 0;const s=u.current[0]+w;return Math.max(0,s-Date.now())},[i,w]),k=r.useCallback(()=>(i(),u.current.length<p),[i,p]),y=r.useCallback(()=>{if(v(),u.current.length>=p){const o=g(),s=Math.ceil(o/1e3);throw new Error(`Too many attempts. Please wait ${s} second${s===1?"":"s"} before trying again.`)}u.current.push(Date.now()),n(o=>o||!0),h()},[v,p,g,h]),x=r.useCallback(()=>{u.current=[],n(o=>o&&!1),h()},[h]);return r.useEffect(()=>{if(!m||!f)return;const o=window.setInterval(()=>{v(),h()},1e3);return()=>{window.clearInterval(o)}},[m,f,h,v]),{checkLimit:y,isAllowed:k,getRemainingAttempts:a,getTimeUntilReset:g,reset:x}}function ie(l){return"mfaRequired"in l&&l.mfaRequired===!0}function U(){const{config:l,_internal:p}=N.useCedrosLogin(),[w,f]=r.useState(!1),[u,m]=r.useState(null),{checkLimit:n,getRemainingAttempts:b,getTimeUntilReset:h,reset:i}=O({maxAttempts:5,windowMs:6e4}),v=r.useMemo(()=>new N.ApiClient({baseUrl:l.serverUrl,timeoutMs:l.requestTimeout,retryAttempts:l.retryAttempts}),[l.serverUrl,l.requestTimeout,l.retryAttempts]),a=l.callbacks,g=r.useCallback(async(x,o)=>{if(!M.validateEmail(x)){const s={code:"VALIDATION_ERROR",message:"Please enter a valid email address"};throw m(s),s}try{n()}catch(s){const t={code:"RATE_LIMITED",message:s instanceof Error?s.message:"Too many attempts"};throw m(t),t}f(!0),m(null);try{const s=await v.post("/login",{email:x,password:o});if(ie(s))return{mfaRequired:!0,mfaToken:s.mfaToken,email:x,userId:s.userId};const t=s;return a?.onLoginSuccess?.(t.user,"email"),p?.handleLoginSuccess(t.user,t.tokens),i(),{mfaRequired:!1,response:t}}catch(s){const t=N.handleApiError(s,"Unable to sign in. Please try again.");throw m(t),t}finally{f(!1)}},[v,a,p,n,i]),k=r.useCallback(async(x,o,s,t,c)=>{if(!M.validateEmail(x)){const d={code:"VALIDATION_ERROR",message:"Please enter a valid email address"};throw m(d),d}try{n()}catch(d){const C={code:"RATE_LIMITED",message:d instanceof Error?d.message:"Too many attempts"};throw m(C),C}f(!0),m(null);try{const d=t??p?.getReferralCode?.()??void 0,C=await v.post("/register",{email:x,password:o,name:s,referral:d,...c?{access_code:c}:{}});return a?.onLoginSuccess?.(C.user,"email"),p?.handleLoginSuccess(C.user,C.tokens),i(),C}catch(d){const C=N.handleApiError(d,"Unable to create your account. Please try again.");throw m(C),C}finally{f(!1)}},[v,a,p,n,i]),y=r.useCallback(()=>m(null),[]);return{login:g,register:k,isLoading:w,error:u,clearError:y,remainingAttempts:b(),timeUntilReset:h()}}function R({label:l="Password",labelAction:p,showStrengthMeter:w=!1,onValidationChange:f,error:u,className:m="",onChange:n,value:b,...h}){const[i,v]=r.useState(!1),[a,g]=r.useState(null),k=r.useId(),y=o=>{const s=o.target.value;if(w||f){const t=M.validatePassword(s);g(t),f?.(t)}n?.(o)},x={weak:"var(--cedros-destructive, #ef4444)",fair:"var(--cedros-warning, #f59e0b)",good:"var(--cedros-success, #22c55e)",strong:"var(--cedros-success, #22c55e)"};return e.jsxs("div",{className:`cedros-password-input ${m}`,children:[e.jsxs("div",{className:"cedros-label-row",children:[e.jsx("label",{htmlFor:k,className:"cedros-label",children:l}),p]}),e.jsxs("div",{className:"cedros-password-wrapper",children:[e.jsx("input",{id:k,type:i?"text":"password",className:"cedros-input",onChange:y,value:b,"aria-invalid":u?"true":void 0,"aria-describedby":u?`${k}-error`:void 0,...h}),e.jsx("button",{type:"button",className:"cedros-password-toggle",onClick:()=>v(!i),"aria-label":i?"Hide password":"Show password","aria-pressed":i,children:i?e.jsxs("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none","aria-hidden":"true",children:[e.jsx("path",{d:"M2.5 10s3-6 7.5-6 7.5 6 7.5 6-3 6-7.5 6-7.5-6-7.5-6z",stroke:"currentColor",strokeWidth:"1.5"}),e.jsx("circle",{cx:"10",cy:"10",r:"2.5",stroke:"currentColor",strokeWidth:"1.5"}),e.jsx("path",{d:"M3 17L17 3",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round"})]}):e.jsxs("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none","aria-hidden":"true",children:[e.jsx("path",{d:"M2.5 10s3-6 7.5-6 7.5 6 7.5 6-3 6-7.5 6-7.5-6-7.5-6z",stroke:"currentColor",strokeWidth:"1.5"}),e.jsx("circle",{cx:"10",cy:"10",r:"2.5",stroke:"currentColor",strokeWidth:"1.5"})]})})]}),u&&e.jsx("p",{id:`${k}-error`,className:"cedros-input-error",children:u}),w&&a&&b?.length>0&&e.jsxs("div",{className:"cedros-password-strength",children:[e.jsx("div",{className:"cedros-strength-bar",children:e.jsx("div",{className:"cedros-strength-fill",style:{width:`${a.strength==="weak"?25:a.strength==="fair"?50:a.strength==="good"?75:100}%`,backgroundColor:x[a.strength]}})}),e.jsx("span",{className:"cedros-strength-label",children:a.strength})]})]})}function W(){const{config:l,_internal:p}=N.useCedrosLogin(),[w,f]=r.useState("idle"),[u,m]=r.useState(!1),[n,b]=r.useState(null),{checkLimit:h,getRemainingAttempts:i,getTimeUntilReset:v,reset:a}=O({maxAttempts:5,windowMs:12e4}),g=r.useMemo(()=>new N.ApiClient({baseUrl:l.serverUrl,timeoutMs:l.requestTimeout,retryAttempts:l.retryAttempts}),[l.serverUrl,l.requestTimeout,l.retryAttempts]),k=r.useCallback(async(o,s)=>{const t=/^[A-Z0-9]{16}$/i.test(s)||/^[A-Z0-9]{4}(-[A-Z0-9]{4}){3}$/i.test(s);if(!(/^\d{6}$/.test(s)||t)){const d={code:"VALIDATION_ERROR",message:"Please enter a valid 6-digit code or recovery code"};throw b(d),d}try{h()}catch(d){const C={code:"RATE_LIMITED",message:d instanceof Error?d.message:"Too many attempts"};throw b(C),C}m(!0),b(null),f("verifying");try{const d=await g.post("/login/mfa",{mfaToken:o,code:s});return f("success"),a(),p&&d.user&&d.tokens&&p.handleLoginSuccess(d.user,d.tokens),d}catch(d){const C=N.handleApiError(d,"Incorrect verification code. Please check and try again.");throw b(C),f("error"),C}finally{m(!1)}},[g,p,h,a]),y=r.useCallback(()=>b(null),[]),x=r.useCallback(()=>{b(null),f("idle"),m(!1)},[]);return{state:w,isLoading:u,error:n,verifyTotp:k,clearError:y,reset:x,remainingAttempts:i(),timeUntilReset:v()}}const E=6;function H({value:l="",onChange:p,onComplete:w,disabled:f=!1,error:u,autoFocus:m=!1,className:n=""}){const b=r.useRef([]),[h,i]=r.useState(l.padEnd(E,"")),v=r.useId();r.useEffect(()=>{i(l.padEnd(E,""))},[l]);const a=r.useCallback(s=>{s>=0&&s<E&&b.current[s]?.focus()},[]),g=r.useCallback(s=>{const t=s.replace(/\D/g,"").slice(0,E);i(t.padEnd(E,"")),p?.(t),t.length===E&&w?.(t)},[p,w]),k=r.useCallback((s,t)=>{if(!/^\d?$/.test(t))return;const c=h.split("");c[s]=t;const d=c.join("").replace(/ /g,"");g(d),t&&s<E-1&&a(s+1)},[h,g,a]),y=r.useCallback((s,t)=>{if(t.key==="Backspace"){t.preventDefault();const c=h.split("");c[s]&&c[s]!==" "?(c[s]=" ",g(c.join("").replace(/ /g,""))):s>0&&(c[s-1]=" ",g(c.join("").replace(/ /g,"")),a(s-1))}else t.key==="ArrowLeft"&&s>0?(t.preventDefault(),a(s-1)):t.key==="ArrowRight"&&s<E-1&&(t.preventDefault(),a(s+1))},[h,g,a]),x=r.useCallback(s=>{s.preventDefault();const c=s.clipboardData.getData("text").replace(/\D/g,"").slice(0,E);c&&(g(c),a(Math.min(c.length,E-1)))},[g,a]),o=r.useCallback(s=>{s.target.select()},[]);return r.useEffect(()=>{m&&!f&&b.current[0]?.focus()},[m,f]),e.jsxs("div",{className:`cedros-otp-input ${n}`,children:[e.jsx("div",{className:"cedros-otp-slots",role:"group","aria-label":"One-time password",children:Array.from({length:E}).map((s,t)=>e.jsx("input",{ref:c=>{b.current[t]=c},id:`${v}-${t}`,type:"text",inputMode:"numeric",pattern:"[0-9]*",maxLength:1,className:`cedros-otp-slot ${u?"cedros-otp-slot-error":""}`,value:h[t]===" "?"":h[t]||"",onChange:c=>k(t,c.target.value),onKeyDown:c=>y(t,c),onPaste:x,onFocus:o,disabled:f,autoComplete:"one-time-code","aria-label":`Digit ${t+1}`,"aria-invalid":u?"true":void 0},t))}),u&&e.jsx("p",{className:"cedros-otp-error",role:"alert",children:u})]})}function K({mfaToken:l,email:p,onSuccess:w,onBack:f,className:u=""}){const{verifyTotp:m,isLoading:n,error:b,clearError:h}=W(),[i,v]=r.useState(""),[a,g]=r.useState(!1),[k,y]=r.useState(""),x=async t=>{const c=t||(a?k:i);if(c)try{await m(l,c),w?.()}catch{a?y(""):v("")}},o=t=>{x(t)},s=()=>{g(!a),h(),v(""),y("")};return e.jsxs("div",{className:`cedros-totp-verify ${u}`,children:[e.jsxs("div",{className:"cedros-totp-verify-header",children:[e.jsxs("svg",{className:"cedros-totp-verify-icon",width:"48",height:"48",viewBox:"0 0 48 48",fill:"none","aria-hidden":"true",children:[e.jsx("rect",{x:"8",y:"20",width:"32",height:"24",rx:"4",stroke:"currentColor",strokeWidth:"2"}),e.jsx("path",{d:"M16 20V14a8 8 0 1 1 16 0v6",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round"}),e.jsx("circle",{cx:"24",cy:"32",r:"3",fill:"currentColor"})]}),e.jsx("h3",{className:"cedros-totp-title",children:"Two-factor authentication"}),e.jsx("p",{className:"cedros-totp-description",children:a?"Enter one of your recovery codes to sign in.":"Enter the 6-digit code from your authenticator app."}),p&&e.jsx("p",{className:"cedros-totp-email",children:p})]}),a?e.jsxs("div",{className:"cedros-totp-backup-input",children:[e.jsx("input",{type:"text",className:`cedros-input ${b?"cedros-input-error":""}`,placeholder:"Enter recovery code",value:k,onChange:t=>{y(t.target.value.toUpperCase()),h()},onKeyDown:t=>{t.key==="Enter"&&k&&x()},disabled:n,autoFocus:!0,autoComplete:"one-time-code"}),b&&e.jsx("p",{className:"cedros-input-error",role:"alert",children:b.message})]}):e.jsx(H,{value:i,onChange:t=>{v(t),h()},onComplete:o,disabled:n,error:b?.message,autoFocus:!0}),e.jsx("button",{type:"button",className:"cedros-button cedros-button-primary cedros-button-md cedros-button-full",onClick:()=>x(),disabled:n||(a?!k:i.length!==6),children:n?e.jsxs(e.Fragment,{children:[e.jsx(P.LoadingSpinner,{size:"sm"}),e.jsx("span",{children:"Verifying..."})]}):"Verify"}),e.jsxs("div",{className:"cedros-totp-verify-footer",children:[e.jsx("button",{type:"button",className:"cedros-link cedros-link-sm",onClick:s,disabled:n,children:a?"Use authenticator app":"Use a recovery code"}),f&&e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"cedros-totp-verify-divider",children:"•"}),e.jsx("button",{type:"button",className:"cedros-link cedros-link-sm",onClick:f,disabled:n,children:"Back to login"})]})]})]})}function de({onSuccess:l,onSwitchToRegister:p,onForgotPassword:w,className:f=""}){const{login:u,isLoading:m,error:n,clearError:b}=U(),[h,i]=r.useState(""),[v,a]=r.useState(""),[g,k]=r.useState(null),[y,x]=r.useState(""),o=async c=>{c.preventDefault();try{const d=await u(h,v);d.mfaRequired?(k(d.mfaToken),x(d.email)):l?.()}catch{}},s=()=>{k(null),x(""),l?.()},t=()=>{k(null),x(""),a("")};return g?e.jsx(K,{mfaToken:g,email:y,onSuccess:s,onBack:t,className:f}):e.jsxs("form",{onSubmit:o,className:`cedros-form ${f}`,children:[e.jsxs("div",{className:"cedros-form-field",children:[e.jsx("label",{htmlFor:"email",className:"cedros-label",children:"Email"}),e.jsx("input",{id:"email",type:"email",className:"cedros-input",value:h,onChange:c=>i(c.target.value),placeholder:"you@example.com",required:!0,"aria-required":"true",autoComplete:"email",disabled:m})]}),e.jsx("div",{className:"cedros-form-field",children:e.jsx(R,{value:v,onChange:c=>a(c.target.value),placeholder:"Enter your password",required:!0,autoComplete:"current-password",disabled:m,labelAction:w?e.jsx("button",{type:"button",className:"cedros-link cedros-link-muted cedros-link-sm",onClick:w,children:"Forgot your password?"}):void 0})}),e.jsx(z.ErrorMessage,{error:n,onDismiss:b}),e.jsx("button",{type:"submit",className:"cedros-button cedros-button-primary cedros-button-md cedros-button-full",disabled:m||!h||!v,"aria-busy":m,children:m?e.jsxs(e.Fragment,{children:[e.jsx(P.LoadingSpinner,{size:"sm",announce:!0,label:"Signing in"}),e.jsx("span",{children:"Signing in..."})]}):"Sign in"}),p&&e.jsxs("p",{className:"cedros-form-footer",children:["Don't have an account?"," ",e.jsx("button",{type:"button",className:"cedros-link cedros-link-muted",onClick:p,children:"Sign up"})]})]})}function ue({onSuccess:l,onSwitchToLogin:p,className:w="",accessCode:f}){const{config:u}=N.useCedrosLogin(),{register:m,isLoading:n,error:b,clearError:h}=U(),{features:i}=B.useServerFeatures(),[v,a]=r.useState(""),[g,k]=r.useState(""),[y,x]=r.useState(""),[o,s]=r.useState(""),[t,c]=r.useState(""),d=f??t,[C,Z]=r.useState(null),[Y,L]=r.useState(null),S=u.forms?.termsOfService,T=u.forms?.emailOptIn,I=S?.show??!1,A=S?.required??!0,G=S?.defaultChecked??!1,$=S?.label??"I agree to the Terms of Service",J=S?.url,F=B.sanitizeExternalUrl(J),Q=T?.show??!1,X=T?.defaultChecked??!1,ee=T?.label??"Send me updates and news",[q,se]=r.useState(G),[te,re]=r.useState(X),D=y===o,ae=C?.isValid??!1,oe=!I||!A||q,V=i?.signupAccessCodeRequired??!1,_=g&&y&&o&&D&&ae&&oe&&(!V||d.trim())&&!n,ne=async j=>{if(j.preventDefault(),L(null),I&&A&&!q){L({code:"VALIDATION_ERROR",message:"You must agree to the Terms of Service to continue"});return}if(_)try{await m(g,y,v||void 0,void 0,V&&d.trim()||void 0),l?.()}catch{}},ce=b||Y,le=()=>{h(),L(null)};return e.jsxs("form",{onSubmit:ne,className:`cedros-form ${w}`,children:[e.jsxs("div",{className:"cedros-form-field",children:[e.jsxs("label",{htmlFor:"name",className:"cedros-label",children:["Name ",e.jsx("span",{className:"cedros-optional",children:"(optional)"})]}),e.jsx("input",{id:"name",type:"text",className:"cedros-input",value:v,onChange:j=>a(j.target.value),placeholder:"Your name",autoComplete:"name",disabled:n})]}),e.jsxs("div",{className:"cedros-form-field",children:[e.jsx("label",{htmlFor:"register-email",className:"cedros-label",children:"Email"}),e.jsx("input",{id:"register-email",type:"email",className:"cedros-input",value:g,onChange:j=>k(j.target.value),placeholder:"you@example.com",required:!0,"aria-required":"true",autoComplete:"email",disabled:n})]}),e.jsx("div",{className:"cedros-form-field",children:e.jsx(R,{value:y,onChange:j=>x(j.target.value),placeholder:"Create a password",required:!0,autoComplete:"new-password",disabled:n,showStrengthMeter:!0,onValidationChange:Z})}),e.jsx("div",{className:"cedros-form-field",children:e.jsx(R,{label:"Confirm Password",value:o,onChange:j=>s(j.target.value),placeholder:"Confirm your password",required:!0,autoComplete:"new-password",disabled:n,"aria-invalid":o&&!D?"true":void 0,error:o&&!D?"Passwords do not match":void 0})}),V&&f===void 0&&e.jsxs("div",{className:"cedros-form-field",children:[e.jsx("label",{htmlFor:"register-access-code",className:"cedros-label",children:"Access Code"}),e.jsx("input",{id:"register-access-code",type:"text",className:"cedros-input",value:t,onChange:j=>c(j.target.value),placeholder:"Enter access code",required:!0,"aria-required":"true",disabled:n,autoComplete:"off"})]}),I&&e.jsx("div",{className:"cedros-form-field cedros-checkbox-field",children:e.jsxs("label",{className:"cedros-checkbox-label",children:[e.jsx("input",{type:"checkbox",className:"cedros-checkbox",checked:q,onChange:j=>se(j.target.checked),disabled:n,"aria-required":A}),e.jsxs("span",{className:"cedros-checkbox-text",children:[F?e.jsxs(e.Fragment,{children:[$.replace("Terms of Service","").trim()||"I agree to the"," ",e.jsx("a",{href:F,target:"_blank",rel:"noopener noreferrer",className:"cedros-link",children:"Terms of Service"})]}):$,A&&e.jsx("span",{className:"cedros-required",children:"*"})]})]})}),Q&&e.jsx("div",{className:"cedros-form-field cedros-checkbox-field",children:e.jsxs("label",{className:"cedros-checkbox-label",children:[e.jsx("input",{type:"checkbox",className:"cedros-checkbox",checked:te,onChange:j=>re(j.target.checked),disabled:n}),e.jsx("span",{className:"cedros-checkbox-text",children:ee})]})}),e.jsx(z.ErrorMessage,{error:ce,onDismiss:le}),e.jsx("button",{type:"submit",className:"cedros-button cedros-button-primary cedros-button-md cedros-button-full",disabled:!_,"aria-busy":n,children:n?e.jsxs(e.Fragment,{children:[e.jsx(P.LoadingSpinner,{size:"sm",announce:!0,label:"Creating account"}),e.jsx("span",{children:"Creating account..."})]}):"Create account"}),p&&e.jsxs("p",{className:"cedros-form-footer",children:["Already have an account?"," ",e.jsx("button",{type:"button",className:"cedros-link cedros-link-muted",onClick:p,children:"Sign in"})]})]})}exports.EmailLoginForm=de;exports.EmailRegisterForm=ue;exports.OtpInput=H;exports.PasswordInput=R;exports.TotpVerify=K;exports.useEmailAuth=U;exports.useRateLimiter=O;exports.useTotpVerify=W;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmailRegisterForm-ByYQ43yL.cjs","sources":["../src/hooks/useRateLimiter.ts","../src/hooks/useEmailAuth.ts","../src/components/email/PasswordInput.tsx","../src/hooks/useTotpVerify.ts","../src/components/totp/OtpInput.tsx","../src/components/totp/TotpVerify.tsx","../src/components/email/EmailLoginForm.tsx","../src/components/email/EmailRegisterForm.tsx"],"sourcesContent":["import { useRef, useCallback, useState, useEffect } from 'react';\n\nexport interface UseRateLimiterOptions {\n /** Maximum number of attempts allowed within the window */\n maxAttempts?: number;\n /** Time window in milliseconds */\n windowMs?: number;\n /**\n * UI-25: Set to true to enable the 1s countdown interval for displaying\n * time-until-reset in the UI. When false (default), no interval is started,\n * saving resources for callers that don't display a countdown.\n */\n showCountdown?: boolean;\n}\n\nexport interface UseRateLimiterReturn {\n /**\n * Check if an action is allowed. Throws an error if rate limited.\n * Call this before performing the action.\n */\n checkLimit: () => void;\n /**\n * Check if an action is allowed without throwing.\n * Returns true if allowed, false if rate limited.\n */\n isAllowed: () => boolean;\n /**\n * Get remaining attempts in current window\n */\n getRemainingAttempts: () => number;\n /**\n * Get time until rate limit resets (in ms)\n */\n getTimeUntilReset: () => number;\n /**\n * Reset the rate limiter (e.g., after successful action)\n */\n reset: () => void;\n}\n\n/**\n * Rate limiting hook to prevent excessive API calls from the client.\n *\n * @param options - Rate limiter configuration\n * @returns Rate limiter functions\n *\n * @example\n * ```tsx\n * function LoginForm() {\n * const { checkLimit, getRemainingAttempts } = useRateLimiter({\n * maxAttempts: 5,\n * windowMs: 60000, // 1 minute\n * });\n *\n * const handleLogin = async () => {\n * try {\n * checkLimit(); // Throws if rate limited\n * await login(email, password);\n * } catch (err) {\n * if (err.message.includes('Too many attempts')) {\n * // Show rate limit message\n * }\n * }\n * };\n * }\n * ```\n */\nexport function useRateLimiter(options: UseRateLimiterOptions = {}): UseRateLimiterReturn {\n const { maxAttempts = 5, windowMs = 60000, showCountdown = false } = options;\n\n // Store timestamps of recent attempts\n const attemptsRef = useRef<number[]>([]);\n const [hasAttempts, setHasAttempts] = useState(false);\n const [, setTick] = useState(0);\n\n const bump = useCallback(() => {\n setTick((value) => value + 1);\n }, []);\n\n /**\n * Remove expired attempts from the tracking array (no state update).\n * Safe to call during render for getter functions.\n */\n const cleanupAttemptsArray = useCallback(() => {\n const now = Date.now();\n attemptsRef.current = attemptsRef.current.filter((timestamp) => now - timestamp < windowMs);\n }, [windowMs]);\n\n /**\n * Remove expired attempts AND update hasAttempts state.\n * M-04: Use functional setState to avoid hasAttempts dependency cascade.\n * Only call from event handlers and effects, NOT during render.\n */\n const cleanupExpiredAttempts = useCallback(() => {\n cleanupAttemptsArray();\n // Functional update avoids dependency on hasAttempts\n setHasAttempts((current) => (attemptsRef.current.length === 0 && current ? false : current));\n }, [cleanupAttemptsArray]);\n\n /**\n * Get the number of remaining attempts\n * Uses cleanupAttemptsArray (no state) so safe to call during render.\n */\n const getRemainingAttempts = useCallback((): number => {\n cleanupAttemptsArray();\n return Math.max(0, maxAttempts - attemptsRef.current.length);\n }, [cleanupAttemptsArray, maxAttempts]);\n\n /**\n * Get time until rate limit resets\n * Uses cleanupAttemptsArray (no state) so safe to call during render.\n */\n const getTimeUntilReset = useCallback((): number => {\n cleanupAttemptsArray();\n if (attemptsRef.current.length === 0) {\n return 0;\n }\n const oldestAttempt = attemptsRef.current[0];\n const resetTime = oldestAttempt + windowMs;\n return Math.max(0, resetTime - Date.now());\n }, [cleanupAttemptsArray, windowMs]);\n\n /**\n * Check if an action is allowed without throwing\n * Uses cleanupAttemptsArray (no state) so safe to call during render.\n */\n const isAllowed = useCallback((): boolean => {\n cleanupAttemptsArray();\n return attemptsRef.current.length < maxAttempts;\n }, [cleanupAttemptsArray, maxAttempts]);\n\n /**\n * Check rate limit and throw if exceeded\n */\n const checkLimit = useCallback((): void => {\n cleanupExpiredAttempts();\n\n if (attemptsRef.current.length >= maxAttempts) {\n const waitTime = getTimeUntilReset();\n const waitSeconds = Math.ceil(waitTime / 1000);\n throw new Error(\n `Too many attempts. Please wait ${waitSeconds} second${waitSeconds === 1 ? '' : 's'} before trying again.`\n );\n }\n\n // Record this attempt\n attemptsRef.current.push(Date.now());\n // M-04: Functional update avoids hasAttempts dependency\n setHasAttempts((current) => (current ? current : true));\n bump();\n }, [cleanupExpiredAttempts, maxAttempts, getTimeUntilReset, bump]);\n\n /**\n * Reset the rate limiter\n */\n const reset = useCallback((): void => {\n attemptsRef.current = [];\n // M-04: Functional update avoids hasAttempts dependency\n setHasAttempts((current) => (current ? false : current));\n bump();\n }, [bump]);\n\n // UI-25: Only start the 1s interval when the caller opts into countdown display.\n // This avoids a tick firing every second for all callers that don't show a countdown.\n useEffect(() => {\n if (!hasAttempts || !showCountdown) return;\n const intervalId = window.setInterval(() => {\n cleanupExpiredAttempts();\n bump();\n }, 1000);\n return () => {\n window.clearInterval(intervalId);\n };\n }, [hasAttempts, showCountdown, bump, cleanupExpiredAttempts]);\n\n return {\n checkLimit,\n isAllowed,\n getRemainingAttempts,\n getTimeUntilReset,\n reset,\n };\n}\n","import { useState, useCallback, useMemo } from 'react';\nimport { useCedrosLogin } from '../context/useCedrosLogin';\nimport { ApiClient, handleApiError } from '../utils/apiClient';\nimport { validateEmail } from '../utils/validation';\nimport { useRateLimiter } from './useRateLimiter';\nimport type { AuthResponse, AuthError } from '../types';\nimport type { MfaRequiredResponse } from '../types';\n\nfunction isMfaRequiredResponse(\n response: AuthResponse | MfaRequiredResponse\n): response is MfaRequiredResponse {\n return 'mfaRequired' in response && response.mfaRequired === true;\n}\n\n/** Result when MFA verification is required */\nexport interface MfaRequiredResult {\n mfaRequired: true;\n mfaToken: string;\n email: string;\n userId: string;\n}\n\n/** Result of successful login (no TOTP required or after TOTP verification) */\nexport interface LoginSuccessResult {\n mfaRequired: false;\n response: AuthResponse;\n}\n\n/** Union type for login result */\nexport type LoginResult = MfaRequiredResult | LoginSuccessResult;\n\nexport interface UseEmailAuthReturn {\n /** Login - may return mfaRequired if 2FA is enabled */\n login: (email: string, password: string) => Promise<LoginResult>;\n register: (email: string, password: string, name?: string, referral?: string, accessCode?: string) => Promise<AuthResponse>;\n isLoading: boolean;\n error: AuthError | null;\n clearError: () => void;\n /**\n * Number of remaining login attempts before rate limiting.\n *\n * M-10: Snapshot Behavior\n * This value is a point-in-time snapshot computed at render time.\n * It may be briefly stale during rapid requests or concurrent renders.\n * For UI display only - actual rate limiting is enforced inside login/register.\n */\n remainingAttempts: number;\n /**\n * Time in ms until rate limit resets (0 if not rate limited).\n *\n * M-10: Snapshot Behavior\n * This value is a point-in-time snapshot computed at render time.\n * It may be briefly stale - use for UI display, not for logic decisions.\n */\n timeUntilReset: number;\n}\n\n/**\n * Hook for email/password authentication.\n *\n * @example\n * ```tsx\n * function LoginForm() {\n * const { login, isLoading, error } = useEmailAuth();\n *\n * const handleSubmit = async (e) => {\n * e.preventDefault();\n * try {\n * await login(email, password);\n * } catch (err) {\n * // Handle error\n * }\n * };\n * }\n * ```\n */\nexport function useEmailAuth(): UseEmailAuthReturn {\n const { config, _internal } = useCedrosLogin();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<AuthError | null>(null);\n\n // Rate limiter for login attempts (5 attempts per minute)\n const {\n checkLimit,\n getRemainingAttempts,\n getTimeUntilReset,\n reset: resetRateLimit,\n } = useRateLimiter({ maxAttempts: 5, windowMs: 60000 });\n\n const apiClient = useMemo(\n () =>\n new ApiClient({\n baseUrl: config.serverUrl,\n timeoutMs: config.requestTimeout,\n retryAttempts: config.retryAttempts,\n }),\n [config.serverUrl, config.requestTimeout, config.retryAttempts]\n );\n\n const callbacks = config.callbacks;\n\n const login = useCallback(\n async (email: string, password: string): Promise<LoginResult> => {\n // Validate email format before API call\n if (!validateEmail(email)) {\n const validationError: AuthError = {\n code: 'VALIDATION_ERROR',\n message: 'Please enter a valid email address',\n };\n setError(validationError);\n throw validationError;\n }\n\n // UI-7: Rate limit is checked BEFORE API call intentionally.\n // This prevents brute force attacks by limiting attempt frequency,\n // not just successful request frequency.\n try {\n checkLimit();\n } catch (err) {\n const rateLimitError: AuthError = {\n code: 'RATE_LIMITED',\n message: err instanceof Error ? err.message : 'Too many attempts',\n };\n setError(rateLimitError);\n throw rateLimitError;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const data = await apiClient.post<AuthResponse | MfaRequiredResponse>('/login', {\n email,\n password,\n });\n\n // Check if MFA verification is required\n if (isMfaRequiredResponse(data)) {\n return {\n mfaRequired: true,\n mfaToken: data.mfaToken,\n email,\n userId: data.userId,\n };\n }\n\n // Normal login success\n const authResponse: AuthResponse = data;\n callbacks?.onLoginSuccess?.(authResponse.user, 'email');\n _internal?.handleLoginSuccess(authResponse.user, authResponse.tokens);\n resetRateLimit(); // Reset on successful login\n return {\n mfaRequired: false,\n response: authResponse,\n };\n } catch (err) {\n const authError = handleApiError(err, 'Unable to sign in. Please try again.');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [apiClient, callbacks, _internal, checkLimit, resetRateLimit]\n );\n\n const register = useCallback(\n async (email: string, password: string, name?: string, referral?: string, accessCode?: string): Promise<AuthResponse> => {\n // Validate email format before API call\n if (!validateEmail(email)) {\n const validationError: AuthError = {\n code: 'VALIDATION_ERROR',\n message: 'Please enter a valid email address',\n };\n setError(validationError);\n throw validationError;\n }\n\n // UI-7: Rate limit is checked BEFORE API call intentionally.\n // This prevents brute force attacks by limiting attempt frequency,\n // not just successful request frequency.\n try {\n checkLimit();\n } catch (err) {\n const rateLimitError: AuthError = {\n code: 'RATE_LIMITED',\n message: err instanceof Error ? err.message : 'Too many attempts',\n };\n setError(rateLimitError);\n throw rateLimitError;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const effectiveReferral = referral ?? _internal?.getReferralCode?.() ?? undefined;\n const data = await apiClient.post<AuthResponse>('/register', {\n email,\n password,\n name,\n referral: effectiveReferral,\n ...(accessCode ? { access_code: accessCode } : {}),\n });\n callbacks?.onLoginSuccess?.(data.user, 'email');\n _internal?.handleLoginSuccess(data.user, data.tokens);\n resetRateLimit(); // Reset on successful registration\n\n return data;\n } catch (err) {\n const authError = handleApiError(err, 'Unable to create your account. Please try again.');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [apiClient, callbacks, _internal, checkLimit, resetRateLimit]\n );\n\n const clearError = useCallback(() => setError(null), []);\n\n return {\n login,\n register,\n isLoading,\n error,\n clearError,\n // M-10: Point-in-time snapshots for UI display (see interface JSDoc)\n remainingAttempts: getRemainingAttempts(),\n timeUntilReset: getTimeUntilReset(),\n };\n}\n","import { useState, useId, type InputHTMLAttributes } from 'react';\nimport { validatePassword } from '../../utils/validation';\nimport type { PasswordValidation } from '../../types';\n\nexport interface PasswordInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type'> {\n label?: string;\n /** Action element shown on the right side of the label (e.g., \"Forgot password?\" link) */\n labelAction?: React.ReactNode;\n showStrengthMeter?: boolean;\n onValidationChange?: (validation: PasswordValidation) => void;\n error?: string;\n}\n\n/**\n * Password input with visibility toggle and optional strength meter\n */\nexport function PasswordInput({\n label = 'Password',\n labelAction,\n showStrengthMeter = false,\n onValidationChange,\n error,\n className = '',\n onChange,\n value,\n ...props\n}: PasswordInputProps) {\n const [showPassword, setShowPassword] = useState(false);\n const [validation, setValidation] = useState<PasswordValidation | null>(null);\n const inputId = useId();\n\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const newValue = e.target.value;\n\n if (showStrengthMeter || onValidationChange) {\n const newValidation = validatePassword(newValue);\n setValidation(newValidation);\n onValidationChange?.(newValidation);\n }\n\n onChange?.(e);\n };\n\n const strengthColors = {\n weak: 'var(--cedros-destructive, #ef4444)',\n fair: 'var(--cedros-warning, #f59e0b)',\n good: 'var(--cedros-success, #22c55e)',\n strong: 'var(--cedros-success, #22c55e)',\n };\n\n return (\n <div className={`cedros-password-input ${className}`}>\n <div className=\"cedros-label-row\">\n <label htmlFor={inputId} className=\"cedros-label\">\n {label}\n </label>\n {labelAction}\n </div>\n <div className=\"cedros-password-wrapper\">\n <input\n id={inputId}\n type={showPassword ? 'text' : 'password'}\n className=\"cedros-input\"\n onChange={handleChange}\n value={value}\n aria-invalid={error ? 'true' : undefined}\n aria-describedby={error ? `${inputId}-error` : undefined}\n {...props}\n />\n <button\n type=\"button\"\n className=\"cedros-password-toggle\"\n onClick={() => setShowPassword(!showPassword)}\n aria-label={showPassword ? 'Hide password' : 'Show password'}\n aria-pressed={showPassword}\n >\n {showPassword ? (\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M2.5 10s3-6 7.5-6 7.5 6 7.5 6-3 6-7.5 6-7.5-6-7.5-6z\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n />\n <circle cx=\"10\" cy=\"10\" r=\"2.5\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n <path d=\"M3 17L17 3\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\n </svg>\n ) : (\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M2.5 10s3-6 7.5-6 7.5 6 7.5 6-3 6-7.5 6-7.5-6-7.5-6z\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n />\n <circle cx=\"10\" cy=\"10\" r=\"2.5\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n </svg>\n )}\n </button>\n </div>\n\n {error && (\n <p id={`${inputId}-error`} className=\"cedros-input-error\">\n {error}\n </p>\n )}\n\n {showStrengthMeter && validation && (value as string)?.length > 0 && (\n <div className=\"cedros-password-strength\">\n <div className=\"cedros-strength-bar\">\n <div\n className=\"cedros-strength-fill\"\n style={{\n width: `${\n validation.strength === 'weak'\n ? 25\n : validation.strength === 'fair'\n ? 50\n : validation.strength === 'good'\n ? 75\n : 100\n }%`,\n backgroundColor: strengthColors[validation.strength],\n }}\n />\n </div>\n <span className=\"cedros-strength-label\">{validation.strength}</span>\n </div>\n )}\n </div>\n );\n}\n","import { useState, useCallback, useMemo } from 'react';\nimport { useCedrosLogin } from '../context/useCedrosLogin';\nimport { ApiClient, handleApiError } from '../utils/apiClient';\nimport { useRateLimiter } from './useRateLimiter';\nimport type { AuthError, AuthResponse, TotpVerifyState } from '../types';\n\nexport interface UseTotpVerifyReturn {\n /** Verification state */\n state: TotpVerifyState;\n /** Whether verification is in progress */\n isLoading: boolean;\n /** Error from the last request */\n error: AuthError | null;\n /** Verify MFA code during login */\n verifyTotp: (mfaToken: string, code: string) => Promise<AuthResponse>;\n /** Clear error state */\n clearError: () => void;\n /** Reset to initial state */\n reset: () => void;\n /** Number of remaining verification attempts before rate limiting */\n remainingAttempts: number;\n /** Time in ms until rate limit resets (0 if not rate limited) */\n timeUntilReset: number;\n}\n\n/**\n * Hook for verifying TOTP codes during the login flow.\n *\n * Used when a user has TOTP enabled and needs to provide\n * their 6-digit code after password authentication.\n *\n * @example\n * ```tsx\n * function TotpVerifyStep({ mfaToken }) {\n * const { verifyTotp, isLoading, error } = useTotpVerify();\n *\n * const handleVerify = async (code: string) => {\n * const response = await verifyTotp(mfaToken, code);\n * // User is now authenticated\n * };\n * }\n * ```\n */\nexport function useTotpVerify(): UseTotpVerifyReturn {\n const { config, _internal } = useCedrosLogin();\n const [state, setState] = useState<TotpVerifyState>('idle');\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<AuthError | null>(null);\n\n // Rate limiter for TOTP verification (5 attempts per 2 minutes)\n // Stricter than login to prevent brute force on short codes\n const {\n checkLimit,\n getRemainingAttempts,\n getTimeUntilReset,\n reset: resetRateLimit,\n } = useRateLimiter({ maxAttempts: 5, windowMs: 120000 });\n\n const apiClient = useMemo(\n () =>\n new ApiClient({\n baseUrl: config.serverUrl,\n timeoutMs: config.requestTimeout,\n retryAttempts: config.retryAttempts,\n }),\n [config.serverUrl, config.requestTimeout, config.retryAttempts]\n );\n\n const verifyTotp = useCallback(\n async (mfaToken: string, code: string): Promise<AuthResponse> => {\n // Validate code format (6 digits or recovery code)\n const isRecoveryCode =\n /^[A-Z0-9]{16}$/i.test(code) || /^[A-Z0-9]{4}(-[A-Z0-9]{4}){3}$/i.test(code);\n const isValidCode = /^\\d{6}$/.test(code) || isRecoveryCode;\n if (!isValidCode) {\n const validationError: AuthError = {\n code: 'VALIDATION_ERROR',\n message: 'Please enter a valid 6-digit code or recovery code',\n };\n setError(validationError);\n throw validationError;\n }\n\n // Rate limit check before API call to prevent brute force\n try {\n checkLimit();\n } catch (err) {\n const rateLimitError: AuthError = {\n code: 'RATE_LIMITED',\n message: err instanceof Error ? err.message : 'Too many attempts',\n };\n setError(rateLimitError);\n throw rateLimitError;\n }\n\n setIsLoading(true);\n setError(null);\n setState('verifying');\n\n try {\n const response = await apiClient.post<AuthResponse>('/login/mfa', { mfaToken, code });\n\n setState('success');\n resetRateLimit(); // Reset on successful verification\n\n // Complete authentication via internal API\n if (_internal && response.user && response.tokens) {\n _internal.handleLoginSuccess(response.user, response.tokens);\n }\n\n return response;\n } catch (err) {\n const authError = handleApiError(err, 'Incorrect verification code. Please check and try again.');\n setError(authError);\n setState('error');\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [apiClient, _internal, checkLimit, resetRateLimit]\n );\n\n const clearError = useCallback(() => setError(null), []);\n\n const reset = useCallback(() => {\n setError(null);\n setState('idle');\n setIsLoading(false);\n }, []);\n\n return {\n state,\n isLoading,\n error,\n verifyTotp,\n clearError,\n reset,\n // Point-in-time snapshots for UI display\n remainingAttempts: getRemainingAttempts(),\n timeUntilReset: getTimeUntilReset(),\n };\n}\n","/**\n * OTP Input component (shadcn-style)\n *\n * A 6-digit input with separate boxes for each digit,\n * designed for TOTP verification codes.\n */\n\nimport { useRef, useCallback, useState, useEffect, useId } from 'react';\n\n/** Number of OTP digits */\nconst OTP_LENGTH = 6;\n\nexport interface OtpInputProps {\n /** Current value (up to 6 digits) */\n value?: string;\n /** Called when the value changes */\n onChange?: (value: string) => void;\n /** Called when all 6 digits are entered */\n onComplete?: (value: string) => void;\n /** Whether the input is disabled */\n disabled?: boolean;\n /** Error message to display */\n error?: string;\n /** Auto-focus the first input on mount */\n autoFocus?: boolean;\n /** Additional CSS class */\n className?: string;\n}\n\n/**\n * OTP input with separate boxes for each digit (shadcn pattern)\n *\n * Features:\n * - Auto-advances to next input on digit entry\n * - Backspace moves to previous input\n * - Supports paste of full code\n * - Numeric keyboard on mobile\n */\nexport function OtpInput({\n value = '',\n onChange,\n onComplete,\n disabled = false,\n error,\n autoFocus = false,\n className = '',\n}: OtpInputProps) {\n const inputRefs = useRef<(HTMLInputElement | null)[]>([]);\n const [localValue, setLocalValue] = useState(value.padEnd(OTP_LENGTH, ''));\n const id = useId();\n\n // Sync with controlled value\n useEffect(() => {\n setLocalValue(value.padEnd(OTP_LENGTH, ''));\n }, [value]);\n\n const focusInput = useCallback((index: number) => {\n if (index >= 0 && index < OTP_LENGTH) {\n inputRefs.current[index]?.focus();\n }\n }, []);\n\n const updateValue = useCallback(\n (newValue: string) => {\n const sanitized = newValue.replace(/\\D/g, '').slice(0, OTP_LENGTH);\n setLocalValue(sanitized.padEnd(OTP_LENGTH, ''));\n onChange?.(sanitized);\n\n if (sanitized.length === OTP_LENGTH) {\n onComplete?.(sanitized);\n }\n },\n [onChange, onComplete]\n );\n\n const handleChange = useCallback(\n (index: number, digit: string) => {\n if (!/^\\d?$/.test(digit)) return;\n\n const chars = localValue.split('');\n chars[index] = digit;\n const newValue = chars.join('').replace(/ /g, '');\n updateValue(newValue);\n\n // Move to next input if digit entered\n if (digit && index < OTP_LENGTH - 1) {\n focusInput(index + 1);\n }\n },\n [localValue, updateValue, focusInput]\n );\n\n const handleKeyDown = useCallback(\n (index: number, e: React.KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Backspace') {\n e.preventDefault();\n const chars = localValue.split('');\n\n if (chars[index] && chars[index] !== ' ') {\n // Clear current digit\n chars[index] = ' ';\n updateValue(chars.join('').replace(/ /g, ''));\n } else if (index > 0) {\n // Move to previous and clear it\n chars[index - 1] = ' ';\n updateValue(chars.join('').replace(/ /g, ''));\n focusInput(index - 1);\n }\n } else if (e.key === 'ArrowLeft' && index > 0) {\n e.preventDefault();\n focusInput(index - 1);\n } else if (e.key === 'ArrowRight' && index < OTP_LENGTH - 1) {\n e.preventDefault();\n focusInput(index + 1);\n }\n },\n [localValue, updateValue, focusInput]\n );\n\n const handlePaste = useCallback(\n (e: React.ClipboardEvent) => {\n e.preventDefault();\n const pasted = e.clipboardData.getData('text');\n const digits = pasted.replace(/\\D/g, '').slice(0, OTP_LENGTH);\n if (digits) {\n updateValue(digits);\n // Focus the next empty slot or the last one\n focusInput(Math.min(digits.length, OTP_LENGTH - 1));\n }\n },\n [updateValue, focusInput]\n );\n\n const handleFocus = useCallback((e: React.FocusEvent<HTMLInputElement>) => {\n e.target.select();\n }, []);\n\n // Auto-focus first input\n useEffect(() => {\n if (autoFocus && !disabled) {\n inputRefs.current[0]?.focus();\n }\n }, [autoFocus, disabled]);\n\n return (\n <div className={`cedros-otp-input ${className}`}>\n <div className=\"cedros-otp-slots\" role=\"group\" aria-label=\"One-time password\">\n {Array.from({ length: OTP_LENGTH }).map((_, index) => (\n <input\n key={index}\n ref={(el) => {\n inputRefs.current[index] = el;\n }}\n id={`${id}-${index}`}\n type=\"text\"\n inputMode=\"numeric\"\n pattern=\"[0-9]*\"\n maxLength={1}\n className={`cedros-otp-slot ${error ? 'cedros-otp-slot-error' : ''}`}\n value={localValue[index] === ' ' ? '' : localValue[index] || ''}\n onChange={(e) => handleChange(index, e.target.value)}\n onKeyDown={(e) => handleKeyDown(index, e)}\n onPaste={handlePaste}\n onFocus={handleFocus}\n disabled={disabled}\n autoComplete=\"one-time-code\"\n aria-label={`Digit ${index + 1}`}\n aria-invalid={error ? 'true' : undefined}\n />\n ))}\n </div>\n {error && (\n <p className=\"cedros-otp-error\" role=\"alert\">\n {error}\n </p>\n )}\n </div>\n );\n}\n","/**\n * TOTP Verification component for login flow\n *\n * Displayed when a user with 2FA enabled needs to\n * enter their verification code to complete login.\n */\n\nimport { useState } from 'react';\nimport { useTotpVerify } from '../../hooks/useTotpVerify';\nimport { OtpInput } from './OtpInput';\nimport { LoadingSpinner } from '../shared/LoadingSpinner';\n\nexport interface TotpVerifyProps {\n /** Temporary token from password authentication */\n mfaToken: string;\n /** Email address (for display) */\n email?: string;\n /** Called when verification succeeds */\n onSuccess?: () => void;\n /** Called when user wants to go back */\n onBack?: () => void;\n /** Additional CSS class */\n className?: string;\n}\n\n/**\n * Two-factor authentication verification for login.\n *\n * Accepts 6-digit codes from authenticator apps\n * or recovery codes for account recovery.\n */\nexport function TotpVerify({\n mfaToken,\n email,\n onSuccess,\n onBack,\n className = '',\n}: TotpVerifyProps) {\n const { verifyTotp, isLoading, error, clearError } = useTotpVerify();\n const [code, setCode] = useState('');\n const [useBackupCode, setUseBackupCode] = useState(false);\n const [backupCode, setBackupCode] = useState('');\n\n const handleVerify = async (verifyCode?: string) => {\n const codeToVerify = verifyCode || (useBackupCode ? backupCode : code);\n if (!codeToVerify) return;\n\n try {\n await verifyTotp(mfaToken, codeToVerify);\n onSuccess?.();\n } catch {\n // Error handled by hook, clear the input\n if (useBackupCode) {\n setBackupCode('');\n } else {\n setCode('');\n }\n }\n };\n\n const handleOtpComplete = (value: string) => {\n handleVerify(value);\n };\n\n const toggleBackupCode = () => {\n setUseBackupCode(!useBackupCode);\n clearError();\n setCode('');\n setBackupCode('');\n };\n\n return (\n <div className={`cedros-totp-verify ${className}`}>\n <div className=\"cedros-totp-verify-header\">\n <svg\n className=\"cedros-totp-verify-icon\"\n width=\"48\"\n height=\"48\"\n viewBox=\"0 0 48 48\"\n fill=\"none\"\n aria-hidden=\"true\"\n >\n <rect x=\"8\" y=\"20\" width=\"32\" height=\"24\" rx=\"4\" stroke=\"currentColor\" strokeWidth=\"2\" />\n <path\n d=\"M16 20V14a8 8 0 1 1 16 0v6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n <circle cx=\"24\" cy=\"32\" r=\"3\" fill=\"currentColor\" />\n </svg>\n <h3 className=\"cedros-totp-title\">Two-factor authentication</h3>\n <p className=\"cedros-totp-description\">\n {useBackupCode\n ? 'Enter one of your recovery codes to sign in.'\n : 'Enter the 6-digit code from your authenticator app.'}\n </p>\n {email && <p className=\"cedros-totp-email\">{email}</p>}\n </div>\n\n {useBackupCode ? (\n <div className=\"cedros-totp-backup-input\">\n <input\n type=\"text\"\n className={`cedros-input ${error ? 'cedros-input-error' : ''}`}\n placeholder=\"Enter recovery code\"\n value={backupCode}\n onChange={(e) => {\n setBackupCode(e.target.value.toUpperCase());\n clearError();\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && backupCode) {\n handleVerify();\n }\n }}\n disabled={isLoading}\n autoFocus\n autoComplete=\"one-time-code\"\n />\n {error && (\n <p className=\"cedros-input-error\" role=\"alert\">\n {error.message}\n </p>\n )}\n </div>\n ) : (\n <OtpInput\n value={code}\n onChange={(value) => {\n setCode(value);\n clearError();\n }}\n onComplete={handleOtpComplete}\n disabled={isLoading}\n error={error?.message}\n autoFocus\n />\n )}\n\n <button\n type=\"button\"\n className=\"cedros-button cedros-button-primary cedros-button-md cedros-button-full\"\n onClick={() => handleVerify()}\n disabled={isLoading || (useBackupCode ? !backupCode : code.length !== 6)}\n >\n {isLoading ? (\n <>\n <LoadingSpinner size=\"sm\" />\n <span>Verifying...</span>\n </>\n ) : (\n 'Verify'\n )}\n </button>\n\n <div className=\"cedros-totp-verify-footer\">\n <button\n type=\"button\"\n className=\"cedros-link cedros-link-sm\"\n onClick={toggleBackupCode}\n disabled={isLoading}\n >\n {useBackupCode ? 'Use authenticator app' : 'Use a recovery code'}\n </button>\n\n {onBack && (\n <>\n <span className=\"cedros-totp-verify-divider\">•</span>\n <button\n type=\"button\"\n className=\"cedros-link cedros-link-sm\"\n onClick={onBack}\n disabled={isLoading}\n >\n Back to login\n </button>\n </>\n )}\n </div>\n </div>\n );\n}\n","import { useState, type FormEvent } from 'react';\nimport { useEmailAuth } from '../../hooks/useEmailAuth';\nimport { PasswordInput } from './PasswordInput';\nimport { LoadingSpinner } from '../shared/LoadingSpinner';\nimport { ErrorMessage } from '../shared/ErrorMessage';\nimport { TotpVerify } from '../totp/TotpVerify';\n\nexport interface EmailLoginFormProps {\n onSuccess?: () => void;\n onSwitchToRegister?: () => void;\n /** Called when user clicks \"Forgot password?\" — navigates to forgot-password screen */\n onForgotPassword?: () => void;\n className?: string;\n}\n\n/**\n * Email/password login form\n */\nexport function EmailLoginForm({\n onSuccess,\n onSwitchToRegister,\n onForgotPassword,\n className = '',\n}: EmailLoginFormProps) {\n const { login, isLoading, error, clearError } = useEmailAuth();\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n // MFA state for 2FA flow\n const [mfaToken, setMfaToken] = useState<string | null>(null);\n const [mfaEmail, setMfaEmail] = useState<string>('');\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n try {\n const result = await login(email, password);\n if (result.mfaRequired) {\n // MFA verification needed\n setMfaToken(result.mfaToken);\n setMfaEmail(result.email);\n } else {\n // Login successful\n onSuccess?.();\n }\n } catch {\n // Error is handled by the hook\n }\n };\n\n const handleTotpSuccess = () => {\n setMfaToken(null);\n setMfaEmail('');\n onSuccess?.();\n };\n\n const handleTotpBack = () => {\n setMfaToken(null);\n setMfaEmail('');\n setPassword(''); // Clear password for security\n };\n\n // Show TOTP verification step\n if (mfaToken) {\n return (\n <TotpVerify\n mfaToken={mfaToken}\n email={mfaEmail}\n onSuccess={handleTotpSuccess}\n onBack={handleTotpBack}\n className={className}\n />\n );\n }\n\n return (\n <form onSubmit={handleSubmit} className={`cedros-form ${className}`}>\n <div className=\"cedros-form-field\">\n <label htmlFor=\"email\" className=\"cedros-label\">\n Email\n </label>\n <input\n id=\"email\"\n type=\"email\"\n className=\"cedros-input\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n placeholder=\"you@example.com\"\n required\n aria-required=\"true\"\n autoComplete=\"email\"\n disabled={isLoading}\n />\n </div>\n\n <div className=\"cedros-form-field\">\n <PasswordInput\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n placeholder=\"Enter your password\"\n required\n autoComplete=\"current-password\"\n disabled={isLoading}\n labelAction={\n onForgotPassword ? (\n <button\n type=\"button\"\n className=\"cedros-link cedros-link-muted cedros-link-sm\"\n onClick={onForgotPassword}\n >\n Forgot your password?\n </button>\n ) : undefined\n }\n />\n </div>\n\n <ErrorMessage error={error} onDismiss={clearError} />\n\n <button\n type=\"submit\"\n className=\"cedros-button cedros-button-primary cedros-button-md cedros-button-full\"\n disabled={isLoading || !email || !password}\n aria-busy={isLoading}\n >\n {isLoading ? (\n <>\n <LoadingSpinner size=\"sm\" announce label=\"Signing in\" />\n <span>Signing in...</span>\n </>\n ) : (\n 'Sign in'\n )}\n </button>\n\n {onSwitchToRegister && (\n <p className=\"cedros-form-footer\">\n Don&apos;t have an account?{' '}\n <button type=\"button\" className=\"cedros-link cedros-link-muted\" onClick={onSwitchToRegister}>\n Sign up\n </button>\n </p>\n )}\n </form>\n );\n}\n","import { useState, type FormEvent } from 'react';\nimport { useCedrosLogin } from '../../context/useCedrosLogin';\nimport { useEmailAuth } from '../../hooks/useEmailAuth';\nimport { useServerFeatures } from '../../hooks/useServerFeatures';\nimport { PasswordInput } from './PasswordInput';\nimport { LoadingSpinner } from '../shared/LoadingSpinner';\nimport { ErrorMessage } from '../shared/ErrorMessage';\nimport { sanitizeExternalUrl } from '../../utils/sanitization';\nimport type { PasswordValidation, AuthError } from '../../types';\n\nexport interface EmailRegisterFormProps {\n onSuccess?: () => void;\n onSwitchToLogin?: () => void;\n className?: string;\n /**\n * Access code value controlled by the parent (e.g. LoginForm).\n * When provided, the internal access code input is hidden and this value\n * is used directly. Allows LoginForm to show a single shared field.\n */\n accessCode?: string;\n}\n\n/** Values collected from the registration form (for callback) */\nexport interface RegistrationData {\n termsAccepted: boolean;\n emailOptIn: boolean;\n}\n\n/**\n * Email/password registration form\n */\nexport function EmailRegisterForm({\n onSuccess,\n onSwitchToLogin,\n className = '',\n accessCode: accessCodeProp,\n}: EmailRegisterFormProps) {\n const { config } = useCedrosLogin();\n const { register, isLoading, error, clearError } = useEmailAuth();\n const { features } = useServerFeatures();\n const [name, setName] = useState('');\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n const [confirmPassword, setConfirmPassword] = useState('');\n // Internal state used only when parent hasn't provided an accessCode prop\n const [accessCodeInternal, setAccessCodeInternal] = useState('');\n const accessCode = accessCodeProp ?? accessCodeInternal;\n const [passwordValidation, setPasswordValidation] = useState<PasswordValidation | null>(null);\n const [termsError, setTermsError] = useState<AuthError | null>(null);\n\n // Get form configuration\n const termsConfig = config.forms?.termsOfService;\n const emailOptInConfig = config.forms?.emailOptIn;\n\n const showTerms = termsConfig?.show ?? false;\n const termsRequired = termsConfig?.required ?? true;\n const termsDefaultChecked = termsConfig?.defaultChecked ?? false;\n const termsLabel = termsConfig?.label ?? 'I agree to the Terms of Service';\n const termsUrl = termsConfig?.url;\n const safeTermsUrl = sanitizeExternalUrl(termsUrl);\n\n const showEmailOptIn = emailOptInConfig?.show ?? false;\n const emailOptInDefaultChecked = emailOptInConfig?.defaultChecked ?? false;\n const emailOptInLabel = emailOptInConfig?.label ?? 'Send me updates and news';\n\n // Initialize checkbox states with defaults\n const [termsAccepted, setTermsAccepted] = useState(termsDefaultChecked);\n const [emailOptIn, setEmailOptIn] = useState(emailOptInDefaultChecked);\n\n const passwordsMatch = password === confirmPassword;\n const isPasswordValid = passwordValidation?.isValid ?? false;\n\n // Terms must be accepted if shown and required\n const termsValid = !showTerms || !termsRequired || termsAccepted;\n\n const accessCodeRequired = features?.signupAccessCodeRequired ?? false;\n\n const canSubmit =\n email &&\n password &&\n confirmPassword &&\n passwordsMatch &&\n isPasswordValid &&\n termsValid &&\n (!accessCodeRequired || accessCode.trim()) &&\n !isLoading;\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n\n // Clear any previous terms error\n setTermsError(null);\n\n // Validate terms if required\n if (showTerms && termsRequired && !termsAccepted) {\n setTermsError({\n code: 'VALIDATION_ERROR',\n message: 'You must agree to the Terms of Service to continue',\n });\n return;\n }\n\n if (!canSubmit) return;\n\n try {\n // Note: termsAccepted and emailOptIn values can be passed to the backend\n // via the register function if needed. For now, we call register with the\n // standard params, but the backend could be extended to accept these values.\n await register(\n email,\n password,\n name || undefined,\n undefined,\n accessCodeRequired ? accessCode.trim() || undefined : undefined\n );\n onSuccess?.();\n } catch {\n // Error is handled by the hook\n }\n };\n\n const combinedError = error || termsError;\n const combinedClearError = () => {\n clearError();\n setTermsError(null);\n };\n\n return (\n <form onSubmit={handleSubmit} className={`cedros-form ${className}`}>\n <div className=\"cedros-form-field\">\n <label htmlFor=\"name\" className=\"cedros-label\">\n Name <span className=\"cedros-optional\">(optional)</span>\n </label>\n <input\n id=\"name\"\n type=\"text\"\n className=\"cedros-input\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"Your name\"\n autoComplete=\"name\"\n disabled={isLoading}\n />\n </div>\n\n <div className=\"cedros-form-field\">\n <label htmlFor=\"register-email\" className=\"cedros-label\">\n Email\n </label>\n <input\n id=\"register-email\"\n type=\"email\"\n className=\"cedros-input\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n placeholder=\"you@example.com\"\n required\n aria-required=\"true\"\n autoComplete=\"email\"\n disabled={isLoading}\n />\n </div>\n\n <div className=\"cedros-form-field\">\n <PasswordInput\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n placeholder=\"Create a password\"\n required\n autoComplete=\"new-password\"\n disabled={isLoading}\n showStrengthMeter\n onValidationChange={setPasswordValidation}\n />\n </div>\n\n <div className=\"cedros-form-field\">\n <PasswordInput\n label=\"Confirm Password\"\n value={confirmPassword}\n onChange={(e) => setConfirmPassword(e.target.value)}\n placeholder=\"Confirm your password\"\n required\n autoComplete=\"new-password\"\n disabled={isLoading}\n aria-invalid={confirmPassword && !passwordsMatch ? 'true' : undefined}\n error={confirmPassword && !passwordsMatch ? 'Passwords do not match' : undefined}\n />\n </div>\n\n {/* Access code field — only shown when required AND parent hasn't provided the value */}\n {accessCodeRequired && accessCodeProp === undefined && (\n <div className=\"cedros-form-field\">\n <label htmlFor=\"register-access-code\" className=\"cedros-label\">\n Access Code\n </label>\n <input\n id=\"register-access-code\"\n type=\"text\"\n className=\"cedros-input\"\n value={accessCodeInternal}\n onChange={(e) => setAccessCodeInternal(e.target.value)}\n placeholder=\"Enter access code\"\n required\n aria-required=\"true\"\n disabled={isLoading}\n autoComplete=\"off\"\n />\n </div>\n )}\n\n {/* Terms of Service checkbox */}\n {showTerms && (\n <div className=\"cedros-form-field cedros-checkbox-field\">\n <label className=\"cedros-checkbox-label\">\n <input\n type=\"checkbox\"\n className=\"cedros-checkbox\"\n checked={termsAccepted}\n onChange={(e) => setTermsAccepted(e.target.checked)}\n disabled={isLoading}\n aria-required={termsRequired}\n />\n <span className=\"cedros-checkbox-text\">\n {safeTermsUrl ? (\n <>\n {termsLabel.replace('Terms of Service', '').trim() || 'I agree to the'}{' '}\n <a\n href={safeTermsUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"cedros-link\"\n >\n Terms of Service\n </a>\n </>\n ) : (\n termsLabel\n )}\n {termsRequired && <span className=\"cedros-required\">*</span>}\n </span>\n </label>\n </div>\n )}\n\n {/* Email opt-in checkbox */}\n {showEmailOptIn && (\n <div className=\"cedros-form-field cedros-checkbox-field\">\n <label className=\"cedros-checkbox-label\">\n <input\n type=\"checkbox\"\n className=\"cedros-checkbox\"\n checked={emailOptIn}\n onChange={(e) => setEmailOptIn(e.target.checked)}\n disabled={isLoading}\n />\n <span className=\"cedros-checkbox-text\">{emailOptInLabel}</span>\n </label>\n </div>\n )}\n\n <ErrorMessage error={combinedError} onDismiss={combinedClearError} />\n\n <button\n type=\"submit\"\n className=\"cedros-button cedros-button-primary cedros-button-md cedros-button-full\"\n disabled={!canSubmit}\n aria-busy={isLoading}\n >\n {isLoading ? (\n <>\n <LoadingSpinner size=\"sm\" announce label=\"Creating account\" />\n <span>Creating account...</span>\n </>\n ) : (\n 'Create account'\n )}\n </button>\n\n {onSwitchToLogin && (\n <p className=\"cedros-form-footer\">\n Already have an account?{' '}\n <button type=\"button\" className=\"cedros-link cedros-link-muted\" onClick={onSwitchToLogin}>\n Sign in\n </button>\n </p>\n )}\n </form>\n );\n}\n"],"names":["useRateLimiter","options","maxAttempts","windowMs","showCountdown","attemptsRef","useRef","hasAttempts","setHasAttempts","useState","setTick","bump","useCallback","value","cleanupAttemptsArray","now","timestamp","cleanupExpiredAttempts","current","getRemainingAttempts","getTimeUntilReset","resetTime","isAllowed","checkLimit","waitTime","waitSeconds","reset","useEffect","intervalId","isMfaRequiredResponse","response","useEmailAuth","config","_internal","useCedrosLogin","isLoading","setIsLoading","error","setError","resetRateLimit","apiClient","useMemo","ApiClient","callbacks","login","email","password","validateEmail","validationError","err","rateLimitError","data","authResponse","authError","handleApiError","register","name","referral","accessCode","effectiveReferral","clearError","PasswordInput","label","labelAction","showStrengthMeter","onValidationChange","className","onChange","props","showPassword","setShowPassword","validation","setValidation","inputId","useId","handleChange","e","newValue","newValidation","validatePassword","strengthColors","jsxs","jsx","useTotpVerify","state","setState","verifyTotp","mfaToken","code","isRecoveryCode","OTP_LENGTH","OtpInput","onComplete","disabled","autoFocus","inputRefs","localValue","setLocalValue","id","focusInput","index","updateValue","sanitized","digit","chars","handleKeyDown","handlePaste","digits","handleFocus","_","el","TotpVerify","onSuccess","onBack","setCode","useBackupCode","setUseBackupCode","backupCode","setBackupCode","handleVerify","verifyCode","codeToVerify","handleOtpComplete","toggleBackupCode","Fragment","LoadingSpinner","EmailLoginForm","onSwitchToRegister","onForgotPassword","setEmail","setPassword","setMfaToken","mfaEmail","setMfaEmail","handleSubmit","result","handleTotpSuccess","handleTotpBack","ErrorMessage","EmailRegisterForm","onSwitchToLogin","accessCodeProp","features","useServerFeatures","setName","confirmPassword","setConfirmPassword","accessCodeInternal","setAccessCodeInternal","passwordValidation","setPasswordValidation","termsError","setTermsError","termsConfig","emailOptInConfig","showTerms","termsRequired","termsDefaultChecked","termsLabel","termsUrl","safeTermsUrl","sanitizeExternalUrl","showEmailOptIn","emailOptInDefaultChecked","emailOptInLabel","termsAccepted","setTermsAccepted","emailOptIn","setEmailOptIn","passwordsMatch","isPasswordValid","termsValid","accessCodeRequired","canSubmit","combinedError","combinedClearError"],"mappings":"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"}