@authon/js 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -131,6 +131,25 @@ var ModalRenderer = class {
131
131
  this.containerElement.innerHTML = "";
132
132
  }
133
133
  }
134
+ showLoading() {
135
+ if (!this.shadowRoot) return;
136
+ this.hideLoading();
137
+ const overlay = document.createElement("div");
138
+ overlay.id = "authon-loading-overlay";
139
+ overlay.innerHTML = `
140
+ <div class="loading-spinner">
141
+ <div class="loading-ring"></div>
142
+ <div class="loading-ring"></div>
143
+ <div class="loading-ring"></div>
144
+ </div>
145
+ <div class="loading-text">Signing in<span class="loading-dots"><span></span><span></span><span></span></span></div>
146
+ `;
147
+ this.shadowRoot.querySelector(".modal-container")?.appendChild(overlay);
148
+ }
149
+ hideLoading() {
150
+ if (!this.shadowRoot) return;
151
+ this.shadowRoot.getElementById("authon-loading-overlay")?.remove();
152
+ }
134
153
  render(view) {
135
154
  const host = document.createElement("div");
136
155
  host.setAttribute("data-authon-modal", "");
@@ -234,6 +253,7 @@ var ModalRenderer = class {
234
253
  border-radius: var(--authon-radius);
235
254
  padding: 32px;
236
255
  width: 400px; max-width: 100%;
256
+ position: ${this.mode === "popup" ? "fixed" : "relative"};
237
257
  ${this.mode === "popup" ? `box-shadow: 0 25px 50px -12px rgba(0,0,0,${dark ? "0.5" : "0.25"}); animation: slideIn 0.3s ease;` : ""}
238
258
  }
239
259
  .logo { display: block; margin: 0 auto 16px; max-height: 48px; }
@@ -288,6 +308,39 @@ var ModalRenderer = class {
288
308
  font-size: 11px; color: var(--authon-dim);
289
309
  }
290
310
  .secured-brand { font-weight: 600; color: var(--authon-muted); }
311
+ /* Loading overlay */
312
+ #authon-loading-overlay {
313
+ position: absolute; inset: 0; z-index: 10;
314
+ background: ${dark ? "rgba(15,23,42,0.92)" : "rgba(255,255,255,0.92)"};
315
+ backdrop-filter: blur(2px);
316
+ border-radius: var(--authon-radius);
317
+ display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 20px;
318
+ animation: fadeIn 0.15s ease;
319
+ }
320
+ .loading-spinner { position: relative; width: 48px; height: 48px; }
321
+ .loading-ring {
322
+ position: absolute; inset: 0;
323
+ border: 2.5px solid transparent; border-top-color: var(--authon-primary-start);
324
+ border-radius: 50%; animation: spin 1s cubic-bezier(.55,.15,.45,.85) infinite;
325
+ }
326
+ .loading-ring:nth-child(2) {
327
+ inset: 5px; border-top-color: transparent; border-right-color: var(--authon-primary-end);
328
+ animation-duration: 1.2s; animation-direction: reverse; opacity: .7;
329
+ }
330
+ .loading-ring:nth-child(3) {
331
+ inset: 10px; border-top-color: transparent; border-bottom-color: var(--authon-primary-start);
332
+ animation-duration: .8s; opacity: .4;
333
+ }
334
+ .loading-text { font-size: 14px; font-weight: 500; color: var(--authon-muted); }
335
+ .loading-dots { display: inline-flex; gap: 2px; margin-left: 2px; }
336
+ .loading-dots span {
337
+ width: 3px; height: 3px; border-radius: 50%;
338
+ background: var(--authon-muted); animation: blink 1.4s infinite both;
339
+ }
340
+ .loading-dots span:nth-child(2) { animation-delay: .2s; }
341
+ .loading-dots span:nth-child(3) { animation-delay: .4s; }
342
+ @keyframes spin { to { transform: rotate(360deg); } }
343
+ @keyframes blink { 0%,80%,100% { opacity: .2; } 40% { opacity: 1; } }
291
344
  @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
292
345
  @keyframes slideIn { from { opacity: 0; transform: translate(-50%, -48%); } to { opacity: 1; transform: translate(-50%, -50%); } }
293
346
  ${b.customCss || ""}
