react-native-nitro-auth 0.5.4 → 0.5.6

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.
Files changed (100) hide show
  1. package/README.md +82 -47
  2. package/android/proguard-rules.pro +7 -1
  3. package/android/src/main/AndroidManifest.xml +12 -0
  4. package/android/src/main/cpp/JniOnLoad.cpp +3 -1
  5. package/android/src/main/cpp/PlatformAuth+Android.cpp +271 -78
  6. package/android/src/main/java/com/auth/AuthAdapter.kt +293 -238
  7. package/android/src/main/java/com/auth/GoogleSignInActivity.kt +9 -5
  8. package/android/src/main/java/com/auth/NitroAuthModule.kt +8 -1
  9. package/cpp/HybridAuth.cpp +79 -64
  10. package/cpp/HybridAuth.hpp +9 -7
  11. package/cpp/JSONSerializer.hpp +3 -0
  12. package/ios/AuthAdapter.swift +226 -79
  13. package/ios/PlatformAuth+iOS.mm +10 -3
  14. package/lib/commonjs/Auth.web.js +50 -10
  15. package/lib/commonjs/Auth.web.js.map +1 -1
  16. package/lib/commonjs/index.js +23 -1
  17. package/lib/commonjs/index.js.map +1 -1
  18. package/lib/commonjs/index.web.js +30 -12
  19. package/lib/commonjs/index.web.js.map +1 -1
  20. package/lib/commonjs/service.js +36 -9
  21. package/lib/commonjs/service.js.map +1 -1
  22. package/lib/commonjs/service.web.js +65 -13
  23. package/lib/commonjs/service.web.js.map +1 -1
  24. package/lib/commonjs/ui/social-button.js +19 -14
  25. package/lib/commonjs/ui/social-button.js.map +1 -1
  26. package/lib/commonjs/ui/social-button.web.js +16 -10
  27. package/lib/commonjs/ui/social-button.web.js.map +1 -1
  28. package/lib/commonjs/use-auth.js +22 -25
  29. package/lib/commonjs/use-auth.js.map +1 -1
  30. package/lib/commonjs/utils/auth-error.js +37 -0
  31. package/lib/commonjs/utils/auth-error.js.map +1 -0
  32. package/lib/commonjs/utils/logger.js +1 -0
  33. package/lib/commonjs/utils/logger.js.map +1 -1
  34. package/lib/module/Auth.web.js +50 -10
  35. package/lib/module/Auth.web.js.map +1 -1
  36. package/lib/module/global.d.js.map +1 -1
  37. package/lib/module/index.js +1 -0
  38. package/lib/module/index.js.map +1 -1
  39. package/lib/module/index.web.js +2 -1
  40. package/lib/module/index.web.js.map +1 -1
  41. package/lib/module/service.js +36 -9
  42. package/lib/module/service.js.map +1 -1
  43. package/lib/module/service.web.js +65 -13
  44. package/lib/module/service.web.js.map +1 -1
  45. package/lib/module/ui/social-button.js +19 -14
  46. package/lib/module/ui/social-button.js.map +1 -1
  47. package/lib/module/ui/social-button.web.js +16 -10
  48. package/lib/module/ui/social-button.web.js.map +1 -1
  49. package/lib/module/use-auth.js +22 -25
  50. package/lib/module/use-auth.js.map +1 -1
  51. package/lib/module/utils/auth-error.js +30 -0
  52. package/lib/module/utils/auth-error.js.map +1 -0
  53. package/lib/module/utils/logger.js +1 -0
  54. package/lib/module/utils/logger.js.map +1 -1
  55. package/lib/typescript/commonjs/Auth.web.d.ts +5 -1
  56. package/lib/typescript/commonjs/Auth.web.d.ts.map +1 -1
  57. package/lib/typescript/commonjs/index.d.ts +1 -0
  58. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  59. package/lib/typescript/commonjs/index.web.d.ts +2 -1
  60. package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
  61. package/lib/typescript/commonjs/service.d.ts.map +1 -1
  62. package/lib/typescript/commonjs/service.web.d.ts +2 -18
  63. package/lib/typescript/commonjs/service.web.d.ts.map +1 -1
  64. package/lib/typescript/commonjs/ui/social-button.d.ts.map +1 -1
  65. package/lib/typescript/commonjs/ui/social-button.web.d.ts.map +1 -1
  66. package/lib/typescript/commonjs/use-auth.d.ts +2 -1
  67. package/lib/typescript/commonjs/use-auth.d.ts.map +1 -1
  68. package/lib/typescript/commonjs/utils/auth-error.d.ts +16 -0
  69. package/lib/typescript/commonjs/utils/auth-error.d.ts.map +1 -0
  70. package/lib/typescript/commonjs/utils/logger.d.ts.map +1 -1
  71. package/lib/typescript/module/Auth.web.d.ts +5 -1
  72. package/lib/typescript/module/Auth.web.d.ts.map +1 -1
  73. package/lib/typescript/module/index.d.ts +1 -0
  74. package/lib/typescript/module/index.d.ts.map +1 -1
  75. package/lib/typescript/module/index.web.d.ts +2 -1
  76. package/lib/typescript/module/index.web.d.ts.map +1 -1
  77. package/lib/typescript/module/service.d.ts.map +1 -1
  78. package/lib/typescript/module/service.web.d.ts +2 -18
  79. package/lib/typescript/module/service.web.d.ts.map +1 -1
  80. package/lib/typescript/module/ui/social-button.d.ts.map +1 -1
  81. package/lib/typescript/module/ui/social-button.web.d.ts.map +1 -1
  82. package/lib/typescript/module/use-auth.d.ts +2 -1
  83. package/lib/typescript/module/use-auth.d.ts.map +1 -1
  84. package/lib/typescript/module/utils/auth-error.d.ts +16 -0
  85. package/lib/typescript/module/utils/auth-error.d.ts.map +1 -0
  86. package/lib/typescript/module/utils/logger.d.ts.map +1 -1
  87. package/nitrogen/generated/android/NitroAuthOnLoad.cpp +22 -17
  88. package/nitrogen/generated/android/NitroAuthOnLoad.hpp +13 -4
  89. package/package.json +8 -10
  90. package/src/Auth.web.ts +77 -11
  91. package/src/global.d.ts +0 -11
  92. package/src/index.ts +5 -0
  93. package/src/index.web.ts +6 -1
  94. package/src/service.ts +37 -9
  95. package/src/service.web.ts +84 -15
  96. package/src/ui/social-button.tsx +21 -9
  97. package/src/ui/social-button.web.tsx +17 -4
  98. package/src/use-auth.ts +29 -67
  99. package/src/utils/auth-error.ts +49 -0
  100. package/src/utils/logger.ts +1 -0
