@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/README.ko.md +154 -0
- package/README.md +412 -50
- package/dist/index.cjs +326 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +98 -7
- package/dist/index.d.ts +98 -7
- package/dist/index.js +321 -17
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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
|
|
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:
|
|
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
|
|
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
|
|
850
|
+
gap,
|
|
851
|
+
compact = false,
|
|
852
|
+
labels,
|
|
570
853
|
buttonProps
|
|
571
854
|
}) {
|
|
572
855
|
const { providers, startOAuth, completeOAuth } = useAuthon();
|
|
573
|
-
const [loadingProvider, setLoadingProvider] =
|
|
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(
|
|
592
|
-
|
|
875
|
+
return /* @__PURE__ */ jsx4(
|
|
876
|
+
View,
|
|
593
877
|
{
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
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
|