react-native-nitro-auth 0.6.1 → 0.6.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.2 - 2026-06-07
4
+
5
+ ### Fixed
6
+
7
+ - Hardened Microsoft authority URL construction to reject absolute tenant URLs and invalid B2C domains while building valid B2C tenant/policy authority paths.
8
+
9
+ ### Changed
10
+
11
+ - Updated the Expo example SDK 56 patch dependencies so Expo Doctor passes cleanly.
12
+
3
13
  ## 0.6.1 - 2026-05-21
4
14
 
5
15
  ### Changed
package/README.md CHANGED
@@ -1,33 +1,18 @@
1
1
  # react-native-nitro-auth
2
2
 
3
- [![npm](https://img.shields.io/badge/npm-v0.6.1-f97316?style=flat-square)](https://www.npmjs.com/package/react-native-nitro-auth)
4
- [![license](https://img.shields.io/badge/license-MIT-007ec6?style=flat-square)](https://github.com/JoaoPauloCMarra/react-native-nitro-auth/blob/main/LICENSE)
5
- [![react-native](https://img.shields.io/badge/react--native-%3E%3D0.75-61dafb?style=flat-square)](https://reactnative.dev/)
6
- [![nitro-modules](https://img.shields.io/badge/nitro--modules-%3E%3D0.35.0-black?style=flat-square)](https://nitro.margelo.com/)
3
+ [![npm version](https://img.shields.io/npm/v/react-native-nitro-auth?color=f97316&label=npm)](https://www.npmjs.com/package/react-native-nitro-auth)
4
+ [![license](https://img.shields.io/npm/l/react-native-nitro-auth?color=007ec6)](https://github.com/JoaoPauloCMarra/react-native-nitro-auth/blob/main/LICENSE)
5
+ [![React Native](https://img.shields.io/badge/react--native-%3E%3D0.75-61dafb)](https://reactnative.dev/)
6
+ [![Expo](https://img.shields.io/badge/expo-SDK%2056-000020)](https://expo.dev/)
7
+ [![Nitro Modules](https://img.shields.io/badge/nitro--modules-%3E%3D0.35.7-black)](https://nitro.margelo.com/)
7
8
 
8
- Fast React Native authentication for Google Sign-In, Apple Sign-In, and Microsoft Entra ID, built on Nitro Modules and JSI.
9
+ Google Sign-In, Apple Sign-In, and Microsoft Entra ID for React Native and
10
+ Expo, powered by Nitro Modules.
9
11
 
10
- `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.
11
-
12
- ## Why Use It?
13
-
14
- - One package for Google, Apple, and Microsoft authentication on React Native.
15
- - Native iOS and Android bridges powered by `react-native-nitro-modules`.
16
- - Expo config plugin for client IDs, URL schemes, entitlements, and Android resources.
17
- - Web implementation for Expo web with Google, Apple, and Microsoft OAuth.
18
- - Typed `useAuth()` hook, `AuthService`, `SocialButton`, and `AuthError`.
19
- - App-owned persistence model: tokens stay in memory unless your app stores a snapshot.
20
- - Built-in flows for silent restore, token refresh, account picker, login hints, and incremental Google scopes.
21
- - Consistent `AuthError` mapping for async `AuthService` failures on native and web.
22
-
23
- ## Choose Your Path
24
-
25
- | Need | Use |
26
- | ---------------------------------------------------------------------- | ----------------------------------------------------------------- |
27
- | Google, Apple, or Microsoft sign-in in an Expo or React Native app | `react-native-nitro-auth` |
28
- | Generic OAuth or OIDC provider not covered by this package | `expo-auth-session` or `react-native-app-auth` |
29
- | Firebase user management, password auth, MFA, and hosted auth platform | `@react-native-firebase/auth`, Auth0, Authgear, or your IDaaS SDK |
30
- | Server-side session validation | Your backend; client JWT decode is display-only |
12
+ Use it when you want one typed authentication API for native social login, web
13
+ OAuth, token refresh, incremental scopes, account listeners, and consistent
14
+ `AuthError` handling. The package keeps tokens in memory; your app decides what
15
+ to persist and where.
31
16
 
32
17
  ## Install
33
18
 
@@ -35,35 +20,25 @@ Fast React Native authentication for Google Sign-In, Apple Sign-In, and Microsof
35
20
  bun add react-native-nitro-auth react-native-nitro-modules
36
21
  ```
37
22
 
38
- For Expo projects, prebuild after adding the config plugin:
23
+ For Expo development builds:
39
24
 
40
25
  ```sh
41
- bunx expo prebuild --clean
26
+ bunx expo install react-native-nitro-auth react-native-nitro-modules
27
+ bunx expo prebuild
42
28
  ```
43
29
 
44
- The example app uses Expo Continuous Native Generation. Its `apps/example/android` and `apps/example/ios` folders are generated local artifacts and intentionally ignored by git.
45
-
46
- For bare React Native projects, install pods after installing the package:
30
+ For bare React Native apps:
47
31
 
48
32
  ```sh
49
33
  cd ios && pod install
50
34
  ```
51
35
 
52
- ## Requirements
53
-
54
- | Runtime | Requirement |
55
- | ------------------ | ---------------------------------------- |
56
- | React Native | `>=0.75` peer range |
57
- | Nitro Modules | `>=0.35` peer range |
58
- | iOS | 16.4+ for Expo SDK 56 |
59
- | Android | min SDK 24+ recommended |
60
- | Validated baseline | Expo SDK 56, React Native 0.85, React 19 |
61
-
62
- The package keeps a wide React Native peer range for existing consumers, but this release is validated against Expo SDK 56, React Native 0.85.3, React 19.2.3, and Nitro Modules 0.35.7.
36
+ Expo Go cannot load Nitro native modules. Use an Expo development build or a
37
+ bare app.
63
38
 
64
- ## Expo Setup
39
+ ## Expo Config
65
40
 
66
- Add the plugin to `app.json` or `app.config.js`.
41
+ Add the plugin to `app.json` or `app.config.js` before prebuild:
67
42
 
68
43
  ```js
69
44
  export default {
@@ -103,464 +78,140 @@ export default {
103
78
  microsoftClientId: process.env.MICROSOFT_CLIENT_ID,
104
79
  microsoftTenant: process.env.MICROSOFT_TENANT,
105
80
  microsoftB2cDomain: process.env.MICROSOFT_B2C_DOMAIN,
106
- nitroAuthWebStorage: "memory",
107
- nitroAuthPersistTokensOnWeb: false,
81
+ nitroAuthWebStorage: "session",
108
82
  },
109
83
  },
110
84
  };
111
85
  ```
112
86
 
113
- ### Plugin Options
114
-
115
- | Option | Platform | Purpose |
116
- | ---------------------------- | -------- | ---------------------------------------------------------------------- |
117
- | `ios.googleClientId` | iOS | Google iOS OAuth client ID |
118
- | `ios.googleServerClientId` | iOS | Google web/server client ID for server auth code flows |
119
- | `ios.googleUrlScheme` | iOS | Reversed iOS client ID URL scheme |
120
- | `ios.appleSignIn` | iOS | Adds Apple Sign-In entitlement when `true` |
121
- | `ios.microsoftClientId` | iOS | Microsoft app/client ID |
122
- | `ios.microsoftTenant` | iOS | Microsoft tenant, `common`, `organizations`, `consumers`, or tenant ID |
123
- | `ios.microsoftB2cDomain` | iOS | Azure AD B2C domain |
124
- | `android.googleClientId` | Android | Google web OAuth client ID |
125
- | `android.microsoftClientId` | Android | Microsoft app/client ID |
126
- | `android.microsoftTenant` | Android | Microsoft tenant |
127
- | `android.microsoftB2cDomain` | Android | Azure AD B2C domain |
128
-
129
- ## Provider Setup
130
-
131
- ### Google Sign-In
132
-
133
- Create OAuth clients in Google Cloud Console:
134
-
135
- - iOS client ID for your bundle identifier.
136
- - Web client ID for Android, web, and server auth code flows.
137
- - Android SHA-1/SHA-256 entries for local debug and release signing.
138
-
139
- Use the iOS reversed client ID as `GOOGLE_IOS_URL_SCHEME`.
140
-
141
- ### Apple Sign-In
142
-
143
- 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.
144
-
145
- Apple Sign-In is supported on iOS and web. It is intentionally reported as `unsupported_provider` on Android.
146
-
147
- ### Microsoft Entra ID
148
-
149
- Create an app registration in Microsoft Entra ID and add redirect URIs:
150
-
151
- - iOS: `msauth.<bundleIdentifier>://auth`
152
- - Android: `msauth://<androidPackage>/<clientId>`
153
- - Web: your web origin, for example `https://app.example.com`
154
-
155
- Use `microsoftTenant` for `common`, `organizations`, `consumers`, a tenant ID, or a B2C policy path. Use `microsoftB2cDomain` for Azure AD B2C.
87
+ Plugin options:
88
+
89
+ | Option | Platform | Required for |
90
+ | ---------------------------- | -------- | -------------------------------- |
91
+ | `ios.googleClientId` | iOS | Google Sign-In on iOS. |
92
+ | `ios.googleServerClientId` | iOS | Google server auth code flow. |
93
+ | `ios.googleUrlScheme` | iOS | Google redirect URL scheme. |
94
+ | `ios.appleSignIn` | iOS | Apple Sign-In entitlement. |
95
+ | `ios.microsoftClientId` | iOS | Microsoft Entra ID native login. |
96
+ | `ios.microsoftTenant` | iOS | Microsoft tenant override. |
97
+ | `ios.microsoftB2cDomain` | iOS | Microsoft B2C hostname. |
98
+ | `android.googleClientId` | Android | Google Sign-In on Android. |
99
+ | `android.microsoftClientId` | Android | Microsoft Entra ID native login. |
100
+ | `android.microsoftTenant` | Android | Microsoft tenant override. |
101
+ | `android.microsoftB2cDomain` | Android | Microsoft B2C hostname. |
102
+
103
+ Web reads provider client IDs from `expo.extra`; native platforms read values
104
+ written by the plugin during prebuild.
105
+
106
+ Microsoft tenant values are validated before opening the authorization URL. Use
107
+ `common`, `organizations`, `consumers`, a tenant ID, or a tenant domain for
108
+ standard Entra ID. For B2C, set `microsoftB2cDomain` to a hostname such as
109
+ `contoso.b2clogin.com` and set `microsoftTenant` to a policy such as
110
+ `B2C_1_signin`. For custom B2C domains, set `microsoftTenant` to a tenant/policy
111
+ path such as `contoso.onmicrosoft.com/B2C_1_signin`.
156
112
 
157
113
  ## Quick Start
158
114
 
159
115
  ```tsx
160
- import { Button, Text, View } from "react-native";
161
- import {
162
- AuthError,
163
- type GoogleLoginOptions,
164
- useAuth,
165
- } from "react-native-nitro-auth";
166
-
167
- const googleOptions = {
168
- scopes: ["email", "profile"],
169
- forceAccountPicker: true,
170
- } satisfies GoogleLoginOptions;
116
+ import { AuthService, AuthProvider, useAuth } from "react-native-nitro-auth";
171
117
 
172
- export function SignInScreen() {
173
- const { user, loading, login, logout, getAccessToken } = useAuth();
118
+ export function SignInButton() {
119
+ const { user, login, logout, loading, error } = useAuth();
174
120
 
175
121
  async function signInWithGoogle() {
176
- try {
177
- await login("google", googleOptions);
178
- } catch (e) {
179
- const error = AuthError.from(e);
180
- console.warn(error.code, error.underlyingMessage);
181
- }
122
+ await login(AuthProvider.Google, {
123
+ scopes: ["openid", "profile", "email"],
124
+ });
182
125
  }
183
126
 
184
- async function readToken() {
185
- const token = await getAccessToken();
186
- console.log(token);
127
+ if (user) {
128
+ return <Button title="Sign out" onPress={logout} />;
187
129
  }
188
130
 
189
- return (
190
- <View>
191
- <Text>{user?.email ?? "Signed out"}</Text>
192
- <Button
193
- title={loading ? "Signing in..." : "Sign in with Google"}
194
- onPress={signInWithGoogle}
195
- />
196
- <Button title="Get access token" onPress={readToken} />
197
- <Button title="Sign out" onPress={logout} />
198
- </View>
199
- );
131
+ return <Button title="Continue with Google" onPress={signInWithGoogle} />;
200
132
  }
201
- ```
202
-
203
- ## SocialButton
204
-
205
- ```tsx
206
- import { SocialButton } from "react-native-nitro-auth";
207
-
208
- export function AuthButtons() {
209
- return (
210
- <>
211
- <SocialButton provider="google" />
212
- <SocialButton provider="apple" variant="black" />
213
- <SocialButton provider="microsoft" variant="outline" />
214
- </>
215
- );
216
- }
217
- ```
218
-
219
- ## AuthService
220
133
 
221
- Use `AuthService` when you need auth outside React components.
222
-
223
- ```ts
224
- import { AuthService } from "react-native-nitro-auth";
225
-
226
- await AuthService.silentRestore();
227
-
228
- const unsubscribe = AuthService.onAuthStateChanged((user) => {
229
- console.log(user?.email);
230
- });
231
-
232
- const tokensUnsubscribe = AuthService.onTokensRefreshed((tokens) => {
233
- console.log(tokens.expirationTime);
234
- });
235
-
236
- unsubscribe();
237
- tokensUnsubscribe();
238
- ```
239
-
240
- ## Login Options
241
-
242
- ```ts
243
- await login("google", {
244
- scopes: ["email", "profile"],
245
- loginHint: "user@example.com",
246
- nonce: "opaque-nonce",
247
- useOneTap: true,
248
- forceAccountPicker: true,
249
- filterByAuthorizedAccounts: true,
250
- useLegacyGoogleSignIn: true,
251
- forceCodeForRefreshToken: true,
252
- hostedDomain: "company.com",
253
- requestVerifiedPhoneNumber: true,
254
- });
255
-
256
- await login("apple", {
257
- scopes: ["email", "name"],
258
- nonce: "opaque-nonce",
259
- });
260
-
261
- await login("microsoft", {
262
- scopes: ["openid", "profile", "email", "offline_access", "User.Read"],
263
- loginHint: "user@example.com",
134
+ await AuthService.login(AuthProvider.Microsoft, {
264
135
  tenant: "organizations",
265
- prompt: "select_account",
266
136
  });
267
137
  ```
268
138
 
269
- | Option | Provider | Platform | Notes |
270
- | ---------------------------- | ------------ | ---------------- | ----------------------------------------------------------- |
271
- | `scopes` | All | iOS, Android, web | Requested OAuth scopes. Apple is unavailable on Android. |
272
- | `loginHint` | Google, Microsoft | iOS, Android, web | Prefills account selection when supported by the provider. |
273
- | `nonce` | Google, Apple | iOS, Android, web | Passed to provider ID-token flows when the SDK supports it. |
274
- | `useOneTap` | Google | Android | Enables Credential Manager auto-select. |
275
- | `useSheet` | Google | iOS | Compatibility alias; prefer `forceAccountPicker`. |
276
- | `forceAccountPicker` | Google | iOS, Android, web | Forces an account picker. Android uses the legacy chooser. |
277
- | `filterByAuthorizedAccounts` | Google | Android | Limits Credential Manager to authorized accounts. |
278
- | `useLegacyGoogleSignIn` | Google | Android | Uses legacy Google Sign-In for server auth code flows. |
279
- | `forceCodeForRefreshToken` | Google | Android | Forces a new server auth code on the legacy Google path. |
280
- | `hostedDomain` | Google | iOS, Android, web | Hints or filters Google Workspace hosted-domain accounts. |
281
- | `openIDRealm` | Google | iOS, web | Adds OpenID realm support where the SDK exposes it. |
282
- | `requestVerifiedPhoneNumber` | Google | Android | Requests verified phone number through Credential Manager. |
283
- | `tenant` | Microsoft | iOS, Android, web | Overrides configured tenant. |
284
- | `prompt` | Microsoft | iOS, Android, web | `login`, `consent`, `select_account`, or `none`. |
285
-
286
- ## Incremental Scopes
287
-
288
- ```ts
289
- const calendarScope = "https://www.googleapis.com/auth/calendar.readonly";
290
-
291
- await requestScopes([calendarScope]);
292
- await revokeScopes([calendarScope]);
293
- ```
294
-
295
- 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.
296
-
297
- ## Storage Model
298
-
299
- 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.
300
-
301
- For app-managed persistence, store only the minimum state your product needs:
302
-
303
- ```ts
304
- import { AuthService } from "react-native-nitro-auth";
305
-
306
- const snapshot = {
307
- user: AuthService.currentUser,
308
- scopes: AuthService.grantedScopes,
309
- updatedAt: Date.now(),
310
- };
311
- ```
312
-
313
- On web, the default is also memory storage. You can opt into browser storage with:
314
-
315
- ```js
316
- extra: {
317
- nitroAuthWebStorage: "session", // "session", "local", or "memory"
318
- nitroAuthPersistTokensOnWeb: true,
319
- }
320
- ```
321
-
322
- ## Error Contract
323
-
324
- All public async APIs throw `AuthError`, including provider errors surfaced by native and web `AuthService` implementations.
325
-
326
- ```ts
327
- try {
328
- await AuthService.login("microsoft");
329
- } catch (e) {
330
- const error = AuthError.from(e);
331
- switch (error.code) {
332
- case "cancelled":
333
- break;
334
- case "configuration_error":
335
- break;
336
- case "token_error":
337
- break;
338
- default:
339
- break;
340
- }
341
- }
342
- ```
343
-
344
- Known error codes:
345
-
346
- ```ts
347
- type AuthErrorCode =
348
- | "cancelled"
349
- | "timeout"
350
- | "popup_blocked"
351
- | "network_error"
352
- | "configuration_error"
353
- | "not_signed_in"
354
- | "operation_in_progress"
355
- | "unsupported_provider"
356
- | "invalid_state"
357
- | "invalid_nonce"
358
- | "token_error"
359
- | "no_id_token"
360
- | "parse_error"
361
- | "refresh_failed"
362
- | "unknown";
363
- ```
364
-
365
- `underlyingMessage` keeps the raw native or OAuth message when it differs from the stable code.
139
+ ## Providers
366
140
 
367
- ## API Reference
141
+ | Provider | Native | Web | Notes |
142
+ | --------- | ------------ | --- | --------------------------------------------------------------------- |
143
+ | Google | iOS, Android | Yes | Supports account picker, login hint, refresh, and incremental scopes. |
144
+ | Apple | iOS | Yes | Apple returns name and email only on first authorization. |
145
+ | Microsoft | iOS, Android | Yes | Supports tenant and B2C configuration. |
368
146
 
369
- ### Exports
147
+ Use `expo-auth-session`, `react-native-app-auth`, Auth0, Firebase Auth, or your
148
+ identity provider SDK when you need a generic OAuth/OIDC provider, password
149
+ auth, MFA, hosted user management, or server session management.
370
150
 
371
- ```ts
372
- export * from "react-native-nitro-auth";
373
- ```
151
+ ## API
374
152
 
375
153
  Main exports:
376
154
 
377
- - `useAuth()`
378
- - `AuthService`
379
- - `SocialButton`
380
- - `AuthError`
381
- - `isAuthErrorCode()`
382
- - `toAuthErrorCode()`
383
- - `AuthProvider`
384
- - `AuthUser`
385
- - `AuthTokens`
386
- - `LoginOptions`
387
- - `ProviderLoginOptions`
388
- - `LoginOptionsByProvider`
389
- - `GoogleLoginOptions`
390
- - `GoogleIOSLoginOptions`
391
- - `GoogleAndroidLoginOptions`
392
- - `GoogleWebLoginOptions`
393
- - `AppleLoginOptions`
394
- - `AppleIOSLoginOptions`
395
- - `AppleWebLoginOptions`
396
- - `MicrosoftLoginOptions`
397
- - `AuthLogin`
398
- - `TypedAuth`
399
-
400
- ### useAuth()
401
-
402
- ```ts
403
- type UseAuthReturn = {
404
- user: AuthUser | undefined;
405
- scopes: string[];
406
- loading: boolean;
407
- error: AuthError | undefined;
408
- hasPlayServices: boolean;
409
- login(provider: AuthProvider, options?: LoginOptions): Promise<void>;
410
- logout(): void;
411
- requestScopes(scopes: string[]): Promise<void>;
412
- revokeScopes(scopes: string[]): Promise<void>;
413
- revokeAccess(): Promise<void>;
414
- getAccessToken(): Promise<string | undefined>;
415
- refreshToken(): Promise<AuthTokens>;
416
- silentRestore(): Promise<void>;
417
- };
418
- ```
419
-
420
- ### Strong Login Types
421
-
422
- `AuthService.login()` and `useAuth().login()` infer the allowed option object from the provider argument. Provider-specific option types intentionally reject unsupported keys, so TypeScript can catch mistakes before they become native configuration bugs.
155
+ - `useAuth()` for React state, login, logout, refresh, and listeners.
156
+ - `AuthService` for imperative login, refresh, logout, and user reads.
157
+ - `SocialButton` for provider-aware UI.
158
+ - `AuthProvider` for Google, Apple, and Microsoft provider names.
159
+ - `AuthError` and `AuthErrorCode` for deterministic failures.
160
+ - Provider option types for strongly typed login calls.
423
161
 
424
- ```ts
425
- await AuthService.login("apple", {
426
- nonce: "opaque-nonce",
427
- });
428
-
429
- await AuthService.login("microsoft", {
430
- tenant: "organizations",
431
- prompt: "select_account",
432
- });
433
- ```
434
-
435
- Provider/platform option helpers are exported for config builders and AI-generated integrations:
436
-
437
- ```ts
438
- import type {
439
- GoogleAndroidLoginOptions,
440
- GoogleIOSLoginOptions,
441
- MicrosoftLoginOptions,
442
- } from "react-native-nitro-auth";
162
+ Login options include `scopes`, `loginHint`, `accountId`, `forceRefresh`,
163
+ `nonce`, `state`, `tenant`, `prompt`, and provider-specific fields.
443
164
 
444
- const androidGoogleOptions = {
445
- useOneTap: true,
446
- filterByAuthorizedAccounts: true,
447
- requestVerifiedPhoneNumber: true,
448
- } satisfies GoogleAndroidLoginOptions;
449
-
450
- const iosGoogleOptions = {
451
- hostedDomain: "company.com",
452
- openIDRealm: "https://example.com",
453
- } satisfies GoogleIOSLoginOptions;
454
-
455
- const microsoftOptions = {
456
- tenant: "organizations",
457
- prompt: "select_account",
458
- } satisfies MicrosoftLoginOptions;
459
- ```
460
-
461
- Examples of mistakes that TypeScript rejects:
462
-
463
- ```ts
464
- await AuthService.login("apple", {
465
- tenant: "organizations",
466
- });
165
+ ## Storage Model
467
166
 
468
- await AuthService.login("microsoft", {
469
- nonce: "opaque-nonce",
470
- });
471
- ```
167
+ Tokens are held in memory. Persist only the snapshot your app actually needs,
168
+ preferably in your own secure storage or backend session. JWT decode on the
169
+ client is for display and routing only; signature validation belongs on your
170
+ server.
472
171
 
473
- ### AuthUser
474
-
475
- ```ts
476
- type AuthUser = {
477
- provider: "google" | "apple" | "microsoft";
478
- email?: string;
479
- name?: string;
480
- photo?: string;
481
- idToken?: string;
482
- accessToken?: string;
483
- refreshToken?: string;
484
- serverAuthCode?: string;
485
- authorizationCode?: string;
486
- userId?: string;
487
- phoneNumber?: string;
488
- hostedDomain?: string;
489
- scopes?: string[];
490
- expirationTime?: number;
491
- underlyingError?: string;
492
- };
493
- ```
172
+ ## Error Contract
494
173
 
495
- ## Example App
174
+ Async public APIs throw `AuthError` with a stable `code`, `provider`, `platform`,
175
+ and `message`. Use `instanceof AuthError` when branching in UI code.
496
176
 
497
- The example app is the fastest way to verify setup and read a complete integration.
177
+ Common codes include `cancelled`, `configuration_error`, `network_error`,
178
+ `provider_unavailable`, `token_refresh_failed`, and `unknown`.
498
179
 
499
- ```sh
500
- cp apps/example/.env.example apps/example/.env.local
501
- bun install
502
- bun run example:prebuild:clean
503
- bun run example:ios
504
- bun run example:android
505
- ```
180
+ ## Platform Support
506
181
 
507
- The demo includes:
182
+ | Platform | Status |
183
+ | -------- | ----------------------------------------------------------- |
184
+ | iOS | Google, Apple, Microsoft native flows. |
185
+ | Android | Google and Microsoft native flows. |
186
+ | Web | Google, Apple, and Microsoft OAuth through Expo web config. |
187
+ | Expo | Development builds with the config plugin. |
508
188
 
509
- - Provider cards for Google, Apple, and Microsoft.
510
- - Token and scope operations.
511
- - Silent restore, account picker, revoke access, and native logging actions.
512
- - Platform-gated controls for each supported Google, Apple, and Microsoft option.
513
- - App-owned disk snapshot example with `react-native-nitro-storage`.
514
- - Runtime smoke tests for the public API.
189
+ Validated baseline: Expo SDK 56, React Native 0.85.3, React 19.2.3, and Nitro
190
+ Modules 0.35.7.
515
191
 
516
192
  ## Troubleshooting
517
193
 
518
- | Symptom | Check |
519
- | ------------------------------------- | -------------------------------------------------------------------------------- |
520
- | `configuration_error` on Google | Client ID is missing or wrong for the current platform |
521
- | Google works in debug but not release | Add release SHA-1/SHA-256 fingerprints to Google Cloud Console |
522
- | Android `hasPlayServices` is false | Use an emulator image with Google Play Services |
523
- | Apple email/name missing | Apple only returns these fields on first authorization |
524
- | Microsoft `invalid_state` | Redirect URI or app resume path is wrong, or an old auth redirect completed late |
525
- | Microsoft `token_error` | Check tenant, client ID, redirect URI, and requested scopes |
526
- | Web popup blocked | Call `login()` from a user gesture such as a button press |
527
- | `operation_in_progress` | A provider flow is already active; wait for it to finish or sign out |
528
-
529
- ## Production Notes
530
-
531
- - Verify ID tokens on your backend. Client-side JWT parsing is for display and expiration hints only.
532
- - Store refresh tokens only in storage your app explicitly owns and secures.
533
- - Keep Google debug and release signing fingerprints in sync with your OAuth clients.
534
- - Add provider-specific redirect URIs for every environment.
535
- - Run the example app on iOS and Android before shipping provider config changes.
194
+ - **Expo Go error:** build a dev client; Expo Go cannot load Nitro modules.
195
+ - **Provider not configured:** verify plugin values, `expo.extra`, and that you
196
+ prebuilt after changing config.
197
+ - **Apple profile missing name/email:** Apple only sends those fields on the
198
+ first authorization.
199
+ - **Microsoft redirect mismatch:** confirm bundle ID, Android package,
200
+ `microsoftClientId`, and tenant/B2C settings match the provider console.
536
201
 
537
- ## Release Checks
538
-
539
- ```sh
540
- bun run release:preflight
541
- ```
542
-
543
- The release preflight runs core-version verification, codegen, build, lint, typecheck, Jest, C++ tests, Expo dependency validation, Expo Doctor, Expo config introspection, package docs sync, pack dry run, and `bun publish --dry-run --ignore-scripts`.
544
-
545
- CI runs the same preflight with registry publish dry-run disabled because GitHub pull-request jobs do not have npm publish credentials.
546
-
547
- For faster local iteration before the full release dry run:
202
+ ## Development
548
203
 
549
204
  ```sh
205
+ bun install
550
206
  bun run check
551
- bun run test:cpp
552
- bun run --cwd packages/react-native-nitro-auth test:coverage -- --runInBand
553
- bun run --cwd packages/react-native-nitro-auth test:cpp:coverage
554
- ```
555
-
556
- Before shipping provider or native config changes, also verify the example app:
557
-
558
- ```sh
559
- bun run example:prebuild
207
+ bun run release:preflight
560
208
  bun run example:android
561
209
  bun run example:ios
562
210
  ```
563
211
 
212
+ Run native example builds before release when changing plugin, native, Nitro, or
213
+ packaging files.
214
+
564
215
  ## License
565
216
 
566
217
  MIT