@@ -1,8 +1,14 @@
1
+ import type {
2
+ Auth,
3
+ AuthProvider,
4
+ AuthTokens,
5
+ LoginOptions,
6
+ AuthUser,
7
+ } from "./Auth.nitro";
1
8
  import { AuthModule } from "./Auth.web";
9
+ import { AuthError } from "./utils/auth-error";
2
10
 
3
- export const AuthService = {
4
- ...AuthModule,
5
-
11
+ export const AuthService: Auth = {
6
12
  get name() {
7
13
  return AuthModule.name;
8
14
  },
@@ -19,16 +25,79 @@ export const AuthService = {
19
25
  return AuthModule.hasPlayServices;
20
26
  },
21
27
 
22
- login: AuthModule.login.bind(AuthModule),
23
- logout: AuthModule.logout.bind(AuthModule),
24
- requestScopes: AuthModule.requestScopes.bind(AuthModule),
25
- revokeScopes: AuthModule.revokeScopes.bind(AuthModule),
26
- getAccessToken: AuthModule.getAccessToken.bind(AuthModule),
27
- refreshToken: AuthModule.refreshToken.bind(AuthModule),
28
- silentRestore: AuthModule.silentRestore.bind(AuthModule),
29
- onAuthStateChanged: AuthModule.onAuthStateChanged.bind(AuthModule),
30
- onTokensRefreshed: AuthModule.onTokensRefreshed.bind(AuthModule),
31
- setLoggingEnabled: AuthModule.setLoggingEnabled.bind(AuthModule),
32
- dispose: AuthModule.dispose.bind(AuthModule),
33
- equals: AuthModule.equals.bind(AuthModule),
28
+ async login(provider: AuthProvider, options?: LoginOptions) {
29
+ try {
30
+ await AuthModule.login(provider, options);
31
+ return;
32
+ } catch (e) {
33
+ throw AuthError.from(e);
34
+ }
35
+ },
36
+
37
+ async requestScopes(scopes: string[]) {
38
+ try {
39
+ await AuthModule.requestScopes(scopes);
40
+ return;
41
+ } catch (e) {
42
+ throw AuthError.from(e);
43
+ }
44
+ },
45
+
46
+ async revokeScopes(scopes: string[]) {
47
+ try {
48
+ await AuthModule.revokeScopes(scopes);
49
+ return;
50
+ } catch (e) {
51
+ throw AuthError.from(e);
52
+ }
53
+ },
54
+
55
+ async getAccessToken() {
56
+ try {
57
+ return await AuthModule.getAccessToken();
58
+ } catch (e) {
59
+ throw AuthError.from(e);
60
+ }
61
+ },
62
+
63
+ async refreshToken() {
64
+ try {
65
+ return await AuthModule.refreshToken();
66
+ } catch (e) {
67
+ throw AuthError.from(e);
68
+ }
69
+ },
70
+
71
+ logout() {
72
+ AuthModule.logout();
73
+ },
74
+
75
+ async silentRestore() {
76
+ try {
77
+ await AuthModule.silentRestore();
78
+ return;
79
+ } catch (e) {
80
+ throw AuthError.from(e);
81
+ }
82
+ },
83
+
84
+ onAuthStateChanged(callback: (user: AuthUser | undefined) => void) {
85
+ return AuthModule.onAuthStateChanged(callback);
86
+ },
87
+
88
+ onTokensRefreshed(callback: (tokens: AuthTokens) => void) {
89
+ return AuthModule.onTokensRefreshed(callback);
90
+ },
91
+
92
+ setLoggingEnabled(enabled: boolean) {
93
+ AuthModule.setLoggingEnabled(enabled);
94
+ },
95
+
96
+ dispose() {
97
+ AuthModule.dispose();
98
+ },
99
+
100
+ equals(other: Parameters<Auth["equals"]>[0]): boolean {
101
+ return AuthModule.equals(other);
102
+ },
34
103
  };
@@ -7,8 +7,9 @@ import {
7
7
  View,
8
8
  ActivityIndicator,
9
9
  } from "react-native";
10
- import { NitroModules } from "react-native-nitro-modules";
11
- import type { Auth, AuthProvider, AuthUser } from "../Auth.nitro";
10
+ import { AuthService } from "../service";
11
+ import { logger } from "../utils/logger";
12
+ import type { AuthProvider, AuthUser } from "../Auth.nitro";
12
13
 
13
14
  export type SocialButtonVariant = "primary" | "outline" | "white" | "black";
14
15
 
@@ -56,8 +57,7 @@ const getTextColor = (variant: SocialButtonVariant): string =>
56
57
  variant === "white" || variant === "outline" ? "#000000" : "#FFFFFF";
57
58
 
58
59
  async function performLogin(provider: AuthProvider): Promise<void> {
59
- const auth = NitroModules.createHybridObject<Auth>("Auth");
60
- await auth.login(provider);
60
+ await AuthService.login(provider);
61
61
  }
62
62
 
63
63
  export const SocialButton = ({
@@ -83,12 +83,16 @@ export const SocialButton = ({
83
83
  setLoading(true);
84
84
  try {
85
85
  await performLogin(provider);
86
- const user = NitroModules.createHybridObject<Auth>("Auth").currentUser;
86
+ const user = AuthService.currentUser;
87
87
  if (user) {
88
88
  onSuccess?.(user);
89
89
  }
90
90
  } catch (error) {
91
- onError?.(error);
91
+ if (onError) {
92
+ onError(error);
93
+ } else if (__DEV__) {
94
+ logger.error("SocialButton unhandled error:", error);
95
+ }
92
96
  } finally {
93
97
  setLoading(false);
94
98
  }
@@ -126,19 +130,21 @@ export const SocialButton = ({
126
130
  <>
127
131
  {provider === "google" && variant !== "primary" && (
128
132
  <View style={styles.iconPlaceholder}>
129
- <Text style={{ fontSize: 18 }}>G</Text>
133
+ <Text style={styles.iconText}>G</Text>
130
134
  </View>
131
135
  )}
132
136
  {provider === "apple" && variant !== "primary" && (
133
137
  <View style={styles.iconPlaceholder}>
134
- <Text style={{ fontSize: 18, color: getTextColor(variant) }}>
138
+ <Text
139
+ style={[styles.iconText, { color: getTextColor(variant) }]}
140
+ >
135
141
 
136
142
  </Text>
137
143
  </View>
138
144
  )}
139
145
  {provider === "microsoft" && variant !== "primary" && (
140
146
  <View style={styles.iconPlaceholder}>
141
- <Text style={{ fontSize: 16 }}>⊞</Text>
147
+ <Text style={styles.microsoftIconText}>⊞</Text>
142
148
  </View>
143
149
  )}
144
150
  <Text
@@ -179,4 +185,10 @@ const styles = StyleSheet.create({
179
185
  fontSize: 16,
180
186
  fontWeight: "600",
181
187
  },
188
+ iconText: {
189
+ fontSize: 18,
190
+ },
191
+ microsoftIconText: {
192
+ fontSize: 16,
193
+ },
182
194
  });
@@ -8,6 +8,7 @@ import {
8
8
  ActivityIndicator,
9
9
  } from "react-native";
10
10
  import { AuthModule } from "../Auth.web";
11
+ import { logger } from "../utils/logger";
11
12
  import type { AuthProvider, AuthUser } from "../Auth.nitro";
12
13
 
13
14
  export type SocialButtonVariant = "primary" | "outline" | "white" | "black";
@@ -87,7 +88,11 @@ export const SocialButton = ({
87
88
  onSuccess?.(user);
88
89
  }
89
90
  } catch (error) {
90
- onError?.(error);
91
+ if (onError) {
92
+ onError(error);
93
+ } else if (process.env.NODE_ENV !== "production") {
94
+ logger.error("SocialButton unhandled error:", error);
95
+ }
91
96
  } finally {
92
97
  setLoading(false);
93
98
  }
@@ -125,19 +130,21 @@ export const SocialButton = ({
125
130
  <>
126
131
  {provider === "google" && variant !== "primary" && (
127
132
  <View style={styles.iconPlaceholder}>
128
- <Text style={{ fontSize: 18 }}>G</Text>
133
+ <Text style={styles.iconText}>G</Text>
129
134
  </View>
130
135
  )}
131
136
  {provider === "apple" && variant !== "primary" && (
132
137
  <View style={styles.iconPlaceholder}>
133
- <Text style={{ fontSize: 18, color: getTextColor(variant) }}>
138
+ <Text
139
+ style={[styles.iconText, { color: getTextColor(variant) }]}
140
+ >
134
141
 
135
142
  </Text>
136
143
  </View>
137
144
  )}
138
145
  {provider === "microsoft" && variant !== "primary" && (
139
146
  <View style={styles.iconPlaceholder}>
140
- <Text style={{ fontSize: 16 }}>⊞</Text>
147
+ <Text style={styles.microsoftIconText}>⊞</Text>
141
148
  </View>
142
149
  )}
143
150
  <Text
@@ -178,4 +185,10 @@ const styles = StyleSheet.create({
178
185
  fontSize: 16,
179
186
  fontWeight: "600",
180
187
  },
188
+ iconText: {
189
+ fontSize: 18,
190
+ },
191
+ microsoftIconText: {
192
+ fontSize: 16,
193
+ },
181
194
  });
package/src/use-auth.ts CHANGED
@@ -6,38 +6,25 @@ import type {
6
6
  AuthTokens,
7
7
  } from "./Auth.nitro";
8
8
  import { AuthService } from "./service";
9
+ import { AuthError } from "./utils/auth-error";
9
10
 
10
11
  type AuthState = {
11
12
  user: AuthUser | undefined;
12
13
  scopes: string[];
13
14
  loading: boolean;
14
- error: Error | undefined;
15
+ error: AuthError | undefined;
15
16
  };
16
17
 
17
18
  const areScopesEqual = (left: string[], right: string[]): boolean => {
18
- if (left.length !== right.length) {
19
- return false;
19
+ if (left.length !== right.length) return false;
20
+ const sortedLeft = [...left].sort();
21
+ const sortedRight = [...right].sort();
22
+ for (let i = 0; i < sortedLeft.length; i += 1) {
23
+ if (sortedLeft[i] !== sortedRight[i]) return false;
20
24
  }
21
-
22
- for (let index = 0; index < left.length; index += 1) {
23
- if (left[index] !== right[index]) {
24
- return false;
25
- }
26
- }
27
-
28
25
  return true;
29
26
  };
30
27
 
31
- class AuthHookError extends Error {
32
- public readonly underlyingError?: string;
33
-
34
- constructor(message: string, underlyingError?: string) {
35
- super(message);
36
- this.name = "AuthHookError";
37
- this.underlyingError = underlyingError;
38
- }
39
- }
40
-
41
28
  export type UseAuthReturn = AuthState & {
42
29
  hasPlayServices: boolean;
43
30
  login: (provider: AuthProvider, options?: LoginOptions) => Promise<void>;
@@ -58,7 +45,7 @@ export function useAuth(): UseAuthReturn {
58
45
  });
59
46
 
60
47
  const syncStateFromService = useCallback(
61
- (nextLoading: boolean, nextError: Error | undefined) => {
48
+ (nextLoading: boolean, nextError: AuthError | undefined) => {
62
49
  const nextUser = AuthService.currentUser;
63
50
  const nextScopes = AuthService.grantedScopes;
64
51
  setState((prev) => {
@@ -70,7 +57,6 @@ export function useAuth(): UseAuthReturn {
70
57
  ) {
71
58
  return prev;
72
59
  }
73
-
74
60
  return {
75
61
  user: nextUser,
76
62
  scopes: nextScopes,
@@ -89,12 +75,8 @@ export function useAuth(): UseAuthReturn {
89
75
  await AuthService.login(provider, options);
90
76
  syncStateFromService(false, undefined);
91
77
  } catch (e) {
92
- const error = e instanceof Error ? e : new Error(String(e));
93
- setState((prev) => ({
94
- ...prev,
95
- loading: false,
96
- error,
97
- }));
78
+ const error = AuthError.from(e);
79
+ setState((prev) => ({ ...prev, loading: false, error }));
98
80
  throw error;
99
81
  }
100
82
  },
@@ -103,12 +85,7 @@ export function useAuth(): UseAuthReturn {
103
85
 
104
86
  const logout = useCallback(() => {
105
87
  AuthService.logout();
106
- setState({
107
- user: undefined,
108
- scopes: [],
109
- loading: false,
110
- error: undefined,
111
- });
88
+ setState({ user: undefined, scopes: [], loading: false, error: undefined });
112
89
  }, []);
113
90
 
114
91
  const requestScopes = useCallback(
@@ -118,12 +95,8 @@ export function useAuth(): UseAuthReturn {
118
95
  await AuthService.requestScopes(newScopes);
119
96
  syncStateFromService(false, undefined);
120
97
  } catch (e) {
121
- const error = e instanceof Error ? e : new Error(String(e));
122
- setState((prev) => ({
123
- ...prev,
124
- loading: false,
125
- error,
126
- }));
98
+ const error = AuthError.from(e);
99
+ setState((prev) => ({ ...prev, loading: false, error }));
127
100
  throw error;
128
101
  }
129
102
  },
@@ -137,12 +110,8 @@ export function useAuth(): UseAuthReturn {
137
110
  await AuthService.revokeScopes(scopesToRevoke);
138
111
  syncStateFromService(false, undefined);
139
112
  } catch (e) {
140
- const error = e instanceof Error ? e : new Error(String(e));
141
- setState((prev) => ({
142
- ...prev,
143
- loading: false,
144
- error,
145
- }));
113
+ const error = AuthError.from(e);
114
+ setState((prev) => ({ ...prev, loading: false, error }));
146
115
  throw error;
147
116
  }
148
117
  },
@@ -158,17 +127,9 @@ export function useAuth(): UseAuthReturn {
158
127
  syncStateFromService(false, undefined);
159
128
  return tokens;
160
129
  } catch (e) {
161
- const msg = e instanceof Error ? e.message : String(e);
162
- const authError = new AuthHookError(
163
- msg,
164
- AuthService.currentUser?.underlyingError,
165
- );
166
- setState((prev) => ({
167
- ...prev,
168
- loading: false,
169
- error: authError,
170
- }));
171
- throw authError;
130
+ const error = AuthError.from(e);
131
+ setState((prev) => ({ ...prev, loading: false, error }));
132
+ throw error;
172
133
  }
173
134
  }, [syncStateFromService]);
174
135
 
@@ -178,18 +139,14 @@ export function useAuth(): UseAuthReturn {
178
139
  await AuthService.silentRestore();
179
140
  syncStateFromService(false, undefined);
180
141
  } catch (e) {
181
- const error = e instanceof Error ? e : new Error(String(e));
182
- setState((prev) => ({
183
- ...prev,
184
- loading: false,
185
- error,
186
- }));
142
+ const error = AuthError.from(e);
143
+ setState((prev) => ({ ...prev, loading: false, error }));
187
144
  throw error;
188
145
  }
189
146
  }, [syncStateFromService]);
190
147
 
191
148
  useEffect(() => {
192
- const unsubscribe = AuthService.onAuthStateChanged((currentUser) => {
149
+ const unsubscribeAuth = AuthService.onAuthStateChanged((currentUser) => {
193
150
  const nextScopes = AuthService.grantedScopes;
194
151
  setState((prev) => {
195
152
  if (
@@ -199,7 +156,6 @@ export function useAuth(): UseAuthReturn {
199
156
  ) {
200
157
  return prev;
201
158
  }
202
-
203
159
  return {
204
160
  ...prev,
205
161
  user: currentUser,
@@ -208,8 +164,14 @@ export function useAuth(): UseAuthReturn {
208
164
  };
209
165
  });
210
166
  });
211
- return unsubscribe;
212
- }, []);
167
+ const unsubscribeTokens = AuthService.onTokensRefreshed?.(() => {
168
+ syncStateFromService(false, undefined);
169
+ });
170
+ return () => {
171
+ unsubscribeAuth();
172
+ unsubscribeTokens?.();
173
+ };
174
+ }, [syncStateFromService]);
213
175
 
214
176
  return useMemo(
215
177
  () => ({
@@ -0,0 +1,49 @@
1
+ import type { AuthErrorCode } from "../Auth.nitro";
2
+
3
+ const AUTH_ERROR_CODES: ReadonlySet<string> = new Set<AuthErrorCode>([
4
+ "cancelled",
5
+ "timeout",
6
+ "popup_blocked",
7
+ "network_error",
8
+ "configuration_error",
9
+ "unsupported_provider",
10
+ "invalid_state",
11
+ "invalid_nonce",
12
+ "token_error",
13
+ "no_id_token",
14
+ "parse_error",
15
+ "refresh_failed",
16
+ "unknown",
17
+ ]);
18
+
19
+ export function isAuthErrorCode(value: string): value is AuthErrorCode {
20
+ return AUTH_ERROR_CODES.has(value);
21
+ }
22
+
23
+ export function toAuthErrorCode(raw: string): AuthErrorCode {
24
+ return isAuthErrorCode(raw) ? raw : "unknown";
25
+ }
26
+
27
+ /**
28
+ * Typed error thrown by all AuthService operations.
29
+ *
30
+ * - `code` — always a valid `AuthErrorCode`, safe to switch on
31
+ * - `underlyingMessage` — the raw platform message when it differs from `code`
32
+ */
33
+ export class AuthError extends Error {
34
+ readonly code: AuthErrorCode;
35
+ readonly underlyingMessage: string | undefined;
36
+
37
+ constructor(raw: unknown) {
38
+ const message = raw instanceof Error ? raw.message : String(raw);
39
+ const code = toAuthErrorCode(message);
40
+ super(code);
41
+ this.name = "AuthError";
42
+ this.code = code;
43
+ this.underlyingMessage = code !== message ? message : undefined;
44
+ }
45
+
46
+ static from(e: unknown): AuthError {
47
+ return e instanceof AuthError ? e : new AuthError(e);
48
+ }
49
+ }
@@ -1,3 +1,4 @@
1
+ /* eslint-disable no-console */
1
2
  let enabled = false;
2
3
 
3
4
  export const logger = {