@authon/react-native 0.2.1 → 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.cjs CHANGED
@@ -37,6 +37,9 @@ __export(index_exports, {
37
37
  SocialButton: () => SocialButton,
38
38
  SocialButtons: () => SocialButtons,
39
39
  useAuthon: () => useAuthon,
40
+ useAuthonPasskeys: () => useAuthonPasskeys,
41
+ useAuthonPasswordless: () => useAuthonPasswordless,
42
+ useAuthonWeb3: () => useAuthonWeb3,
40
43
  useUser: () => useUser
41
44
  });
42
45
  module.exports = __toCommonJS(index_exports);
@@ -227,6 +230,100 @@ var AuthonMobileClient = class {
227
230
  getApiUrl() {
228
231
  return this.apiUrl;
229
232
  }
233
+ // ── Web3 ──
234
+ async web3GetNonce(address, chain, walletType, chainId) {
235
+ return this.request("POST", "/v1/auth/web3/nonce", {
236
+ address,
237
+ chain,
238
+ walletType,
239
+ ...chainId != null ? { chainId } : {}
240
+ });
241
+ }
242
+ async web3Verify(message, signature, address, chain, walletType) {
243
+ const res = await this.request("POST", "/v1/auth/web3/verify", {
244
+ message,
245
+ signature,
246
+ address,
247
+ chain,
248
+ walletType
249
+ });
250
+ this.tokens = this.toTokenPair(res);
251
+ this.user = res.user;
252
+ await this.persistTokens();
253
+ this.scheduleRefresh(this.tokens.expiresAt);
254
+ this.emit("signedIn", res.user);
255
+ return { tokens: this.tokens, user: res.user };
256
+ }
257
+ async web3GetWallets() {
258
+ return this.request("GET", "/v1/auth/web3/wallets");
259
+ }
260
+ async web3LinkWallet(params) {
261
+ return this.request("POST", "/v1/auth/web3/wallets/link", params);
262
+ }
263
+ async web3UnlinkWallet(walletId) {
264
+ await this.requestAuth("DELETE", `/v1/auth/web3/wallets/${walletId}`);
265
+ }
266
+ // ── Passwordless ──
267
+ async passwordlessSendCode(identifier, type = "email") {
268
+ if (type === "sms") {
269
+ await this.request("POST", "/v1/auth/passwordless/sms-otp", { phone: identifier });
270
+ } else {
271
+ await this.request("POST", "/v1/auth/passwordless/email-otp", { email: identifier });
272
+ }
273
+ }
274
+ async passwordlessVerifyCode(identifier, code) {
275
+ const res = await this.request("POST", "/v1/auth/passwordless/verify", {
276
+ email: identifier,
277
+ code
278
+ });
279
+ this.tokens = this.toTokenPair(res);
280
+ this.user = res.user;
281
+ await this.persistTokens();
282
+ this.scheduleRefresh(this.tokens.expiresAt);
283
+ this.emit("signedIn", res.user);
284
+ return { tokens: this.tokens, user: res.user };
285
+ }
286
+ // ── Passkeys ──
287
+ async passkeyStartRegister(name) {
288
+ return this.requestAuth(
289
+ "POST",
290
+ "/v1/auth/passkeys/register/options",
291
+ name ? { name } : void 0
292
+ );
293
+ }
294
+ async passkeyCompleteRegister(credential) {
295
+ return this.requestAuth(
296
+ "POST",
297
+ "/v1/auth/passkeys/register/verify",
298
+ credential
299
+ );
300
+ }
301
+ async passkeyStartAuth(email) {
302
+ return this.request(
303
+ "POST",
304
+ "/v1/auth/passkeys/authenticate/options",
305
+ email ? { email } : void 0
306
+ );
307
+ }
308
+ async passkeyCompleteAuth(credential) {
309
+ const res = await this.request(
310
+ "POST",
311
+ "/v1/auth/passkeys/authenticate/verify",
312
+ credential
313
+ );
314
+ this.tokens = this.toTokenPair(res);
315
+ this.user = res.user;
316
+ await this.persistTokens();
317
+ this.scheduleRefresh(this.tokens.expiresAt);
318
+ this.emit("signedIn", res.user);
319
+ return { tokens: this.tokens, user: res.user };
320
+ }
321
+ async passkeyList() {
322
+ return this.requestAuth("GET", "/v1/auth/passkeys");
323
+ }
324
+ async passkeyDelete(credentialId) {
325
+ await this.requestAuth("DELETE", `/v1/auth/passkeys/${credentialId}`);
326
+ }
230
327
  // ── Event system ──
231
328
  on(event, listener) {
232
329
  if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
@@ -279,6 +376,10 @@ var AuthonMobileClient = class {
279
376
  expiresAt: Date.now() + res.expiresIn * 1e3
280
377
  };
281
378
  }
379
+ async requestAuth(method, path, body) {
380
+ if (!this.tokens?.accessToken) throw new Error("Must be signed in");
381
+ return this.request(method, path, body);
382
+ }
282
383
  async request(method, path, body) {
283
384
  const headers = {
284
385
  "Content-Type": "application/json",
@@ -461,8 +562,162 @@ function useUser() {
461
562
  return { isLoaded, isSignedIn, user };
462
563
  }
463
564
 
464
- // src/icons.tsx
565
+ // src/useAuthonWeb3.ts
465
566
  var import_react3 = require("react");
567
+ function useAuthonWeb3() {
568
+ const ctx = (0, import_react3.useContext)(AuthonContext);
569
+ if (!ctx) throw new Error("useAuthonWeb3 must be used within <AuthonProvider>");
570
+ const [isLoading, setIsLoading] = (0, import_react3.useState)(false);
571
+ const [error, setError] = (0, import_react3.useState)(null);
572
+ const wrap = (0, import_react3.useCallback)(async (fn) => {
573
+ setIsLoading(true);
574
+ setError(null);
575
+ try {
576
+ return await fn();
577
+ } catch (err) {
578
+ setError(err instanceof Error ? err : new Error(String(err)));
579
+ return null;
580
+ } finally {
581
+ setIsLoading(false);
582
+ }
583
+ }, []);
584
+ const getNonce = (0, import_react3.useCallback)(
585
+ (address, chain, walletType, chainId) => wrap(() => ctx.client.web3GetNonce(address, chain, walletType, chainId)),
586
+ [ctx.client, wrap]
587
+ );
588
+ const verify = (0, import_react3.useCallback)(
589
+ async (message, signature, address, chain, walletType) => {
590
+ const result = await wrap(
591
+ () => ctx.client.web3Verify(message, signature, address, chain, walletType)
592
+ );
593
+ return result !== null;
594
+ },
595
+ [ctx.client, wrap]
596
+ );
597
+ const getWallets = (0, import_react3.useCallback)(
598
+ () => wrap(() => ctx.client.web3GetWallets()),
599
+ [ctx.client, wrap]
600
+ );
601
+ const linkWallet = (0, import_react3.useCallback)(
602
+ (params) => wrap(() => ctx.client.web3LinkWallet(params)),
603
+ [ctx.client, wrap]
604
+ );
605
+ const unlinkWallet = (0, import_react3.useCallback)(
606
+ async (walletId) => {
607
+ const result = await wrap(() => ctx.client.web3UnlinkWallet(walletId));
608
+ return result !== null;
609
+ },
610
+ [ctx.client, wrap]
611
+ );
612
+ return {
613
+ getNonce,
614
+ verify,
615
+ getWallets,
616
+ linkWallet,
617
+ unlinkWallet,
618
+ isLoading,
619
+ error
620
+ };
621
+ }
622
+
623
+ // src/useAuthonPasswordless.ts
624
+ var import_react4 = require("react");
625
+ function useAuthonPasswordless() {
626
+ const ctx = (0, import_react4.useContext)(AuthonContext);
627
+ if (!ctx) throw new Error("useAuthonPasswordless must be used within <AuthonProvider>");
628
+ const [isLoading, setIsLoading] = (0, import_react4.useState)(false);
629
+ const [error, setError] = (0, import_react4.useState)(null);
630
+ const wrap = (0, import_react4.useCallback)(async (fn) => {
631
+ setIsLoading(true);
632
+ setError(null);
633
+ try {
634
+ return await fn();
635
+ } catch (err) {
636
+ setError(err instanceof Error ? err : new Error(String(err)));
637
+ return null;
638
+ } finally {
639
+ setIsLoading(false);
640
+ }
641
+ }, []);
642
+ const sendCode = (0, import_react4.useCallback)(
643
+ async (identifier, type = "email") => {
644
+ const result = await wrap(() => ctx.client.passwordlessSendCode(identifier, type));
645
+ return result !== null;
646
+ },
647
+ [ctx.client, wrap]
648
+ );
649
+ const verifyCode = (0, import_react4.useCallback)(
650
+ (identifier, code) => wrap(() => ctx.client.passwordlessVerifyCode(identifier, code)),
651
+ [ctx.client, wrap]
652
+ );
653
+ return {
654
+ sendCode,
655
+ verifyCode,
656
+ isLoading,
657
+ error
658
+ };
659
+ }
660
+
661
+ // src/useAuthonPasskeys.ts
662
+ var import_react5 = require("react");
663
+ function useAuthonPasskeys() {
664
+ const ctx = (0, import_react5.useContext)(AuthonContext);
665
+ if (!ctx) throw new Error("useAuthonPasskeys must be used within <AuthonProvider>");
666
+ const [isLoading, setIsLoading] = (0, import_react5.useState)(false);
667
+ const [error, setError] = (0, import_react5.useState)(null);
668
+ const wrap = (0, import_react5.useCallback)(async (fn) => {
669
+ setIsLoading(true);
670
+ setError(null);
671
+ try {
672
+ return await fn();
673
+ } catch (err) {
674
+ setError(err instanceof Error ? err : new Error(String(err)));
675
+ return null;
676
+ } finally {
677
+ setIsLoading(false);
678
+ }
679
+ }, []);
680
+ const startRegister = (0, import_react5.useCallback)(
681
+ (name) => wrap(() => ctx.client.passkeyStartRegister(name)),
682
+ [ctx.client, wrap]
683
+ );
684
+ const completeRegister = (0, import_react5.useCallback)(
685
+ (credential) => wrap(() => ctx.client.passkeyCompleteRegister(credential)),
686
+ [ctx.client, wrap]
687
+ );
688
+ const startAuth = (0, import_react5.useCallback)(
689
+ (email) => wrap(() => ctx.client.passkeyStartAuth(email)),
690
+ [ctx.client, wrap]
691
+ );
692
+ const completeAuth = (0, import_react5.useCallback)(
693
+ (credential) => wrap(() => ctx.client.passkeyCompleteAuth(credential)),
694
+ [ctx.client, wrap]
695
+ );
696
+ const listPasskeys = (0, import_react5.useCallback)(
697
+ () => wrap(() => ctx.client.passkeyList()),
698
+ [ctx.client, wrap]
699
+ );
700
+ const deletePasskey = (0, import_react5.useCallback)(
701
+ async (credentialId) => {
702
+ const result = await wrap(() => ctx.client.passkeyDelete(credentialId));
703
+ return result !== null;
704
+ },
705
+ [ctx.client, wrap]
706
+ );
707
+ return {
708
+ startRegister,
709
+ completeRegister,
710
+ startAuth,
711
+ completeAuth,
712
+ listPasskeys,
713
+ deletePasskey,
714
+ isLoading,
715
+ error
716
+ };
717
+ }
718
+
719
+ // src/icons.tsx
720
+ var import_react6 = require("react");
466
721
  var import_react_native_svg = __toESM(require("react-native-svg"), 1);
467
722
  var import_jsx_runtime2 = require("react/jsx-runtime");
468
723
  function GoogleIcon({ size = 20 }) {
@@ -524,7 +779,7 @@ function ProviderIcon({ provider, size = 20, color }) {
524
779
  }
525
780
 
526
781
  // src/SocialButton.tsx
527
- var import_react4 = require("react");
782
+ var import_react7 = require("react");
528
783
  var import_react_native = require("react-native");
529
784
  var import_shared = require("@authon/shared");
530
785
  var import_jsx_runtime3 = require("react/jsx-runtime");
@@ -534,16 +789,41 @@ function SocialButton({
534
789
  loading = false,
535
790
  disabled = false,
536
791
  label,
792
+ compact = false,
537
793
  style,
538
794
  labelStyle,
539
- iconSize = 20,
795
+ iconSize,
540
796
  borderRadius = 10,
541
- height = 48
797
+ height = 48,
798
+ size = 48
542
799
  }) {
543
800
  const colors = import_shared.PROVIDER_COLORS[provider] || { bg: "#333", text: "#fff" };
544
801
  const displayName = import_shared.PROVIDER_DISPLAY_NAMES[provider] || provider;
545
802
  const buttonLabel = label ?? `Continue with ${displayName}`;
546
803
  const needsBorder = colors.bg.toLowerCase() === "#ffffff";
804
+ const resolvedIconSize = iconSize ?? (compact ? 24 : 20);
805
+ if (compact) {
806
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
807
+ import_react_native.TouchableOpacity,
808
+ {
809
+ style: [
810
+ styles.compactButton,
811
+ {
812
+ backgroundColor: colors.bg,
813
+ borderRadius,
814
+ width: size,
815
+ height: size
816
+ },
817
+ needsBorder && styles.bordered,
818
+ style
819
+ ],
820
+ onPress: () => onPress(provider),
821
+ disabled: disabled || loading,
822
+ activeOpacity: 0.8,
823
+ children: loading ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native.ActivityIndicator, { color: colors.text, size: "small" }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ProviderIcon, { provider, size: resolvedIconSize, color: colors.text })
824
+ }
825
+ );
826
+ }
547
827
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
548
828
  import_react_native.TouchableOpacity,
549
829
  {
@@ -561,7 +841,7 @@ function SocialButton({
561
841
  disabled: disabled || loading,
562
842
  activeOpacity: 0.8,
563
843
  children: loading ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native.ActivityIndicator, { color: colors.text, size: "small" }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
564
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ProviderIcon, { provider, size: iconSize, color: colors.text }),
844
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ProviderIcon, { provider, size: resolvedIconSize, color: colors.text }),
565
845
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
566
846
  import_react_native.Text,
567
847
  {
@@ -586,6 +866,10 @@ var styles = import_react_native.StyleSheet.create({
586
866
  gap: 10,
587
867
  paddingHorizontal: 16
588
868
  },
869
+ compactButton: {
870
+ alignItems: "center",
871
+ justifyContent: "center"
872
+ },
589
873
  bordered: {
590
874
  borderWidth: 1,
591
875
  borderColor: "#dadce0"
@@ -597,19 +881,22 @@ var styles = import_react_native.StyleSheet.create({
597
881
  });
598
882
 
599
883
  // src/SocialButtons.tsx
600
- var import_react5 = require("react");
884
+ var import_react8 = require("react");
601
885
  var import_react_native2 = require("react-native");
602
886
  var import_jsx_runtime4 = require("react/jsx-runtime");
603
887
  function SocialButtons({
604
888
  onSuccess,
605
889
  onError,
606
890
  style,
607
- gap = 10,
891
+ gap,
892
+ compact = false,
893
+ labels,
608
894
  buttonProps
609
895
  }) {
610
896
  const { providers, startOAuth, completeOAuth } = useAuthon();
611
- const [loadingProvider, setLoadingProvider] = (0, import_react5.useState)(null);
897
+ const [loadingProvider, setLoadingProvider] = (0, import_react8.useState)(null);
612
898
  if (providers.length === 0) return null;
899
+ const resolvedGap = gap ?? (compact ? 12 : 10);
613
900
  const handlePress = async (provider) => {
614
901
  setLoadingProvider(provider);
615
902
  try {
@@ -626,20 +913,37 @@ function SocialButtons({
626
913
  setLoadingProvider(null);
627
914
  }
628
915
  };
629
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native2.View, { style: [styles2.container, { gap }, style], children: providers.map((provider) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
630
- SocialButton,
916
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
917
+ import_react_native2.View,
631
918
  {
632
- provider,
633
- onPress: handlePress,
634
- loading: loadingProvider === provider,
635
- disabled: !!loadingProvider,
636
- ...buttonProps
637
- },
638
- provider
639
- )) });
919
+ style: [
920
+ compact ? styles2.compactContainer : styles2.container,
921
+ { gap: resolvedGap },
922
+ style
923
+ ],
924
+ children: providers.map((provider) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
925
+ SocialButton,
926
+ {
927
+ provider,
928
+ onPress: handlePress,
929
+ loading: loadingProvider === provider,
930
+ disabled: !!loadingProvider,
931
+ compact,
932
+ label: labels?.[provider],
933
+ ...buttonProps
934
+ },
935
+ provider
936
+ ))
937
+ }
938
+ );
640
939
  }
641
940
  var styles2 = import_react_native2.StyleSheet.create({
642
- container: {}
941
+ container: {},
942
+ compactContainer: {
943
+ flexDirection: "row",
944
+ flexWrap: "wrap",
945
+ justifyContent: "center"
946
+ }
643
947
  });
644
948
  // Annotate the CommonJS export names for ESM import in node:
645
949
  0 && (module.exports = {
@@ -650,6 +954,9 @@ var styles2 = import_react_native2.StyleSheet.create({
650
954
  SocialButton,
651
955
  SocialButtons,
652
956
  useAuthon,
957
+ useAuthonPasskeys,
958
+ useAuthonPasswordless,
959
+ useAuthonWeb3,
653
960
  useUser
654
961
  });
655
962
  //# sourceMappingURL=index.cjs.map