@authon/js 0.3.1 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -37,6 +37,8 @@ declare class Authon {
37
37
  private providers;
38
38
  private providerFlowModes;
39
39
  private initialized;
40
+ private captchaEnabled;
41
+ private turnstileSiteKey;
40
42
  constructor(publishableKey: string, config?: AuthonConfig);
41
43
  getProviders(): Promise<OAuthProviderType[]>;
42
44
  openSignIn(): Promise<void>;
@@ -44,9 +46,10 @@ declare class Authon {
44
46
  /** Update theme at runtime without destroying form state */
45
47
  setTheme(theme: 'light' | 'dark' | 'auto'): void;
46
48
  signInWithOAuth(provider: OAuthProviderType, options?: OAuthSignInOptions): Promise<void>;
47
- signInWithEmail(email: string, password: string): Promise<AuthonUser>;
49
+ signInWithEmail(email: string, password: string, turnstileToken?: string): Promise<AuthonUser>;
48
50
  signUpWithEmail(email: string, password: string, meta?: {
49
51
  displayName?: string;
52
+ turnstileToken?: string;
50
53
  }): Promise<AuthonUser>;
51
54
  signOut(): Promise<void>;
52
55
  getUser(): AuthonUser | null;
@@ -108,6 +111,7 @@ declare class Authon {
108
111
  leave: (orgId: string) => Promise<void>;
109
112
  };
110
113
  destroy(): void;
114
+ private loadTurnstileScript;
111
115
  private emit;
112
116
  private ensureInitialized;
113
117
  private getModal;
package/dist/index.d.ts CHANGED
@@ -37,6 +37,8 @@ declare class Authon {
37
37
  private providers;
38
38
  private providerFlowModes;
39
39
  private initialized;
40
+ private captchaEnabled;
41
+ private turnstileSiteKey;
40
42
  constructor(publishableKey: string, config?: AuthonConfig);
41
43
  getProviders(): Promise<OAuthProviderType[]>;
42
44
  openSignIn(): Promise<void>;
@@ -44,9 +46,10 @@ declare class Authon {
44
46
  /** Update theme at runtime without destroying form state */
45
47
  setTheme(theme: 'light' | 'dark' | 'auto'): void;
46
48
  signInWithOAuth(provider: OAuthProviderType, options?: OAuthSignInOptions): Promise<void>;
47
- signInWithEmail(email: string, password: string): Promise<AuthonUser>;
49
+ signInWithEmail(email: string, password: string, turnstileToken?: string): Promise<AuthonUser>;
48
50
  signUpWithEmail(email: string, password: string, meta?: {
49
51
  displayName?: string;
52
+ turnstileToken?: string;
50
53
  }): Promise<AuthonUser>;
51
54
  signOut(): Promise<void>;
52
55
  getUser(): AuthonUser | null;
@@ -108,6 +111,7 @@ declare class Authon {
108
111
  leave: (orgId: string) => Promise<void>;
109
112
  };
110
113
  destroy(): void;
114
+ private loadTurnstileScript;
111
115
  private emit;
112
116
  private ensureInitialized;
113
117
  private getModal;
package/dist/index.js CHANGED
@@ -120,10 +120,16 @@ var ModalRenderer = class {
120
120
  selectedWallet = "";
121
121
  overlayEmail = "";
122
122
  overlayError = "";
123
+ // Turnstile CAPTCHA
124
+ captchaSiteKey = "";
125
+ turnstileWidgetId = null;
126
+ turnstileToken = "";
127
+ turnstileWrapper = null;
123
128
  constructor(options) {
124
129
  this.mode = options.mode;
125
130
  this.theme = options.theme || "auto";
126
131
  this.branding = { ...DEFAULT_BRANDING, ...options.branding };
132
+ this.captchaSiteKey = options.captchaSiteKey || "";
127
133
  this.onProviderClick = options.onProviderClick;
128
134
  this.onEmailSubmit = options.onEmailSubmit;
129
135
  this.onClose = options.onClose;
@@ -161,6 +167,15 @@ var ModalRenderer = class {
161
167
  document.removeEventListener("keydown", this.escHandler);
162
168
  this.escHandler = null;
163
169
  }
170
+ if (this.turnstileWidgetId !== null) {
171
+ window.turnstile?.remove(this.turnstileWidgetId);
172
+ this.turnstileWidgetId = null;
173
+ this.turnstileToken = "";
174
+ }
175
+ if (this.turnstileWrapper) {
176
+ this.turnstileWrapper.remove();
177
+ this.turnstileWrapper = null;
178
+ }
164
179
  if (this.hostElement) {
165
180
  this.hostElement.remove();
166
181
  this.hostElement = null;
@@ -171,6 +186,15 @@ var ModalRenderer = class {
171
186
  }
172
187
  this.currentOverlay = "none";
173
188
  }
189
+ getTurnstileToken() {
190
+ return this.turnstileToken;
191
+ }
192
+ resetTurnstile() {
193
+ if (this.turnstileWidgetId !== null) {
194
+ window.turnstile?.reset(this.turnstileWidgetId);
195
+ this.turnstileToken = "";
196
+ }
197
+ }
174
198
  /** Update theme at runtime without destroying form state */
175
199
  setTheme(theme) {
176
200
  this.theme = theme;
@@ -369,10 +393,12 @@ var ModalRenderer = class {
369
393
  </button>`;
370
394
  }).join("") : "";
371
395
  const divider = showProviders && b.showDivider !== false && b.showEmailPassword !== false ? `<div class="divider"><span>or</span></div>` : "";
396
+ const captchaContainer = this.captchaSiteKey ? '<div id="turnstile-container" style="display:flex;justify-content:center;margin:4px 0"></div>' : "";
372
397
  const emailForm = b.showEmailPassword !== false ? `<form class="email-form" id="email-form">
373
398
  <input type="email" placeholder="Email address" name="email" required class="input" autocomplete="email" />
374
399
  <input type="password" placeholder="Password" name="password" required class="input" autocomplete="${isSignUp ? "new-password" : "current-password"}" />
375
400
  ${isSignUp ? '<p class="password-hint">Must contain uppercase, lowercase, and a number (min 8 chars)</p>' : ""}
401
+ ${captchaContainer}
376
402
  <button type="submit" class="submit-btn">${isSignUp ? "Sign up" : "Sign in"}</button>
377
403
  </form>` : "";
378
404
  const hasMethodAbove = showProviders && this.enabledProviders.length > 0 || b.showEmailPassword !== false;
@@ -429,6 +455,58 @@ var ModalRenderer = class {
429
455
  ${b.showSecuredBy !== false ? `<div class="secured-by">Secured by <a href="https://authon.dev" target="_blank" rel="noopener noreferrer" class="secured-link">Authon</a></div>` : ""}
430
456
  `;
431
457
  }
458
+ renderTurnstile() {
459
+ if (!this.captchaSiteKey || !this.shadowRoot) return;
460
+ const anchor = this.shadowRoot.getElementById("turnstile-container");
461
+ if (!anchor) return;
462
+ const w = window;
463
+ const positionWrapper = () => {
464
+ if (!this.turnstileWrapper || !anchor.isConnected) return;
465
+ const rect = anchor.getBoundingClientRect();
466
+ Object.assign(this.turnstileWrapper.style, {
467
+ position: "fixed",
468
+ top: `${rect.top}px`,
469
+ left: `${rect.left}px`,
470
+ width: `${rect.width}px`,
471
+ zIndex: "2147483647",
472
+ display: "flex",
473
+ justifyContent: "center"
474
+ });
475
+ };
476
+ const tryRender = () => {
477
+ if (!w.turnstile || !anchor.isConnected) return;
478
+ this.turnstileWrapper = document.createElement("div");
479
+ document.body.appendChild(this.turnstileWrapper);
480
+ positionWrapper();
481
+ this.turnstileWidgetId = w.turnstile.render(this.turnstileWrapper, {
482
+ sitekey: this.captchaSiteKey,
483
+ callback: (token) => {
484
+ this.turnstileToken = token;
485
+ },
486
+ "expired-callback": () => {
487
+ this.turnstileToken = "";
488
+ },
489
+ "error-callback": () => {
490
+ this.turnstileToken = "";
491
+ },
492
+ theme: this.isDark() ? "dark" : "light",
493
+ size: "flexible"
494
+ });
495
+ window.addEventListener("scroll", positionWrapper, { passive: true });
496
+ window.addEventListener("resize", positionWrapper, { passive: true });
497
+ };
498
+ if (w.turnstile) {
499
+ tryRender();
500
+ } else {
501
+ const interval = setInterval(() => {
502
+ if (w.turnstile) {
503
+ clearInterval(interval);
504
+ tryRender();
505
+ }
506
+ }, 200);
507
+ setTimeout(() => clearInterval(interval), 1e4);
508
+ }
509
+ }
432
510
  isDark() {
433
511
  if (this.theme === "dark") return true;
434
512
  if (this.theme === "light") return false;
@@ -1011,6 +1089,7 @@ var ModalRenderer = class {
1011
1089
  );
1012
1090
  });
1013
1091
  }
1092
+ this.renderTurnstile();
1014
1093
  const backBtn = this.shadowRoot.getElementById("back-btn");
1015
1094
  if (backBtn) {
1016
1095
  backBtn.addEventListener("click", () => {
@@ -1538,6 +1617,8 @@ var Authon = class {
1538
1617
  providers = [];
1539
1618
  providerFlowModes = {};
1540
1619
  initialized = false;
1620
+ captchaEnabled = false;
1621
+ turnstileSiteKey = "";
1541
1622
  constructor(publishableKey, config) {
1542
1623
  this.publishableKey = publishableKey;
1543
1624
  this.config = {
@@ -1572,10 +1653,12 @@ var Authon = class {
1572
1653
  await this.ensureInitialized();
1573
1654
  await this.startOAuthFlow(provider, options);
1574
1655
  }
1575
- async signInWithEmail(email, password) {
1656
+ async signInWithEmail(email, password, turnstileToken) {
1657
+ const body = { email, password };
1658
+ if (turnstileToken) body.turnstileToken = turnstileToken;
1576
1659
  const res = await this.apiPost(
1577
1660
  "/v1/auth/signin",
1578
- { email, password }
1661
+ body
1579
1662
  );
1580
1663
  if (res.mfaRequired && res.mfaToken) {
1581
1664
  this.emit("mfaRequired", res.mfaToken);
@@ -1869,6 +1952,14 @@ var Authon = class {
1869
1952
  this.listeners.clear();
1870
1953
  }
1871
1954
  // ── Internal ──
1955
+ loadTurnstileScript() {
1956
+ if (typeof document === "undefined") return;
1957
+ if (document.querySelector('script[src*="challenges.cloudflare.com/turnstile"]')) return;
1958
+ const script = document.createElement("script");
1959
+ script.src = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
1960
+ script.async = true;
1961
+ document.head.appendChild(script);
1962
+ }
1872
1963
  emit(event, ...args) {
1873
1964
  this.listeners.get(event)?.forEach((fn) => fn(...args));
1874
1965
  }
@@ -1880,6 +1971,11 @@ var Authon = class {
1880
1971
  this.apiGet("/v1/auth/providers")
1881
1972
  ]);
1882
1973
  this.branding = { ...branding, ...this.config.appearance };
1974
+ this.captchaEnabled = !!branding.captchaEnabled;
1975
+ this.turnstileSiteKey = branding.turnstileSiteKey || "";
1976
+ if (this.captchaEnabled && this.turnstileSiteKey) {
1977
+ this.loadTurnstileScript();
1978
+ }
1883
1979
  this.providers = providersRes.providers;
1884
1980
  this.providerFlowModes = {};
1885
1981
  for (const provider of this.providers) {
@@ -1900,11 +1996,14 @@ var Authon = class {
1900
1996
  theme: this.config.theme,
1901
1997
  containerId: this.config.containerId,
1902
1998
  branding: this.branding || void 0,
1999
+ captchaSiteKey: this.captchaEnabled ? this.turnstileSiteKey : void 0,
1903
2000
  onProviderClick: (provider) => this.startOAuthFlow(provider),
1904
2001
  onEmailSubmit: (email, password, isSignUp) => {
1905
2002
  this.modal?.clearError();
1906
- const promise = isSignUp ? this.signUpWithEmail(email, password) : this.signInWithEmail(email, password);
2003
+ const turnstileToken = this.modal?.getTurnstileToken?.() || void 0;
2004
+ const promise = isSignUp ? this.signUpWithEmail(email, password, { turnstileToken }) : this.signInWithEmail(email, password, turnstileToken);
1907
2005
  promise.then(() => this.modal?.close()).catch((err) => {
2006
+ this.modal?.resetTurnstile?.();
1908
2007
  const msg = err instanceof Error ? err.message : String(err);
1909
2008
  this.modal?.showError(msg || "Authentication failed");
1910
2009
  this.emit("error", err instanceof Error ? err : new Error(msg));