@@ -339,6 +392,7 @@ var ModalRenderer = class {
339
392
  // src/session.ts
340
393
  var SessionManager = class {
341
394
  accessToken = null;
395
+ refreshToken = null;
342
396
  user = null;
343
397
  refreshTimer = null;
344
398
  apiUrl;
@@ -355,11 +409,15 @@ var SessionManager = class {
355
409
  }
356
410
  setSession(tokens) {
357
411
  this.accessToken = tokens.accessToken;
412
+ this.refreshToken = tokens.refreshToken;
358
413
  this.user = tokens.user;
359
- this.scheduleRefresh(tokens.expiresIn);
414
+ if (tokens.expiresIn && tokens.expiresIn > 0) {
415
+ this.scheduleRefresh(tokens.expiresIn);
416
+ }
360
417
  }
361
418
  clearSession() {
362
419
  this.accessToken = null;
420
+ this.refreshToken = null;
363
421
  this.user = null;
364
422
  if (this.refreshTimer) {
365
423
  clearTimeout(this.refreshTimer);
@@ -368,10 +426,14 @@ var SessionManager = class {
368
426
  }
369
427
  scheduleRefresh(expiresIn) {
370
428
  if (this.refreshTimer) clearTimeout(this.refreshTimer);
371
- const refreshIn = Math.max((expiresIn - 60) * 1e3, 5e3);
429
+ const refreshIn = Math.max((expiresIn - 60) * 1e3, 3e4);
372
430
  this.refreshTimer = setTimeout(() => this.refresh(), refreshIn);
373
431
  }
374
432
  async refresh() {
433
+ if (!this.refreshToken) {
434
+ this.clearSession();
435
+ return null;
436
+ }
375
437
  try {
376
438
  const res = await fetch(`${this.apiUrl}/v1/auth/token/refresh`, {
377
439
  method: "POST",
@@ -379,7 +441,8 @@ var SessionManager = class {
379
441
  "Content-Type": "application/json",
380
442
  "x-api-key": this.publishableKey
381
443
  },
382
- credentials: "include"
444
+ credentials: "include",
445
+ body: JSON.stringify({ refreshToken: this.refreshToken })
383
446
  });
384
447
  if (!res.ok) {
385
448
  this.clearSession();
@@ -532,6 +595,7 @@ var Authon = class {
532
595
  const { url } = await this.apiGet(
533
596
  `/v1/auth/oauth/${provider}/url?redirectUri=${encodeURIComponent(redirectUri)}`
534
597
  );
598
+ this.modal?.showLoading();
535
599
  const width = 500;
536
600
  const height = 700;
537
601
  const left = window.screenX + (window.outerWidth - width) / 2;
@@ -541,27 +605,42 @@ var Authon = class {
541
605
  "authon-oauth",
542
606
  `width=${width},height=${height},left=${left},top=${top},toolbar=no,menubar=no`
543
607
  );
544
- const handler = async (e) => {
545
- if (e.data?.type === "authon-oauth-callback") {
546
- window.removeEventListener("message", handler);
547
- popup?.close();
548
- try {
549
- const tokens = await this.apiPost("/v1/auth/oauth/callback", {
550
- code: e.data.code,
551
- state: e.data.state,
552
- codeVerifier: e.data.codeVerifier,
553
- provider
554
- });
555
- this.session.setSession(tokens);
556
- this.modal?.close();
557
- this.emit("signedIn", tokens.user);
558
- } catch (err) {
559
- this.emit("error", err instanceof Error ? err : new Error(String(err)));
608
+ let callbackReceived = false;
609
+ const pollTimer = setInterval(() => {
610
+ if (callbackReceived) return;
611
+ try {
612
+ if (!popup || popup.closed) {
613
+ clearInterval(pollTimer);
614
+ if (!callbackReceived) {
615
+ window.removeEventListener("message", handler);
616
+ this.modal?.hideLoading();
617
+ }
560
618
  }
619
+ } catch {
620
+ }
621
+ }, 500);
622
+ const handler = async (e) => {
623
+ if (e.data?.type !== "authon-oauth-callback") return;
624
+ callbackReceived = true;
625
+ window.removeEventListener("message", handler);
626
+ clearInterval(pollTimer);
627
+ try {
628
+ const tokens = await this.apiPost("/v1/auth/oauth/callback", {
629
+ code: e.data.code,
630
+ state: e.data.state,
631
+ provider
632
+ });
633
+ this.session.setSession(tokens);
634
+ this.modal?.close();
635
+ this.emit("signedIn", tokens.user);
636
+ } catch (err) {
637
+ this.modal?.hideLoading();
638
+ this.emit("error", err instanceof Error ? err : new Error(String(err)));
561
639
  }
562
640
  };
563
641
  window.addEventListener("message", handler);
564
642
  } catch (err) {
643
+ this.modal?.hideLoading();
565
644
  this.emit("error", err instanceof Error ? err : new Error(String(err)));
566
645
  }
567
646
  }
package/dist/index.mjs CHANGED
@@ -104,6 +104,25 @@ var ModalRenderer = class {
104
104
  this.containerElement.innerHTML = "";
105
105
  }
106
106
  }
107
+ showLoading() {
108
+ if (!this.shadowRoot) return;
109
+ this.hideLoading();
110
+ const overlay = document.createElement("div");
111
+ overlay.id = "authon-loading-overlay";
112
+ overlay.innerHTML = `
113
+ <div class="loading-spinner">
114
+ <div class="loading-ring"></div>
115
+ <div class="loading-ring"></div>
116
+ <div class="loading-ring"></div>
117
+ </div>
118
+ <div class="loading-text">Signing in<span class="loading-dots"><span></span><span></span><span></span></span></div>
119
+ `;
120
+ this.shadowRoot.querySelector(".modal-container")?.appendChild(overlay);
121
+ }
122
+ hideLoading() {
123
+ if (!this.shadowRoot) return;
124
+ this.shadowRoot.getElementById("authon-loading-overlay")?.remove();
125
+ }
107
126
  render(view) {
108
127
  const host = document.createElement("div");
109
128
  host.setAttribute("data-authon-modal", "");
@@ -207,6 +226,7 @@ var ModalRenderer = class {
207
226
  border-radius: var(--authon-radius);
208
227
  padding: 32px;
209
228
  width: 400px; max-width: 100%;
229
+ position: ${this.mode === "popup" ? "fixed" : "relative"};
210
230
  ${this.mode === "popup" ? `box-shadow: 0 25px 50px -12px rgba(0,0,0,${dark ? "0.5" : "0.25"}); animation: slideIn 0.3s ease;` : ""}
211
231
  }
212
232
  .logo { display: block; margin: 0 auto 16px; max-height: 48px; }
@@ -261,6 +281,39 @@ var ModalRenderer = class {
261
281
  font-size: 11px; color: var(--authon-dim);
262
282
  }
263
283
  .secured-brand { font-weight: 600; color: var(--authon-muted); }
284
+ /* Loading overlay */
285
+ #authon-loading-overlay {
286
+ position: absolute; inset: 0; z-index: 10;
287
+ background: ${dark ? "rgba(15,23,42,0.92)" : "rgba(255,255,255,0.92)"};
288
+ backdrop-filter: blur(2px);
289
+ border-radius: var(--authon-radius);
290
+ display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 20px;
291
+ animation: fadeIn 0.15s ease;
292
+ }
293
+ .loading-spinner { position: relative; width: 48px; height: 48px; }
294
+ .loading-ring {
295
+ position: absolute; inset: 0;
296
+ border: 2.5px solid transparent; border-top-color: var(--authon-primary-start);
297
+ border-radius: 50%; animation: spin 1s cubic-bezier(.55,.15,.45,.85) infinite;
298
+ }
299
+ .loading-ring:nth-child(2) {
300
+ inset: 5px; border-top-color: transparent; border-right-color: var(--authon-primary-end);
301
+ animation-duration: 1.2s; animation-direction: reverse; opacity: .7;
302
+ }
303
+ .loading-ring:nth-child(3) {
304
+ inset: 10px; border-top-color: transparent; border-bottom-color: var(--authon-primary-start);
305
+ animation-duration: .8s; opacity: .4;
306
+ }
307
+ .loading-text { font-size: 14px; font-weight: 500; color: var(--authon-muted); }
308
+ .loading-dots { display: inline-flex; gap: 2px; margin-left: 2px; }
309
+ .loading-dots span {
310
+ width: 3px; height: 3px; border-radius: 50%;
311
+ background: var(--authon-muted); animation: blink 1.4s infinite both;
312
+ }
313
+ .loading-dots span:nth-child(2) { animation-delay: .2s; }
314
+ .loading-dots span:nth-child(3) { animation-delay: .4s; }
315
+ @keyframes spin { to { transform: rotate(360deg); } }
316
+ @keyframes blink { 0%,80%,100% { opacity: .2; } 40% { opacity: 1; } }
264
317
  @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
265
318
  @keyframes slideIn { from { opacity: 0; transform: translate(-50%, -48%); } to { opacity: 1; transform: translate(-50%, -50%); } }
266
319
  ${b.customCss || ""}
@@ -312,6 +365,7 @@ var ModalRenderer = class {
312
365
  // src/session.ts
313
366
  var SessionManager = class {
314
367
  accessToken = null;
368
+ refreshToken = null;
315
369
  user = null;
316
370
  refreshTimer = null;
317
371
  apiUrl;
@@ -328,11 +382,15 @@ var SessionManager = class {
328
382
  }
329
383
  setSession(tokens) {
330
384
  this.accessToken = tokens.accessToken;
385
+ this.refreshToken = tokens.refreshToken;
331
386
  this.user = tokens.user;
332
- this.scheduleRefresh(tokens.expiresIn);
387
+ if (tokens.expiresIn && tokens.expiresIn > 0) {
388
+ this.scheduleRefresh(tokens.expiresIn);
389
+ }
333
390
  }
334
391
  clearSession() {
335
392
  this.accessToken = null;
393
+ this.refreshToken = null;
336
394
  this.user = null;
337
395
  if (this.refreshTimer) {
338
396
  clearTimeout(this.refreshTimer);
@@ -341,10 +399,14 @@ var SessionManager = class {
341
399
  }
342
400
  scheduleRefresh(expiresIn) {
343
401
  if (this.refreshTimer) clearTimeout(this.refreshTimer);
344
- const refreshIn = Math.max((expiresIn - 60) * 1e3, 5e3);
402
+ const refreshIn = Math.max((expiresIn - 60) * 1e3, 3e4);
345
403
  this.refreshTimer = setTimeout(() => this.refresh(), refreshIn);
346
404
  }
347
405
  async refresh() {
406
+ if (!this.refreshToken) {
407
+ this.clearSession();
408
+ return null;
409
+ }
348
410
  try {
349
411
  const res = await fetch(`${this.apiUrl}/v1/auth/token/refresh`, {
350
412
  method: "POST",
@@ -352,7 +414,8 @@ var SessionManager = class {
352
414
  "Content-Type": "application/json",
353
415
  "x-api-key": this.publishableKey
354
416
  },
355
- credentials: "include"
417
+ credentials: "include",
418
+ body: JSON.stringify({ refreshToken: this.refreshToken })
356
419
  });
357
420
  if (!res.ok) {
358
421
  this.clearSession();
@@ -505,6 +568,7 @@ var Authon = class {
505
568
  const { url } = await this.apiGet(
506
569
  `/v1/auth/oauth/${provider}/url?redirectUri=${encodeURIComponent(redirectUri)}`
507
570
  );
571
+ this.modal?.showLoading();
508
572
  const width = 500;
509
573
  const height = 700;
510
574
  const left = window.screenX + (window.outerWidth - width) / 2;
@@ -514,27 +578,42 @@ var Authon = class {
514
578
  "authon-oauth",
515
579
  `width=${width},height=${height},left=${left},top=${top},toolbar=no,menubar=no`
516
580
  );
517
- const handler = async (e) => {
518
- if (e.data?.type === "authon-oauth-callback") {
519
- window.removeEventListener("message", handler);
520
- popup?.close();
521
- try {
522
- const tokens = await this.apiPost("/v1/auth/oauth/callback", {
523
- code: e.data.code,
524
- state: e.data.state,
525
- codeVerifier: e.data.codeVerifier,
526
- provider
527
- });
528
- this.session.setSession(tokens);
529
- this.modal?.close();
530
- this.emit("signedIn", tokens.user);
531
- } catch (err) {
532
- this.emit("error", err instanceof Error ? err : new Error(String(err)));
581
+ let callbackReceived = false;
582
+ const pollTimer = setInterval(() => {
583
+ if (callbackReceived) return;
584
+ try {
585
+ if (!popup || popup.closed) {
586
+ clearInterval(pollTimer);
587
+ if (!callbackReceived) {
588
+ window.removeEventListener("message", handler);
589
+ this.modal?.hideLoading();
590
+ }
533
591
  }
592
+ } catch {
593
+ }
594
+ }, 500);
595
+ const handler = async (e) => {
596
+ if (e.data?.type !== "authon-oauth-callback") return;
597
+ callbackReceived = true;
598
+ window.removeEventListener("message", handler);
599
+ clearInterval(pollTimer);
600
+ try {
601
+ const tokens = await this.apiPost("/v1/auth/oauth/callback", {
602
+ code: e.data.code,
603
+ state: e.data.state,
604
+ provider
605
+ });
606
+ this.session.setSession(tokens);
607
+ this.modal?.close();
608
+ this.emit("signedIn", tokens.user);
609
+ } catch (err) {
610
+ this.modal?.hideLoading();
611
+ this.emit("error", err instanceof Error ? err : new Error(String(err)));
534
612
  }
535
613
  };
536
614
  window.addEventListener("message", handler);
537
615
  } catch (err) {
616
+ this.modal?.hideLoading();
538
617
  this.emit("error", err instanceof Error ? err : new Error(String(err)));
539
618
  }
540
619
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@authon/js",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Authon core SDK — ShadowDOM login modal for any app",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",