@authon/js 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,3 +1,13 @@
1
+ // src/types.ts
2
+ var AuthonMfaRequiredError = class extends Error {
3
+ mfaToken;
4
+ constructor(mfaToken) {
5
+ super("MFA verification required");
6
+ this.name = "AuthonMfaRequiredError";
7
+ this.mfaToken = mfaToken;
8
+ }
9
+ };
10
+
1
11
  // src/modal.ts
2
12
  import { DEFAULT_BRANDING } from "@authon/shared";
3
13
 
@@ -470,6 +480,9 @@ var SessionManager = class {
470
480
  this.scheduleRefresh(tokens.expiresIn);
471
481
  }
472
482
  }
483
+ updateUser(user) {
484
+ this.user = user;
485
+ }
473
486
  clearSession() {
474
487
  this.accessToken = null;
475
488
  this.refreshToken = null;
@@ -531,6 +544,404 @@ var SessionManager = class {
531
544
  }
532
545
  };
533
546
 
547
+ // src/qrcode.ts
548
+ var EXP = [];
549
+ var LOG = new Array(256).fill(0);
550
+ (() => {
551
+ let v = 1;
552
+ for (let i = 0; i < 255; i++) {
553
+ EXP[i] = v;
554
+ LOG[v] = i;
555
+ v <<= 1;
556
+ if (v & 256) v ^= 285;
557
+ }
558
+ for (let i = 255; i < 512; i++) EXP[i] = EXP[i - 255];
559
+ })();
560
+ var gfMul = (a, b) => a && b ? EXP[LOG[a] + LOG[b]] : 0;
561
+ function rsEncode(data, ecLen) {
562
+ let g = [1];
563
+ for (let i = 0; i < ecLen; i++) {
564
+ const ng = new Array(g.length + 1).fill(0);
565
+ for (let j = 0; j < g.length; j++) {
566
+ ng[j] ^= gfMul(g[j], EXP[i]);
567
+ ng[j + 1] ^= g[j];
568
+ }
569
+ g = ng;
570
+ }
571
+ const rem = new Array(ecLen).fill(0);
572
+ for (const d of data) {
573
+ const fb = d ^ rem[0];
574
+ for (let j = 0; j < ecLen - 1; j++) {
575
+ rem[j] = rem[j + 1] ^ gfMul(g[ecLen - 1 - j], fb);
576
+ }
577
+ rem[ecLen - 1] = gfMul(g[0], fb);
578
+ }
579
+ return rem;
580
+ }
581
+ var VER = [
582
+ { total: 0, ec: 0, g1: 0, g1d: 0, g2: 0, g2d: 0, align: [] },
583
+ // dummy
584
+ { total: 26, ec: 7, g1: 1, g1d: 19, g2: 0, g2d: 0, align: [] },
585
+ { total: 44, ec: 10, g1: 1, g1d: 34, g2: 0, g2d: 0, align: [6, 18] },
586
+ { total: 70, ec: 15, g1: 1, g1d: 55, g2: 0, g2d: 0, align: [6, 22] },
587
+ { total: 100, ec: 20, g1: 1, g1d: 80, g2: 0, g2d: 0, align: [6, 26] },
588
+ { total: 134, ec: 26, g1: 1, g1d: 108, g2: 0, g2d: 0, align: [6, 30] },
589
+ { total: 172, ec: 18, g1: 2, g1d: 68, g2: 0, g2d: 0, align: [6, 34] },
590
+ { total: 196, ec: 20, g1: 2, g1d: 78, g2: 0, g2d: 0, align: [6, 22, 38] },
591
+ { total: 242, ec: 24, g1: 2, g1d: 97, g2: 0, g2d: 0, align: [6, 24, 42] },
592
+ { total: 292, ec: 30, g1: 2, g1d: 116, g2: 0, g2d: 0, align: [6, 26, 46] },
593
+ { total: 346, ec: 18, g1: 2, g1d: 68, g2: 2, g2d: 69, align: [6, 28, 50] },
594
+ { total: 404, ec: 20, g1: 4, g1d: 81, g2: 0, g2d: 0, align: [6, 30, 54] },
595
+ { total: 466, ec: 24, g1: 2, g1d: 92, g2: 2, g2d: 93, align: [6, 32, 58] },
596
+ { total: 532, ec: 26, g1: 4, g1d: 107, g2: 0, g2d: 0, align: [6, 34, 62] }
597
+ ];
598
+ function dataCapacity(ver) {
599
+ const v = VER[ver];
600
+ return v.g1 * v.g1d + v.g2 * v.g2d;
601
+ }
602
+ function pickVersion(byteLen) {
603
+ for (let v = 1; v < VER.length; v++) {
604
+ const headerBits = 4 + (v <= 9 ? 8 : 16);
605
+ const available = dataCapacity(v) * 8 - headerBits;
606
+ if (byteLen * 8 <= available) return v;
607
+ }
608
+ throw new Error(`Data too long for QR code (${byteLen} bytes)`);
609
+ }
610
+ function encodeData(bytes, ver) {
611
+ const cap = dataCapacity(ver);
612
+ const countBits = ver <= 9 ? 8 : 16;
613
+ const bits = [];
614
+ const push = (val, len) => {
615
+ for (let i = len - 1; i >= 0; i--) bits.push(val >> i & 1);
616
+ };
617
+ push(4, 4);
618
+ push(bytes.length, countBits);
619
+ for (const b of bytes) push(b, 8);
620
+ push(0, Math.min(4, cap * 8 - bits.length));
621
+ while (bits.length % 8) bits.push(0);
622
+ const padBytes = [236, 17];
623
+ let pi = 0;
624
+ while (bits.length < cap * 8) {
625
+ push(padBytes[pi], 8);
626
+ pi ^= 1;
627
+ }
628
+ const cw = [];
629
+ for (let i = 0; i < bits.length; i += 8) {
630
+ let byte = 0;
631
+ for (let j = 0; j < 8; j++) byte = byte << 1 | bits[i + j];
632
+ cw.push(byte);
633
+ }
634
+ return cw;
635
+ }
636
+ function computeCodewords(ver, dataCW) {
637
+ const v = VER[ver];
638
+ const blocks = [];
639
+ const ecBlocks = [];
640
+ let offset = 0;
641
+ for (let i = 0; i < v.g1; i++) {
642
+ const block = dataCW.slice(offset, offset + v.g1d);
643
+ blocks.push(block);
644
+ ecBlocks.push(rsEncode(block, v.ec));
645
+ offset += v.g1d;
646
+ }
647
+ for (let i = 0; i < v.g2; i++) {
648
+ const block = dataCW.slice(offset, offset + v.g2d);
649
+ blocks.push(block);
650
+ ecBlocks.push(rsEncode(block, v.ec));
651
+ offset += v.g2d;
652
+ }
653
+ const result = [];
654
+ const maxDataLen = Math.max(v.g1d, v.g2d || 0);
655
+ for (let i = 0; i < maxDataLen; i++) {
656
+ for (const block of blocks) {
657
+ if (i < block.length) result.push(block[i]);
658
+ }
659
+ }
660
+ for (let i = 0; i < v.ec; i++) {
661
+ for (const block of ecBlocks) result.push(block[i]);
662
+ }
663
+ return result;
664
+ }
665
+ var UNSET = -1;
666
+ var DARK = 1;
667
+ var LIGHT = 0;
668
+ function createMatrix(size) {
669
+ return Array.from({ length: size }, () => new Array(size).fill(UNSET));
670
+ }
671
+ function setModule(m, r, c, dark) {
672
+ if (r >= 0 && r < m.length && c >= 0 && c < m.length) m[r][c] = dark ? DARK : LIGHT;
673
+ }
674
+ function placeFinderPattern(m, row, col) {
675
+ for (let r = -1; r <= 7; r++) {
676
+ for (let c = -1; c <= 7; c++) {
677
+ const dark = r >= 0 && r <= 6 && c >= 0 && c <= 6 && (r === 0 || r === 6 || c === 0 || c === 6 || r >= 2 && r <= 4 && c >= 2 && c <= 4);
678
+ setModule(m, row + r, col + c, dark);
679
+ }
680
+ }
681
+ }
682
+ function placeAlignmentPattern(m, row, col) {
683
+ for (let r = -2; r <= 2; r++) {
684
+ for (let c = -2; c <= 2; c++) {
685
+ const dark = Math.abs(r) === 2 || Math.abs(c) === 2 || r === 0 && c === 0;
686
+ m[row + r][col + c] = dark ? DARK : LIGHT;
687
+ }
688
+ }
689
+ }
690
+ function isReserved(m, r, c) {
691
+ return m[r][c] !== UNSET;
692
+ }
693
+ function buildMatrix(ver, codewords) {
694
+ const size = ver * 4 + 17;
695
+ const m = createMatrix(size);
696
+ placeFinderPattern(m, 0, 0);
697
+ placeFinderPattern(m, 0, size - 7);
698
+ placeFinderPattern(m, size - 7, 0);
699
+ for (let i = 8; i < size - 8; i++) {
700
+ m[6][i] = i % 2 === 0 ? DARK : LIGHT;
701
+ m[i][6] = i % 2 === 0 ? DARK : LIGHT;
702
+ }
703
+ const ap = VER[ver].align;
704
+ if (ap.length > 0) {
705
+ for (const r of ap) {
706
+ for (const c of ap) {
707
+ if (r <= 8 && c <= 8) continue;
708
+ if (r <= 8 && c >= size - 8) continue;
709
+ if (r >= size - 8 && c <= 8) continue;
710
+ placeAlignmentPattern(m, r, c);
711
+ }
712
+ }
713
+ }
714
+ m[4 * ver + 9][8] = DARK;
715
+ for (let i = 0; i < 9; i++) {
716
+ if (m[8][i] === UNSET) m[8][i] = LIGHT;
717
+ if (m[i][8] === UNSET) m[i][8] = LIGHT;
718
+ }
719
+ for (let i = 0; i < 8; i++) {
720
+ if (m[8][size - 1 - i] === UNSET) m[8][size - 1 - i] = LIGHT;
721
+ if (m[size - 1 - i][8] === UNSET) m[size - 1 - i][8] = LIGHT;
722
+ }
723
+ if (ver >= 7) {
724
+ for (let i = 0; i < 6; i++) {
725
+ for (let j = 0; j < 3; j++) {
726
+ m[i][size - 11 + j] = LIGHT;
727
+ m[size - 11 + j][i] = LIGHT;
728
+ }
729
+ }
730
+ }
731
+ let bitIdx = 0;
732
+ const totalBits = codewords.length * 8;
733
+ let upward = true;
734
+ for (let right = size - 1; right >= 0; right -= 2) {
735
+ if (right === 6) right = 5;
736
+ for (let i = 0; i < size; i++) {
737
+ const row = upward ? size - 1 - i : i;
738
+ for (const dc of [0, -1]) {
739
+ const col = right + dc;
740
+ if (col < 0 || col >= size) continue;
741
+ if (isReserved(m, row, col)) continue;
742
+ if (bitIdx < totalBits) {
743
+ const byteIdx = bitIdx >> 3;
744
+ const bitPos = 7 - (bitIdx & 7);
745
+ m[row][col] = codewords[byteIdx] >> bitPos & 1;
746
+ bitIdx++;
747
+ } else {
748
+ m[row][col] = LIGHT;
749
+ }
750
+ }
751
+ }
752
+ upward = !upward;
753
+ }
754
+ return m;
755
+ }
756
+ var MASKS = [
757
+ (r, c) => (r + c) % 2 === 0,
758
+ (r) => r % 2 === 0,
759
+ (_, c) => c % 3 === 0,
760
+ (r, c) => (r + c) % 3 === 0,
761
+ (r, c) => (Math.floor(r / 2) + Math.floor(c / 3)) % 2 === 0,
762
+ (r, c) => r * c % 2 + r * c % 3 === 0,
763
+ (r, c) => (r * c % 2 + r * c % 3) % 2 === 0,
764
+ (r, c) => ((r + c) % 2 + r * c % 3) % 2 === 0
765
+ ];
766
+ function applyMask(m, maskIdx, template) {
767
+ const size = m.length;
768
+ const result = m.map((row) => [...row]);
769
+ const fn = MASKS[maskIdx];
770
+ for (let r = 0; r < size; r++) {
771
+ for (let c = 0; c < size; c++) {
772
+ if (template[r][c] !== UNSET) continue;
773
+ if (fn(r, c)) result[r][c] ^= 1;
774
+ }
775
+ }
776
+ return result;
777
+ }
778
+ function penalty(m) {
779
+ const size = m.length;
780
+ let score = 0;
781
+ for (let r = 0; r < size; r++) {
782
+ let count = 1;
783
+ for (let c = 1; c < size; c++) {
784
+ if (m[r][c] === m[r][c - 1]) {
785
+ count++;
786
+ } else {
787
+ if (count >= 5) score += count - 2;
788
+ count = 1;
789
+ }
790
+ }
791
+ if (count >= 5) score += count - 2;
792
+ }
793
+ for (let c = 0; c < size; c++) {
794
+ let count = 1;
795
+ for (let r = 1; r < size; r++) {
796
+ if (m[r][c] === m[r - 1][c]) {
797
+ count++;
798
+ } else {
799
+ if (count >= 5) score += count - 2;
800
+ count = 1;
801
+ }
802
+ }
803
+ if (count >= 5) score += count - 2;
804
+ }
805
+ for (let r = 0; r < size - 1; r++) {
806
+ for (let c = 0; c < size - 1; c++) {
807
+ const v = m[r][c];
808
+ if (v === m[r][c + 1] && v === m[r + 1][c] && v === m[r + 1][c + 1]) score += 3;
809
+ }
810
+ }
811
+ const pat1 = [1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0];
812
+ const pat2 = [0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1];
813
+ for (let r = 0; r < size; r++) {
814
+ for (let c = 0; c <= size - 11; c++) {
815
+ let match1 = true, match2 = true;
816
+ for (let k = 0; k < 11; k++) {
817
+ if (m[r][c + k] !== pat1[k]) match1 = false;
818
+ if (m[r][c + k] !== pat2[k]) match2 = false;
819
+ }
820
+ if (match1 || match2) score += 40;
821
+ }
822
+ }
823
+ for (let c = 0; c < size; c++) {
824
+ for (let r = 0; r <= size - 11; r++) {
825
+ let match1 = true, match2 = true;
826
+ for (let k = 0; k < 11; k++) {
827
+ if (m[r + k][c] !== pat1[k]) match1 = false;
828
+ if (m[r + k][c] !== pat2[k]) match2 = false;
829
+ }
830
+ if (match1 || match2) score += 40;
831
+ }
832
+ }
833
+ let dark = 0;
834
+ for (let r = 0; r < size; r++) for (let c = 0; c < size; c++) if (m[r][c]) dark++;
835
+ const pct = dark * 100 / (size * size);
836
+ const prev5 = Math.floor(pct / 5) * 5;
837
+ const next5 = prev5 + 5;
838
+ score += Math.min(Math.abs(prev5 - 50) / 5, Math.abs(next5 - 50) / 5) * 10;
839
+ return score;
840
+ }
841
+ function bchEncode(data, gen, dataBits) {
842
+ let d = data << 15 - dataBits;
843
+ const genLen = Math.floor(Math.log2(gen)) + 1;
844
+ const totalBits = dataBits + (genLen - 1);
845
+ d = data << totalBits - dataBits;
846
+ for (let i = dataBits - 1; i >= 0; i--) {
847
+ if (d & 1 << i + genLen - 1) d ^= gen << i;
848
+ }
849
+ return data << genLen - 1 | d;
850
+ }
851
+ function placeFormatInfo(m, maskIdx) {
852
+ const size = m.length;
853
+ const data = 1 << 3 | maskIdx;
854
+ let format = bchEncode(data, 1335, 5);
855
+ format ^= 21522;
856
+ const bits = [];
857
+ for (let i = 14; i >= 0; i--) bits.push(format >> i & 1);
858
+ const hPos = [0, 1, 2, 3, 4, 5, 7, 8, size - 8, size - 7, size - 6, size - 5, size - 4, size - 3, size - 2];
859
+ for (let i = 0; i < 15; i++) m[8][hPos[i]] = bits[i];
860
+ const vPos = [size - 1, size - 2, size - 3, size - 4, size - 5, size - 6, size - 7, size - 8, 7, 5, 4, 3, 2, 1, 0];
861
+ for (let i = 0; i < 15; i++) m[vPos[i]][8] = bits[i];
862
+ }
863
+ function placeVersionInfo(m, ver) {
864
+ if (ver < 7) return;
865
+ const size = m.length;
866
+ let info = bchEncode(ver, 7973, 6);
867
+ for (let i = 0; i < 18; i++) {
868
+ const bit = info >> i & 1;
869
+ const r = Math.floor(i / 3);
870
+ const c = size - 11 + i % 3;
871
+ m[r][c] = bit;
872
+ m[c][r] = bit;
873
+ }
874
+ }
875
+ function generateQrSvg(text, moduleSize = 4) {
876
+ const bytes = Array.from(new TextEncoder().encode(text));
877
+ const ver = pickVersion(bytes.length);
878
+ const dataCW = encodeData(bytes, ver);
879
+ const allCW = computeCodewords(ver, dataCW);
880
+ const size = ver * 4 + 17;
881
+ const template = createMatrix(size);
882
+ placeFinderPattern(template, 0, 0);
883
+ placeFinderPattern(template, 0, size - 7);
884
+ placeFinderPattern(template, size - 7, 0);
885
+ for (let i = 8; i < size - 8; i++) {
886
+ template[6][i] = LIGHT;
887
+ template[i][6] = LIGHT;
888
+ }
889
+ const ap = VER[ver].align;
890
+ for (const r of ap) {
891
+ for (const c of ap) {
892
+ if (r <= 8 && c <= 8) continue;
893
+ if (r <= 8 && c >= size - 8) continue;
894
+ if (r >= size - 8 && c <= 8) continue;
895
+ for (let dr = -2; dr <= 2; dr++) for (let dc = -2; dc <= 2; dc++) template[r + dr][c + dc] = LIGHT;
896
+ }
897
+ }
898
+ template[4 * ver + 9][8] = LIGHT;
899
+ for (let i = 0; i < 9; i++) {
900
+ if (template[8][i] === UNSET) template[8][i] = LIGHT;
901
+ if (template[i][8] === UNSET) template[i][8] = LIGHT;
902
+ }
903
+ for (let i = 0; i < 8; i++) {
904
+ if (template[8][size - 1 - i] === UNSET) template[8][size - 1 - i] = LIGHT;
905
+ if (template[size - 1 - i][8] === UNSET) template[size - 1 - i][8] = LIGHT;
906
+ }
907
+ if (ver >= 7) {
908
+ for (let i = 0; i < 6; i++) for (let j = 0; j < 3; j++) {
909
+ template[i][size - 11 + j] = LIGHT;
910
+ template[size - 11 + j][i] = LIGHT;
911
+ }
912
+ }
913
+ const base = buildMatrix(ver, allCW);
914
+ let bestMask = 0;
915
+ let bestScore = Infinity;
916
+ for (let mask = 0; mask < 8; mask++) {
917
+ const masked = applyMask(base, mask, template);
918
+ placeFormatInfo(masked, mask);
919
+ placeVersionInfo(masked, ver);
920
+ const s = penalty(masked);
921
+ if (s < bestScore) {
922
+ bestScore = s;
923
+ bestMask = mask;
924
+ }
925
+ }
926
+ const final = applyMask(base, bestMask, template);
927
+ placeFormatInfo(final, bestMask);
928
+ placeVersionInfo(final, ver);
929
+ const quiet = 4;
930
+ const total = size + quiet * 2;
931
+ const px = total * moduleSize;
932
+ let svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${total} ${total}" width="${px}" height="${px}" shape-rendering="crispEdges">`;
933
+ svg += `<rect width="${total}" height="${total}" fill="#fff"/>`;
934
+ for (let r = 0; r < size; r++) {
935
+ for (let c = 0; c < size; c++) {
936
+ if (final[r][c] === DARK) {
937
+ svg += `<rect x="${c + quiet}" y="${r + quiet}" width="1" height="1" fill="#000"/>`;
938
+ }
939
+ }
940
+ }
941
+ svg += "</svg>";
942
+ return svg;
943
+ }
944
+
534
945
  // src/authon.ts
