@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.js CHANGED
@@ -184,6 +184,100 @@ var AuthonMobileClient = class {
184
184
  getApiUrl() {
185
185
  return this.apiUrl;
186
186
  }
187
+ // ── Web3 ──
188
+ async web3GetNonce(address, chain, walletType, chainId) {
189
+ return this.request("POST", "/v1/auth/web3/nonce", {
190
+ address,
191
+ chain,
192
+ walletType,
193
+ ...chainId != null ? { chainId } : {}
194
+ });
195
+ }
196
+ async web3Verify(message, signature, address, chain, walletType) {
197
+ const res = await this.request("POST", "/v1/auth/web3/verify", {
198
+ message,
199
+ signature,
200
+ address,
201
+ chain,
202
+ walletType
203
+ });
204
+ this.tokens = this.toTokenPair(res);
205
+ this.user = res.user;
206
+ await this.persistTokens();
207
+ this.scheduleRefresh(this.tokens.expiresAt);
208
+ this.emit("signedIn", res.user);
209
+ return { tokens: this.tokens, user: res.user };
210
+ }
211
+ async web3GetWallets() {
212
+ return this.request("GET", "/v1/auth/web3/wallets");
213
+ }
214
+ async web3LinkWallet(params) {
215
+ return this.request("POST", "/v1/auth/web3/wallets/link", params);
216
+ }
217
+ async web3UnlinkWallet(walletId) {
218
+ await this.requestAuth("DELETE", `/v1/auth/web3/wallets/${walletId}`);
219
+ }
220
+ // ── Passwordless ──
221
+ async passwordlessSendCode(identifier, type = "email") {
222
+ if (type === "sms") {
223
+ await this.request("POST", "/v1/auth/passwordless/sms-otp", { phone: identifier });
224
+ } else {
225
+ await this.request("POST", "/v1/auth/passwordless/email-otp", { email: identifier });
226
+ }
227
+ }
228
+ async passwordlessVerifyCode(identifier, code) {
229
+ const res = await this.request("POST", "/v1/auth/passwordless/verify", {
230
+ email: identifier,
231
+ code
232
+ });
233
+ this.tokens = this.toTokenPair(res);
234
+ this.user = res.user;
235
+ await this.persistTokens();
236
+ this.scheduleRefresh(this.tokens.expiresAt);
237
+ this.emit("signedIn", res.user);
238
+ return { tokens: this.tokens, user: res.user };
239
+ }
240
+ // ── Passkeys ──
241
+ async passkeyStartRegister(name) {
242
+ return this.requestAuth(
243
+ "POST",
244
+ "/v1/auth/passkeys/register/options",
245
+ name ? { name } : void 0
246
+ );
247
+ }
248
+ async passkeyCompleteRegister(credential) {
249
+ return this.requestAuth(
250
+ "POST",
251
+ "/v1/auth/passkeys/register/verify",
252
+ credential
253
+ );
254
+ }
255
+ async passkeyStartAuth(email) {
256
+ return this.request(
257
+ "POST",
258
+ "/v1/auth/passkeys/authenticate/options",
259
+ email ? { email } : void 0
260
+ );
261
+ }
262
+ async passkeyCompleteAuth(credential) {
263
+ const res = await this.request(
264
+ "POST",
265
+ "/v1/auth/passkeys/authenticate/verify",
266
+ credential
267
+ );
268
+ this.tokens = this.toTokenPair(res);
269
+ this.user = res.user;
270
+ await this.persistTokens();
271
+ this.scheduleRefresh(this.tokens.expiresAt);
272
+ this.emit("signedIn", res.user);
273
+ return { tokens: this.tokens, user: res.user };
274
+ }
275
+ async passkeyList() {
276
+ return this.requestAuth("GET", "/v1/auth/passkeys");
277
+ }
278
+ async passkeyDelete(credentialId) {
279
+ await this.requestAuth("DELETE", `/v1/auth/passkeys/${credentialId}`);
280
+ }
187
281
  // ── Event system ──
188
282
  on(event, listener) {
189
283
  if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
@@ -236,6 +330,10 @@ var AuthonMobileClient = class {
236
330
  expiresAt: Date.now() + res.expiresIn * 1e3
237
331
  };
238
332
  }
333
+ async requestAuth(method, path, body) {
334
+ if (!this.tokens?.accessToken) throw new Error("Must be signed in");
335
+ return this.request(method, path, body);
336
+ }
239
337
  async request(method, path, body) {
240
338
  const headers = {
241
339
  "Content-Type": "application/json",
@@ -418,6 +516,160 @@ function useUser() {
418
516
  return { isLoaded, isSignedIn, user };
419
517
  }
420
518
 
519
+ // src/useAuthonWeb3.ts
520
+ import { useCallback as useCallback2, useContext as useContext2, useState as useState2 } from "react";
521
+ function useAuthonWeb3() {
522
+ const ctx = useContext2(AuthonContext);
523
+ if (!ctx) throw new Error("useAuthonWeb3 must be used within <AuthonProvider>");
524
+ const [isLoading, setIsLoading] = useState2(false);
525
+ const [error, setError] = useState2(null);
526
+ const wrap = useCallback2(async (fn) => {
527
+ setIsLoading(true);
528
+ setError(null);
529
+ try {
530
+ return await fn();
531
+ } catch (err) {
532
+ setError(err instanceof Error ? err : new Error(String(err)));
533
+ return null;
534
+ } finally {
535
+ setIsLoading(false);
536
+ }
537
+ }, []);
538
+ const getNonce = useCallback2(
539
+ (address, chain, walletType, chainId) => wrap(() => ctx.client.web3GetNonce(address, chain, walletType, chainId)),
540
+ [ctx.client, wrap]
541
+ );
542
+ const verify = useCallback2(
543
+ async (message, signature, address, chain, walletType) => {
544
+ const result = await wrap(
545
+ () => ctx.client.web3Verify(message, signature, address, chain, walletType)
546
+ );
547
+ return result !== null;
548
+ },
549
+ [ctx.client, wrap]
550
+ );
551
+ const getWallets = useCallback2(
552
+ () => wrap(() => ctx.client.web3GetWallets()),
553
+ [ctx.client, wrap]
554
+ );
555
+ const linkWallet = useCallback2(
556
+ (params) => wrap(() => ctx.client.web3LinkWallet(params)),
557
+ [ctx.client, wrap]
558
+ );
559
+ const unlinkWallet = useCallback2(
560
+ async (walletId) => {
561
+ const result = await wrap(() => ctx.client.web3UnlinkWallet(walletId));
562
+ return result !== null;
563
+ },
564
+ [ctx.client, wrap]
565
+ );
566
+ return {
567
+ getNonce,
568
+ verify,
569
+ getWallets,
570
+ linkWallet,
571
+ unlinkWallet,
572
+ isLoading,
573
+ error
574
+ };
575
+ }
576
+
577
+ // src/useAuthonPasswordless.ts
578
+ import { useCallback as useCallback3, useContext as useContext3, useState as useState3 } from "react";
579
+ function useAuthonPasswordless() {
580
+ const ctx = useContext3(AuthonContext);
581
+ if (!ctx) throw new Error("useAuthonPasswordless must be used within <AuthonProvider>");
582
+ const [isLoading, setIsLoading] = useState3(false);
583
+ const [error, setError] = useState3(null);
584
+ const wrap = useCallback3(async (fn) => {
585
+ setIsLoading(true);
586
+ setError(null);
587
+ try {
588
+ return await fn();
589
+ } catch (err) {
590
+ setError(err instanceof Error ? err : new Error(String(err)));
591
+ return null;
592
+ } finally {
593
+ setIsLoading(false);
594
+ }
595
+ }, []);
596
+ const sendCode = useCallback3(
597
+ async (identifier, type = "email") => {
598
+ const result = await wrap(() => ctx.client.passwordlessSendCode(identifier, type));
599
+ return result !== null;
600
+ },
601
+ [ctx.client, wrap]
602
+ );
603
+ const verifyCode = useCallback3(
604
+ (identifier, code) => wrap(() => ctx.client.passwordlessVerifyCode(identifier, code)),
605
+ [ctx.client, wrap]
606
+ );
607
+ return {
608
+ sendCode,
609
+ verifyCode,
610
+ isLoading,
611
+ error
612
+ };
613
+ }
614
+
615
+ // src/useAuthonPasskeys.ts
616
+ import { useCallback as useCallback4, useContext as useContext4, useState as useState4 } from "react";
617
+ function useAuthonPasskeys() {
618
+ const ctx = useContext4(AuthonContext);
619
+ if (!ctx) throw new Error("useAuthonPasskeys must be used within <AuthonProvider>");
620
+ const [isLoading, setIsLoading] = useState4(false);
621
+ const [error, setError] = useState4(null);
622
+ const wrap = useCallback4(async (fn) => {
623
+ setIsLoading(true);
624
+ setError(null);
625
+ try {
626
+ return await fn();
627
+ } catch (err) {
628
+ setError(err instanceof Error ? err : new Error(String(err)));
629
+ return null;
630
+ } finally {
631
+ setIsLoading(false);
632
+ }
633
+ }, []);
634
+ const startRegister = useCallback4(
635
+ (name) => wrap(() => ctx.client.passkeyStartRegister(name)),
636
+ [ctx.client, wrap]
637
+ );
638
+ const completeRegister = useCallback4(
639
+ (credential) => wrap(() => ctx.client.passkeyCompleteRegister(credential)),
640
+ [ctx.client, wrap]
641
+ );
642
+ const startAuth = useCallback4(
643
+ (email) => wrap(() => ctx.client.passkeyStartAuth(email)),
644
+ [ctx.client, wrap]
645
+ );
646
+ const completeAuth = useCallback4(
647
+ (credential) => wrap(() => ctx.client.passkeyCompleteAuth(credential)),
648
+ [ctx.client, wrap]
649
+ );
650
+ const listPasskeys = useCallback4(
651
+ () => wrap(() => ctx.client.passkeyList()),
652
+ [ctx.client, wrap]
653
+ );
654
+ const deletePasskey = useCallback4(
655
+ async (credentialId) => {
656
+ const result = await wrap(() => ctx.client.passkeyDelete(credentialId));
657
+ return result !== null;
658
+ },
659
+ [ctx.client, wrap]
660
+ );
661
+ return {
662
+ startRegister,
663
+ completeRegister,
664
+ startAuth,
665
+ completeAuth,
666
+ listPasskeys,
667
+ deletePasskey,
668
+ isLoading,
669
+ error
670
+ };
671
+ }
672
+
421
673
  // src/icons.tsx
422
674
  import "react";
423
675
  import Svg, { Path, Rect } from "react-native-svg";
@@ -496,16 +748,41 @@ function SocialButton({
496
748
  loading = false,
497
749
  disabled = false,
498
750
  label,
751
+ compact = false,
499
752
  style,
500
753
  labelStyle,
501
- iconSize = 20,
754
+ iconSize,
502
755
  borderRadius = 10,
503
- height = 48
756
+ height = 48,
757
+ size = 48
504
758
  }) {
505
759
  const colors = PROVIDER_COLORS[provider] || { bg: "#333", text: "#fff" };
506
760
  const displayName = PROVIDER_DISPLAY_NAMES[provider] || provider;
507
761
  const buttonLabel = label ?? `Continue with ${displayName}`;
508
762
  const needsBorder = colors.bg.toLowerCase() === "#ffffff";
763
+ const resolvedIconSize = iconSize ?? (compact ? 24 : 20);
764
+ if (compact) {
765
+ return /* @__PURE__ */ jsx3(
766
+ TouchableOpacity,
767
+ {
768
+ style: [
769
+ styles.compactButton,
770
+ {
771
+ backgroundColor: colors.bg,
772
+ borderRadius,
773
+ width: size,
774
+ height: size
775
+ },
776
+ needsBorder && styles.bordered,
777
+ style
778
+ ],
779
+ onPress: () => onPress(provider),
780
+ disabled: disabled || loading,
781
+ activeOpacity: 0.8,
782
+ children: loading ? /* @__PURE__ */ jsx3(ActivityIndicator, { color: colors.text, size: "small" }) : /* @__PURE__ */ jsx3(ProviderIcon, { provider, size: resolvedIconSize, color: colors.text })
783
+ }
784
+ );
785
+ }
509
786
  return /* @__PURE__ */ jsx3(
510
787
  TouchableOpacity,
511
788
  {
@@ -523,7 +800,7 @@ function SocialButton({
523
800
  disabled: disabled || loading,
524
801
  activeOpacity: 0.8,
525
802
  children: loading ? /* @__PURE__ */ jsx3(ActivityIndicator, { color: colors.text, size: "small" }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
526
- /* @__PURE__ */ jsx3(ProviderIcon, { provider, size: iconSize, color: colors.text }),
803
+ /* @__PURE__ */ jsx3(ProviderIcon, { provider, size: resolvedIconSize, color: colors.text }),
527
804
  /* @__PURE__ */ jsx3(
528
805
  Text,
529
806
  {
@@ -548,6 +825,10 @@ var styles = StyleSheet.create({
548
825
  gap: 10,
549
826
  paddingHorizontal: 16
550
827
  },
828
+ compactButton: {
829
+ alignItems: "center",
830
+ justifyContent: "center"
831
+ },
551
832
  bordered: {
552
833
  borderWidth: 1,
553
834
  borderColor: "#dadce0"
@@ -559,19 +840,22 @@ var styles = StyleSheet.create({
559
840
  });
560
841
 
561
842
  // src/SocialButtons.tsx
562
- import { useState as useState2 } from "react";
843
+ import { useState as useState5 } from "react";
563
844
  import { View, StyleSheet as StyleSheet2, Linking } from "react-native";
564
845
  import { jsx as jsx4 } from "react/jsx-runtime";
565
846
  function SocialButtons({
566
847
  onSuccess,
567
848
  onError,
568
849
  style,
569
- gap = 10,
850
+ gap,
851
+ compact = false,
852
+ labels,
570
853
  buttonProps
571
854
  }) {
572
855
  const { providers, startOAuth, completeOAuth } = useAuthon();
573
- const [loadingProvider, setLoadingProvider] = useState2(null);
856
+ const [loadingProvider, setLoadingProvider] = useState5(null);
574
857
  if (providers.length === 0) return null;
858
+ const resolvedGap = gap ?? (compact ? 12 : 10);
575
859
  const handlePress = async (provider) => {
576
860
  setLoadingProvider(provider);
577
861
  try {
@@ -588,20 +872,37 @@ function SocialButtons({
588
872
  setLoadingProvider(null);
589
873
  }
590
874
  };
591
- return /* @__PURE__ */ jsx4(View, { style: [styles2.container, { gap }, style], children: providers.map((provider) => /* @__PURE__ */ jsx4(
592
- SocialButton,
875
+ return /* @__PURE__ */ jsx4(
876
+ View,
593
877
  {
594
- provider,
595
- onPress: handlePress,
596
- loading: loadingProvider === provider,
597
- disabled: !!loadingProvider,
598
- ...buttonProps
599
- },
600
- provider
601
- )) });
878
+ style: [
879
+ compact ? styles2.compactContainer : styles2.container,
880
+ { gap: resolvedGap },
881
+ style
882
+ ],
883
+ children: providers.map((provider) => /* @__PURE__ */ jsx4(
884
+ SocialButton,
885
+ {
886
+ provider,
887
+ onPress: handlePress,
888
+ loading: loadingProvider === provider,
889
+ disabled: !!loadingProvider,
890
+ compact,
891
+ label: labels?.[provider],
892
+ ...buttonProps
893
+ },
894
+ provider
895
+ ))
896
+ }
897
+ );
602
898
  }
603
899
  var styles2 = StyleSheet2.create({
604
- container: {}
900
+ container: {},
901
+ compactContainer: {
902
+ flexDirection: "row",
903
+ flexWrap: "wrap",
904
+ justifyContent: "center"
905
+ }
605
906
  });
606
907
  export {
607
908
  AuthonContext,
@@ -611,6 +912,9 @@ export {
611
912
  SocialButton,
612
913
  SocialButtons,
613
914
  useAuthon,
915
+ useAuthonPasskeys,
916
+ useAuthonPasswordless,
917
+ useAuthonWeb3,
614
918
  useUser
615
919
  };
616
920
  //# sourceMappingURL=index.js.map