react-native-nitro-auth 0.5.7 → 0.5.8
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 +288 -913
- package/android/src/main/java/com/auth/AuthAdapter.kt +22 -6
- package/cpp/HybridAuth.cpp +58 -7
- package/cpp/HybridAuth.hpp +1 -0
- package/ios/AuthAdapter.swift +2 -2
- package/lib/commonjs/utils/auth-error.js +8 -1
- package/lib/commonjs/utils/auth-error.js.map +1 -1
- package/lib/module/utils/auth-error.js +8 -1
- package/lib/module/utils/auth-error.js.map +1 -1
- package/lib/typescript/commonjs/utils/auth-error.d.ts.map +1 -1
- package/lib/typescript/module/utils/auth-error.d.ts.map +1 -1
- package/package.json +7 -6
- package/src/utils/auth-error.ts +10 -1
package/README.md
CHANGED
|
@@ -1,213 +1,70 @@
|
|
|
1
1
|
# react-native-nitro-auth
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
[](https://opensource.org/licenses/MIT)
|
|
5
|
-
[](https://nitro.margelo.com)
|
|
3
|
+
Fast React Native authentication for Google Sign-In, Apple Sign-In, and Microsoft Entra ID, built on Nitro Modules and JSI.
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
`react-native-nitro-auth` gives Expo and React Native apps one typed API for native social login, web OAuth, token refresh, incremental scopes, and auth state listeners without owning your app's long-term token storage.
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
## Why Use It?
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
- One package for Google, Apple, and Microsoft authentication on React Native.
|
|
10
|
+
- Native iOS and Android bridges powered by `react-native-nitro-modules`.
|
|
11
|
+
- Expo config plugin for client IDs, URL schemes, entitlements, and Android resources.
|
|
12
|
+
- Web implementation for Expo web with Google, Apple, and Microsoft OAuth.
|
|
13
|
+
- Typed `useAuth()` hook, `AuthService`, `SocialButton`, and `AuthError`.
|
|
14
|
+
- App-owned persistence model: tokens stay in memory unless your app stores a snapshot.
|
|
15
|
+
- Built-in flows for silent restore, token refresh, account picker, login hints, and incremental Google scopes.
|
|
12
16
|
|
|
13
|
-
|
|
17
|
+
## Choose Your Path
|
|
14
18
|
|
|
15
|
-
|
|
|
16
|
-
|
|
|
17
|
-
|
|
|
18
|
-
|
|
|
19
|
-
|
|
|
20
|
-
|
|
|
21
|
-
| **Provider Data** | Varies | **Normalized auth payload** |
|
|
19
|
+
| Need | Use |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| Google, Apple, or Microsoft sign-in in an Expo or React Native app | `react-native-nitro-auth` |
|
|
22
|
+
| Generic OAuth or OIDC provider not covered by this package | `expo-auth-session` or `react-native-app-auth` |
|
|
23
|
+
| Firebase user management, password auth, MFA, and hosted auth platform | `@react-native-firebase/auth`, Auth0, Authgear, or your IDaaS SDK |
|
|
24
|
+
| Server-side session validation | Your backend; client JWT decode is display-only |
|
|
22
25
|
|
|
23
|
-
##
|
|
26
|
+
## Install
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
- **Fully Type-Safe**: Shared types between TypeScript, C++, Swift, and Kotlin.
|
|
27
|
-
- **Incremental Auth**: Request additional OAuth scopes on the fly.
|
|
28
|
-
- **Expo Ready**: Comes with a powerful Config Plugin for zero-config setup.
|
|
29
|
-
- **Cross-Platform**: Unified API for iOS, Android, and Web.
|
|
30
|
-
- **Token Lifecycle Helpers**: `getAccessToken()` refreshes near-expiry tokens; `refreshToken()` allows explicit refresh control.
|
|
31
|
-
- **Google One-Tap / Sheet**: Modern login experience on Android (Credential Manager) and iOS (Sign-In Sheet).
|
|
32
|
-
- **Error Metadata**: Detailed native error messages for easier debugging.
|
|
33
|
-
- **Normalized Provider Payload**: Exposes provider/user/token fields in a consistent cross-platform shape.
|
|
34
|
-
- **App-Owned Persistence**: Native is stateless by default. Web keeps a non-sensitive session snapshot; apps decide long-term persistence strategy.
|
|
35
|
-
|
|
36
|
-
## Design Philosophy
|
|
37
|
-
|
|
38
|
-
This is an **auth-only package** - on **native** (iOS/Android) it does NOT store auth data by default.
|
|
39
|
-
On **web**, Nitro Auth stores a non-sensitive auth snapshot in `sessionStorage` by default; sensitive tokens stay memory-only unless explicitly enabled.
|
|
40
|
-
|
|
41
|
-
The package provides:
|
|
42
|
-
|
|
43
|
-
- Login/logout functionality for Google, Apple, and Microsoft
|
|
44
|
-
- Token management (access token, refresh token, ID token)
|
|
45
|
-
- Scope management (request/revoke scopes)
|
|
46
|
-
- Consistent provider/user/token field exposure across iOS, Android, and Web
|
|
47
|
-
|
|
48
|
-
**Storage is the responsibility of the app using this package.** Use your own storage layer (for example [react-native-nitro-storage](https://github.com/JoaoPauloCMarra/react-native-nitro-storage)) to persist app-level auth snapshots/tokens when needed.
|
|
49
|
-
|
|
50
|
-
### Provider Data Availability
|
|
51
|
-
|
|
52
|
-
Token/data shape is normalized, but provider SDKs do not always return all fields at login time:
|
|
53
|
-
|
|
54
|
-
| Provider + Flow | `idToken` | `accessToken` | `serverAuthCode` | `expirationTime` |
|
|
55
|
-
| --------------------------- | ---------- | ---------------------------- | ---------------------------- | ----------------------------------- |
|
|
56
|
-
| Google (iOS) | Usually ✅ | Provider-dependent | Optional (if configured) | Optional |
|
|
57
|
-
| Google (Android One-Tap) | ✅ | Usually `undefined` at login | `undefined` | Derived from ID token when possible |
|
|
58
|
-
| Google (Android Legacy) | ✅ | Usually `undefined` at login | ✅ (if server client is set) | Derived from ID token when possible |
|
|
59
|
-
| Google (Web) | ✅ | ✅ | Optional (`code`) | Usually ✅ |
|
|
60
|
-
| Microsoft (iOS/Android/Web) | ✅ | Usually ✅ | `undefined` | Usually ✅ |
|
|
61
|
-
| Apple (iOS/Web) | ✅ | `undefined` | `undefined` | `undefined` |
|
|
62
|
-
|
|
63
|
-
> [!NOTE]
|
|
64
|
-
> On Apple, email/name can be limited after first consent depending on Apple policy.
|
|
65
|
-
|
|
66
|
-
## Installation
|
|
67
|
-
|
|
68
|
-
```bash
|
|
28
|
+
```sh
|
|
69
29
|
bun add react-native-nitro-auth react-native-nitro-modules
|
|
70
30
|
```
|
|
71
31
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
| Dependency | Version |
|
|
75
|
-
| ---------------------------- | ------------ |
|
|
76
|
-
| `react-native` | `>= 0.75.0` |
|
|
77
|
-
| `react-native-nitro-modules` | `>= 0.35.0` |
|
|
78
|
-
| `react` | `*` |
|
|
79
|
-
|
|
80
|
-
For Expo projects, rebuild native code after installation:
|
|
32
|
+
For Expo projects, prebuild after adding the config plugin:
|
|
81
33
|
|
|
82
|
-
```
|
|
83
|
-
bunx expo prebuild
|
|
34
|
+
```sh
|
|
35
|
+
bunx expo prebuild --clean
|
|
84
36
|
```
|
|
85
37
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
Fastest way to confirm the package and Microsoft login work:
|
|
89
|
-
|
|
90
|
-
1. **Azure app (one-time)**
|
|
91
|
-
In [Azure Portal](https://portal.azure.com) → **Azure Active Directory** → **App registrations** → **New registration**:
|
|
92
|
-
- Name: e.g. `Nitro Auth Example`
|
|
93
|
-
- Supported account types: **Accounts in any organizational directory and personal Microsoft accounts**
|
|
94
|
-
- Redirect URI (add after creation):
|
|
95
|
-
- **Android**: `msauth://com.auth.example/<client-id>`
|
|
96
|
-
- **iOS**: `msauth.com.auth.example://auth` (use your bundle id)
|
|
97
|
-
- Under **Authentication** → **Platform configurations** → add **Mobile and desktop applications** with the Android redirect URI above and the iOS one if testing on iOS.
|
|
98
|
-
Copy the **Application (client) ID**.
|
|
99
|
-
|
|
100
|
-
2. **Env file**
|
|
101
|
-
From the repo root:
|
|
102
|
-
|
|
103
|
-
```bash
|
|
104
|
-
cd apps/example
|
|
105
|
-
cp .env.example .env.local
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
Edit `.env.local` and set at least:
|
|
109
|
-
|
|
110
|
-
```bash
|
|
111
|
-
MICROSOFT_CLIENT_ID=<your-application-client-id>
|
|
112
|
-
MICROSOFT_TENANT=common
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
(Google/Apple can stay placeholder if you only care about Microsoft.)
|
|
116
|
-
|
|
117
|
-
3. **Run the app**
|
|
118
|
-
From the **monorepo root**:
|
|
119
|
-
|
|
120
|
-
```bash
|
|
121
|
-
bun install
|
|
122
|
-
bun run start
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
In a second terminal:
|
|
126
|
-
|
|
127
|
-
```bash
|
|
128
|
-
bun run example:android
|
|
129
|
-
# or
|
|
130
|
-
bun run example:ios
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
Wait for the app to install and open.
|
|
38
|
+
For bare React Native projects, install pods after installing the package:
|
|
134
39
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
If you see "configuration_error", check `MICROSOFT_CLIENT_ID` and that the redirect URI in Azure matches your app (e.g. `msauth://com.auth.example/<client-id>` for the example app).
|
|
138
|
-
|
|
139
|
-
> [!TIP]
|
|
140
|
-
> In the example app on Android, you can toggle **Legacy Google Sign-In** to compare Credential Manager vs legacy GoogleSignIn (and to get `serverAuthCode`).
|
|
141
|
-
|
|
142
|
-
### Expo Setup
|
|
143
|
-
|
|
144
|
-
Add the plugin to `app.json` or `app.config.js`:
|
|
145
|
-
|
|
146
|
-
```json
|
|
147
|
-
{
|
|
148
|
-
"expo": {
|
|
149
|
-
"plugins": [
|
|
150
|
-
[
|
|
151
|
-
"react-native-nitro-auth",
|
|
152
|
-
{
|
|
153
|
-
"ios": {
|
|
154
|
-
"googleClientId": "YOUR_IOS_CLIENT_ID.apps.googleusercontent.com",
|
|
155
|
-
"googleServerClientId": "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com",
|
|
156
|
-
"googleUrlScheme": "com.googleusercontent.apps.YOUR_IOS_CLIENT_ID",
|
|
157
|
-
"appleSignIn": true,
|
|
158
|
-
"microsoftClientId": "YOUR_AZURE_AD_CLIENT_ID",
|
|
159
|
-
"microsoftTenant": "common",
|
|
160
|
-
"microsoftB2cDomain": "your-tenant.b2clogin.com"
|
|
161
|
-
},
|
|
162
|
-
"android": {
|
|
163
|
-
"googleClientId": "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com",
|
|
164
|
-
"microsoftClientId": "YOUR_AZURE_AD_CLIENT_ID",
|
|
165
|
-
"microsoftTenant": "common",
|
|
166
|
-
"microsoftB2cDomain": "your-tenant.b2clogin.com"
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
]
|
|
170
|
-
],
|
|
171
|
-
"extra": {
|
|
172
|
-
"googleWebClientId": "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com",
|
|
173
|
-
"microsoftClientId": "YOUR_AZURE_AD_CLIENT_ID",
|
|
174
|
-
"microsoftTenant": "common",
|
|
175
|
-
"microsoftB2cDomain": "your-tenant.b2clogin.com",
|
|
176
|
-
"appleWebClientId": "com.example.web"
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
40
|
+
```sh
|
|
41
|
+
cd ios && pod install
|
|
180
42
|
```
|
|
181
43
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
Create a `.env.local` file:
|
|
44
|
+
## Requirements
|
|
185
45
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
46
|
+
| Runtime | Requirement |
|
|
47
|
+
| --- | --- |
|
|
48
|
+
| React Native | `>=0.75` |
|
|
49
|
+
| Nitro Modules | `>=0.35` |
|
|
50
|
+
| iOS | 15.1+ recommended |
|
|
51
|
+
| Android | min SDK 24+ recommended |
|
|
52
|
+
| Expo example baseline | Expo SDK 55, React Native 0.83, React 19 |
|
|
191
53
|
|
|
192
|
-
|
|
193
|
-
GOOGLE_WEB_CLIENT_ID=your-web-client-id.apps.googleusercontent.com
|
|
54
|
+
## Expo Setup
|
|
194
55
|
|
|
195
|
-
|
|
196
|
-
MICROSOFT_CLIENT_ID=your-azure-ad-application-id
|
|
197
|
-
MICROSOFT_TENANT=common
|
|
198
|
-
MICROSOFT_B2C_DOMAIN=your-tenant.b2clogin.com
|
|
199
|
-
|
|
200
|
-
# Apple (web only)
|
|
201
|
-
APPLE_WEB_CLIENT_ID=com.example.web
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
Then reference them in `app.config.js`:
|
|
205
|
-
|
|
206
|
-
```javascript
|
|
207
|
-
import "dotenv/config";
|
|
56
|
+
Add the plugin to `app.json` or `app.config.js`.
|
|
208
57
|
|
|
58
|
+
```js
|
|
209
59
|
export default {
|
|
210
60
|
expo: {
|
|
61
|
+
scheme: "myapp",
|
|
62
|
+
ios: {
|
|
63
|
+
bundleIdentifier: "com.company.myapp",
|
|
64
|
+
},
|
|
65
|
+
android: {
|
|
66
|
+
package: "com.company.myapp",
|
|
67
|
+
},
|
|
211
68
|
plugins: [
|
|
212
69
|
[
|
|
213
70
|
"react-native-nitro-auth",
|
|
@@ -232,841 +89,359 @@ export default {
|
|
|
232
89
|
],
|
|
233
90
|
extra: {
|
|
234
91
|
googleWebClientId: process.env.GOOGLE_WEB_CLIENT_ID,
|
|
92
|
+
appleWebClientId: process.env.APPLE_WEB_CLIENT_ID,
|
|
235
93
|
microsoftClientId: process.env.MICROSOFT_CLIENT_ID,
|
|
236
94
|
microsoftTenant: process.env.MICROSOFT_TENANT,
|
|
237
95
|
microsoftB2cDomain: process.env.MICROSOFT_B2C_DOMAIN,
|
|
238
|
-
|
|
96
|
+
nitroAuthWebStorage: "memory",
|
|
97
|
+
nitroAuthPersistTokensOnWeb: false,
|
|
239
98
|
},
|
|
240
99
|
},
|
|
241
100
|
};
|
|
242
101
|
```
|
|
243
102
|
|
|
244
|
-
|
|
245
|
-
>
|
|
246
|
-
> - `appleSignIn` on iOS is `false` by default to avoid unnecessary entitlements. Set it to `true` to enable Apple Sign-In.
|
|
247
|
-
> - For Android, use your **Web Client ID** (not Android Client ID) for proper OAuth flow.
|
|
248
|
-
> - If you need `serverAuthCode`, set `googleServerClientId` to your Web Client ID.
|
|
249
|
-
> - Add `googleWebClientId` to `expo.extra` for web platform support.
|
|
250
|
-
> - The `serverAuthCode` is automatically included in `AuthUser` when available (requires backend integration setup in Google Cloud Console).
|
|
251
|
-
> - For Microsoft Sign-In, use `common` tenant for multi-tenant apps, or specify your Azure AD tenant ID for single-tenant apps.
|
|
252
|
-
> - For Azure AD B2C, set `microsoftB2cDomain` and pass the B2C tenant in `microsoftTenant`.
|
|
253
|
-
|
|
254
|
-
### Google OAuth Setup
|
|
255
|
-
|
|
256
|
-
1. Create OAuth client IDs in Google Cloud Console:
|
|
257
|
-
- **iOS client ID** (used by iOS)
|
|
258
|
-
- **Web client ID** (used by Android and for `serverAuthCode`)
|
|
259
|
-
2. Configure your app:
|
|
260
|
-
- Expo: set `googleClientId`, `googleServerClientId`, and `googleUrlScheme`
|
|
261
|
-
- Bare iOS: add `GIDClientID`, `GIDServerClientID`, and URL scheme in `Info.plist`
|
|
262
|
-
- Bare Android: set `nitro_auth_google_client_id` to your **Web client ID**
|
|
263
|
-
3. If you use `serverAuthCode`, make sure OAuth consent screen is configured in Google Cloud.
|
|
264
|
-
|
|
265
|
-
### Apple Sign-In Setup
|
|
266
|
-
|
|
267
|
-
1. **iOS**: enable the “Sign in with Apple” capability in Xcode and in your Apple Developer account.
|
|
268
|
-
2. **Web**: create a Service ID and configure the domain + return URL in Apple Developer.
|
|
269
|
-
3. Configure your app:
|
|
270
|
-
- Expo: set `appleSignIn: true` for iOS.
|
|
271
|
-
- Web: set `appleWebClientId` in `expo.extra` (or `.env`).
|
|
272
|
-
|
|
273
|
-
### Microsoft Azure AD Setup
|
|
274
|
-
|
|
275
|
-
To enable Microsoft Sign-In, you need to register an application in the Azure Portal:
|
|
276
|
-
|
|
277
|
-
1. Go to [Azure Portal](https://portal.azure.com) > Azure Active Directory > App registrations
|
|
278
|
-
2. Click "New registration"
|
|
279
|
-
3. Set the redirect URIs:
|
|
280
|
-
- **iOS**: `msauth.{bundle-identifier}://auth` (e.g., `msauth.com.myapp://auth`)
|
|
281
|
-
- **Android**: `msauth://{package-name}/{client-id}` (e.g., `msauth://com.myapp/00000000-0000-0000-0000-000000000000`)
|
|
282
|
-
- **Web**: `https://your-domain.com` (the page that loads the app)
|
|
283
|
-
4. Under "API permissions", add `openid`, `email`, `profile`, and `User.Read` (Microsoft Graph)
|
|
284
|
-
5. Copy the Application (client) ID for use in your config
|
|
285
|
-
|
|
286
|
-
**Tenant Options:**
|
|
287
|
-
|
|
288
|
-
- `common` - Any Azure AD or personal Microsoft account
|
|
289
|
-
- `organizations` - Any Azure AD account (work/school)
|
|
290
|
-
- `consumers` - Personal Microsoft accounts only
|
|
291
|
-
- `{tenant-id}` - Specific Azure AD tenant
|
|
292
|
-
- **B2C**: set `microsoftB2cDomain` (e.g. `your-tenant.b2clogin.com`) and use a tenant value like `your-tenant.onmicrosoft.com/B2C_1_signin` (or pass a full `https://.../` authority URL).
|
|
293
|
-
|
|
294
|
-
### Bare React Native
|
|
295
|
-
|
|
296
|
-
**iOS**
|
|
297
|
-
|
|
298
|
-
- Add to `Info.plist`: `GIDClientID`, `GIDServerClientID` (optional), `MSALClientID`, `MSALTenant` (optional), `MSALB2cDomain` (optional).
|
|
299
|
-
- Add URL schemes in `Info.plist`:
|
|
300
|
-
- Google: `com.googleusercontent.apps.<YOUR_IOS_CLIENT_ID>`
|
|
301
|
-
- Microsoft: `msauth.<your.bundle.id>` (used for `msauth.<bundle.id>://auth`)
|
|
302
|
-
- Enable the “Sign in with Apple” capability if you use Apple Sign-In.
|
|
303
|
-
|
|
304
|
-
**Android**
|
|
305
|
-
|
|
306
|
-
- Add string resources in `res/values/strings.xml`:
|
|
307
|
-
- `nitro_auth_google_client_id` (Web client ID)
|
|
308
|
-
- `nitro_auth_microsoft_client_id`
|
|
309
|
-
- `nitro_auth_microsoft_tenant` (optional)
|
|
310
|
-
- `nitro_auth_microsoft_b2c_domain` (optional)
|
|
311
|
-
- `MicrosoftAuthActivity` is **automatically declared** by the library manifest — no manual `AndroidManifest.xml` entry is required for basic Microsoft OAuth redirect handling. If you need to customize the intent-filter (e.g., restrict the host/path to your specific package and client ID), you can override it in your app manifest:
|
|
312
|
-
|
|
313
|
-
```xml
|
|
314
|
-
<activity
|
|
315
|
-
android:name="com.auth.MicrosoftAuthActivity"
|
|
316
|
-
android:exported="true"
|
|
317
|
-
android:launchMode="singleTask">
|
|
318
|
-
<intent-filter>
|
|
319
|
-
<action android:name="android.intent.action.VIEW" />
|
|
320
|
-
<category android:name="android.intent.category.DEFAULT" />
|
|
321
|
-
<category android:name="android.intent.category.BROWSABLE" />
|
|
322
|
-
<data
|
|
323
|
-
android:scheme="msauth"
|
|
324
|
-
android:host="${applicationId}"
|
|
325
|
-
android:path="/YOUR_MICROSOFT_CLIENT_ID" />
|
|
326
|
-
</intent-filter>
|
|
327
|
-
</activity>
|
|
328
|
-
```
|
|
329
|
-
|
|
330
|
-
### Web Setup
|
|
331
|
-
|
|
332
|
-
Nitro Auth reads web configuration from `expo.extra`:
|
|
333
|
-
|
|
334
|
-
```json
|
|
335
|
-
{
|
|
336
|
-
"expo": {
|
|
337
|
-
"extra": {
|
|
338
|
-
"googleWebClientId": "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com",
|
|
339
|
-
"microsoftClientId": "YOUR_AZURE_AD_CLIENT_ID",
|
|
340
|
-
"microsoftTenant": "common",
|
|
341
|
-
"microsoftB2cDomain": "your-tenant.b2clogin.com",
|
|
342
|
-
"appleWebClientId": "com.example.web",
|
|
343
|
-
"nitroAuthWebStorage": "session",
|
|
344
|
-
"nitroAuthPersistTokensOnWeb": false
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
For Apple web sign-in, `appleWebClientId` must be your Apple Service ID. For Microsoft web, make sure your Azure app includes a Web redirect URI matching your site.
|
|
103
|
+
### Plugin Options
|
|
351
104
|
|
|
352
|
-
|
|
353
|
-
|
|
105
|
+
| Option | Platform | Purpose |
|
|
106
|
+
| --- | --- | --- |
|
|
107
|
+
| `ios.googleClientId` | iOS | Google iOS OAuth client ID |
|
|
108
|
+
| `ios.googleServerClientId` | iOS | Google web/server client ID for server auth code flows |
|
|
109
|
+
| `ios.googleUrlScheme` | iOS | Reversed iOS client ID URL scheme |
|
|
110
|
+
| `ios.appleSignIn` | iOS | Adds Apple Sign-In entitlement when `true` |
|
|
111
|
+
| `ios.microsoftClientId` | iOS | Microsoft app/client ID |
|
|
112
|
+
| `ios.microsoftTenant` | iOS | Microsoft tenant, `common`, `organizations`, `consumers`, or tenant ID |
|
|
113
|
+
| `ios.microsoftB2cDomain` | iOS | Azure AD B2C domain |
|
|
114
|
+
| `android.googleClientId` | Android | Google web OAuth client ID |
|
|
115
|
+
| `android.microsoftClientId` | Android | Microsoft app/client ID |
|
|
116
|
+
| `android.microsoftTenant` | Android | Microsoft tenant |
|
|
117
|
+
| `android.microsoftB2cDomain` | Android | Azure AD B2C domain |
|
|
354
118
|
|
|
355
|
-
##
|
|
119
|
+
## Provider Setup
|
|
356
120
|
|
|
357
|
-
###
|
|
121
|
+
### Google Sign-In
|
|
358
122
|
|
|
359
|
-
|
|
360
|
-
import { useAuth, SocialButton } from "react-native-nitro-auth";
|
|
361
|
-
|
|
362
|
-
function LoginScreen() {
|
|
363
|
-
const { user, loading, error, login, logout, hasPlayServices } = useAuth();
|
|
364
|
-
|
|
365
|
-
if (user) {
|
|
366
|
-
return (
|
|
367
|
-
<View>
|
|
368
|
-
<Image source={{ uri: user.photo }} />
|
|
369
|
-
<Text>{user.name}</Text>
|
|
370
|
-
<Button title="Sign Out" onPress={logout} />
|
|
371
|
-
</View>
|
|
372
|
-
);
|
|
373
|
-
}
|
|
123
|
+
Create OAuth clients in Google Cloud Console:
|
|
374
124
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
{!hasPlayServices && <Text>Please install Google Play Services</Text>}
|
|
125
|
+
- iOS client ID for your bundle identifier.
|
|
126
|
+
- Web client ID for Android, web, and server auth code flows.
|
|
127
|
+
- Android SHA-1/SHA-256 entries for local debug and release signing.
|
|
379
128
|
|
|
380
|
-
|
|
381
|
-
provider="google"
|
|
382
|
-
onPress={() => login("google")}
|
|
383
|
-
disabled={loading || !hasPlayServices}
|
|
384
|
-
/>
|
|
385
|
-
<SocialButton
|
|
386
|
-
provider="apple"
|
|
387
|
-
onPress={() => login("apple")}
|
|
388
|
-
disabled={loading}
|
|
389
|
-
/>
|
|
390
|
-
<SocialButton
|
|
391
|
-
provider="microsoft"
|
|
392
|
-
onPress={() => login("microsoft")}
|
|
393
|
-
disabled={loading}
|
|
394
|
-
/>
|
|
395
|
-
</View>
|
|
396
|
-
);
|
|
397
|
-
}
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
### Microsoft Login Options
|
|
401
|
-
|
|
402
|
-
```tsx
|
|
403
|
-
// Login with specific tenant
|
|
404
|
-
await login("microsoft", {
|
|
405
|
-
tenant: "your-tenant-id",
|
|
406
|
-
prompt: "select_account", // 'login' | 'consent' | 'select_account' | 'none'
|
|
407
|
-
scopes: ["openid", "email", "profile", "User.Read"],
|
|
408
|
-
loginHint: "user@example.com",
|
|
409
|
-
});
|
|
410
|
-
```
|
|
129
|
+
Use the iOS reversed client ID as `GOOGLE_IOS_URL_SCHEME`.
|
|
411
130
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
```tsx
|
|
415
|
-
await login("microsoft", {
|
|
416
|
-
tenant: "your-tenant.onmicrosoft.com/B2C_1_signin",
|
|
417
|
-
scopes: ["openid", "email", "profile", "offline_access"],
|
|
418
|
-
});
|
|
419
|
-
```
|
|
131
|
+
### Apple Sign-In
|
|
420
132
|
|
|
421
|
-
|
|
133
|
+
Set `ios.appleSignIn: true` in the config plugin. Apple returns name and email only on the first authorization for a user. Store any profile fields you need in your own backend or app state.
|
|
422
134
|
|
|
423
|
-
|
|
135
|
+
Apple Sign-In is supported on iOS and web. It is intentionally reported as `unsupported_provider` on Android.
|
|
424
136
|
|
|
425
|
-
###
|
|
137
|
+
### Microsoft Entra ID
|
|
426
138
|
|
|
427
|
-
|
|
428
|
-
bun remove @react-native-google-signin/google-signin
|
|
429
|
-
bun add react-native-nitro-auth react-native-nitro-modules
|
|
430
|
-
```
|
|
139
|
+
Create an app registration in Microsoft Entra ID and add redirect URIs:
|
|
431
140
|
|
|
432
|
-
|
|
141
|
+
- iOS: `msauth.<bundleIdentifier>://auth`
|
|
142
|
+
- Android: `msauth://<androidPackage>/<clientId>`
|
|
143
|
+
- Web: your web origin, for example `https://app.example.com`
|
|
433
144
|
|
|
434
|
-
|
|
145
|
+
Use `microsoftTenant` for `common`, `organizations`, `consumers`, a tenant ID, or a B2C policy path. Use `microsoftB2cDomain` for Azure AD B2C.
|
|
435
146
|
|
|
436
|
-
|
|
147
|
+
## Quick Start
|
|
437
148
|
|
|
438
|
-
```
|
|
439
|
-
{
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
]
|
|
454
|
-
],
|
|
455
|
-
"extra": {
|
|
456
|
-
"googleWebClientId": "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com"
|
|
149
|
+
```tsx
|
|
150
|
+
import { Button, Text, View } from "react-native";
|
|
151
|
+
import { AuthError, useAuth } from "react-native-nitro-auth";
|
|
152
|
+
|
|
153
|
+
export function SignInScreen() {
|
|
154
|
+
const { user, loading, login, logout, getAccessToken } = useAuth();
|
|
155
|
+
|
|
156
|
+
async function signInWithGoogle() {
|
|
157
|
+
try {
|
|
158
|
+
await login("google", {
|
|
159
|
+
scopes: ["email", "profile"],
|
|
160
|
+
});
|
|
161
|
+
} catch (e) {
|
|
162
|
+
const error = AuthError.from(e);
|
|
163
|
+
console.warn(error.code, error.underlyingMessage);
|
|
457
164
|
}
|
|
458
165
|
}
|
|
459
|
-
}
|
|
460
|
-
```
|
|
461
166
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
### 3) Update API usage
|
|
468
|
-
|
|
469
|
-
| @react-native-google-signin/google-signin | Nitro Auth |
|
|
470
|
-
| ----------------------------------------- | --------------------------------------------------------- |
|
|
471
|
-
| `GoogleSignin.configure({...})` | Configure in plugin / native config |
|
|
472
|
-
| `GoogleSignin.signIn()` | `login("google")` or `<SocialButton provider="google" />` |
|
|
473
|
-
| `GoogleSignin.signOut()` | `logout()` |
|
|
474
|
-
| `GoogleSignin.getTokens()` | `getAccessToken()` or `refreshToken()` |
|
|
475
|
-
| `GoogleSignin.hasPlayServices()` | `hasPlayServices` from `useAuth()` |
|
|
476
|
-
|
|
477
|
-
**Example migration:**
|
|
478
|
-
|
|
479
|
-
```tsx
|
|
480
|
-
// Before
|
|
481
|
-
import { GoogleSignin } from "@react-native-google-signin/google-signin";
|
|
482
|
-
|
|
483
|
-
await GoogleSignin.signIn();
|
|
484
|
-
const tokens = await GoogleSignin.getTokens();
|
|
485
|
-
|
|
486
|
-
// After
|
|
487
|
-
import { useAuth } from "react-native-nitro-auth";
|
|
488
|
-
|
|
489
|
-
const { login, getAccessToken } = useAuth();
|
|
167
|
+
async function readToken() {
|
|
168
|
+
const token = await getAccessToken();
|
|
169
|
+
console.log(token);
|
|
170
|
+
}
|
|
490
171
|
|
|
491
|
-
|
|
492
|
-
|
|
172
|
+
return (
|
|
173
|
+
<View>
|
|
174
|
+
<Text>{user?.email ?? "Signed out"}</Text>
|
|
175
|
+
<Button
|
|
176
|
+
title={loading ? "Signing in..." : "Sign in with Google"}
|
|
177
|
+
onPress={signInWithGoogle}
|
|
178
|
+
/>
|
|
179
|
+
<Button title="Get access token" onPress={readToken} />
|
|
180
|
+
<Button title="Sign out" onPress={logout} />
|
|
181
|
+
</View>
|
|
182
|
+
);
|
|
183
|
+
}
|
|
493
184
|
```
|
|
494
185
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
If you previously called `GoogleSignin.configure()` at app startup, remove it. Nitro Auth loads configuration from the plugin/native settings at runtime.
|
|
498
|
-
|
|
499
|
-
## Advanced Features
|
|
500
|
-
|
|
501
|
-
### Silent Restore
|
|
502
|
-
|
|
503
|
-
Attempts to restore provider SDK sessions on app startup.
|
|
504
|
-
|
|
505
|
-
- Google: restore is supported via provider SDK session state.
|
|
506
|
-
- Apple: provider credentials are re-requested by OS flow.
|
|
507
|
-
- Microsoft: no internal persistence; restore requires your app/backend session strategy.
|
|
186
|
+
## SocialButton
|
|
508
187
|
|
|
509
188
|
```tsx
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
189
|
+
import { SocialButton } from "react-native-nitro-auth";
|
|
190
|
+
|
|
191
|
+
export function AuthButtons() {
|
|
192
|
+
return (
|
|
193
|
+
<>
|
|
194
|
+
<SocialButton provider="google" />
|
|
195
|
+
<SocialButton provider="apple" variant="black" />
|
|
196
|
+
<SocialButton provider="microsoft" variant="outline" />
|
|
197
|
+
</>
|
|
198
|
+
);
|
|
199
|
+
}
|
|
513
200
|
```
|
|
514
201
|
|
|
515
|
-
|
|
202
|
+
## AuthService
|
|
516
203
|
|
|
517
|
-
|
|
204
|
+
Use `AuthService` when you need auth outside React components.
|
|
518
205
|
|
|
519
206
|
```ts
|
|
520
207
|
import { AuthService } from "react-native-nitro-auth";
|
|
521
208
|
|
|
209
|
+
await AuthService.silentRestore();
|
|
210
|
+
|
|
522
211
|
const unsubscribe = AuthService.onAuthStateChanged((user) => {
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
212
|
+
console.log(user?.email);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
const tokensUnsubscribe = AuthService.onTokensRefreshed((tokens) => {
|
|
216
|
+
console.log(tokens.expirationTime);
|
|
528
217
|
});
|
|
529
218
|
|
|
530
|
-
// Later...
|
|
531
219
|
unsubscribe();
|
|
220
|
+
tokensUnsubscribe();
|
|
532
221
|
```
|
|
533
222
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
Be notified whenever tokens are refreshed automatically (or manually):
|
|
223
|
+
## Login Options
|
|
537
224
|
|
|
538
225
|
```ts
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
226
|
+
await login("google", {
|
|
227
|
+
scopes: ["email", "profile"],
|
|
228
|
+
loginHint: "user@example.com",
|
|
229
|
+
useOneTap: true,
|
|
230
|
+
useSheet: true,
|
|
231
|
+
forceAccountPicker: true,
|
|
232
|
+
useLegacyGoogleSignIn: true,
|
|
544
233
|
});
|
|
545
|
-
```
|
|
546
|
-
|
|
547
|
-
### Incremental Authorization
|
|
548
|
-
|
|
549
|
-
Request new scopes when you need them without logging the user out:
|
|
550
234
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
await requestScopes(["https://www.googleapis.com/auth/calendar.readonly"]);
|
|
557
|
-
console.log("Got calendar access!");
|
|
558
|
-
} catch (e) {
|
|
559
|
-
console.error("Scope request failed");
|
|
560
|
-
}
|
|
561
|
-
};
|
|
235
|
+
await login("microsoft", {
|
|
236
|
+
scopes: ["openid", "profile", "email", "offline_access", "User.Read"],
|
|
237
|
+
tenant: "organizations",
|
|
238
|
+
prompt: "select_account",
|
|
239
|
+
});
|
|
562
240
|
```
|
|
563
241
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
242
|
+
| Option | Applies to | Notes |
|
|
243
|
+
| --- | --- | --- |
|
|
244
|
+
| `scopes` | Google, Microsoft | Requested OAuth scopes |
|
|
245
|
+
| `loginHint` | Google, Microsoft | Prefills account selection when supported |
|
|
246
|
+
| `useOneTap` | Android Google | Enables Credential Manager auto-select |
|
|
247
|
+
| `useSheet` | iOS Google | Uses native sign-in sheet behavior |
|
|
248
|
+
| `forceAccountPicker` | Google | Forces account picker |
|
|
249
|
+
| `useLegacyGoogleSignIn` | Android Google | Uses legacy Google Sign-In path for server auth code |
|
|
250
|
+
| `tenant` | Microsoft | Overrides configured tenant |
|
|
251
|
+
| `prompt` | Microsoft | `login`, `consent`, `select_account`, or `none` |
|
|
567
252
|
|
|
568
|
-
|
|
253
|
+
## Incremental Scopes
|
|
569
254
|
|
|
570
255
|
```ts
|
|
571
|
-
|
|
572
|
-
import { createStorageItem, StorageScope } from "react-native-nitro-storage";
|
|
573
|
-
|
|
574
|
-
type AuthSnapshot = {
|
|
575
|
-
user: AuthUser | undefined;
|
|
576
|
-
scopes: string[];
|
|
577
|
-
updatedAt: number | undefined;
|
|
578
|
-
};
|
|
579
|
-
|
|
580
|
-
const authSnapshotItem = createStorageItem<AuthSnapshot>({
|
|
581
|
-
key: "auth_snapshot",
|
|
582
|
-
scope: StorageScope.Disk,
|
|
583
|
-
defaultValue: {
|
|
584
|
-
user: undefined,
|
|
585
|
-
scopes: [],
|
|
586
|
-
updatedAt: undefined,
|
|
587
|
-
},
|
|
588
|
-
});
|
|
256
|
+
const calendarScope = "https://www.googleapis.com/auth/calendar.readonly";
|
|
589
257
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
if (!user) return;
|
|
593
|
-
|
|
594
|
-
authSnapshotItem.set({
|
|
595
|
-
user,
|
|
596
|
-
scopes: AuthService.grantedScopes,
|
|
597
|
-
updatedAt: Date.now(),
|
|
598
|
-
});
|
|
599
|
-
});
|
|
600
|
-
|
|
601
|
-
// Keep token/expiration fields fresh in persisted snapshot
|
|
602
|
-
AuthService.onTokensRefreshed((tokens) => {
|
|
603
|
-
authSnapshotItem.set((prev) => {
|
|
604
|
-
if (!prev.user) return prev;
|
|
605
|
-
|
|
606
|
-
return {
|
|
607
|
-
...prev,
|
|
608
|
-
user: {
|
|
609
|
-
...prev.user,
|
|
610
|
-
accessToken: tokens.accessToken ?? prev.user.accessToken,
|
|
611
|
-
idToken: tokens.idToken ?? prev.user.idToken,
|
|
612
|
-
refreshToken: tokens.refreshToken ?? prev.user.refreshToken,
|
|
613
|
-
expirationTime: tokens.expirationTime ?? prev.user.expirationTime,
|
|
614
|
-
},
|
|
615
|
-
updatedAt: Date.now(),
|
|
616
|
-
};
|
|
617
|
-
});
|
|
618
|
-
});
|
|
619
|
-
|
|
620
|
-
// Clear on logout
|
|
621
|
-
function logout() {
|
|
622
|
-
AuthService.logout();
|
|
623
|
-
authSnapshotItem.set({
|
|
624
|
-
user: undefined,
|
|
625
|
-
scopes: [],
|
|
626
|
-
updatedAt: undefined,
|
|
627
|
-
});
|
|
628
|
-
}
|
|
258
|
+
await requestScopes([calendarScope]);
|
|
259
|
+
await revokeScopes([calendarScope]);
|
|
629
260
|
```
|
|
630
261
|
|
|
631
|
-
|
|
262
|
+
On Android, incremental Google scope requests use the legacy Google Sign-In APIs because Credential Manager does not expose an equivalent existing-account scope query.
|
|
632
263
|
|
|
633
|
-
|
|
264
|
+
## Storage Model
|
|
634
265
|
|
|
635
|
-
|
|
636
|
-
- **Apple Sign-In**: iOS + web only.
|
|
637
|
-
- **Microsoft (Azure AD / B2C)**: iOS, Android, and web with PKCE/state/nonce protections.
|
|
266
|
+
Native tokens are kept in memory by design. The package does not persist Microsoft refresh tokens or provider tokens to disk. Your app owns persistence and secure storage policy.
|
|
638
267
|
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
1. Configure client IDs, URL schemes, and redirect URIs per platform/provider.
|
|
642
|
-
2. Call `silentRestore()` during app startup to rehydrate provider session state when available.
|
|
643
|
-
3. Persist only app-owned snapshots/tokens (for example with `react-native-nitro-storage`), then clear them on logout.
|
|
644
|
-
4. On Android Google, use `useLegacyGoogleSignIn: true` when backend flows require `serverAuthCode`.
|
|
645
|
-
5. Treat token fields as optional and branch by provider/flow.
|
|
646
|
-
6. Keep web sensitive tokens memory-only unless you explicitly require persistent web tokens (`nitroAuthPersistTokensOnWeb: true`).
|
|
647
|
-
7. Enable logging only in development, and monitor normalized error codes in production.
|
|
648
|
-
|
|
649
|
-
### Logging & Debugging
|
|
650
|
-
|
|
651
|
-
Enable verbose logging to see detailed OAuth flow information in the console:
|
|
268
|
+
For app-managed persistence, store only the minimum state your product needs:
|
|
652
269
|
|
|
653
270
|
```ts
|
|
654
271
|
import { AuthService } from "react-native-nitro-auth";
|
|
655
272
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
Nitro Auth provides synchronous access to in-memory state, while token retrieval remains async:
|
|
662
|
-
|
|
663
|
-
```ts
|
|
664
|
-
// Quick access to what we have in memory
|
|
665
|
-
const user = AuthService.currentUser;
|
|
666
|
-
const scopes = AuthService.grantedScopes;
|
|
667
|
-
|
|
668
|
-
// Async access ensures fresh tokens (will refresh if expired)
|
|
669
|
-
const freshToken = await AuthService.getAccessToken();
|
|
273
|
+
const snapshot = {
|
|
274
|
+
user: AuthService.currentUser,
|
|
275
|
+
scopes: AuthService.grantedScopes,
|
|
276
|
+
updatedAt: Date.now(),
|
|
277
|
+
};
|
|
670
278
|
```
|
|
671
279
|
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
All errors thrown by `AuthService` and `useAuth` are `AuthError` instances with a type-safe `code` field (always a valid `AuthErrorCode`) and an optional `underlyingMessage` with the raw provider string when it differs from the code.
|
|
675
|
-
|
|
676
|
-
```ts
|
|
677
|
-
import { AuthError } from "react-native-nitro-auth";
|
|
280
|
+
On web, the default is also memory storage. You can opt into browser storage with:
|
|
678
281
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
switch (e.code) {
|
|
684
|
-
case "cancelled":
|
|
685
|
-
// User closed the popup/picker
|
|
686
|
-
break;
|
|
687
|
-
case "network_error":
|
|
688
|
-
// Connection issues
|
|
689
|
-
break;
|
|
690
|
-
default:
|
|
691
|
-
console.error(e.code, e.underlyingMessage);
|
|
692
|
-
}
|
|
693
|
-
}
|
|
282
|
+
```js
|
|
283
|
+
extra: {
|
|
284
|
+
nitroAuthWebStorage: "session", // "session", "local", or "memory"
|
|
285
|
+
nitroAuthPersistTokensOnWeb: true,
|
|
694
286
|
}
|
|
695
287
|
```
|
|
696
288
|
|
|
697
|
-
|
|
698
|
-
| ---------------------- | --------------------------------------------------------------- |
|
|
699
|
-
| `cancelled` | The user cancelled the sign-in flow or dismissed the popup |
|
|
700
|
-
| `timeout` | The login popup/flow timed out |
|
|
701
|
-
| `popup_blocked` | The browser blocked the popup window |
|
|
702
|
-
| `network_error` | A network or connectivity error occurred |
|
|
703
|
-
| `configuration_error` | Missing client IDs, invalid tenant, or misconfigured setup |
|
|
704
|
-
| `not_signed_in` | The operation requires an authenticated user/session |
|
|
705
|
-
| `operation_in_progress`| Another auth flow of the same kind is already running |
|
|
706
|
-
| `unsupported_provider` | The provider is not supported on this platform |
|
|
707
|
-
| `invalid_state` | PKCE state mismatch — possible CSRF attack |
|
|
708
|
-
| `invalid_nonce` | Nonce mismatch in token response — possible replay attack |
|
|
709
|
-
| `token_error` | Token exchange or storage failed |
|
|
710
|
-
| `no_id_token` | No `id_token` in token response |
|
|
711
|
-
| `parse_error` | Failed to parse token response |
|
|
712
|
-
| `refresh_failed` | Refresh token flow failed (token may be expired or revoked) |
|
|
713
|
-
| `unknown` | An unknown or unmapped error occurred |
|
|
714
|
-
|
|
715
|
-
### Native Error Metadata
|
|
716
|
-
|
|
717
|
-
`AuthError` carries the raw provider/native message in `underlyingMessage` when the platform error didn't map to a known code:
|
|
289
|
+
## Error Contract
|
|
718
290
|
|
|
719
|
-
|
|
720
|
-
import { AuthError } from "react-native-nitro-auth";
|
|
291
|
+
All public async APIs throw `AuthError`.
|
|
721
292
|
|
|
293
|
+
```ts
|
|
722
294
|
try {
|
|
723
|
-
await login("
|
|
295
|
+
await AuthService.login("microsoft");
|
|
724
296
|
} catch (e) {
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
297
|
+
const error = AuthError.from(e);
|
|
298
|
+
switch (error.code) {
|
|
299
|
+
case "cancelled":
|
|
300
|
+
break;
|
|
301
|
+
case "configuration_error":
|
|
302
|
+
break;
|
|
303
|
+
case "token_error":
|
|
304
|
+
break;
|
|
305
|
+
default:
|
|
306
|
+
break;
|
|
729
307
|
}
|
|
730
308
|
}
|
|
731
309
|
```
|
|
732
310
|
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
### Troubleshooting
|
|
736
|
-
|
|
737
|
-
- `configuration_error`: verify client IDs, URL schemes, and redirect URIs are set for the current platform. On Android, check that the `microsoftTenant` is a non-empty valid value.
|
|
738
|
-
- `invalid_state` or `invalid_nonce`: ensure the redirect URI in your provider console matches your app config exactly. These indicate a potential CSRF or replay attack — do not retry silently.
|
|
739
|
-
- `refresh_failed`: the refresh token is likely expired or revoked. Clear the session and prompt the user to sign in again. On Android and web, structured error details from the provider (e.g. `invalid_grant`) are surfaced via `error.underlyingMessage`.
|
|
740
|
-
- `hasPlayServices` is false: prompt the user to install/update Google Play Services or disable One-Tap.
|
|
741
|
-
- Apple web login fails: confirm `appleWebClientId` is set and your domain is registered with Apple.
|
|
742
|
-
- Microsoft Android redirect hangs: confirm the `msauth://` redirect URI in your Azure app matches your package name and client ID.
|
|
743
|
-
|
|
744
|
-
### Automatic Token Refresh
|
|
745
|
-
|
|
746
|
-
The `getAccessToken()` method automatically checks if the current token is expired (or about to expire) and triggers a silent refresh if possible:
|
|
747
|
-
|
|
748
|
-
```ts
|
|
749
|
-
const { getAccessToken } = useAuth();
|
|
750
|
-
|
|
751
|
-
// This will silently refresh if needed!
|
|
752
|
-
const token = await getAccessToken();
|
|
753
|
-
```
|
|
754
|
-
|
|
755
|
-
### Offline Access (Server Auth Code)
|
|
756
|
-
|
|
757
|
-
If you need to access Google APIs from your backend (e.g., Google Calendar integration), you can use the `serverAuthCode`. This code is returned during login and can be exchanged for tokens on your server:
|
|
311
|
+
Known error codes:
|
|
758
312
|
|
|
759
313
|
```ts
|
|
760
|
-
|
|
314
|
+
type AuthErrorCode =
|
|
315
|
+
| "cancelled"
|
|
316
|
+
| "timeout"
|
|
317
|
+
| "popup_blocked"
|
|
318
|
+
| "network_error"
|
|
319
|
+
| "configuration_error"
|
|
320
|
+
| "not_signed_in"
|
|
321
|
+
| "operation_in_progress"
|
|
322
|
+
| "unsupported_provider"
|
|
323
|
+
| "invalid_state"
|
|
324
|
+
| "invalid_nonce"
|
|
325
|
+
| "token_error"
|
|
326
|
+
| "no_id_token"
|
|
327
|
+
| "parse_error"
|
|
328
|
+
| "refresh_failed"
|
|
329
|
+
| "unknown";
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
`underlyingMessage` keeps the raw native or OAuth message when it differs from the stable code.
|
|
761
333
|
|
|
762
|
-
|
|
763
|
-
// Send this to your backend!
|
|
764
|
-
await api.verifyGoogleAccess(user.serverAuthCode);
|
|
765
|
-
}
|
|
766
|
-
```
|
|
767
|
-
|
|
768
|
-
### Google One-Tap & Sheet
|
|
334
|
+
## API Reference
|
|
769
335
|
|
|
770
|
-
|
|
336
|
+
### Exports
|
|
771
337
|
|
|
772
338
|
```ts
|
|
773
|
-
|
|
774
|
-
useOneTap: true, // Android
|
|
775
|
-
useSheet: true, // iOS
|
|
776
|
-
});
|
|
339
|
+
export * from "react-native-nitro-auth";
|
|
777
340
|
```
|
|
778
341
|
|
|
779
|
-
|
|
780
|
-
> One-Tap requires Google Play Services. You can check `hasPlayServices` from `useAuth()` and show a fallback UI if needed.
|
|
342
|
+
Main exports:
|
|
781
343
|
|
|
782
|
-
|
|
344
|
+
- `useAuth()`
|
|
345
|
+
- `AuthService`
|
|
346
|
+
- `SocialButton`
|
|
347
|
+
- `AuthError`
|
|
348
|
+
- `isAuthErrorCode()`
|
|
349
|
+
- `toAuthErrorCode()`
|
|
350
|
+
- `AuthProvider`
|
|
351
|
+
- `AuthUser`
|
|
352
|
+
- `AuthTokens`
|
|
353
|
+
- `LoginOptions`
|
|
783
354
|
|
|
784
|
-
|
|
785
|
-
If your backend requires `serverAuthCode`, opt into the legacy flow:
|
|
355
|
+
### useAuth()
|
|
786
356
|
|
|
787
357
|
```ts
|
|
788
|
-
|
|
358
|
+
type UseAuthReturn = {
|
|
359
|
+
user: AuthUser | undefined;
|
|
360
|
+
scopes: string[];
|
|
361
|
+
loading: boolean;
|
|
362
|
+
error: AuthError | undefined;
|
|
363
|
+
hasPlayServices: boolean;
|
|
364
|
+
login(provider: AuthProvider, options?: LoginOptions): Promise<void>;
|
|
365
|
+
logout(): void;
|
|
366
|
+
requestScopes(scopes: string[]): Promise<void>;
|
|
367
|
+
revokeScopes(scopes: string[]): Promise<void>;
|
|
368
|
+
getAccessToken(): Promise<string | undefined>;
|
|
369
|
+
refreshToken(): Promise<AuthTokens>;
|
|
370
|
+
silentRestore(): Promise<void>;
|
|
371
|
+
};
|
|
789
372
|
```
|
|
790
373
|
|
|
791
|
-
###
|
|
792
|
-
|
|
793
|
-
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:
|
|
374
|
+
### AuthUser
|
|
794
375
|
|
|
795
376
|
```ts
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
377
|
+
type AuthUser = {
|
|
378
|
+
provider: "google" | "apple" | "microsoft";
|
|
379
|
+
email?: string;
|
|
380
|
+
name?: string;
|
|
381
|
+
photo?: string;
|
|
382
|
+
idToken?: string;
|
|
383
|
+
accessToken?: string;
|
|
384
|
+
refreshToken?: string;
|
|
385
|
+
serverAuthCode?: string;
|
|
386
|
+
scopes?: string[];
|
|
387
|
+
expirationTime?: number;
|
|
388
|
+
underlyingError?: string;
|
|
389
|
+
};
|
|
800
390
|
```
|
|
801
391
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
- Users want to connect a different Google account for calendar integration
|
|
805
|
-
- You need to ensure the user can select any account they've added to their device
|
|
806
|
-
- The cached session is interfering with the expected account selection UX
|
|
392
|
+
## Example App
|
|
807
393
|
|
|
808
|
-
|
|
394
|
+
The example app is the fastest way to verify setup and read a complete integration.
|
|
809
395
|
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
AuthService,
|
|
817
|
-
AuthError,
|
|
818
|
-
isAuthErrorCode,
|
|
819
|
-
toAuthErrorCode,
|
|
820
|
-
SocialButton,
|
|
821
|
-
useAuth,
|
|
822
|
-
type UseAuthReturn,
|
|
823
|
-
type Auth,
|
|
824
|
-
type AuthUser,
|
|
825
|
-
type AuthTokens,
|
|
826
|
-
type AuthProvider,
|
|
827
|
-
type AuthErrorCode,
|
|
828
|
-
type LoginOptions,
|
|
829
|
-
} from "react-native-nitro-auth";
|
|
396
|
+
```sh
|
|
397
|
+
cp apps/example/.env.example apps/example/.env.local
|
|
398
|
+
bun install
|
|
399
|
+
bun example:prebuild:clean
|
|
400
|
+
bun example:ios
|
|
401
|
+
bun example:android
|
|
830
402
|
```
|
|
831
403
|
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
| Type | Definition |
|
|
835
|
-
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
836
|
-
| `AuthProvider` | `"google" \| "apple" \| "microsoft"` |
|
|
837
|
-
| `AuthErrorCode` | `"cancelled" \| "timeout" \| "popup_blocked" \| "network_error" \| "configuration_error" \| "not_signed_in" \| "operation_in_progress" \| "unsupported_provider" \| "invalid_state" \| "invalid_nonce" \| "token_error" \| "no_id_token" \| "parse_error" \| "refresh_failed" \| "unknown"` |
|
|
838
|
-
| `MicrosoftPrompt` | `"login" \| "consent" \| "select_account" \| "none"` |
|
|
839
|
-
|
|
840
|
-
### `AuthUser`
|
|
841
|
-
|
|
842
|
-
| Field | Type | Description |
|
|
843
|
-
| ----------------- | ----------------------- | -------------------------------------------------------------------------- |
|
|
844
|
-
| `provider` | `AuthProvider` | Provider that authenticated the user |
|
|
845
|
-
| `email` | `string \| undefined` | User email |
|
|
846
|
-
| `name` | `string \| undefined` | Display name |
|
|
847
|
-
| `photo` | `string \| undefined` | Profile image URL (Google) |
|
|
848
|
-
| `idToken` | `string \| undefined` | OIDC ID token |
|
|
849
|
-
| `accessToken` | `string \| undefined` | OAuth access token |
|
|
850
|
-
| `refreshToken` | `string \| undefined` | OAuth refresh token |
|
|
851
|
-
| `serverAuthCode` | `string \| undefined` | Google server auth code (legacy Android flow + backend exchange scenarios) |
|
|
852
|
-
| `scopes` | `string[] \| undefined` | Granted scopes for current session |
|
|
853
|
-
| `expirationTime` | `number \| undefined` | Expiration timestamp in milliseconds since epoch |
|
|
854
|
-
| `underlyingError` | `string \| undefined` | Raw provider/native error message |
|
|
855
|
-
|
|
856
|
-
> [!NOTE]
|
|
857
|
-
> On Android Google One-Tap/Credential Manager, `idToken` is typically available immediately, while `accessToken` and `expirationTime` may be `undefined` until refresh/exchange flow provides them.
|
|
858
|
-
|
|
859
|
-
### `AuthTokens`
|
|
860
|
-
|
|
861
|
-
| Field | Type | Description |
|
|
862
|
-
| ---------------- | --------------------- | ----------------------------- |
|
|
863
|
-
| `accessToken` | `string \| undefined` | Refreshed access token |
|
|
864
|
-
| `idToken` | `string \| undefined` | Refreshed ID token |
|
|
865
|
-
| `refreshToken` | `string \| undefined` | Refresh token (if available) |
|
|
866
|
-
| `expirationTime` | `number \| undefined` | Optional expiration timestamp |
|
|
867
|
-
|
|
868
|
-
### `LoginOptions`
|
|
869
|
-
|
|
870
|
-
| Option | Type | Platform | Description |
|
|
871
|
-
| ----------------------- | ----------------- | --------- | --------------------------------------------------------------------------------- |
|
|
872
|
-
| `scopes` | `string[]` | All | Requested scopes (defaults are provider-specific) |
|
|
873
|
-
| `loginHint` | `string` | All | Prefills account identifier |
|
|
874
|
-
| `useOneTap` | `boolean` | Android | Use Credential Manager/One-Tap flow |
|
|
875
|
-
| `useSheet` | `boolean` | iOS | Use native Google Sign-In sheet |
|
|
876
|
-
| `forceAccountPicker` | `boolean` | All | Always show account chooser; Android Google uses the legacy chooser path |
|
|
877
|
-
| `useLegacyGoogleSignIn` | `boolean` | Android | Use legacy Google Sign-In (required when you need `serverAuthCode`) |
|
|
878
|
-
| `tenant` | `string` | Microsoft | Tenant (`common`, `organizations`, `consumers`, tenant id, or full authority URL) |
|
|
879
|
-
| `prompt` | `MicrosoftPrompt` | Microsoft | Prompt behavior |
|
|
880
|
-
|
|
881
|
-
### `useAuth()`
|
|
882
|
-
|
|
883
|
-
```ts
|
|
884
|
-
declare function useAuth(): UseAuthReturn;
|
|
885
|
-
```
|
|
404
|
+
The demo includes:
|
|
886
405
|
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
| `error` | `AuthError \| undefined` | Last operation error (typed, safe to switch on `.code`) |
|
|
893
|
-
| `hasPlayServices` | `boolean` | Android Play Services availability |
|
|
894
|
-
| `login` | `(provider: AuthProvider, options?: LoginOptions) => Promise<void>` | Starts provider login |
|
|
895
|
-
| `logout` | `() => void` | Clears current session |
|
|
896
|
-
| `requestScopes` | `(scopes: string[]) => Promise<void>` | Requests additional scopes |
|
|
897
|
-
| `revokeScopes` | `(scopes: string[]) => Promise<void>` | Revokes scopes in current session |
|
|
898
|
-
| `getAccessToken` | `() => Promise<string \| undefined>` | Returns access token, auto-refreshing when supported |
|
|
899
|
-
| `refreshToken` | `() => Promise<AuthTokens>` | Explicit refresh |
|
|
900
|
-
| `silentRestore` | `() => Promise<void>` | Restores provider SDK session (if available) |
|
|
901
|
-
|
|
902
|
-
### `AuthService`
|
|
903
|
-
|
|
904
|
-
Synchronous state + async operations (useful outside React trees).
|
|
905
|
-
|
|
906
|
-
#### Readonly state
|
|
907
|
-
|
|
908
|
-
| Property | Type | Description |
|
|
909
|
-
| ----------------- | ----------------------- | ---------------------------------- |
|
|
910
|
-
| `name` | `string` | Hybrid object name (`"Auth"`) |
|
|
911
|
-
| `currentUser` | `AuthUser \| undefined` | Current user snapshot |
|
|
912
|
-
| `grantedScopes` | `string[]` | Current scope snapshot |
|
|
913
|
-
| `hasPlayServices` | `boolean` | Android Play Services availability |
|
|
914
|
-
|
|
915
|
-
#### Methods
|
|
916
|
-
|
|
917
|
-
| Method | Signature | Description |
|
|
918
|
-
| -------------------- | -------------------------------------------------------- | -------------------------------- |
|
|
919
|
-
| `login` | `(provider, options?) => Promise<void>` | Starts login |
|
|
920
|
-
| `logout` | `() => void` | Clears session |
|
|
921
|
-
| `requestScopes` | `(scopes) => Promise<void>` | Incremental auth |
|
|
922
|
-
| `revokeScopes` | `(scopes) => Promise<void>` | Scope revoke |
|
|
923
|
-
| `getAccessToken` | `() => Promise<string \| undefined>` | Access token getter with refresh |
|
|
924
|
-
| `refreshToken` | `() => Promise<AuthTokens>` | Explicit token refresh |
|
|
925
|
-
| `silentRestore` | `() => Promise<void>` | Restore provider SDK session |
|
|
926
|
-
| `onAuthStateChanged` | `(callback: (user?: AuthUser) => void) => () => void` | Auth change listener |
|
|
927
|
-
| `onTokensRefreshed` | `(callback: (tokens: AuthTokens) => void) => () => void` | Token refresh listener |
|
|
928
|
-
| `setLoggingEnabled` | `(enabled: boolean) => void` | Debug logging toggle |
|
|
929
|
-
| `dispose` | `() => void` | Disposes hybrid object |
|
|
930
|
-
| `equals` | `(other: unknown) => boolean` | Hybrid object identity check |
|
|
931
|
-
|
|
932
|
-
### Storage Contract
|
|
933
|
-
|
|
934
|
-
Nitro Auth does not provide persistence APIs. Persist the auth data you need in your app layer (for example with `react-native-nitro-storage`, MMKV, Keychain wrappers, or backend-issued sessions).
|
|
935
|
-
There is no public `setStorageAdapter`/`setJSStorageAdapter` API in this package.
|
|
936
|
-
|
|
937
|
-
### `SocialButton`
|
|
938
|
-
|
|
939
|
-
| Prop | Type | Default | Description |
|
|
940
|
-
| -------------- | ---------------------------------------------- | ----------- | ------------------------------------------- |
|
|
941
|
-
| `provider` | `AuthProvider` | required | Provider |
|
|
942
|
-
| `variant` | `"primary" \| "outline" \| "white" \| "black"` | `"primary"` | Visual style |
|
|
943
|
-
| `borderRadius` | `number` | `8` | Border radius |
|
|
944
|
-
| `style` | `ViewStyle` | `undefined` | Container style override |
|
|
945
|
-
| `textStyle` | `TextStyle` | `undefined` | Text style override |
|
|
946
|
-
| `disabled` | `boolean` | `false` | Disabled state |
|
|
947
|
-
| `onPress` | `() => void` | `undefined` | Custom press handler (skips built-in login) |
|
|
948
|
-
| `onSuccess` | `(user: AuthUser) => void` | `undefined` | Called after successful default login |
|
|
949
|
-
| `onError` | `(error: unknown) => void` | `undefined` | Called when default login fails |
|
|
950
|
-
|
|
951
|
-
### Config Plugin API (`app.json` / `app.config.js`)
|
|
952
|
-
|
|
953
|
-
`plugins: [["react-native-nitro-auth", { ios: {...}, android: {...} }]]`
|
|
954
|
-
|
|
955
|
-
#### iOS plugin options
|
|
956
|
-
|
|
957
|
-
| Option | Type | Description |
|
|
958
|
-
| ---------------------- | --------- | -------------------------------------------- |
|
|
959
|
-
| `googleClientId` | `string` | Writes `GIDClientID` |
|
|
960
|
-
| `googleServerClientId` | `string` | Writes `GIDServerClientID` |
|
|
961
|
-
| `googleUrlScheme` | `string` | Adds Google URL scheme |
|
|
962
|
-
| `appleSignIn` | `boolean` | Enables Apple Sign-In entitlement |
|
|
963
|
-
| `microsoftClientId` | `string` | Writes `MSALClientID` + MSAL redirect scheme |
|
|
964
|
-
| `microsoftTenant` | `string` | Writes `MSALTenant` |
|
|
965
|
-
| `microsoftB2cDomain` | `string` | Writes `MSALB2cDomain` |
|
|
966
|
-
|
|
967
|
-
#### Android plugin options
|
|
968
|
-
|
|
969
|
-
| Option | Type | Description |
|
|
970
|
-
| -------------------- | -------- | ---------------------------------------------------------------- |
|
|
971
|
-
| `googleClientId` | `string` | Writes `nitro_auth_google_client_id` string |
|
|
972
|
-
| `microsoftClientId` | `string` | Writes `nitro_auth_microsoft_client_id` + redirect intent filter |
|
|
973
|
-
| `microsoftTenant` | `string` | Writes `nitro_auth_microsoft_tenant` |
|
|
974
|
-
| `microsoftB2cDomain` | `string` | Writes `nitro_auth_microsoft_b2c_domain` |
|
|
975
|
-
|
|
976
|
-
### Web runtime config (`expo.extra`)
|
|
977
|
-
|
|
978
|
-
| Key | Type | Default | Description |
|
|
979
|
-
| ----------------------------- | ---------------------------------- | ----------- | --------------------------------------------------------- |
|
|
980
|
-
| `googleWebClientId` | `string` | `undefined` | Google web OAuth client id |
|
|
981
|
-
| `microsoftClientId` | `string` | `undefined` | Microsoft app client id |
|
|
982
|
-
| `microsoftTenant` | `string` | `"common"` | Microsoft tenant/authority |
|
|
983
|
-
| `microsoftB2cDomain` | `string` | `undefined` | B2C domain when applicable |
|
|
984
|
-
| `appleWebClientId` | `string` | `undefined` | Apple Service ID |
|
|
985
|
-
| `nitroAuthWebStorage` | `"session" \| "local" \| "memory"` | `"session"` | Storage for non-sensitive web cache |
|
|
986
|
-
| `nitroAuthPersistTokensOnWeb` | `boolean` | `false` | Persist sensitive tokens on web storage instead of memory |
|
|
987
|
-
|
|
988
|
-
### `AuthError`
|
|
989
|
-
|
|
990
|
-
All thrown errors from `AuthService` and `useAuth` are `AuthError` instances.
|
|
406
|
+
- Provider cards for Google, Apple, and Microsoft.
|
|
407
|
+
- Token and scope operations.
|
|
408
|
+
- Silent restore and account picker actions.
|
|
409
|
+
- App-owned disk snapshot example with `react-native-nitro-storage`.
|
|
410
|
+
- Runtime smoke tests for the public API.
|
|
991
411
|
|
|
992
|
-
|
|
993
|
-
class AuthError extends Error {
|
|
994
|
-
readonly code: AuthErrorCode; // Always a valid AuthErrorCode — safe to switch on
|
|
995
|
-
readonly underlyingMessage?: string; // Raw native/platform string when it differs from code
|
|
996
|
-
static from(e: unknown): AuthError; // Wraps any value; passes AuthError instances through
|
|
997
|
-
}
|
|
412
|
+
## Troubleshooting
|
|
998
413
|
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
414
|
+
| Symptom | Check |
|
|
415
|
+
| --- | --- |
|
|
416
|
+
| `configuration_error` on Google | Client ID is missing or wrong for the current platform |
|
|
417
|
+
| Google works in debug but not release | Add release SHA-1/SHA-256 fingerprints to Google Cloud Console |
|
|
418
|
+
| Android `hasPlayServices` is false | Use an emulator image with Google Play Services |
|
|
419
|
+
| Apple email/name missing | Apple only returns these fields on first authorization |
|
|
420
|
+
| Microsoft `invalid_state` | Redirect URI or app resume path is wrong, or an old auth redirect completed late |
|
|
421
|
+
| Microsoft `token_error` | Check tenant, client ID, redirect URI, and requested scopes |
|
|
422
|
+
| Web popup blocked | Call `login()` from a user gesture such as a button press |
|
|
423
|
+
| `operation_in_progress` | A provider flow is already active; wait for it to finish or sign out |
|
|
1002
424
|
|
|
1003
|
-
|
|
1004
|
-
| ---------------------- | ---------------------------------------------- |
|
|
1005
|
-
| `cancelled` | User cancelled the login flow |
|
|
1006
|
-
| `timeout` | Provider popup did not complete before timeout |
|
|
1007
|
-
| `popup_blocked` | Browser blocked popup opening |
|
|
1008
|
-
| `network_error` | Network failure |
|
|
1009
|
-
| `configuration_error` | Missing/invalid provider configuration |
|
|
1010
|
-
| `not_signed_in` | Operation requires an active authenticated user |
|
|
1011
|
-
| `operation_in_progress`| Another auth flow is already running |
|
|
1012
|
-
| `unsupported_provider` | Provider not supported on this platform |
|
|
1013
|
-
| `invalid_state` | PKCE state mismatch (possible CSRF) |
|
|
1014
|
-
| `invalid_nonce` | Nonce mismatch in token response |
|
|
1015
|
-
| `token_error` | Token exchange failed |
|
|
1016
|
-
| `no_id_token` | No `id_token` in token response |
|
|
1017
|
-
| `parse_error` | Failed to parse token response |
|
|
1018
|
-
| `refresh_failed` | Refresh token flow failed |
|
|
1019
|
-
| `unknown` | Unrecognized error |
|
|
1020
|
-
|
|
1021
|
-
## Quality Gates
|
|
1022
|
-
|
|
1023
|
-
From monorepo root:
|
|
1024
|
-
|
|
1025
|
-
```bash
|
|
1026
|
-
bun run format:check
|
|
1027
|
-
bun run lint
|
|
1028
|
-
bun run typecheck
|
|
1029
|
-
```
|
|
425
|
+
## Production Notes
|
|
1030
426
|
|
|
1031
|
-
|
|
427
|
+
- Verify ID tokens on your backend. Client-side JWT parsing is for display and expiration hints only.
|
|
428
|
+
- Store refresh tokens only in storage your app explicitly owns and secures.
|
|
429
|
+
- Keep Google debug and release signing fingerprints in sync with your OAuth clients.
|
|
430
|
+
- Add provider-specific redirect URIs for every environment.
|
|
431
|
+
- Run the example app on iOS and Android before shipping provider config changes.
|
|
1032
432
|
|
|
1033
|
-
|
|
1034
|
-
# apps/example
|
|
1035
|
-
bun run format
|
|
1036
|
-
bun run lint
|
|
1037
|
-
bun run typecheck
|
|
433
|
+
## Release Checks
|
|
1038
434
|
|
|
1039
|
-
|
|
1040
|
-
bun run
|
|
1041
|
-
bun run
|
|
1042
|
-
bun run
|
|
1043
|
-
bun run test
|
|
435
|
+
```sh
|
|
436
|
+
bun run codegen
|
|
437
|
+
bun run build
|
|
438
|
+
bun run check
|
|
439
|
+
bun run test:cpp
|
|
440
|
+
bun example:prebuild:clean
|
|
441
|
+
bun example:ios
|
|
442
|
+
bun example:android
|
|
1044
443
|
```
|
|
1045
444
|
|
|
1046
|
-
## Platform Support
|
|
1047
|
-
|
|
1048
|
-
| Feature | iOS | Android | Web |
|
|
1049
|
-
| -------------------------- | --- | ------- | --- |
|
|
1050
|
-
| Google Sign-In | ✅ | ✅ | ✅ |
|
|
1051
|
-
| Apple Sign-In | ✅ | ❌ | ✅ |
|
|
1052
|
-
| Microsoft Sign-In | ✅ | ✅ | ✅ |
|
|
1053
|
-
| Custom OAuth Scopes | ✅ | ✅ | ✅ |
|
|
1054
|
-
| Incremental Authorization | ✅ | ✅ | ✅ |
|
|
1055
|
-
| Token Refresh | ✅ | ✅ | ✅ |
|
|
1056
|
-
| Native Session Persistence | ❌ | ❌ | — |
|
|
1057
|
-
| Web Auth Snapshot Cache | — | — | ✅ |
|
|
1058
|
-
| App-Owned Persistence | ✅ | ✅ | ✅ |
|
|
1059
|
-
| Auto-Refresh | ✅ | ✅ | ✅ |
|
|
1060
|
-
| Native C++ Performance | ✅ | ✅ | — |
|
|
1061
|
-
|
|
1062
|
-
## Architecture
|
|
1063
|
-
|
|
1064
|
-
`react-native-nitro-auth` is built using [Nitro Modules](https://github.com/mrousavy/nitro). Unlike traditional React Native modules, Nitro uses JSI to provide:
|
|
1065
|
-
|
|
1066
|
-
- **Zero-bridge overhead**: Calls are made directly from JS to C++.
|
|
1067
|
-
- **Type safety**: TypeScript types are automatically kept in sync with native C++ and Swift/Kotlin code.
|
|
1068
|
-
- **Synchronous access**: Properties like `currentUser` are accessible synchronously without async overhead.
|
|
1069
|
-
|
|
1070
445
|
## License
|
|
1071
446
|
|
|
1072
447
|
MIT
|