react-native-nitro-auth 0.1.6 → 0.4.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.md +350 -38
- package/android/build.gradle +1 -1
- package/android/src/main/cpp/PlatformAuth+Android.cpp +27 -9
- package/android/src/main/java/com/auth/AuthAdapter.kt +18 -18
- package/android/src/main/java/com/auth/GoogleSignInActivity.kt +18 -3
- package/app.plugin.js +3 -3
- package/cpp/HybridAuth.cpp +20 -0
- package/cpp/HybridAuth.hpp +1 -0
- package/ios/AuthAdapter.swift +48 -47
- package/ios/PlatformAuth+iOS.mm +37 -24
- package/lib/commonjs/Auth.web.js +27 -21
- package/lib/commonjs/Auth.web.js.map +1 -1
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/service.js +130 -1
- package/lib/commonjs/service.js.map +1 -1
- package/lib/commonjs/service.web.js +40 -6
- package/lib/commonjs/service.web.js.map +1 -1
- package/lib/commonjs/ui/social-button.js +35 -7
- package/lib/commonjs/ui/social-button.js.map +1 -1
- package/lib/commonjs/ui/social-button.web.js +35 -7
- package/lib/commonjs/ui/social-button.web.js.map +1 -1
- package/lib/commonjs/use-auth.js +29 -2
- package/lib/commonjs/use-auth.js.map +1 -1
- package/lib/module/Auth.web.js +27 -21
- package/lib/module/Auth.web.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/package.json +1 -0
- package/lib/module/service.js +130 -1
- package/lib/module/service.js.map +1 -1
- package/lib/module/service.web.js +40 -1
- package/lib/module/service.web.js.map +1 -1
- package/lib/module/ui/social-button.js +36 -8
- package/lib/module/ui/social-button.js.map +1 -1
- package/lib/module/ui/social-button.web.js +36 -8
- package/lib/module/ui/social-button.web.js.map +1 -1
- package/lib/module/use-auth.js +29 -2
- package/lib/module/use-auth.js.map +1 -1
- package/lib/typescript/{Auth.nitro.d.ts → commonjs/Auth.nitro.d.ts} +7 -0
- package/lib/typescript/commonjs/Auth.nitro.d.ts.map +1 -0
- package/lib/typescript/{Auth.web.d.ts → commonjs/Auth.web.d.ts} +2 -1
- package/lib/typescript/commonjs/Auth.web.d.ts.map +1 -0
- package/lib/typescript/{AuthStorage.nitro.d.ts → commonjs/AuthStorage.nitro.d.ts} +7 -0
- package/lib/typescript/commonjs/AuthStorage.nitro.d.ts.map +1 -0
- package/lib/typescript/{index.d.ts → commonjs/index.d.ts} +1 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/index.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/package.json +1 -0
- package/lib/typescript/commonjs/service.d.ts +10 -0
- package/lib/typescript/commonjs/service.d.ts.map +1 -0
- package/lib/typescript/commonjs/service.web.d.ts +30 -0
- package/lib/typescript/commonjs/service.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/social-button.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/social-button.web.d.ts.map +1 -0
- package/lib/typescript/{use-auth.d.ts → commonjs/use-auth.d.ts} +1 -0
- package/lib/typescript/commonjs/use-auth.d.ts.map +1 -0
- package/lib/typescript/commonjs/utils/logger.d.ts.map +1 -0
- package/lib/typescript/module/Auth.nitro.d.ts +51 -0
- package/lib/typescript/module/Auth.nitro.d.ts.map +1 -0
- package/lib/typescript/module/Auth.web.d.ts +41 -0
- package/lib/typescript/module/Auth.web.d.ts.map +1 -0
- package/lib/typescript/module/AuthStorage.nitro.d.ts +26 -0
- package/lib/typescript/module/AuthStorage.nitro.d.ts.map +1 -0
- package/lib/typescript/module/index.d.ts +6 -0
- package/lib/typescript/module/index.d.ts.map +1 -0
- package/lib/typescript/module/index.web.d.ts +6 -0
- package/lib/typescript/module/index.web.d.ts.map +1 -0
- package/lib/typescript/module/package.json +1 -0
- package/lib/typescript/module/service.d.ts +10 -0
- package/lib/typescript/module/service.d.ts.map +1 -0
- package/lib/typescript/module/service.web.d.ts +30 -0
- package/lib/typescript/module/service.web.d.ts.map +1 -0
- package/lib/typescript/module/ui/social-button.d.ts +17 -0
- package/lib/typescript/module/ui/social-button.d.ts.map +1 -0
- package/lib/typescript/module/ui/social-button.web.d.ts +17 -0
- package/lib/typescript/module/ui/social-button.web.d.ts.map +1 -0
- package/lib/typescript/module/use-auth.d.ts +16 -0
- package/lib/typescript/module/use-auth.d.ts.map +1 -0
- package/lib/typescript/module/utils/logger.d.ts +8 -0
- package/lib/typescript/module/utils/logger.d.ts.map +1 -0
- package/nitrogen/generated/android/NitroAuth+autolinking.cmake +1 -1
- package/nitrogen/generated/android/NitroAuth+autolinking.gradle +1 -1
- package/nitrogen/generated/android/NitroAuthOnLoad.cpp +1 -1
- package/nitrogen/generated/android/NitroAuthOnLoad.hpp +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/auth/NitroAuthOnLoad.kt +1 -1
- package/nitrogen/generated/ios/NitroAuth+autolinking.rb +2 -2
- package/nitrogen/generated/ios/NitroAuth-Swift-Cxx-Bridge.cpp +1 -1
- package/nitrogen/generated/ios/NitroAuth-Swift-Cxx-Bridge.hpp +1 -1
- package/nitrogen/generated/ios/NitroAuth-Swift-Cxx-Umbrella.hpp +1 -1
- package/nitrogen/generated/ios/NitroAuthAutolinking.mm +1 -1
- package/nitrogen/generated/ios/NitroAuthAutolinking.swift +5 -1
- package/nitrogen/generated/shared/c++/AuthProvider.hpp +1 -1
- package/nitrogen/generated/shared/c++/AuthTokens.hpp +19 -11
- package/nitrogen/generated/shared/c++/AuthUser.hpp +42 -30
- package/nitrogen/generated/shared/c++/HybridAuthSpec.cpp +2 -1
- package/nitrogen/generated/shared/c++/HybridAuthSpec.hpp +2 -1
- package/nitrogen/generated/shared/c++/HybridAuthStorageAdapterSpec.cpp +1 -1
- package/nitrogen/generated/shared/c++/HybridAuthStorageAdapterSpec.hpp +1 -1
- package/nitrogen/generated/shared/c++/LoginOptions.hpp +28 -12
- package/package.json +7 -5
- package/react-native-nitro-auth.podspec +1 -1
- package/src/Auth.nitro.ts +8 -1
- package/src/Auth.web.ts +50 -30
- package/src/AuthStorage.nitro.ts +11 -2
- package/src/index.ts +1 -1
- package/src/service.ts +167 -2
- package/src/service.web.ts +50 -1
- package/src/ui/social-button.tsx +25 -3
- package/src/ui/social-button.web.tsx +25 -3
- package/src/use-auth.ts +27 -2
- package/lib/typescript/Auth.nitro.d.ts.map +0 -1
- package/lib/typescript/Auth.web.d.ts.map +0 -1
- package/lib/typescript/AuthStorage.nitro.d.ts.map +0 -1
- package/lib/typescript/index.d.ts.map +0 -1
- package/lib/typescript/index.web.d.ts.map +0 -1
- package/lib/typescript/service.d.ts +0 -3
- package/lib/typescript/service.d.ts.map +0 -1
- package/lib/typescript/service.web.d.ts +0 -2
- package/lib/typescript/service.web.d.ts.map +0 -1
- package/lib/typescript/ui/social-button.d.ts.map +0 -1
- package/lib/typescript/ui/social-button.web.d.ts.map +0 -1
- package/lib/typescript/use-auth.d.ts.map +0 -1
- package/lib/typescript/utils/logger.d.ts.map +0 -1
- /package/lib/typescript/{index.web.d.ts → commonjs/index.web.d.ts} +0 -0
- /package/lib/typescript/{ui → commonjs/ui}/social-button.d.ts +0 -0
- /package/lib/typescript/{ui → commonjs/ui}/social-button.web.d.ts +0 -0
- /package/lib/typescript/{utils → commonjs/utils}/logger.d.ts +0 -0
package/README.md
CHANGED
|
@@ -27,20 +27,32 @@ Nitro Auth is designed to replace legacy modules like `@react-native-google-sign
|
|
|
27
27
|
- **Expo Ready**: Comes with a powerful Config Plugin for zero-config setup.
|
|
28
28
|
- **Cross-Platform**: Unified API for iOS, Android, and Web.
|
|
29
29
|
- **Auto-Refresh**: Synchronous access to tokens with automatic silent refresh.
|
|
30
|
-
- **Google One-Tap**: Modern login experience on Android
|
|
31
|
-
- **
|
|
30
|
+
- **Google One-Tap / Sheet**: Modern login experience on Android (Credential Manager) and iOS (Sign-In Sheet).
|
|
31
|
+
- **Error Metadata**: Detailed native error messages for easier debugging.
|
|
32
|
+
- **Custom Storage**: Pluggable storage adapters for secure persistence (e.g. Keychain, MMKV, AsyncStorage).
|
|
32
33
|
- **Refresh Interceptors**: Listen to token updates globally.
|
|
33
34
|
|
|
34
35
|
## Installation
|
|
35
36
|
|
|
36
37
|
```bash
|
|
37
38
|
bun add react-native-nitro-auth react-native-nitro-modules
|
|
38
|
-
|
|
39
|
+
# or
|
|
40
|
+
npm install react-native-nitro-auth react-native-nitro-modules
|
|
41
|
+
# or
|
|
42
|
+
yarn add react-native-nitro-auth react-native-nitro-modules
|
|
43
|
+
# or
|
|
44
|
+
pnpm add react-native-nitro-auth react-native-nitro-modules
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
For Expo projects, rebuild native code after installation:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
bunx expo prebuild
|
|
39
51
|
```
|
|
40
52
|
|
|
41
53
|
### Expo Setup
|
|
42
54
|
|
|
43
|
-
Add the plugin to `app.json`:
|
|
55
|
+
Add the plugin to `app.json` or `app.config.js`:
|
|
44
56
|
|
|
45
57
|
```json
|
|
46
58
|
{
|
|
@@ -51,23 +63,70 @@ Add the plugin to `app.json`:
|
|
|
51
63
|
{
|
|
52
64
|
"ios": {
|
|
53
65
|
"googleClientId": "YOUR_IOS_CLIENT_ID.apps.googleusercontent.com",
|
|
54
|
-
"googleServerClientId": "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com",
|
|
55
66
|
"googleUrlScheme": "com.googleusercontent.apps.YOUR_IOS_CLIENT_ID",
|
|
56
67
|
"appleSignIn": true
|
|
57
68
|
},
|
|
58
69
|
"android": {
|
|
59
|
-
"googleClientId": "
|
|
70
|
+
"googleClientId": "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com"
|
|
60
71
|
}
|
|
61
72
|
}
|
|
62
73
|
]
|
|
63
|
-
]
|
|
74
|
+
],
|
|
75
|
+
"extra": {
|
|
76
|
+
"googleWebClientId": "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com"
|
|
77
|
+
}
|
|
64
78
|
}
|
|
65
79
|
}
|
|
66
80
|
```
|
|
67
81
|
|
|
82
|
+
**Using environment variables (recommended):**
|
|
83
|
+
|
|
84
|
+
Create a `.env.local` file:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# iOS Client ID
|
|
88
|
+
GOOGLE_IOS_CLIENT_ID=your-ios-client-id.apps.googleusercontent.com
|
|
89
|
+
GOOGLE_IOS_URL_SCHEME=com.googleusercontent.apps.your-ios-client-id
|
|
90
|
+
|
|
91
|
+
# Web Client ID (used for Android OAuth flow)
|
|
92
|
+
GOOGLE_WEB_CLIENT_ID=your-web-client-id.apps.googleusercontent.com
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Then reference them in `app.config.js`:
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
import "dotenv/config";
|
|
99
|
+
|
|
100
|
+
export default {
|
|
101
|
+
expo: {
|
|
102
|
+
plugins: [
|
|
103
|
+
[
|
|
104
|
+
"react-native-nitro-auth",
|
|
105
|
+
{
|
|
106
|
+
ios: {
|
|
107
|
+
googleClientId: process.env.GOOGLE_IOS_CLIENT_ID,
|
|
108
|
+
googleUrlScheme: process.env.GOOGLE_IOS_URL_SCHEME,
|
|
109
|
+
appleSignIn: true,
|
|
110
|
+
},
|
|
111
|
+
android: {
|
|
112
|
+
googleClientId: process.env.GOOGLE_WEB_CLIENT_ID,
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
],
|
|
117
|
+
extra: {
|
|
118
|
+
googleWebClientId: process.env.GOOGLE_WEB_CLIENT_ID,
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
```
|
|
123
|
+
|
|
68
124
|
> [!NOTE]
|
|
69
|
-
>
|
|
70
|
-
> `
|
|
125
|
+
>
|
|
126
|
+
> - `appleSignIn` on iOS is `false` by default to avoid unnecessary entitlements. Set it to `true` to enable Apple Sign-In.
|
|
127
|
+
> - For Android, use your **Web Client ID** (not Android Client ID) for proper OAuth flow.
|
|
128
|
+
> - Add `googleWebClientId` to `expo.extra` for web platform support.
|
|
129
|
+
> - The `serverAuthCode` is automatically included in `AuthUser` when available (requires backend integration setup in Google Cloud Console).
|
|
71
130
|
|
|
72
131
|
### Bare React Native
|
|
73
132
|
|
|
@@ -110,8 +169,96 @@ function LoginScreen() {
|
|
|
110
169
|
}
|
|
111
170
|
```
|
|
112
171
|
|
|
172
|
+
## Migration from @react-native-google-signin/google-signin
|
|
173
|
+
|
|
174
|
+
If you are using `@react-native-google-signin/google-signin`, the migration to Nitro Auth is mostly a drop-in at the API level, but the setup is different because Nitro Auth uses a config plugin and JSI.
|
|
175
|
+
|
|
176
|
+
### 1) Replace the dependency
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
bun remove @react-native-google-signin/google-signin
|
|
180
|
+
bun add react-native-nitro-auth react-native-nitro-modules
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### 2) Move configuration to the Nitro Auth plugin
|
|
184
|
+
|
|
185
|
+
Nitro Auth does not use `GoogleSignin.configure(...)`. Instead, set your client IDs via the config plugin (Expo) or native config (bare).
|
|
186
|
+
|
|
187
|
+
**Expo** (recommended):
|
|
188
|
+
|
|
189
|
+
```json
|
|
190
|
+
{
|
|
191
|
+
"expo": {
|
|
192
|
+
"plugins": [
|
|
193
|
+
[
|
|
194
|
+
"react-native-nitro-auth",
|
|
195
|
+
{
|
|
196
|
+
"ios": {
|
|
197
|
+
"googleClientId": "YOUR_IOS_CLIENT_ID.apps.googleusercontent.com",
|
|
198
|
+
"googleUrlScheme": "com.googleusercontent.apps.YOUR_IOS_CLIENT_ID"
|
|
199
|
+
},
|
|
200
|
+
"android": {
|
|
201
|
+
"googleClientId": "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com"
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
]
|
|
205
|
+
],
|
|
206
|
+
"extra": {
|
|
207
|
+
"googleWebClientId": "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com"
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**Bare React Native:**
|
|
214
|
+
|
|
215
|
+
- iOS: add `GIDClientID` (and optionally `GIDServerClientID`) to `Info.plist` and set the URL scheme.
|
|
216
|
+
- Android: add `nitro_auth_google_client_id` string resource in `res/values/strings.xml` (use your Web Client ID).
|
|
217
|
+
|
|
218
|
+
### 3) Update API usage
|
|
219
|
+
|
|
220
|
+
| @react-native-google-signin/google-signin | Nitro Auth |
|
|
221
|
+
| ----------------------------------------- | --------------------------------------------------------- |
|
|
222
|
+
| `GoogleSignin.configure({...})` | Configure in plugin / native config |
|
|
223
|
+
| `GoogleSignin.signIn()` | `login("google")` or `<SocialButton provider="google" />` |
|
|
224
|
+
| `GoogleSignin.signOut()` | `logout()` |
|
|
225
|
+
| `GoogleSignin.getTokens()` | `getAccessToken()` or `refreshToken()` |
|
|
226
|
+
| `GoogleSignin.hasPlayServices()` | `hasPlayServices` from `useAuth()` |
|
|
227
|
+
|
|
228
|
+
**Example migration:**
|
|
229
|
+
|
|
230
|
+
```tsx
|
|
231
|
+
// Before
|
|
232
|
+
import { GoogleSignin } from "@react-native-google-signin/google-signin";
|
|
233
|
+
|
|
234
|
+
await GoogleSignin.signIn();
|
|
235
|
+
const tokens = await GoogleSignin.getTokens();
|
|
236
|
+
|
|
237
|
+
// After
|
|
238
|
+
import { useAuth } from "react-native-nitro-auth";
|
|
239
|
+
|
|
240
|
+
const { login, getAccessToken } = useAuth();
|
|
241
|
+
|
|
242
|
+
await login("google");
|
|
243
|
+
const accessToken = await getAccessToken();
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### 4) Remove manual init
|
|
247
|
+
|
|
248
|
+
If you previously called `GoogleSignin.configure()` at app startup, remove it. Nitro Auth loads configuration from the plugin/native settings at runtime.
|
|
249
|
+
|
|
113
250
|
## Advanced Features
|
|
114
251
|
|
|
252
|
+
### Silent Restore
|
|
253
|
+
|
|
254
|
+
Automatically restore the user session on app startup. This is faster than a full login and works offline if the session is cached.
|
|
255
|
+
|
|
256
|
+
```tsx
|
|
257
|
+
useEffect(() => {
|
|
258
|
+
AuthService.silentRestore();
|
|
259
|
+
}, []);
|
|
260
|
+
```
|
|
261
|
+
|
|
115
262
|
### Global Auth State Listener
|
|
116
263
|
|
|
117
264
|
Subscribe to authentication changes outside of React components:
|
|
@@ -131,6 +278,95 @@ const unsubscribe = AuthService.onAuthStateChanged((user) => {
|
|
|
131
278
|
unsubscribe();
|
|
132
279
|
```
|
|
133
280
|
|
|
281
|
+
### Global Token Refresh Listener
|
|
282
|
+
|
|
283
|
+
Be notified whenever tokens are refreshed automatically (or manually):
|
|
284
|
+
|
|
285
|
+
```ts
|
|
286
|
+
import { AuthService } from "react-native-nitro-auth";
|
|
287
|
+
|
|
288
|
+
const unsubscribe = AuthService.onTokensRefreshed((tokens) => {
|
|
289
|
+
console.log("New tokens:", tokens.accessToken);
|
|
290
|
+
// Update your API client / Apollo links
|
|
291
|
+
});
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Incremental Authorization
|
|
295
|
+
|
|
296
|
+
Request new scopes when you need them without logging the user out:
|
|
297
|
+
|
|
298
|
+
```tsx
|
|
299
|
+
const { requestScopes, revokeScopes, scopes } = useAuth();
|
|
300
|
+
|
|
301
|
+
const handleCalendar = async () => {
|
|
302
|
+
try {
|
|
303
|
+
await requestScopes(["https://www.googleapis.com/auth/calendar.readonly"]);
|
|
304
|
+
console.log("Got calendar access!");
|
|
305
|
+
} catch (e) {
|
|
306
|
+
console.error("Scope request failed");
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Pluggable Storage Adapters
|
|
312
|
+
|
|
313
|
+
Nitro Auth persists the session automatically. By default, it uses simple file-based storage on native and `localStorage` on web.
|
|
314
|
+
|
|
315
|
+
#### 1) JS Storage (AsyncStorage, MMKV, etc.)
|
|
316
|
+
|
|
317
|
+
Easily swap the default storage with your preferred library from the JS layer:
|
|
318
|
+
|
|
319
|
+
```ts
|
|
320
|
+
import { AuthService, type JSStorageAdapter } from "react-native-nitro-auth";
|
|
321
|
+
import { MMKV } from "react-native-mmkv";
|
|
322
|
+
|
|
323
|
+
const storage = new MMKV();
|
|
324
|
+
|
|
325
|
+
const mmkvAdapter: JSStorageAdapter = {
|
|
326
|
+
save: (key, value) => storage.set(key, value),
|
|
327
|
+
load: (key) => storage.getString(key),
|
|
328
|
+
remove: (key) => storage.delete(key),
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
// Set it once at app startup
|
|
332
|
+
AuthService.setJSStorageAdapter(mmkvAdapter);
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
#### 2) Native Storage (Keychain, etc.)
|
|
336
|
+
|
|
337
|
+
For maximum security, you can implement a native HybridObject (C++, Swift, or Kotlin) and pass it to Nitro. This runs directly in memory at the C++ layer.
|
|
338
|
+
|
|
339
|
+
```ts
|
|
340
|
+
import { AuthService } from "react-native-nitro-auth";
|
|
341
|
+
// Import your native Nitro module
|
|
342
|
+
import { KeychainStorage } from "./native/KeychainStorage";
|
|
343
|
+
|
|
344
|
+
AuthService.setStorageAdapter(KeychainStorage);
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Logging & Debugging
|
|
348
|
+
|
|
349
|
+
Enable verbose logging to see detailed OAuth flow information in the console:
|
|
350
|
+
|
|
351
|
+
```ts
|
|
352
|
+
import { AuthService } from "react-native-nitro-auth";
|
|
353
|
+
|
|
354
|
+
AuthService.setLoggingEnabled(true);
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Sync Access to Tokens
|
|
358
|
+
|
|
359
|
+
Nitro Auth provides synchronous access to the current state, while still supporting silent refresh:
|
|
360
|
+
|
|
361
|
+
```ts
|
|
362
|
+
// Quick access to what we have in memory
|
|
363
|
+
const user = AuthService.currentUser;
|
|
364
|
+
const scopes = AuthService.grantedScopes;
|
|
365
|
+
|
|
366
|
+
// Async access ensures fresh tokens (will refresh if expired)
|
|
367
|
+
const freshToken = await AuthService.getAccessToken();
|
|
368
|
+
```
|
|
369
|
+
|
|
134
370
|
### Standardized Error Codes
|
|
135
371
|
|
|
136
372
|
Handle failures reliably with predictable error strings:
|
|
@@ -139,20 +375,42 @@ Handle failures reliably with predictable error strings:
|
|
|
139
375
|
try {
|
|
140
376
|
await login("google");
|
|
141
377
|
} catch (e) {
|
|
142
|
-
|
|
378
|
+
const error = e as Error;
|
|
379
|
+
if (error.message === "cancelled") {
|
|
143
380
|
// User closed the popup/picker
|
|
144
|
-
} else if (
|
|
381
|
+
} else if (error.message === "network_error") {
|
|
145
382
|
// Connection issues
|
|
146
383
|
}
|
|
147
384
|
}
|
|
148
385
|
```
|
|
149
386
|
|
|
150
|
-
| Code
|
|
387
|
+
| Error Code | Description |
|
|
151
388
|
| ---------------------- | ---------------------------------------------- |
|
|
152
389
|
| `cancelled` | The user cancelled the sign-in flow |
|
|
153
390
|
| `network_error` | A network error occurred |
|
|
154
391
|
| `configuration_error` | Missing client IDs or invalid setup |
|
|
155
392
|
| `unsupported_provider` | The provider is not supported on this platform |
|
|
393
|
+
| `unknown` | An unknown error occurred |
|
|
394
|
+
|
|
395
|
+
### Native Error Metadata
|
|
396
|
+
|
|
397
|
+
For more detailed debugging, Nitro Auth captures the raw native error message. You can access it from the authenticated user or cast the error:
|
|
398
|
+
|
|
399
|
+
```ts
|
|
400
|
+
// From authenticated user (on success)
|
|
401
|
+
const { user } = useAuth();
|
|
402
|
+
if (user?.underlyingError) {
|
|
403
|
+
console.warn("Auth warning:", user.underlyingError);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// From error (on failure)
|
|
407
|
+
try {
|
|
408
|
+
await login("google");
|
|
409
|
+
} catch (e) {
|
|
410
|
+
const error = e as Error & { underlyingError?: string };
|
|
411
|
+
console.log("Native error:", error.underlyingError);
|
|
412
|
+
}
|
|
413
|
+
```
|
|
156
414
|
|
|
157
415
|
### Automatic Token Refresh
|
|
158
416
|
|
|
@@ -177,6 +435,9 @@ await requestScopes(["https://www.googleapis.com/auth/calendar.readonly"]);
|
|
|
177
435
|
|
|
178
436
|
// Check granted scopes
|
|
179
437
|
console.log("Granted:", scopes);
|
|
438
|
+
|
|
439
|
+
// Revoke specific scopes
|
|
440
|
+
await revokeScopes(["https://www.googleapis.com/auth/calendar.readonly"]);
|
|
180
441
|
```
|
|
181
442
|
|
|
182
443
|
### Offline Access (Server Auth Code)
|
|
@@ -194,24 +455,38 @@ if (user?.serverAuthCode) {
|
|
|
194
455
|
|
|
195
456
|
### Custom Storage Adapter
|
|
196
457
|
|
|
197
|
-
By default, Nitro Auth uses standard local storage. You can provide a custom adapter for better security
|
|
458
|
+
By default, Nitro Auth uses standard local storage. You can provide a custom adapter for better security.
|
|
459
|
+
|
|
460
|
+
> [!IMPORTANT]
|
|
461
|
+
> `AuthStorageAdapter` must be implemented as a **native Nitro HybridObject** in C++, Swift, or Kotlin. Plain JavaScript objects are not supported due to Nitro's type system. See [Nitro Hybrid Objects documentation](https://nitro.margelo.com/docs/hybrid-objects) for implementation details.
|
|
462
|
+
|
|
463
|
+
**Example (Swift):**
|
|
464
|
+
|
|
465
|
+
```swift
|
|
466
|
+
class HybridKeychainStorage: HybridAuthStorageAdapterSpec {
|
|
467
|
+
func save(key: String, value: String) {
|
|
468
|
+
// Save to Keychain
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
func load(key: String) -> String? {
|
|
472
|
+
// Load from Keychain
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
func remove(key: String) {
|
|
476
|
+
// Remove from Keychain
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
**Usage (TypeScript):**
|
|
198
482
|
|
|
199
483
|
```ts
|
|
484
|
+
import { NitroModules } from "react-native-nitro-modules";
|
|
200
485
|
import { AuthService, AuthStorageAdapter } from "react-native-nitro-auth";
|
|
201
486
|
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
},
|
|
206
|
-
load: (key) => {
|
|
207
|
-
/* Load from Keychain */
|
|
208
|
-
},
|
|
209
|
-
remove: (key) => {
|
|
210
|
-
/* Clear from Keychain */
|
|
211
|
-
},
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
AuthService.setStorageAdapter(myStorage);
|
|
487
|
+
const keychainStorage =
|
|
488
|
+
NitroModules.createHybridObject<AuthStorageAdapter>("KeychainStorage");
|
|
489
|
+
AuthService.setStorageAdapter(keychainStorage);
|
|
215
490
|
```
|
|
216
491
|
|
|
217
492
|
### Token Refresh Listeners
|
|
@@ -221,20 +496,39 @@ Perfect for updating your API client (e.g., Axios/Fetch) whenever tokens are ref
|
|
|
221
496
|
```ts
|
|
222
497
|
AuthService.onTokensRefreshed((tokens) => {
|
|
223
498
|
console.log("Tokens were updated!", tokens.accessToken);
|
|
224
|
-
apiClient.defaults.headers.common[
|
|
225
|
-
|
|
226
|
-
] = `Bearer ${tokens.accessToken}`;
|
|
499
|
+
apiClient.defaults.headers.common["Authorization"] =
|
|
500
|
+
`Bearer ${tokens.accessToken}`;
|
|
227
501
|
});
|
|
228
502
|
```
|
|
229
503
|
|
|
230
|
-
### Google One-Tap
|
|
504
|
+
### Google One-Tap & Sheet
|
|
231
505
|
|
|
232
|
-
Explicitly enable the modern One-Tap flow on Android:
|
|
506
|
+
Explicitly enable the modern One-Tap flow on Android or the Sign-In Sheet on iOS:
|
|
233
507
|
|
|
234
508
|
```ts
|
|
235
|
-
await login("google", {
|
|
509
|
+
await login("google", {
|
|
510
|
+
useOneTap: true, // Android
|
|
511
|
+
useSheet: true, // iOS
|
|
512
|
+
});
|
|
236
513
|
```
|
|
237
514
|
|
|
515
|
+
### Force Account Picker
|
|
516
|
+
|
|
517
|
+
When connecting additional services (like Google Calendar), you may want to let users pick a different account than the one they signed in with. Use `forceAccountPicker` to clear any cached session and show the account picker:
|
|
518
|
+
|
|
519
|
+
```ts
|
|
520
|
+
await login("google", {
|
|
521
|
+
scopes: ["https://www.googleapis.com/auth/calendar.readonly"],
|
|
522
|
+
forceAccountPicker: true, // Always show account picker
|
|
523
|
+
});
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
This is useful for scenarios where:
|
|
527
|
+
|
|
528
|
+
- Users want to connect a different Google account for calendar integration
|
|
529
|
+
- You need to ensure the user can select any account they've added to their device
|
|
530
|
+
- The cached session is interfering with the expected account selection UX
|
|
531
|
+
|
|
238
532
|
## API Reference
|
|
239
533
|
|
|
240
534
|
### useAuth Hook
|
|
@@ -248,18 +542,36 @@ await login("google", { useOneTap: true });
|
|
|
248
542
|
| `hasPlayServices` | `boolean` | (Android) True if Play Services available |
|
|
249
543
|
| `login` | `(provider, options?) => Promise` | Start login flow |
|
|
250
544
|
| `logout` | `() => void` | Clear session (synchronous) |
|
|
545
|
+
| `silentRestore` | `() => Promise<void>` | Restore session automatically on startup |
|
|
251
546
|
| `requestScopes` | `(scopes) => Promise` | Request additional OAuth scopes |
|
|
547
|
+
| `revokeScopes` | `(scopes) => Promise` | Revoke previously granted scopes |
|
|
252
548
|
| `getAccessToken` | `() => Promise<string?>` | Get current access token (auto-refreshes) |
|
|
253
549
|
| `refreshToken` | `() => Promise<AuthTokens>` | Explicitly refresh and return new tokens |
|
|
254
550
|
|
|
551
|
+
### LoginOptions
|
|
552
|
+
|
|
553
|
+
| Option | Type | Platform | Description |
|
|
554
|
+
| -------------------- | ---------- | -------- | ----------------------------------------------- |
|
|
555
|
+
| `scopes` | `string[]` | All | Required OAuth scopes (default: email, profile) |
|
|
556
|
+
| `loginHint` | `string` | All | Pre-fill email address in the login picker |
|
|
557
|
+
| `useOneTap` | `boolean` | Android | Enable Google One-Tap (Credential Manager) |
|
|
558
|
+
| `useSheet` | `boolean` | iOS | Enable iOS Google Sign-In Sheet |
|
|
559
|
+
| `forceAccountPicker` | `boolean` | All | Always show the account selection screen |
|
|
560
|
+
| `webClientId` | `string` | Web | Override the default Google Web Client ID |
|
|
561
|
+
|
|
255
562
|
### SocialButton Props
|
|
256
563
|
|
|
257
|
-
| Prop
|
|
258
|
-
|
|
|
259
|
-
| `provider`
|
|
260
|
-
| `variant`
|
|
261
|
-
| `
|
|
262
|
-
| `
|
|
564
|
+
| Prop | Type | Default | Description |
|
|
565
|
+
| -------------- | ---------------------------------------------- | ----------- | --------------------------------------------- |
|
|
566
|
+
| `provider` | `"google" \| "apple"` | required | Authentication provider |
|
|
567
|
+
| `variant` | `"primary" \| "outline" \| "white" \| "black"` | `"primary"` | Button style variant |
|
|
568
|
+
| `onPress` | `() => void` | — | Custom handler (disables default login) |
|
|
569
|
+
| `onSuccess` | `(user: AuthUser) => void` | — | Called with user data on success (auto-login) |
|
|
570
|
+
| `onError` | `(error: unknown) => void` | — | Called on failure (auto-login) |
|
|
571
|
+
| `disabled` | `boolean` | `false` | Disable button interaction |
|
|
572
|
+
| `style` | `ViewStyle` | — | Custom container styles |
|
|
573
|
+
| `textStyle` | `TextStyle` | — | Custom text styles |
|
|
574
|
+
| `borderRadius` | `number` | `8` | Button border radius |
|
|
263
575
|
|
|
264
576
|
## Platform Support
|
|
265
577
|
|
package/android/build.gradle
CHANGED
|
@@ -93,7 +93,7 @@ dependencies {
|
|
|
93
93
|
implementation project(":react-native-nitro-modules")
|
|
94
94
|
|
|
95
95
|
// Google Sign-In SDK (full scope support)
|
|
96
|
-
implementation "com.google.android.gms:play-services-auth:21.
|
|
96
|
+
implementation "com.google.android.gms:play-services-auth:21.5.0"
|
|
97
97
|
|
|
98
98
|
// Activity result APIs
|
|
99
99
|
implementation "androidx.activity:activity-ktx:1.9.3"
|
|
@@ -41,11 +41,13 @@ std::shared_ptr<Promise<AuthUser>> PlatformAuth::login(AuthProvider provider, co
|
|
|
41
41
|
std::vector<std::string> scopes = {"email", "profile"};
|
|
42
42
|
std::optional<std::string> loginHint;
|
|
43
43
|
bool useOneTap = false;
|
|
44
|
+
bool forceAccountPicker = false;
|
|
44
45
|
|
|
45
46
|
if (options) {
|
|
46
47
|
if (options->scopes) scopes = *options->scopes;
|
|
47
48
|
loginHint = options->loginHint;
|
|
48
49
|
useOneTap = options->useOneTap.value_or(false);
|
|
50
|
+
forceAccountPicker = options->forceAccountPicker.value_or(false);
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
JNIEnv* env = Environment::current();
|
|
@@ -58,14 +60,15 @@ std::shared_ptr<Promise<AuthUser>> PlatformAuth::login(AuthProvider provider, co
|
|
|
58
60
|
jstring jLoginHint = loginHint.has_value() ? make_jstring(loginHint.value()).get() : nullptr;
|
|
59
61
|
jclass adapterClass = env->FindClass("com/auth/AuthAdapter");
|
|
60
62
|
jmethodID loginMethod = env->GetStaticMethodID(adapterClass, "loginSync",
|
|
61
|
-
"(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;
|
|
63
|
+
"(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;ZZ)V");
|
|
62
64
|
env->CallStaticVoidMethod(adapterClass, loginMethod,
|
|
63
65
|
contextPtr,
|
|
64
66
|
make_jstring(providerStr).get(),
|
|
65
67
|
nullptr,
|
|
66
68
|
jScopes,
|
|
67
69
|
jLoginHint,
|
|
68
|
-
(jboolean)useOneTap
|
|
70
|
+
(jboolean)useOneTap,
|
|
71
|
+
(jboolean)forceAccountPicker);
|
|
69
72
|
|
|
70
73
|
return promise;
|
|
71
74
|
}
|
|
@@ -225,13 +228,14 @@ extern "C" JNIEXPORT void JNICALL Java_com_auth_AuthAdapter_nativeOnLoginSuccess
|
|
|
225
228
|
jmethodID longValueMethod = env->GetMethodID(longClass, "longValue", "()J");
|
|
226
229
|
user.expirationTime = (double)env->CallLongMethod(expirationTime, longValueMethod);
|
|
227
230
|
}
|
|
231
|
+
|
|
228
232
|
if (loginPromise) loginPromise->resolve(user);
|
|
229
233
|
if (scopesPromise) scopesPromise->resolve(user);
|
|
230
234
|
if (silentPromise) silentPromise->resolve(user);
|
|
231
235
|
}
|
|
232
236
|
|
|
233
237
|
extern "C" JNIEXPORT void JNICALL Java_com_auth_AuthAdapter_nativeOnLoginError(
|
|
234
|
-
JNIEnv* env, jclass, jstring error) {
|
|
238
|
+
JNIEnv* env, jclass, jstring error, jstring underlyingError) {
|
|
235
239
|
|
|
236
240
|
std::shared_ptr<Promise<AuthUser>> loginPromise;
|
|
237
241
|
std::shared_ptr<Promise<AuthUser>> scopesPromise;
|
|
@@ -249,12 +253,19 @@ extern "C" JNIEXPORT void JNICALL Java_com_auth_AuthAdapter_nativeOnLoginError(
|
|
|
249
253
|
const char* errorCStr = env->GetStringUTFChars(error, nullptr);
|
|
250
254
|
std::string errorStr(errorCStr);
|
|
251
255
|
env->ReleaseStringUTFChars(error, errorCStr);
|
|
256
|
+
|
|
257
|
+
std::string finalError = errorStr;
|
|
258
|
+
if (underlyingError) {
|
|
259
|
+
const char* uCStr = env->GetStringUTFChars(underlyingError, nullptr);
|
|
260
|
+
finalError = std::string(uCStr);
|
|
261
|
+
env->ReleaseStringUTFChars(underlyingError, uCStr);
|
|
262
|
+
}
|
|
252
263
|
|
|
253
|
-
if (loginPromise) loginPromise->reject(std::make_exception_ptr(std::runtime_error(
|
|
254
|
-
if (scopesPromise) scopesPromise->reject(std::make_exception_ptr(std::runtime_error(
|
|
264
|
+
if (loginPromise) loginPromise->reject(std::make_exception_ptr(std::runtime_error(finalError)));
|
|
265
|
+
if (scopesPromise) scopesPromise->reject(std::make_exception_ptr(std::runtime_error(finalError)));
|
|
255
266
|
if (silentPromise) {
|
|
256
267
|
if (errorStr == "No session") silentPromise->resolve(std::nullopt);
|
|
257
|
-
else silentPromise->reject(std::make_exception_ptr(std::runtime_error(
|
|
268
|
+
else silentPromise->reject(std::make_exception_ptr(std::runtime_error(finalError)));
|
|
258
269
|
}
|
|
259
270
|
}
|
|
260
271
|
|
|
@@ -290,7 +301,7 @@ extern "C" JNIEXPORT void JNICALL Java_com_auth_AuthAdapter_nativeOnRefreshSucce
|
|
|
290
301
|
}
|
|
291
302
|
|
|
292
303
|
extern "C" JNIEXPORT void JNICALL Java_com_auth_AuthAdapter_nativeOnRefreshError(
|
|
293
|
-
JNIEnv* env, jclass, jstring error) {
|
|
304
|
+
JNIEnv* env, jclass, jstring error, jstring underlyingError) {
|
|
294
305
|
|
|
295
306
|
std::shared_ptr<Promise<AuthTokens>> refreshPromise;
|
|
296
307
|
{
|
|
@@ -299,10 +310,17 @@ extern "C" JNIEXPORT void JNICALL Java_com_auth_AuthAdapter_nativeOnRefreshError
|
|
|
299
310
|
gRefreshPromise = nullptr;
|
|
300
311
|
}
|
|
301
312
|
if (refreshPromise) {
|
|
313
|
+
std::string finalError;
|
|
302
314
|
const char* errorCStr = env->GetStringUTFChars(error, nullptr);
|
|
303
|
-
std::string
|
|
315
|
+
finalError = std::string(errorCStr);
|
|
304
316
|
env->ReleaseStringUTFChars(error, errorCStr);
|
|
305
|
-
|
|
317
|
+
|
|
318
|
+
if (underlyingError) {
|
|
319
|
+
const char* uCStr = env->GetStringUTFChars(underlyingError, nullptr);
|
|
320
|
+
finalError = std::string(uCStr);
|
|
321
|
+
env->ReleaseStringUTFChars(underlyingError, uCStr);
|
|
322
|
+
}
|
|
323
|
+
refreshPromise->reject(std::make_exception_ptr(std::runtime_error(finalError)));
|
|
306
324
|
}
|
|
307
325
|
}
|
|
308
326
|
|