@authon/js 0.3.1 → 0.3.2

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.cjs CHANGED
@@ -149,10 +149,15 @@ var ModalRenderer = class {
149
149
  selectedWallet = "";
150
150
  overlayEmail = "";
151
151
  overlayError = "";
152
+ // Turnstile CAPTCHA
153
+ captchaSiteKey = "";
154
+ turnstileWidgetId = null;
155
+ turnstileToken = "";
152
156
  constructor(options) {
153
157
  this.mode = options.mode;
154
158
  this.theme = options.theme || "auto";
155
159
  this.branding = { ...DEFAULT_BRANDING, ...options.branding };
160
+ this.captchaSiteKey = options.captchaSiteKey || "";
156
161
  this.onProviderClick = options.onProviderClick;
157
162
  this.onEmailSubmit = options.onEmailSubmit;
158
163
  this.onClose = options.onClose;
@@ -190,6 +195,11 @@ var ModalRenderer = class {
190
195
  document.removeEventListener("keydown", this.escHandler);
191
196
  this.escHandler = null;
192
197
  }
198
+ if (this.turnstileWidgetId !== null) {
199
+ window.turnstile?.remove(this.turnstileWidgetId);
200
+ this.turnstileWidgetId = null;
201
+ this.turnstileToken = "";
202
+ }
193
203
  if (this.hostElement) {
194
204
  this.hostElement.remove();
195
205
  this.hostElement = null;
@@ -200,6 +210,15 @@ var ModalRenderer = class {
200
210
  }
201
211
  this.currentOverlay = "none";
202
212
  }
213
+ getTurnstileToken() {
214
+ return this.turnstileToken;
215
+ }
216
+ resetTurnstile() {
217
+ if (this.turnstileWidgetId !== null) {
218
+ window.turnstile?.reset(this.turnstileWidgetId);
219
+ this.turnstileToken = "";
220
+ }
221
+ }
203
222
  /** Update theme at runtime without destroying form state */
204
223
  setTheme(theme) {
205
224
  this.theme = theme;
@@ -398,10 +417,12 @@ var ModalRenderer = class {
398
417
  </button>`;
399
418
  }).join("") : "";
400
419
  const divider = showProviders && b.showDivider !== false && b.showEmailPassword !== false ? `<div class="divider"><span>or</span></div>` : "";
420
+ const captchaContainer = this.captchaSiteKey ? '<div id="turnstile-container" style="display:flex;justify-content:center;margin:4px 0"></div>' : "";
401
421
  const emailForm = b.showEmailPassword !== false ? `<form class="email-form" id="email-form">
402
422
  <input type="email" placeholder="Email address" name="email" required class="input" autocomplete="email" />
403
423
  <input type="password" placeholder="Password" name="password" required class="input" autocomplete="${isSignUp ? "new-password" : "current-password"}" />
404
424
  ${isSignUp ? '<p class="password-hint">Must contain uppercase, lowercase, and a number (min 8 chars)</p>' : ""}
425
+ ${captchaContainer}
405
426
  <button type="submit" class="submit-btn">${isSignUp ? "Sign up" : "Sign in"}</button>
406
427
  </form>` : "";
407
428
  const hasMethodAbove = showProviders && this.enabledProviders.length > 0 || b.showEmailPassword !== false;
@@ -458,6 +479,45 @@ var ModalRenderer = class {
458
479
  ${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>` : ""}
459
480
  `;
460
481
  }
482
+ renderTurnstile() {
483
+ if (!this.captchaSiteKey || !this.shadowRoot) return;
484
+ const container = this.shadowRoot.getElementById("turnstile-container");
485
+ if (!container) return;
486
+ const w = window;
487
+ const tryRender = () => {
488
+ if (!w.turnstile || !container.isConnected) return;
489
+ const wrapper = document.createElement("div");
490
+ wrapper.style.display = "flex";
491
+ wrapper.style.justifyContent = "center";
492
+ document.body.appendChild(wrapper);
493
+ this.turnstileWidgetId = w.turnstile.render(wrapper, {
494
+ sitekey: this.captchaSiteKey,
495
+ callback: (token) => {
496
+ this.turnstileToken = token;
497
+ },
498
+ "expired-callback": () => {
499
+ this.turnstileToken = "";
500
+ },
501
+ "error-callback": () => {
502
+ this.turnstileToken = "";
503
+ },
504
+ theme: this.isDark() ? "dark" : "light",
505
+ size: "flexible"
506
+ });
507
+ container.appendChild(wrapper);
508
+ };
509
+ if (w.turnstile) {
510
+ tryRender();
511
+ } else {
512
+ const interval = setInterval(() => {
513
+ if (w.turnstile) {
514
+ clearInterval(interval);
515
+ tryRender();
516
+ }
517
+ }, 200);
518
+ setTimeout(() => clearInterval(interval), 1e4);
519
+ }
520
+ }
461
521
  isDark() {
462
522
  if (this.theme === "dark") return true;
463
523
  if (this.theme === "light") return false;
@@ -1040,6 +1100,7 @@ var ModalRenderer = class {
1040
1100
  );
1041
1101
  });
1042
1102
  }
1103
+ this.renderTurnstile();
1043
1104
  const backBtn = this.shadowRoot.getElementById("back-btn");
1044
1105
  if (backBtn) {
1045
1106
  backBtn.addEventListener("click", () => {
@@ -1567,6 +1628,8 @@ var Authon = class {
1567
1628
  providers = [];
1568
1629
  providerFlowModes = {};
1569
1630
  initialized = false;
1631
+ captchaEnabled = false;
1632
+ turnstileSiteKey = "";
1570
1633
  constructor(publishableKey, config) {
1571
1634
  this.publishableKey = publishableKey;
1572
1635
  this.config = {
@@ -1601,10 +1664,12 @@ var Authon = class {
1601
1664
  await this.ensureInitialized();
1602
1665
  await this.startOAuthFlow(provider, options);
1603
1666
  }
1604
- async signInWithEmail(email, password) {
1667
+ async signInWithEmail(email, password, turnstileToken) {
1668
+ const body = { email, password };
1669
+ if (turnstileToken) body.turnstileToken = turnstileToken;
1605
1670
  const res = await this.apiPost(
1606
1671
  "/v1/auth/signin",
1607
- { email, password }
1672
+ body
1608
1673
  );
1609
1674
  if (res.mfaRequired && res.mfaToken) {
1610
1675
  this.emit("mfaRequired", res.mfaToken);
@@ -1898,6 +1963,14 @@ var Authon = class {
1898
1963
  this.listeners.clear();
1899
1964
  }
1900
1965
  // ── Internal ──
1966
+ loadTurnstileScript() {
1967
+ if (typeof document === "undefined") return;
1968
+ if (document.querySelector('script[src*="challenges.cloudflare.com/turnstile"]')) return;
1969
+ const script = document.createElement("script");
1970
+ script.src = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
1971
+ script.async = true;
1972
+ document.head.appendChild(script);
1973
+ }
1901
1974
  emit(event, ...args) {
1902
1975
  this.listeners.get(event)?.forEach((fn) => fn(...args));
1903
1976
  }
@@ -1909,6 +1982,11 @@ var Authon = class {
1909
1982
  this.apiGet("/v1/auth/providers")
1910
1983
  ]);
1911
1984
  this.branding = { ...branding, ...this.config.appearance };
1985
+ this.captchaEnabled = !!branding.captchaEnabled;
1986
+ this.turnstileSiteKey = branding.turnstileSiteKey || "";
1987
+ if (this.captchaEnabled && this.turnstileSiteKey) {
1988
+ this.loadTurnstileScript();
1989
+ }
1912
1990
  this.providers = providersRes.providers;
1913
1991
  this.providerFlowModes = {};
1914
1992
  for (const provider of this.providers) {
@@ -1929,11 +2007,14 @@ var Authon = class {
1929
2007
  theme: this.config.theme,
1930
2008
  containerId: this.config.containerId,
1931
2009
  branding: this.branding || void 0,
2010
+ captchaSiteKey: this.captchaEnabled ? this.turnstileSiteKey : void 0,
1932
2011
  onProviderClick: (provider) => this.startOAuthFlow(provider),
1933
2012
  onEmailSubmit: (email, password, isSignUp) => {
1934
2013
  this.modal?.clearError();
1935
- const promise = isSignUp ? this.signUpWithEmail(email, password) : this.signInWithEmail(email, password);
2014
+ const turnstileToken = this.modal?.getTurnstileToken?.() || void 0;
2015
+ const promise = isSignUp ? this.signUpWithEmail(email, password, { turnstileToken }) : this.signInWithEmail(email, password, turnstileToken);
1936
2016
  promise.then(() => this.modal?.close()).catch((err) => {
2017
+ this.modal?.resetTurnstile?.();
1937
2018
  const msg = err instanceof Error ? err.message : String(err);
1938
2019
  this.modal?.showError(msg || "Authentication failed");
1939
2020
  this.emit("error", err instanceof Error ? err : new Error(msg));