535
946
  var Authon = class {
536
947
  publishableKey;
@@ -556,6 +967,10 @@ var Authon = class {
556
967
  this.consumeRedirectResultFromUrl();
557
968
  }
558
969
  // ── Public API ──
970
+ async getProviders() {
971
+ await this.ensureInitialized();
972
+ return [...this.providers];
973
+ }
559
974
  async openSignIn() {
560
975
  await this.ensureInitialized();
561
976
  this.getModal().open("signIn");
@@ -569,10 +984,17 @@ var Authon = class {
569
984
  await this.startOAuthFlow(provider, options);
570
985
  }
571
986
  async signInWithEmail(email, password) {
572
- const tokens = await this.apiPost("/v1/auth/signin", { email, password });
573
- this.session.setSession(tokens);
574
- this.emit("signedIn", tokens.user);
575
- return tokens.user;
987
+ const res = await this.apiPost(
988
+ "/v1/auth/signin",
989
+ { email, password }
990
+ );
991
+ if (res.mfaRequired && res.mfaToken) {
992
+ this.emit("mfaRequired", res.mfaToken);
993
+ throw new AuthonMfaRequiredError(res.mfaToken);
994
+ }
995
+ this.session.setSession(res);
996
+ this.emit("signedIn", res.user);
997
+ return res.user;
576
998
  }
577
999
  async signUpWithEmail(email, password, meta) {
578
1000
  const tokens = await this.apiPost("/v1/auth/signup", {
@@ -600,6 +1022,258 @@ var Authon = class {
600
1022
  set.add(listener);
601
1023
  return () => set.delete(listener);
602
1024
  }
1025
+ // ── MFA ──
1026
+ async setupMfa() {
1027
+ const token = this.session.getToken();
1028
+ if (!token) throw new Error("Must be signed in to setup MFA");
1029
+ const res = await this.apiPostAuth("/v1/auth/mfa/totp/setup", void 0, token);
1030
+ return { ...res, qrCodeSvg: generateQrSvg(res.qrCodeUri) };
1031
+ }
1032
+ async verifyMfaSetup(code) {
1033
+ const token = this.session.getToken();
1034
+ if (!token) throw new Error("Must be signed in to verify MFA setup");
1035
+ await this.apiPostAuth("/v1/auth/mfa/totp/verify-setup", { code }, token);
1036
+ }
1037
+ async verifyMfa(mfaToken, code) {
1038
+ const res = await this.apiPost("/v1/auth/mfa/verify", { mfaToken, code });
1039
+ this.session.setSession(res);
1040
+ this.emit("signedIn", res.user);
1041
+ return res.user;
1042
+ }
1043
+ async disableMfa(code) {
1044
+ const token = this.session.getToken();
1045
+ if (!token) throw new Error("Must be signed in to disable MFA");
1046
+ await this.apiPostAuth("/v1/auth/mfa/disable", { code }, token);
1047
+ }
1048
+ async getMfaStatus() {
1049
+ const token = this.session.getToken();
1050
+ if (!token) throw new Error("Must be signed in to get MFA status");
1051
+ const res = await fetch(`${this.config.apiUrl}/v1/auth/mfa/status`, {
1052
+ headers: {
1053
+ "x-api-key": this.publishableKey,
1054
+ Authorization: `Bearer ${token}`
1055
+ },
1056
+ credentials: "include"
1057
+ });
1058
+ if (!res.ok) throw new Error(await this.parseApiError(res, "/v1/auth/mfa/status"));
1059
+ return res.json();
1060
+ }
1061
+ async regenerateBackupCodes(code) {
1062
+ const token = this.session.getToken();
1063
+ if (!token) throw new Error("Must be signed in to regenerate backup codes");
1064
+ const res = await this.apiPostAuth(
1065
+ "/v1/auth/mfa/backup-codes/regenerate",
1066
+ { code },
1067
+ token
1068
+ );
1069
+ return res.backupCodes;
1070
+ }
1071
+ // ── Passwordless ──
1072
+ async sendMagicLink(email) {
1073
+ await this.apiPost("/v1/auth/passwordless/magic-link", { email });
1074
+ }
1075
+ async sendEmailOtp(email) {
1076
+ await this.apiPost("/v1/auth/passwordless/email-otp", { email });
1077
+ }
1078
+ async verifyPasswordless(options) {
1079
+ const res = await this.apiPost("/v1/auth/passwordless/verify", options);
1080
+ this.session.setSession(res);
1081
+ this.emit("signedIn", res.user);
1082
+ return res.user;
1083
+ }
1084
+ // ── Passkeys ──
1085
+ async registerPasskey(name) {
1086
+ const token = this.session.getToken();
1087
+ if (!token) throw new Error("Must be signed in to register a passkey");
1088
+ const options = await this.apiPostAuth(
1089
+ "/v1/auth/passkeys/register/options",
1090
+ name ? { name } : void 0,
1091
+ token
1092
+ );
1093
+ const credential = await navigator.credentials.create({
1094
+ publicKey: this.deserializeCreationOptions(options.options)
1095
+ });
1096
+ const attestation = credential.response;
1097
+ const result = await this.apiPostAuth(
1098
+ "/v1/auth/passkeys/register/verify",
1099
+ {
1100
+ id: credential.id,
1101
+ rawId: this.bufferToBase64url(credential.rawId),
1102
+ type: credential.type,
1103
+ response: {
1104
+ attestationObject: this.bufferToBase64url(attestation.attestationObject),
1105
+ clientDataJSON: this.bufferToBase64url(attestation.clientDataJSON)
1106
+ }
1107
+ },
1108
+ token
1109
+ );
1110
+ this.emit("passkeyRegistered", result);
1111
+ return result;
1112
+ }
1113
+ async authenticateWithPasskey(email) {
1114
+ const options = await this.apiPost(
1115
+ "/v1/auth/passkeys/authenticate/options",
1116
+ email ? { email } : void 0
1117
+ );
1118
+ const credential = await navigator.credentials.get({
1119
+ publicKey: this.deserializeRequestOptions(options.options)
1120
+ });
1121
+ const assertion = credential.response;
1122
+ const res = await this.apiPost("/v1/auth/passkeys/authenticate/verify", {
1123
+ id: credential.id,
1124
+ rawId: this.bufferToBase64url(credential.rawId),
1125
+ type: credential.type,
1126
+ response: {
1127
+ authenticatorData: this.bufferToBase64url(assertion.authenticatorData),
1128
+ clientDataJSON: this.bufferToBase64url(assertion.clientDataJSON),
1129
+ signature: this.bufferToBase64url(assertion.signature),
1130
+ userHandle: assertion.userHandle ? this.bufferToBase64url(assertion.userHandle) : void 0
1131
+ }
1132
+ });
1133
+ this.session.setSession(res);
1134
+ this.emit("signedIn", res.user);
1135
+ return res.user;
1136
+ }
1137
+ async listPasskeys() {
1138
+ const token = this.session.getToken();
1139
+ if (!token) throw new Error("Must be signed in to list passkeys");
1140
+ return this.apiGetAuth("/v1/auth/passkeys", token);
1141
+ }
1142
+ async renamePasskey(passkeyId, name) {
1143
+ const token = this.session.getToken();
1144
+ if (!token) throw new Error("Must be signed in to rename a passkey");
1145
+ return this.apiPatchAuth(`/v1/auth/passkeys/${passkeyId}`, { name }, token);
1146
+ }
1147
+ async revokePasskey(passkeyId) {
1148
+ const token = this.session.getToken();
1149
+ if (!token) throw new Error("Must be signed in to revoke a passkey");
1150
+ await this.apiDeleteAuth(`/v1/auth/passkeys/${passkeyId}`, token);
1151
+ }
1152
+ // ── Web3 ──
1153
+ async web3GetNonce(address, chain, walletType, chainId) {
1154
+ return this.apiPost("/v1/auth/web3/nonce", {
1155
+ address,
1156
+ chain,
1157
+ walletType,
1158
+ ...chainId != null ? { chainId } : {}
1159
+ });
1160
+ }
1161
+ async web3Verify(message, signature, address, chain, walletType) {
1162
+ const res = await this.apiPost("/v1/auth/web3/verify", {
1163
+ message,
1164
+ signature,
1165
+ address,
1166
+ chain,
1167
+ walletType
1168
+ });
1169
+ this.session.setSession(res);
1170
+ this.emit("signedIn", res.user);
1171
+ return res.user;
1172
+ }
1173
+ async listWallets() {
1174
+ const token = this.session.getToken();
1175
+ if (!token) throw new Error("Must be signed in to list wallets");
1176
+ return this.apiGetAuth("/v1/auth/web3/wallets", token);
1177
+ }
1178
+ async linkWallet(params) {
1179
+ const token = this.session.getToken();
1180
+ if (!token) throw new Error("Must be signed in to link a wallet");
1181
+ const wallet = await this.apiPostAuth("/v1/auth/web3/wallets/link", params, token);
1182
+ this.emit("web3Connected", wallet);
1183
+ return wallet;
1184
+ }
1185
+ async unlinkWallet(walletId) {
1186
+ const token = this.session.getToken();
1187
+ if (!token) throw new Error("Must be signed in to unlink a wallet");
1188
+ await this.apiDeleteAuth(`/v1/auth/web3/wallets/${walletId}`, token);
1189
+ }
1190
+ // ── User Profile ──
1191
+ async updateProfile(data) {
1192
+ const token = this.session.getToken();
1193
+ if (!token) throw new Error("Must be signed in to update profile");
1194
+ const user = await this.apiPatchAuth("/v1/auth/me", data, token);
1195
+ this.session.updateUser(user);
1196
+ return user;
1197
+ }
1198
+ // ── Session Management ──
1199
+ async listSessions() {
1200
+ const token = this.session.getToken();
1201
+ if (!token) throw new Error("Must be signed in to list sessions");
1202
+ return this.apiGetAuth("/v1/auth/me/sessions", token);
1203
+ }
1204
+ async revokeSession(sessionId) {
1205
+ const token = this.session.getToken();
1206
+ if (!token) throw new Error("Must be signed in to revoke a session");
1207
+ await this.apiDeleteAuth(`/v1/auth/me/sessions/${sessionId}`, token);
1208
+ }
1209
+ // ── Organizations ──
1210
+ organizations = {
1211
+ list: async () => {
1212
+ const token = this.session.getToken();
1213
+ if (!token) throw new Error("Must be signed in to list organizations");
1214
+ return this.apiGetAuth("/v1/auth/organizations", token);
1215
+ },
1216
+ create: async (params) => {
1217
+ const token = this.session.getToken();
1218
+ if (!token) throw new Error("Must be signed in to create an organization");
1219
+ return this.apiPostAuth("/v1/auth/organizations", params, token);
1220
+ },
1221
+ get: async (orgId) => {
1222
+ const token = this.session.getToken();
1223
+ if (!token) throw new Error("Must be signed in to get organization");
1224
+ return this.apiGetAuth(`/v1/auth/organizations/${orgId}`, token);
1225
+ },
1226
+ update: async (orgId, params) => {
1227
+ const token = this.session.getToken();
1228
+ if (!token) throw new Error("Must be signed in to update organization");
1229
+ return this.apiPatchAuth(`/v1/auth/organizations/${orgId}`, params, token);
1230
+ },
1231
+ delete: async (orgId) => {
1232
+ const token = this.session.getToken();
1233
+ if (!token) throw new Error("Must be signed in to delete organization");
1234
+ await this.apiDeleteAuth(`/v1/auth/organizations/${orgId}`, token);
1235
+ },
1236
+ getMembers: async (orgId) => {
1237
+ const token = this.session.getToken();
1238
+ if (!token) throw new Error("Must be signed in to get organization members");
1239
+ return this.apiGetAuth(`/v1/auth/organizations/${orgId}/members`, token);
1240
+ },
1241
+ invite: async (orgId, params) => {
1242
+ const token = this.session.getToken();
1243
+ if (!token) throw new Error("Must be signed in to invite a member");
1244
+ return this.apiPostAuth(`/v1/auth/organizations/${orgId}/invitations`, params, token);
1245
+ },
1246
+ getInvitations: async (orgId) => {
1247
+ const token = this.session.getToken();
1248
+ if (!token) throw new Error("Must be signed in to get invitations");
1249
+ return this.apiGetAuth(`/v1/auth/organizations/${orgId}/invitations`, token);
1250
+ },
1251
+ acceptInvitation: async (token) => {
1252
+ const authToken = this.session.getToken();
1253
+ if (!authToken) throw new Error("Must be signed in to accept an invitation");
1254
+ return this.apiPostAuth(`/v1/auth/organizations/invitations/${token}/accept`, void 0, authToken);
1255
+ },
1256
+ rejectInvitation: async (token) => {
1257
+ const authToken = this.session.getToken();
1258
+ if (!authToken) throw new Error("Must be signed in to reject an invitation");
1259
+ await this.apiPostAuth(`/v1/auth/organizations/invitations/${token}/reject`, void 0, authToken);
1260
+ },
1261
+ removeMember: async (orgId, memberId) => {
1262
+ const token = this.session.getToken();
1263
+ if (!token) throw new Error("Must be signed in to remove a member");
1264
+ await this.apiDeleteAuth(`/v1/auth/organizations/${orgId}/members/${memberId}`, token);
1265
+ },
1266
+ updateMemberRole: async (orgId, memberId, role) => {
1267
+ const token = this.session.getToken();
1268
+ if (!token) throw new Error("Must be signed in to update member role");
1269
+ return this.apiPatchAuth(`/v1/auth/organizations/${orgId}/members/${memberId}`, { role }, token);
1270
+ },
1271
+ leave: async (orgId) => {
1272
+ const token = this.session.getToken();
1273
+ if (!token) throw new Error("Must be signed in to leave organization");
1274
+ await this.apiPostAuth(`/v1/auth/organizations/${orgId}/leave`, void 0, token);
1275
+ }
1276
+ };
603
1277
  destroy() {
604
1278
  this.modal?.close();
605
1279
  this.session.destroy();
@@ -890,6 +1564,106 @@ var Authon = class {
890
1564
  if (!res.ok) throw new Error(await this.parseApiError(res, path));
891
1565
  return res.json();
892
1566
  }
1567
+ async apiPostAuth(path, body, token) {
1568
+ const res = await fetch(`${this.config.apiUrl}${path}`, {
1569
+ method: "POST",
1570
+ headers: {
1571
+ "Content-Type": "application/json",
1572
+ "x-api-key": this.publishableKey,
1573
+ Authorization: `Bearer ${token}`
1574
+ },
1575
+ credentials: "include",
1576
+ body: body ? JSON.stringify(body) : void 0
1577
+ });
1578
+ if (!res.ok) throw new Error(await this.parseApiError(res, path));
1579
+ return res.json();
1580
+ }
1581
+ async apiGetAuth(path, token) {
1582
+ const res = await fetch(`${this.config.apiUrl}${path}`, {
1583
+ headers: {
1584
+ "x-api-key": this.publishableKey,
1585
+ Authorization: `Bearer ${token}`
1586
+ },
1587
+ credentials: "include"
1588
+ });
1589
+ if (!res.ok) throw new Error(await this.parseApiError(res, path));
1590
+ return res.json();
1591
+ }
1592
+ async apiPatchAuth(path, body, token) {
1593
+ const res = await fetch(`${this.config.apiUrl}${path}`, {
1594
+ method: "PATCH",
1595
+ headers: {
1596
+ "Content-Type": "application/json",
1597
+ "x-api-key": this.publishableKey,
1598
+ Authorization: `Bearer ${token}`
1599
+ },
1600
+ credentials: "include",
1601
+ body: body ? JSON.stringify(body) : void 0
1602
+ });
1603
+ if (!res.ok) throw new Error(await this.parseApiError(res, path));
1604
+ return res.json();
1605
+ }
1606
+ async apiDeleteAuth(path, token) {
1607
+ const res = await fetch(`${this.config.apiUrl}${path}`, {
1608
+ method: "DELETE",
1609
+ headers: {
1610
+ "x-api-key": this.publishableKey,
1611
+ Authorization: `Bearer ${token}`
1612
+ },
1613
+ credentials: "include"
1614
+ });
1615
+ if (!res.ok) throw new Error(await this.parseApiError(res, path));
1616
+ }
1617
+ // ── WebAuthn helpers ──
1618
+ bufferToBase64url(buffer) {
1619
+ const bytes = new Uint8Array(buffer);
1620
+ let binary = "";
1621
+ for (let i = 0; i < bytes.length; i++) {
1622
+ binary += String.fromCharCode(bytes[i]);
1623
+ }
1624
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
1625
+ }
1626
+ base64urlToBuffer(base64url) {
1627
+ const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/");
1628
+ const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
1629
+ const binary = atob(padded);
1630
+ const bytes = new Uint8Array(binary.length);
1631
+ for (let i = 0; i < binary.length; i++) {
1632
+ bytes[i] = binary.charCodeAt(i);
1633
+ }
1634
+ return bytes.buffer;
1635
+ }
1636
+ deserializeCreationOptions(options) {
1637
+ const opts = { ...options };
1638
+ if (typeof opts.challenge === "string") {
1639
+ opts.challenge = this.base64urlToBuffer(opts.challenge);
1640
+ }
1641
+ if (opts.user && typeof opts.user.id === "string") {
1642
+ opts.user.id = this.base64urlToBuffer(
1643
+ opts.user.id
1644
+ );
1645
+ }
1646
+ if (Array.isArray(opts.excludeCredentials)) {
1647
+ opts.excludeCredentials = opts.excludeCredentials.map((c) => ({
1648
+ ...c,
1649
+ id: typeof c.id === "string" ? this.base64urlToBuffer(c.id) : c.id
1650
+ }));
1651
+ }
1652
+ return opts;
1653
+ }
1654
+ deserializeRequestOptions(options) {
1655
+ const opts = { ...options };
1656
+ if (typeof opts.challenge === "string") {
1657
+ opts.challenge = this.base64urlToBuffer(opts.challenge);
1658
+ }
1659
+ if (Array.isArray(opts.allowCredentials)) {
1660
+ opts.allowCredentials = opts.allowCredentials.map((c) => ({
1661
+ ...c,
1662
+ id: typeof c.id === "string" ? this.base64urlToBuffer(c.id) : c.id
1663
+ }));
1664
+ }
1665
+ return opts;
1666
+ }
893
1667
  async parseApiError(res, path) {
894
1668
  try {
895
1669
  const body = await res.json();
@@ -906,6 +1680,8 @@ var Authon = class {
906
1680
  };
907
1681
  export {
908
1682
  Authon,
1683
+ AuthonMfaRequiredError,
1684
+ generateQrSvg,
909
1685
  getProviderButtonConfig
910
1686
  };
911
1687
  //# sourceMappingURL=index.js.map