react-native-nitro-auth 0.4.0 → 0.5.1
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 -28
- package/android/build.gradle +8 -2
- package/android/gradle.properties +2 -2
- package/android/src/main/cpp/JniOnLoad.cpp +1 -0
- package/android/src/main/cpp/PlatformAuth+Android.cpp +37 -4
- package/android/src/main/java/com/auth/AuthAdapter.kt +626 -78
- package/android/src/main/java/com/auth/GoogleSignInActivity.kt +3 -4
- package/android/src/main/java/com/auth/MicrosoftAuthActivity.kt +25 -0
- package/android/src/main/java/com/auth/NitroAuthPackage.kt +2 -0
- package/app.plugin.js +113 -3
- package/cpp/AuthCache.cpp +72 -19
- package/ios/AuthAdapter.swift +457 -52
- package/ios/KeychainStore.swift +43 -0
- package/ios/PlatformAuth+iOS.mm +29 -3
- package/lib/commonjs/Auth.web.js +246 -7
- package/lib/commonjs/Auth.web.js.map +1 -1
- package/lib/commonjs/index.js +7 -11
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/service.js.map +1 -1
- package/lib/commonjs/service.web.js +0 -8
- package/lib/commonjs/service.web.js.map +1 -1
- package/lib/commonjs/ui/social-button.js +12 -2
- package/lib/commonjs/ui/social-button.js.map +1 -1
- package/lib/commonjs/ui/social-button.web.js +12 -2
- package/lib/commonjs/ui/social-button.web.js.map +1 -1
- package/lib/commonjs/use-auth.js.map +1 -1
- package/lib/commonjs/utils/logger.js +1 -1
- package/lib/commonjs/utils/logger.js.map +1 -1
- package/lib/module/Auth.web.js +246 -7
- package/lib/module/Auth.web.js.map +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/service.js.map +1 -1
- package/lib/module/service.web.js +0 -8
- package/lib/module/service.web.js.map +1 -1
- package/lib/module/ui/social-button.js +12 -2
- package/lib/module/ui/social-button.js.map +1 -1
- package/lib/module/ui/social-button.web.js +12 -2
- package/lib/module/ui/social-button.web.js.map +1 -1
- package/lib/module/use-auth.js.map +1 -1
- package/lib/module/utils/logger.js +1 -1
- package/lib/module/utils/logger.js.map +1 -1
- package/lib/typescript/commonjs/Auth.nitro.d.ts +9 -2
- package/lib/typescript/commonjs/Auth.nitro.d.ts.map +1 -1
- package/lib/typescript/commonjs/Auth.web.d.ts +7 -0
- package/lib/typescript/commonjs/Auth.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +1 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/service.d.ts.map +1 -1
- package/lib/typescript/commonjs/service.web.d.ts +2 -6
- package/lib/typescript/commonjs/service.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/social-button.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/social-button.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/use-auth.d.ts +12 -8
- package/lib/typescript/commonjs/use-auth.d.ts.map +1 -1
- package/lib/typescript/commonjs/utils/logger.d.ts +5 -5
- package/lib/typescript/commonjs/utils/logger.d.ts.map +1 -1
- package/lib/typescript/module/Auth.nitro.d.ts +9 -2
- package/lib/typescript/module/Auth.nitro.d.ts.map +1 -1
- package/lib/typescript/module/Auth.web.d.ts +7 -0
- package/lib/typescript/module/Auth.web.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +1 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/service.d.ts.map +1 -1
- package/lib/typescript/module/service.web.d.ts +2 -6
- package/lib/typescript/module/service.web.d.ts.map +1 -1
- package/lib/typescript/module/ui/social-button.d.ts.map +1 -1
- package/lib/typescript/module/ui/social-button.web.d.ts.map +1 -1
- package/lib/typescript/module/use-auth.d.ts +12 -8
- package/lib/typescript/module/use-auth.d.ts.map +1 -1
- package/lib/typescript/module/utils/logger.d.ts +5 -5
- package/lib/typescript/module/utils/logger.d.ts.map +1 -1
- package/nitrogen/generated/shared/c++/AuthProvider.hpp +4 -0
- package/nitrogen/generated/shared/c++/LoginOptions.hpp +17 -3
- package/nitrogen/generated/shared/c++/MicrosoftPrompt.hpp +84 -0
- package/package.json +6 -4
- package/react-native-nitro-auth.podspec +4 -2
- package/src/Auth.nitro.ts +15 -1
- package/src/Auth.web.ts +350 -7
- package/src/index.ts +1 -1
- package/src/service.ts +4 -3
- package/src/service.web.ts +3 -12
- package/src/ui/social-button.tsx +10 -2
- package/src/ui/social-button.web.tsx +10 -2
- package/src/use-auth.ts +12 -1
- package/src/utils/logger.ts +5 -5
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
🚀 **High-performance, JSI-powered Authentication for React Native.**
|
|
8
8
|
|
|
9
|
-
Nitro Auth is a modern authentication library for React Native built on top of [Nitro Modules](https://github.com/mrousavy/nitro). It provides a unified, type-safe API for Google and
|
|
9
|
+
Nitro Auth is a modern authentication library for React Native built on top of [Nitro Modules](https://github.com/mrousavy/nitro). It provides a unified, type-safe API for Google, Apple, and Microsoft Sign-In with zero-bridge overhead.
|
|
10
10
|
|
|
11
11
|
## Why Nitro Auth?
|
|
12
12
|
|
|
@@ -50,6 +50,54 @@ For Expo projects, rebuild native code after installation:
|
|
|
50
50
|
bunx expo prebuild
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
+
### Testing locally (example app + Microsoft login)
|
|
54
|
+
|
|
55
|
+
Fastest way to confirm the package and Microsoft login work:
|
|
56
|
+
|
|
57
|
+
1. **Azure app (one-time)**
|
|
58
|
+
In [Azure Portal](https://portal.azure.com) → **Azure Active Directory** → **App registrations** → **New registration**:
|
|
59
|
+
- Name: e.g. `Nitro Auth Example`
|
|
60
|
+
- Supported account types: **Accounts in any organizational directory and personal Microsoft accounts**
|
|
61
|
+
- Redirect URI (add after creation):
|
|
62
|
+
- **Android**: `msauth://com.auth.example/<client-id>`
|
|
63
|
+
- **iOS**: `msauth.com.auth.example://auth` (use your bundle id)
|
|
64
|
+
- Under **Authentication** → **Platform configurations** → add **Mobile and desktop applications** with the Android redirect URI above and the iOS one if testing on iOS.
|
|
65
|
+
Copy the **Application (client) ID**.
|
|
66
|
+
|
|
67
|
+
2. **Env file**
|
|
68
|
+
From the repo root:
|
|
69
|
+
```bash
|
|
70
|
+
cd apps/example
|
|
71
|
+
cp .env.example .env.local
|
|
72
|
+
```
|
|
73
|
+
Edit `.env.local` and set at least:
|
|
74
|
+
```bash
|
|
75
|
+
MICROSOFT_CLIENT_ID=<your-application-client-id>
|
|
76
|
+
MICROSOFT_TENANT=common
|
|
77
|
+
```
|
|
78
|
+
(Google/Apple can stay placeholder if you only care about Microsoft.)
|
|
79
|
+
|
|
80
|
+
3. **Run the app**
|
|
81
|
+
From the **monorepo root**:
|
|
82
|
+
```bash
|
|
83
|
+
bun install
|
|
84
|
+
bun run start
|
|
85
|
+
```
|
|
86
|
+
In a second terminal:
|
|
87
|
+
```bash
|
|
88
|
+
bun run example:android
|
|
89
|
+
# or
|
|
90
|
+
bun run example:ios
|
|
91
|
+
```
|
|
92
|
+
Wait for the app to install and open.
|
|
93
|
+
|
|
94
|
+
4. **Test Microsoft**
|
|
95
|
+
In the app, tap **Sign in with Microsoft**. A browser or in-app tab opens; sign in with a Microsoft/personal account, then you should return to the app with the user shown (email, name, provider MICROSOFT).
|
|
96
|
+
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).
|
|
97
|
+
|
|
98
|
+
> [!TIP]
|
|
99
|
+
> In the example app on Android, you can toggle **Legacy Google Sign-In** to compare Credential Manager vs legacy GoogleSignIn (and to get `serverAuthCode`).
|
|
100
|
+
|
|
53
101
|
### Expo Setup
|
|
54
102
|
|
|
55
103
|
Add the plugin to `app.json` or `app.config.js`:
|
|
@@ -63,17 +111,28 @@ Add the plugin to `app.json` or `app.config.js`:
|
|
|
63
111
|
{
|
|
64
112
|
"ios": {
|
|
65
113
|
"googleClientId": "YOUR_IOS_CLIENT_ID.apps.googleusercontent.com",
|
|
114
|
+
"googleServerClientId": "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com",
|
|
66
115
|
"googleUrlScheme": "com.googleusercontent.apps.YOUR_IOS_CLIENT_ID",
|
|
67
|
-
"appleSignIn": true
|
|
116
|
+
"appleSignIn": true,
|
|
117
|
+
"microsoftClientId": "YOUR_AZURE_AD_CLIENT_ID",
|
|
118
|
+
"microsoftTenant": "common",
|
|
119
|
+
"microsoftB2cDomain": "your-tenant.b2clogin.com"
|
|
68
120
|
},
|
|
69
121
|
"android": {
|
|
70
|
-
"googleClientId": "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com"
|
|
122
|
+
"googleClientId": "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com",
|
|
123
|
+
"microsoftClientId": "YOUR_AZURE_AD_CLIENT_ID",
|
|
124
|
+
"microsoftTenant": "common",
|
|
125
|
+
"microsoftB2cDomain": "your-tenant.b2clogin.com"
|
|
71
126
|
}
|
|
72
127
|
}
|
|
73
128
|
]
|
|
74
129
|
],
|
|
75
130
|
"extra": {
|
|
76
|
-
"googleWebClientId": "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com"
|
|
131
|
+
"googleWebClientId": "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com",
|
|
132
|
+
"microsoftClientId": "YOUR_AZURE_AD_CLIENT_ID",
|
|
133
|
+
"microsoftTenant": "common",
|
|
134
|
+
"microsoftB2cDomain": "your-tenant.b2clogin.com",
|
|
135
|
+
"appleWebClientId": "com.example.web"
|
|
77
136
|
}
|
|
78
137
|
}
|
|
79
138
|
}
|
|
@@ -87,9 +146,18 @@ Create a `.env.local` file:
|
|
|
87
146
|
# iOS Client ID
|
|
88
147
|
GOOGLE_IOS_CLIENT_ID=your-ios-client-id.apps.googleusercontent.com
|
|
89
148
|
GOOGLE_IOS_URL_SCHEME=com.googleusercontent.apps.your-ios-client-id
|
|
149
|
+
GOOGLE_SERVER_CLIENT_ID=your-web-client-id.apps.googleusercontent.com
|
|
90
150
|
|
|
91
151
|
# Web Client ID (used for Android OAuth flow)
|
|
92
152
|
GOOGLE_WEB_CLIENT_ID=your-web-client-id.apps.googleusercontent.com
|
|
153
|
+
|
|
154
|
+
# Microsoft/Azure AD (optional)
|
|
155
|
+
MICROSOFT_CLIENT_ID=your-azure-ad-application-id
|
|
156
|
+
MICROSOFT_TENANT=common
|
|
157
|
+
MICROSOFT_B2C_DOMAIN=your-tenant.b2clogin.com
|
|
158
|
+
|
|
159
|
+
# Apple (web only)
|
|
160
|
+
APPLE_WEB_CLIENT_ID=com.example.web
|
|
93
161
|
```
|
|
94
162
|
|
|
95
163
|
Then reference them in `app.config.js`:
|
|
@@ -105,17 +173,28 @@ export default {
|
|
|
105
173
|
{
|
|
106
174
|
ios: {
|
|
107
175
|
googleClientId: process.env.GOOGLE_IOS_CLIENT_ID,
|
|
176
|
+
googleServerClientId: process.env.GOOGLE_SERVER_CLIENT_ID,
|
|
108
177
|
googleUrlScheme: process.env.GOOGLE_IOS_URL_SCHEME,
|
|
109
178
|
appleSignIn: true,
|
|
179
|
+
microsoftClientId: process.env.MICROSOFT_CLIENT_ID,
|
|
180
|
+
microsoftTenant: process.env.MICROSOFT_TENANT,
|
|
181
|
+
microsoftB2cDomain: process.env.MICROSOFT_B2C_DOMAIN,
|
|
110
182
|
},
|
|
111
183
|
android: {
|
|
112
184
|
googleClientId: process.env.GOOGLE_WEB_CLIENT_ID,
|
|
185
|
+
microsoftClientId: process.env.MICROSOFT_CLIENT_ID,
|
|
186
|
+
microsoftTenant: process.env.MICROSOFT_TENANT,
|
|
187
|
+
microsoftB2cDomain: process.env.MICROSOFT_B2C_DOMAIN,
|
|
113
188
|
},
|
|
114
189
|
},
|
|
115
190
|
],
|
|
116
191
|
],
|
|
117
192
|
extra: {
|
|
118
193
|
googleWebClientId: process.env.GOOGLE_WEB_CLIENT_ID,
|
|
194
|
+
microsoftClientId: process.env.MICROSOFT_CLIENT_ID,
|
|
195
|
+
microsoftTenant: process.env.MICROSOFT_TENANT,
|
|
196
|
+
microsoftB2cDomain: process.env.MICROSOFT_B2C_DOMAIN,
|
|
197
|
+
appleWebClientId: process.env.APPLE_WEB_CLIENT_ID,
|
|
119
198
|
},
|
|
120
199
|
},
|
|
121
200
|
};
|
|
@@ -125,14 +204,106 @@ export default {
|
|
|
125
204
|
>
|
|
126
205
|
> - `appleSignIn` on iOS is `false` by default to avoid unnecessary entitlements. Set it to `true` to enable Apple Sign-In.
|
|
127
206
|
> - For Android, use your **Web Client ID** (not Android Client ID) for proper OAuth flow.
|
|
207
|
+
> - If you need `serverAuthCode`, set `googleServerClientId` to your Web Client ID.
|
|
128
208
|
> - Add `googleWebClientId` to `expo.extra` for web platform support.
|
|
129
209
|
> - The `serverAuthCode` is automatically included in `AuthUser` when available (requires backend integration setup in Google Cloud Console).
|
|
210
|
+
> - For Microsoft Sign-In, use `common` tenant for multi-tenant apps, or specify your Azure AD tenant ID for single-tenant apps.
|
|
211
|
+
> - For Azure AD B2C, set `microsoftB2cDomain` and pass the B2C tenant in `microsoftTenant`.
|
|
212
|
+
|
|
213
|
+
### Google OAuth Setup
|
|
214
|
+
|
|
215
|
+
1. Create OAuth client IDs in Google Cloud Console:
|
|
216
|
+
- **iOS client ID** (used by iOS)
|
|
217
|
+
- **Web client ID** (used by Android and for `serverAuthCode`)
|
|
218
|
+
2. Configure your app:
|
|
219
|
+
- Expo: set `googleClientId`, `googleServerClientId`, and `googleUrlScheme`
|
|
220
|
+
- Bare iOS: add `GIDClientID`, `GIDServerClientID`, and URL scheme in `Info.plist`
|
|
221
|
+
- Bare Android: set `nitro_auth_google_client_id` to your **Web client ID**
|
|
222
|
+
3. If you use `serverAuthCode`, make sure OAuth consent screen is configured in Google Cloud.
|
|
223
|
+
|
|
224
|
+
### Apple Sign-In Setup
|
|
225
|
+
|
|
226
|
+
1. **iOS**: enable the “Sign in with Apple” capability in Xcode and in your Apple Developer account.
|
|
227
|
+
2. **Web**: create a Service ID and configure the domain + return URL in Apple Developer.
|
|
228
|
+
3. Configure your app:
|
|
229
|
+
- Expo: set `appleSignIn: true` for iOS.
|
|
230
|
+
- Web: set `appleWebClientId` in `expo.extra` (or `.env`).
|
|
231
|
+
|
|
232
|
+
### Microsoft Azure AD Setup
|
|
233
|
+
|
|
234
|
+
To enable Microsoft Sign-In, you need to register an application in the Azure Portal:
|
|
235
|
+
|
|
236
|
+
1. Go to [Azure Portal](https://portal.azure.com) > Azure Active Directory > App registrations
|
|
237
|
+
2. Click "New registration"
|
|
238
|
+
3. Set the redirect URIs:
|
|
239
|
+
- **iOS**: `msauth.{bundle-identifier}://auth` (e.g., `msauth.com.myapp://auth`)
|
|
240
|
+
- **Android**: `msauth://{package-name}/{client-id}` (e.g., `msauth://com.myapp/00000000-0000-0000-0000-000000000000`)
|
|
241
|
+
- **Web**: `https://your-domain.com` (the page that loads the app)
|
|
242
|
+
4. Under "API permissions", add `openid`, `email`, `profile`, and `User.Read` (Microsoft Graph)
|
|
243
|
+
5. Copy the Application (client) ID for use in your config
|
|
244
|
+
|
|
245
|
+
**Tenant Options:**
|
|
246
|
+
|
|
247
|
+
- `common` - Any Azure AD or personal Microsoft account
|
|
248
|
+
- `organizations` - Any Azure AD account (work/school)
|
|
249
|
+
- `consumers` - Personal Microsoft accounts only
|
|
250
|
+
- `{tenant-id}` - Specific Azure AD tenant
|
|
251
|
+
- **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).
|
|
130
252
|
|
|
131
253
|
### Bare React Native
|
|
132
254
|
|
|
133
|
-
**iOS
|
|
255
|
+
**iOS**
|
|
256
|
+
|
|
257
|
+
- Add to `Info.plist`: `GIDClientID`, `GIDServerClientID` (optional), `MSALClientID`, `MSALTenant` (optional), `MSALB2cDomain` (optional).
|
|
258
|
+
- Add URL schemes in `Info.plist`:
|
|
259
|
+
- Google: `com.googleusercontent.apps.<YOUR_IOS_CLIENT_ID>`
|
|
260
|
+
- Microsoft: `msauth.<your.bundle.id>` (used for `msauth.<bundle.id>://auth`)
|
|
261
|
+
- Enable the “Sign in with Apple” capability if you use Apple Sign-In.
|
|
262
|
+
|
|
263
|
+
**Android**
|
|
264
|
+
|
|
265
|
+
- Add string resources in `res/values/strings.xml`:
|
|
266
|
+
- `nitro_auth_google_client_id` (Web client ID)
|
|
267
|
+
- `nitro_auth_microsoft_client_id`
|
|
268
|
+
- `nitro_auth_microsoft_tenant` (optional)
|
|
269
|
+
- `nitro_auth_microsoft_b2c_domain` (optional)
|
|
270
|
+
- Add the Microsoft redirect activity to `AndroidManifest.xml`:
|
|
271
|
+
|
|
272
|
+
```xml
|
|
273
|
+
<activity
|
|
274
|
+
android:name="com.auth.MicrosoftAuthActivity"
|
|
275
|
+
android:exported="true">
|
|
276
|
+
<intent-filter>
|
|
277
|
+
<action android:name="android.intent.action.VIEW" />
|
|
278
|
+
<category android:name="android.intent.category.DEFAULT" />
|
|
279
|
+
<category android:name="android.intent.category.BROWSABLE" />
|
|
280
|
+
<data
|
|
281
|
+
android:scheme="msauth"
|
|
282
|
+
android:host="${applicationId}"
|
|
283
|
+
android:path="/YOUR_MICROSOFT_CLIENT_ID" />
|
|
284
|
+
</intent-filter>
|
|
285
|
+
</activity>
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Web Setup
|
|
289
|
+
|
|
290
|
+
Nitro Auth reads web configuration from `expo.extra`:
|
|
134
291
|
|
|
135
|
-
|
|
292
|
+
```json
|
|
293
|
+
{
|
|
294
|
+
"expo": {
|
|
295
|
+
"extra": {
|
|
296
|
+
"googleWebClientId": "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com",
|
|
297
|
+
"microsoftClientId": "YOUR_AZURE_AD_CLIENT_ID",
|
|
298
|
+
"microsoftTenant": "common",
|
|
299
|
+
"microsoftB2cDomain": "your-tenant.b2clogin.com",
|
|
300
|
+
"appleWebClientId": "com.example.web"
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
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.
|
|
136
307
|
|
|
137
308
|
## Quick Start
|
|
138
309
|
|
|
@@ -164,11 +335,42 @@ function LoginScreen() {
|
|
|
164
335
|
onPress={() => login("google")}
|
|
165
336
|
disabled={loading || !hasPlayServices}
|
|
166
337
|
/>
|
|
338
|
+
<SocialButton
|
|
339
|
+
provider="apple"
|
|
340
|
+
onPress={() => login("apple")}
|
|
341
|
+
disabled={loading}
|
|
342
|
+
/>
|
|
343
|
+
<SocialButton
|
|
344
|
+
provider="microsoft"
|
|
345
|
+
onPress={() => login("microsoft")}
|
|
346
|
+
disabled={loading}
|
|
347
|
+
/>
|
|
167
348
|
</View>
|
|
168
349
|
);
|
|
169
350
|
}
|
|
170
351
|
```
|
|
171
352
|
|
|
353
|
+
### Microsoft Login Options
|
|
354
|
+
|
|
355
|
+
```tsx
|
|
356
|
+
// Login with specific tenant
|
|
357
|
+
await login("microsoft", {
|
|
358
|
+
tenant: "your-tenant-id",
|
|
359
|
+
prompt: "select_account", // 'login' | 'consent' | 'select_account' | 'none'
|
|
360
|
+
scopes: ["openid", "email", "profile", "User.Read"],
|
|
361
|
+
loginHint: "user@example.com",
|
|
362
|
+
});
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
**B2C example:**
|
|
366
|
+
|
|
367
|
+
```tsx
|
|
368
|
+
await login("microsoft", {
|
|
369
|
+
tenant: "your-tenant.onmicrosoft.com/B2C_1_signin",
|
|
370
|
+
scopes: ["openid", "email", "profile", "offline_access"],
|
|
371
|
+
});
|
|
372
|
+
```
|
|
373
|
+
|
|
172
374
|
## Migration from @react-native-google-signin/google-signin
|
|
173
375
|
|
|
174
376
|
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.
|
|
@@ -310,7 +512,7 @@ const handleCalendar = async () => {
|
|
|
310
512
|
|
|
311
513
|
### Pluggable Storage Adapters
|
|
312
514
|
|
|
313
|
-
Nitro Auth persists the session automatically. By default, it uses
|
|
515
|
+
Nitro Auth persists the session automatically. By default, it uses secure storage on native (Keychain on iOS, EncryptedSharedPreferences on Android) and `localStorage` on web.
|
|
314
516
|
|
|
315
517
|
#### 1) JS Storage (AsyncStorage, MMKV, etc.)
|
|
316
518
|
|
|
@@ -332,6 +534,9 @@ const mmkvAdapter: JSStorageAdapter = {
|
|
|
332
534
|
AuthService.setJSStorageAdapter(mmkvAdapter);
|
|
333
535
|
```
|
|
334
536
|
|
|
537
|
+
> [!NOTE]
|
|
538
|
+
> Call `setJSStorageAdapter` before your first `useAuth()` or `AuthService` call so cached values are loaded before UI renders.
|
|
539
|
+
|
|
335
540
|
#### 2) Native Storage (Keychain, etc.)
|
|
336
541
|
|
|
337
542
|
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.
|
|
@@ -344,6 +549,18 @@ import { KeychainStorage } from "./native/KeychainStorage";
|
|
|
344
549
|
AuthService.setStorageAdapter(KeychainStorage);
|
|
345
550
|
```
|
|
346
551
|
|
|
552
|
+
**Production recommendation:** If you need custom storage policies, auditability, or a different encryption model, provide your own adapter (Keychain, EncryptedSharedPreferences, or a secure JS store). See [Pluggable Storage Adapters](#pluggable-storage-adapters) above.
|
|
553
|
+
|
|
554
|
+
### Production Readiness
|
|
555
|
+
|
|
556
|
+
Nitro Auth is suitable for production use:
|
|
557
|
+
|
|
558
|
+
- **Google Sign-In**: Full support including One-Tap / Sheet, incremental scopes, and token refresh on iOS, Android, and Web.
|
|
559
|
+
- **Apple Sign-In**: Supported on iOS and Web (not available on Android).
|
|
560
|
+
- **Microsoft (Azure AD / B2C)**: Login, incremental scopes, and token refresh are supported on all platforms. Uses PKCE, state, and nonce for security.
|
|
561
|
+
|
|
562
|
+
**Token storage:** By default, tokens are stored in secure platform storage on native (Keychain / EncryptedSharedPreferences) and in `localStorage` on web. On Android API < 23, storage falls back to unencrypted `SharedPreferences`. For high-security web requirements or custom storage needs, configure a [custom storage adapter](#pluggable-storage-adapters).
|
|
563
|
+
|
|
347
564
|
### Logging & Debugging
|
|
348
565
|
|
|
349
566
|
Enable verbose logging to see detailed OAuth flow information in the console:
|
|
@@ -369,7 +586,7 @@ const freshToken = await AuthService.getAccessToken();
|
|
|
369
586
|
|
|
370
587
|
### Standardized Error Codes
|
|
371
588
|
|
|
372
|
-
Handle failures reliably with predictable error strings:
|
|
589
|
+
Handle failures reliably with predictable error strings. Some flows can surface provider-specific codes (listed below):
|
|
373
590
|
|
|
374
591
|
```ts
|
|
375
592
|
try {
|
|
@@ -390,6 +607,12 @@ try {
|
|
|
390
607
|
| `network_error` | A network error occurred |
|
|
391
608
|
| `configuration_error` | Missing client IDs or invalid setup |
|
|
392
609
|
| `unsupported_provider` | The provider is not supported on this platform |
|
|
610
|
+
| `invalid_state` | PKCE state mismatch (possible CSRF) |
|
|
611
|
+
| `invalid_nonce` | Nonce mismatch in token response |
|
|
612
|
+
| `token_error` | Token exchange failed |
|
|
613
|
+
| `no_id_token` | No `id_token` in token response |
|
|
614
|
+
| `parse_error` | Failed to parse token response |
|
|
615
|
+
| `refresh_failed` | Refresh token flow failed |
|
|
393
616
|
| `unknown` | An unknown error occurred |
|
|
394
617
|
|
|
395
618
|
### Native Error Metadata
|
|
@@ -412,6 +635,13 @@ try {
|
|
|
412
635
|
}
|
|
413
636
|
```
|
|
414
637
|
|
|
638
|
+
### Troubleshooting
|
|
639
|
+
|
|
640
|
+
- `configuration_error`: verify client IDs, URL schemes, and redirect URIs are set for the current platform.
|
|
641
|
+
- `invalid_state` or `invalid_nonce`: ensure the redirect URI in your provider console matches your app config exactly.
|
|
642
|
+
- `hasPlayServices` is false: prompt the user to install/update Google Play Services or disable One-Tap.
|
|
643
|
+
- Apple web login fails: confirm `appleWebClientId` is set and your domain is registered with Apple.
|
|
644
|
+
|
|
415
645
|
### Automatic Token Refresh
|
|
416
646
|
|
|
417
647
|
The `getAccessToken()` method automatically checks if the current token is expired (or about to expire) and triggers a silent refresh if possible:
|
|
@@ -423,23 +653,6 @@ const { getAccessToken } = useAuth();
|
|
|
423
653
|
const token = await getAccessToken();
|
|
424
654
|
```
|
|
425
655
|
|
|
426
|
-
### Incremental Authorization
|
|
427
|
-
|
|
428
|
-
Add more scopes after initial login — no need to re-authenticate:
|
|
429
|
-
|
|
430
|
-
```tsx
|
|
431
|
-
const { requestScopes, revokeScopes, scopes } = useAuth();
|
|
432
|
-
|
|
433
|
-
// Request additional scope
|
|
434
|
-
await requestScopes(["https://www.googleapis.com/auth/calendar.readonly"]);
|
|
435
|
-
|
|
436
|
-
// Check granted scopes
|
|
437
|
-
console.log("Granted:", scopes);
|
|
438
|
-
|
|
439
|
-
// Revoke specific scopes
|
|
440
|
-
await revokeScopes(["https://www.googleapis.com/auth/calendar.readonly"]);
|
|
441
|
-
```
|
|
442
|
-
|
|
443
656
|
### Offline Access (Server Auth Code)
|
|
444
657
|
|
|
445
658
|
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:
|
|
@@ -455,7 +668,7 @@ if (user?.serverAuthCode) {
|
|
|
455
668
|
|
|
456
669
|
### Custom Storage Adapter
|
|
457
670
|
|
|
458
|
-
By default, Nitro Auth uses
|
|
671
|
+
By default, Nitro Auth uses secure native storage on iOS/Android and `localStorage` on web. You can provide a custom adapter for different security or storage requirements.
|
|
459
672
|
|
|
460
673
|
> [!IMPORTANT]
|
|
461
674
|
> `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.
|
|
@@ -512,6 +725,18 @@ await login("google", {
|
|
|
512
725
|
});
|
|
513
726
|
```
|
|
514
727
|
|
|
728
|
+
> [!NOTE]
|
|
729
|
+
> One-Tap requires Google Play Services. You can check `hasPlayServices` from `useAuth()` and show a fallback UI if needed.
|
|
730
|
+
|
|
731
|
+
### Android Legacy Google Sign-In (Server Auth Code)
|
|
732
|
+
|
|
733
|
+
Credential Manager is the recommended default on Android, but it **does not return** `serverAuthCode`.
|
|
734
|
+
If your backend requires `serverAuthCode`, opt into the legacy flow:
|
|
735
|
+
|
|
736
|
+
```ts
|
|
737
|
+
await login("google", { useLegacyGoogleSignIn: true });
|
|
738
|
+
```
|
|
739
|
+
|
|
515
740
|
### Force Account Picker
|
|
516
741
|
|
|
517
742
|
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:
|
|
@@ -548,6 +773,38 @@ This is useful for scenarios where:
|
|
|
548
773
|
| `getAccessToken` | `() => Promise<string?>` | Get current access token (auto-refreshes) |
|
|
549
774
|
| `refreshToken` | `() => Promise<AuthTokens>` | Explicitly refresh and return new tokens |
|
|
550
775
|
|
|
776
|
+
### AuthService
|
|
777
|
+
|
|
778
|
+
| Method | Type | Description |
|
|
779
|
+
| -------------------------- | ----------------------------------------- | -------------------------------------------------- |
|
|
780
|
+
| `login` | `(provider, options?) => Promise<void>` | Start login flow |
|
|
781
|
+
| `logout` | `() => void` | Clear session |
|
|
782
|
+
| `silentRestore` | `() => Promise<void>` | Restore session on startup |
|
|
783
|
+
| `requestScopes` | `(scopes) => Promise<void>` | Request additional OAuth scopes |
|
|
784
|
+
| `revokeScopes` | `(scopes) => Promise<void>` | Revoke previously granted scopes |
|
|
785
|
+
| `getAccessToken` | `() => Promise<string \| undefined>` | Get current access token (auto-refreshes) |
|
|
786
|
+
| `refreshToken` | `() => Promise<AuthTokens>` | Explicitly refresh and return new tokens |
|
|
787
|
+
| `onAuthStateChanged` | `(callback) => () => void` | Subscribe to auth state changes |
|
|
788
|
+
| `onTokensRefreshed` | `(callback) => () => void` | Subscribe to token refresh events |
|
|
789
|
+
| `setLoggingEnabled` | `(enabled: boolean) => void` | Enable or disable verbose logging |
|
|
790
|
+
| `setStorageAdapter` | `(adapter?: AuthStorageAdapter) => void` | Set native storage adapter |
|
|
791
|
+
| `setJSStorageAdapter` | `(adapter?: JSStorageAdapter) => Promise<void>` | Set JS storage adapter |
|
|
792
|
+
|
|
793
|
+
### AuthUser
|
|
794
|
+
|
|
795
|
+
| Field | Type | Description |
|
|
796
|
+
| --------------- | -------------------- | ------------------------------------------------ |
|
|
797
|
+
| `provider` | `"google" \| "apple" \| "microsoft"` | Provider that authenticated the user |
|
|
798
|
+
| `email` | `string?` | User email (if provided) |
|
|
799
|
+
| `name` | `string?` | User display name |
|
|
800
|
+
| `photo` | `string?` | Profile image URL (Google only) |
|
|
801
|
+
| `idToken` | `string?` | OIDC ID token |
|
|
802
|
+
| `accessToken` | `string?` | Access token (if available) |
|
|
803
|
+
| `serverAuthCode`| `string?` | Google server auth code (if configured) |
|
|
804
|
+
| `scopes` | `string[]?` | Granted OAuth scopes |
|
|
805
|
+
| `expirationTime`| `number?` | Token expiration time (ms since epoch) |
|
|
806
|
+
| `underlyingError` | `string?` | Raw native error message |
|
|
807
|
+
|
|
551
808
|
### LoginOptions
|
|
552
809
|
|
|
553
810
|
| Option | Type | Platform | Description |
|
|
@@ -557,13 +814,15 @@ This is useful for scenarios where:
|
|
|
557
814
|
| `useOneTap` | `boolean` | Android | Enable Google One-Tap (Credential Manager) |
|
|
558
815
|
| `useSheet` | `boolean` | iOS | Enable iOS Google Sign-In Sheet |
|
|
559
816
|
| `forceAccountPicker` | `boolean` | All | Always show the account selection screen |
|
|
560
|
-
| `
|
|
817
|
+
| `useLegacyGoogleSignIn` | `boolean` | Android | Use legacy Google Sign-In (supports `serverAuthCode`) |
|
|
818
|
+
| `tenant` | `string` | Microsoft | Azure AD tenant (`common`, `organizations`, etc.) |
|
|
819
|
+
| `prompt` | `string` | Microsoft | Prompt behavior (`login`, `consent`, `select_account`, `none`) |
|
|
561
820
|
|
|
562
821
|
### SocialButton Props
|
|
563
822
|
|
|
564
823
|
| Prop | Type | Default | Description |
|
|
565
824
|
| -------------- | ---------------------------------------------- | ----------- | --------------------------------------------- |
|
|
566
|
-
| `provider` | `"google" \| "apple"`
|
|
825
|
+
| `provider` | `"google" \| "apple" \| "microsoft"` | required | Authentication provider |
|
|
567
826
|
| `variant` | `"primary" \| "outline" \| "white" \| "black"` | `"primary"` | Button style variant |
|
|
568
827
|
| `onPress` | `() => void` | — | Custom handler (disables default login) |
|
|
569
828
|
| `onSuccess` | `(user: AuthUser) => void` | — | Called with user data on success (auto-login) |
|
|
@@ -579,6 +838,7 @@ This is useful for scenarios where:
|
|
|
579
838
|
| ------------------------- | --- | ------- | --- |
|
|
580
839
|
| Google Sign-In | ✅ | ✅ | ✅ |
|
|
581
840
|
| Apple Sign-In | ✅ | ❌ | ✅ |
|
|
841
|
+
| Microsoft Sign-In | ✅ | ✅ | ✅ |
|
|
582
842
|
| Custom OAuth Scopes | ✅ | ✅ | ✅ |
|
|
583
843
|
| Incremental Authorization | ✅ | ✅ | ✅ |
|
|
584
844
|
| Token Refresh | ✅ | ✅ | ✅ |
|
package/android/build.gradle
CHANGED
|
@@ -89,17 +89,23 @@ repositories {
|
|
|
89
89
|
|
|
90
90
|
dependencies {
|
|
91
91
|
implementation "com.facebook.react:react-native:+"
|
|
92
|
-
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.
|
|
92
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib:2.1.0"
|
|
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.2.0"
|
|
97
97
|
|
|
98
98
|
// Activity result APIs
|
|
99
99
|
implementation "androidx.activity:activity-ktx:1.9.3"
|
|
100
100
|
|
|
101
|
+
// Custom Tabs (Microsoft OAuth in-app browser)
|
|
102
|
+
implementation "androidx.browser:browser:1.8.0"
|
|
103
|
+
|
|
101
104
|
// Google Credential Manager (One-Tap / Passkeys)
|
|
102
105
|
implementation "androidx.credentials:credentials:1.5.0"
|
|
103
106
|
implementation "androidx.credentials:credentials-play-services-auth:1.5.0"
|
|
104
107
|
implementation "com.google.android.libraries.identity.googleid:googleid:1.1.1"
|
|
108
|
+
|
|
109
|
+
// Secure storage (EncryptedSharedPreferences)
|
|
110
|
+
implementation "androidx.security:security-crypto:1.0.0"
|
|
105
111
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
#include "AuthUser.hpp"
|
|
3
3
|
#include "AuthTokens.hpp"
|
|
4
4
|
#include "AuthCache.hpp"
|
|
5
|
+
#include "MicrosoftPrompt.hpp"
|
|
5
6
|
#include <fbjni/fbjni.h>
|
|
6
7
|
#include <NitroModules/NitroLogger.hpp>
|
|
7
8
|
#include <NitroModules/Promise.hpp>
|
|
@@ -37,17 +38,36 @@ std::shared_ptr<Promise<AuthUser>> PlatformAuth::login(AuthProvider provider, co
|
|
|
37
38
|
gLoginPromise = promise;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
std::string providerStr
|
|
41
|
+
std::string providerStr;
|
|
42
|
+
switch (provider) {
|
|
43
|
+
case AuthProvider::GOOGLE: providerStr = "google"; break;
|
|
44
|
+
case AuthProvider::APPLE: providerStr = "apple"; break;
|
|
45
|
+
case AuthProvider::MICROSOFT: providerStr = "microsoft"; break;
|
|
46
|
+
}
|
|
47
|
+
|
|
41
48
|
std::vector<std::string> scopes = {"email", "profile"};
|
|
42
49
|
std::optional<std::string> loginHint;
|
|
50
|
+
std::optional<std::string> tenant;
|
|
51
|
+
std::optional<std::string> prompt;
|
|
43
52
|
bool useOneTap = false;
|
|
44
53
|
bool forceAccountPicker = false;
|
|
54
|
+
bool useLegacyGoogleSignIn = false;
|
|
45
55
|
|
|
46
56
|
if (options) {
|
|
47
57
|
if (options->scopes) scopes = *options->scopes;
|
|
48
58
|
loginHint = options->loginHint;
|
|
59
|
+
tenant = options->tenant;
|
|
60
|
+
if (options->prompt.has_value()) {
|
|
61
|
+
switch (options->prompt.value()) {
|
|
62
|
+
case MicrosoftPrompt::LOGIN: prompt = "login"; break;
|
|
63
|
+
case MicrosoftPrompt::CONSENT: prompt = "consent"; break;
|
|
64
|
+
case MicrosoftPrompt::SELECT_ACCOUNT: prompt = "select_account"; break;
|
|
65
|
+
case MicrosoftPrompt::NONE: prompt = "none"; break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
49
68
|
useOneTap = options->useOneTap.value_or(false);
|
|
50
69
|
forceAccountPicker = options->forceAccountPicker.value_or(false);
|
|
70
|
+
useLegacyGoogleSignIn = options->useLegacyGoogleSignIn.value_or(false);
|
|
51
71
|
}
|
|
52
72
|
|
|
53
73
|
JNIEnv* env = Environment::current();
|
|
@@ -58,9 +78,12 @@ std::shared_ptr<Promise<AuthUser>> PlatformAuth::login(AuthProvider provider, co
|
|
|
58
78
|
}
|
|
59
79
|
|
|
60
80
|
jstring jLoginHint = loginHint.has_value() ? make_jstring(loginHint.value()).get() : nullptr;
|
|
81
|
+
jstring jTenant = tenant.has_value() ? make_jstring(tenant.value()).get() : nullptr;
|
|
82
|
+
jstring jPrompt = prompt.has_value() ? make_jstring(prompt.value()).get() : nullptr;
|
|
83
|
+
|
|
61
84
|
jclass adapterClass = env->FindClass("com/auth/AuthAdapter");
|
|
62
85
|
jmethodID loginMethod = env->GetStaticMethodID(adapterClass, "loginSync",
|
|
63
|
-
"(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;
|
|
86
|
+
"(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;ZZZLjava/lang/String;Ljava/lang/String;)V");
|
|
64
87
|
env->CallStaticVoidMethod(adapterClass, loginMethod,
|
|
65
88
|
contextPtr,
|
|
66
89
|
make_jstring(providerStr).get(),
|
|
@@ -68,7 +91,10 @@ std::shared_ptr<Promise<AuthUser>> PlatformAuth::login(AuthProvider provider, co
|
|
|
68
91
|
jScopes,
|
|
69
92
|
jLoginHint,
|
|
70
93
|
(jboolean)useOneTap,
|
|
71
|
-
(jboolean)forceAccountPicker
|
|
94
|
+
(jboolean)forceAccountPicker,
|
|
95
|
+
(jboolean)useLegacyGoogleSignIn,
|
|
96
|
+
jTenant,
|
|
97
|
+
jPrompt);
|
|
72
98
|
|
|
73
99
|
return promise;
|
|
74
100
|
}
|
|
@@ -179,7 +205,14 @@ extern "C" JNIEXPORT void JNICALL Java_com_auth_AuthAdapter_nativeOnLoginSuccess
|
|
|
179
205
|
|
|
180
206
|
AuthUser user;
|
|
181
207
|
const char* providerCStr = env->GetStringUTFChars(provider, nullptr);
|
|
182
|
-
|
|
208
|
+
std::string providerStr(providerCStr);
|
|
209
|
+
if (providerStr == "google") {
|
|
210
|
+
user.provider = AuthProvider::GOOGLE;
|
|
211
|
+
} else if (providerStr == "microsoft") {
|
|
212
|
+
user.provider = AuthProvider::MICROSOFT;
|
|
213
|
+
} else {
|
|
214
|
+
user.provider = AuthProvider::APPLE;
|
|
215
|
+
}
|
|
183
216
|
env->ReleaseStringUTFChars(provider, providerCStr);
|
|
184
217
|
|
|
185
218
|
if (email) {
|