react-native-nitro-auth 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +275 -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 +34 -4
- package/android/src/main/java/com/auth/AuthAdapter.kt +584 -67
- package/android/src/main/java/com/auth/GoogleSignInActivity.kt +1 -3
- package/android/src/main/java/com/auth/MicrosoftAuthActivity.kt +25 -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 +7 -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 +7 -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 +13 -3
- package/nitrogen/generated/shared/c++/MicrosoftPrompt.hpp +84 -0
- package/package.json +11 -9
- package/react-native-nitro-auth.podspec +4 -2
- package/src/Auth.nitro.ts +13 -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,51 @@ 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
|
+
|
|
53
98
|
### Expo Setup
|
|
54
99
|
|
|
55
100
|
Add the plugin to `app.json` or `app.config.js`:
|
|
@@ -63,17 +108,28 @@ Add the plugin to `app.json` or `app.config.js`:
|
|
|
63
108
|
{
|
|
64
109
|
"ios": {
|
|
65
110
|
"googleClientId": "YOUR_IOS_CLIENT_ID.apps.googleusercontent.com",
|
|
111
|
+
"googleServerClientId": "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com",
|
|
66
112
|
"googleUrlScheme": "com.googleusercontent.apps.YOUR_IOS_CLIENT_ID",
|
|
67
|
-
"appleSignIn": true
|
|
113
|
+
"appleSignIn": true,
|
|
114
|
+
"microsoftClientId": "YOUR_AZURE_AD_CLIENT_ID",
|
|
115
|
+
"microsoftTenant": "common",
|
|
116
|
+
"microsoftB2cDomain": "your-tenant.b2clogin.com"
|
|
68
117
|
},
|
|
69
118
|
"android": {
|
|
70
|
-
"googleClientId": "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com"
|
|
119
|
+
"googleClientId": "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com",
|
|
120
|
+
"microsoftClientId": "YOUR_AZURE_AD_CLIENT_ID",
|
|
121
|
+
"microsoftTenant": "common",
|
|
122
|
+
"microsoftB2cDomain": "your-tenant.b2clogin.com"
|
|
71
123
|
}
|
|
72
124
|
}
|
|
73
125
|
]
|
|
74
126
|
],
|
|
75
127
|
"extra": {
|
|
76
|
-
"googleWebClientId": "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com"
|
|
128
|
+
"googleWebClientId": "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com",
|
|
129
|
+
"microsoftClientId": "YOUR_AZURE_AD_CLIENT_ID",
|
|
130
|
+
"microsoftTenant": "common",
|
|
131
|
+
"microsoftB2cDomain": "your-tenant.b2clogin.com",
|
|
132
|
+
"appleWebClientId": "com.example.web"
|
|
77
133
|
}
|
|
78
134
|
}
|
|
79
135
|
}
|
|
@@ -87,9 +143,18 @@ Create a `.env.local` file:
|
|
|
87
143
|
# iOS Client ID
|
|
88
144
|
GOOGLE_IOS_CLIENT_ID=your-ios-client-id.apps.googleusercontent.com
|
|
89
145
|
GOOGLE_IOS_URL_SCHEME=com.googleusercontent.apps.your-ios-client-id
|
|
146
|
+
GOOGLE_SERVER_CLIENT_ID=your-web-client-id.apps.googleusercontent.com
|
|
90
147
|
|
|
91
148
|
# Web Client ID (used for Android OAuth flow)
|
|
92
149
|
GOOGLE_WEB_CLIENT_ID=your-web-client-id.apps.googleusercontent.com
|
|
150
|
+
|
|
151
|
+
# Microsoft/Azure AD (optional)
|
|
152
|
+
MICROSOFT_CLIENT_ID=your-azure-ad-application-id
|
|
153
|
+
MICROSOFT_TENANT=common
|
|
154
|
+
MICROSOFT_B2C_DOMAIN=your-tenant.b2clogin.com
|
|
155
|
+
|
|
156
|
+
# Apple (web only)
|
|
157
|
+
APPLE_WEB_CLIENT_ID=com.example.web
|
|
93
158
|
```
|
|
94
159
|
|
|
95
160
|
Then reference them in `app.config.js`:
|
|
@@ -105,17 +170,28 @@ export default {
|
|
|
105
170
|
{
|
|
106
171
|
ios: {
|
|
107
172
|
googleClientId: process.env.GOOGLE_IOS_CLIENT_ID,
|
|
173
|
+
googleServerClientId: process.env.GOOGLE_SERVER_CLIENT_ID,
|
|
108
174
|
googleUrlScheme: process.env.GOOGLE_IOS_URL_SCHEME,
|
|
109
175
|
appleSignIn: true,
|
|
176
|
+
microsoftClientId: process.env.MICROSOFT_CLIENT_ID,
|
|
177
|
+
microsoftTenant: process.env.MICROSOFT_TENANT,
|
|
178
|
+
microsoftB2cDomain: process.env.MICROSOFT_B2C_DOMAIN,
|
|
110
179
|
},
|
|
111
180
|
android: {
|
|
112
181
|
googleClientId: process.env.GOOGLE_WEB_CLIENT_ID,
|
|
182
|
+
microsoftClientId: process.env.MICROSOFT_CLIENT_ID,
|
|
183
|
+
microsoftTenant: process.env.MICROSOFT_TENANT,
|
|
184
|
+
microsoftB2cDomain: process.env.MICROSOFT_B2C_DOMAIN,
|
|
113
185
|
},
|
|
114
186
|
},
|
|
115
187
|
],
|
|
116
188
|
],
|
|
117
189
|
extra: {
|
|
118
190
|
googleWebClientId: process.env.GOOGLE_WEB_CLIENT_ID,
|
|
191
|
+
microsoftClientId: process.env.MICROSOFT_CLIENT_ID,
|
|
192
|
+
microsoftTenant: process.env.MICROSOFT_TENANT,
|
|
193
|
+
microsoftB2cDomain: process.env.MICROSOFT_B2C_DOMAIN,
|
|
194
|
+
appleWebClientId: process.env.APPLE_WEB_CLIENT_ID,
|
|
119
195
|
},
|
|
120
196
|
},
|
|
121
197
|
};
|
|
@@ -125,14 +201,106 @@ export default {
|
|
|
125
201
|
>
|
|
126
202
|
> - `appleSignIn` on iOS is `false` by default to avoid unnecessary entitlements. Set it to `true` to enable Apple Sign-In.
|
|
127
203
|
> - For Android, use your **Web Client ID** (not Android Client ID) for proper OAuth flow.
|
|
204
|
+
> - If you need `serverAuthCode`, set `googleServerClientId` to your Web Client ID.
|
|
128
205
|
> - Add `googleWebClientId` to `expo.extra` for web platform support.
|
|
129
206
|
> - The `serverAuthCode` is automatically included in `AuthUser` when available (requires backend integration setup in Google Cloud Console).
|
|
207
|
+
> - For Microsoft Sign-In, use `common` tenant for multi-tenant apps, or specify your Azure AD tenant ID for single-tenant apps.
|
|
208
|
+
> - For Azure AD B2C, set `microsoftB2cDomain` and pass the B2C tenant in `microsoftTenant`.
|
|
209
|
+
|
|
210
|
+
### Google OAuth Setup
|
|
211
|
+
|
|
212
|
+
1. Create OAuth client IDs in Google Cloud Console:
|
|
213
|
+
- **iOS client ID** (used by iOS)
|
|
214
|
+
- **Web client ID** (used by Android and for `serverAuthCode`)
|
|
215
|
+
2. Configure your app:
|
|
216
|
+
- Expo: set `googleClientId`, `googleServerClientId`, and `googleUrlScheme`
|
|
217
|
+
- Bare iOS: add `GIDClientID`, `GIDServerClientID`, and URL scheme in `Info.plist`
|
|
218
|
+
- Bare Android: set `nitro_auth_google_client_id` to your **Web client ID**
|
|
219
|
+
3. If you use `serverAuthCode`, make sure OAuth consent screen is configured in Google Cloud.
|
|
220
|
+
|
|
221
|
+
### Apple Sign-In Setup
|
|
222
|
+
|
|
223
|
+
1. **iOS**: enable the “Sign in with Apple” capability in Xcode and in your Apple Developer account.
|
|
224
|
+
2. **Web**: create a Service ID and configure the domain + return URL in Apple Developer.
|
|
225
|
+
3. Configure your app:
|
|
226
|
+
- Expo: set `appleSignIn: true` for iOS.
|
|
227
|
+
- Web: set `appleWebClientId` in `expo.extra` (or `.env`).
|
|
228
|
+
|
|
229
|
+
### Microsoft Azure AD Setup
|
|
230
|
+
|
|
231
|
+
To enable Microsoft Sign-In, you need to register an application in the Azure Portal:
|
|
232
|
+
|
|
233
|
+
1. Go to [Azure Portal](https://portal.azure.com) > Azure Active Directory > App registrations
|
|
234
|
+
2. Click "New registration"
|
|
235
|
+
3. Set the redirect URIs:
|
|
236
|
+
- **iOS**: `msauth.{bundle-identifier}://auth` (e.g., `msauth.com.myapp://auth`)
|
|
237
|
+
- **Android**: `msauth://{package-name}/{client-id}` (e.g., `msauth://com.myapp/00000000-0000-0000-0000-000000000000`)
|
|
238
|
+
- **Web**: `https://your-domain.com` (the page that loads the app)
|
|
239
|
+
4. Under "API permissions", add `openid`, `email`, `profile`, and `User.Read` (Microsoft Graph)
|
|
240
|
+
5. Copy the Application (client) ID for use in your config
|
|
241
|
+
|
|
242
|
+
**Tenant Options:**
|
|
243
|
+
|
|
244
|
+
- `common` - Any Azure AD or personal Microsoft account
|
|
245
|
+
- `organizations` - Any Azure AD account (work/school)
|
|
246
|
+
- `consumers` - Personal Microsoft accounts only
|
|
247
|
+
- `{tenant-id}` - Specific Azure AD tenant
|
|
248
|
+
- **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
249
|
|
|
131
250
|
### Bare React Native
|
|
132
251
|
|
|
133
|
-
**iOS
|
|
252
|
+
**iOS**
|
|
253
|
+
|
|
254
|
+
- Add to `Info.plist`: `GIDClientID`, `GIDServerClientID` (optional), `MSALClientID`, `MSALTenant` (optional), `MSALB2cDomain` (optional).
|
|
255
|
+
- Add URL schemes in `Info.plist`:
|
|
256
|
+
- Google: `com.googleusercontent.apps.<YOUR_IOS_CLIENT_ID>`
|
|
257
|
+
- Microsoft: `msauth.<your.bundle.id>` (used for `msauth.<bundle.id>://auth`)
|
|
258
|
+
- Enable the “Sign in with Apple” capability if you use Apple Sign-In.
|
|
259
|
+
|
|
260
|
+
**Android**
|
|
261
|
+
|
|
262
|
+
- Add string resources in `res/values/strings.xml`:
|
|
263
|
+
- `nitro_auth_google_client_id` (Web client ID)
|
|
264
|
+
- `nitro_auth_microsoft_client_id`
|
|
265
|
+
- `nitro_auth_microsoft_tenant` (optional)
|
|
266
|
+
- `nitro_auth_microsoft_b2c_domain` (optional)
|
|
267
|
+
- Add the Microsoft redirect activity to `AndroidManifest.xml`:
|
|
268
|
+
|
|
269
|
+
```xml
|
|
270
|
+
<activity
|
|
271
|
+
android:name="com.auth.MicrosoftAuthActivity"
|
|
272
|
+
android:exported="true">
|
|
273
|
+
<intent-filter>
|
|
274
|
+
<action android:name="android.intent.action.VIEW" />
|
|
275
|
+
<category android:name="android.intent.category.DEFAULT" />
|
|
276
|
+
<category android:name="android.intent.category.BROWSABLE" />
|
|
277
|
+
<data
|
|
278
|
+
android:scheme="msauth"
|
|
279
|
+
android:host="${applicationId}"
|
|
280
|
+
android:path="/YOUR_MICROSOFT_CLIENT_ID" />
|
|
281
|
+
</intent-filter>
|
|
282
|
+
</activity>
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Web Setup
|
|
134
286
|
|
|
135
|
-
|
|
287
|
+
Nitro Auth reads web configuration from `expo.extra`:
|
|
288
|
+
|
|
289
|
+
```json
|
|
290
|
+
{
|
|
291
|
+
"expo": {
|
|
292
|
+
"extra": {
|
|
293
|
+
"googleWebClientId": "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com",
|
|
294
|
+
"microsoftClientId": "YOUR_AZURE_AD_CLIENT_ID",
|
|
295
|
+
"microsoftTenant": "common",
|
|
296
|
+
"microsoftB2cDomain": "your-tenant.b2clogin.com",
|
|
297
|
+
"appleWebClientId": "com.example.web"
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
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
304
|
|
|
137
305
|
## Quick Start
|
|
138
306
|
|
|
@@ -164,11 +332,42 @@ function LoginScreen() {
|
|
|
164
332
|
onPress={() => login("google")}
|
|
165
333
|
disabled={loading || !hasPlayServices}
|
|
166
334
|
/>
|
|
335
|
+
<SocialButton
|
|
336
|
+
provider="apple"
|
|
337
|
+
onPress={() => login("apple")}
|
|
338
|
+
disabled={loading}
|
|
339
|
+
/>
|
|
340
|
+
<SocialButton
|
|
341
|
+
provider="microsoft"
|
|
342
|
+
onPress={() => login("microsoft")}
|
|
343
|
+
disabled={loading}
|
|
344
|
+
/>
|
|
167
345
|
</View>
|
|
168
346
|
);
|
|
169
347
|
}
|
|
170
348
|
```
|
|
171
349
|
|
|
350
|
+
### Microsoft Login Options
|
|
351
|
+
|
|
352
|
+
```tsx
|
|
353
|
+
// Login with specific tenant
|
|
354
|
+
await login("microsoft", {
|
|
355
|
+
tenant: "your-tenant-id",
|
|
356
|
+
prompt: "select_account", // 'login' | 'consent' | 'select_account' | 'none'
|
|
357
|
+
scopes: ["openid", "email", "profile", "User.Read"],
|
|
358
|
+
loginHint: "user@example.com",
|
|
359
|
+
});
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
**B2C example:**
|
|
363
|
+
|
|
364
|
+
```tsx
|
|
365
|
+
await login("microsoft", {
|
|
366
|
+
tenant: "your-tenant.onmicrosoft.com/B2C_1_signin",
|
|
367
|
+
scopes: ["openid", "email", "profile", "offline_access"],
|
|
368
|
+
});
|
|
369
|
+
```
|
|
370
|
+
|
|
172
371
|
## Migration from @react-native-google-signin/google-signin
|
|
173
372
|
|
|
174
373
|
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 +509,7 @@ const handleCalendar = async () => {
|
|
|
310
509
|
|
|
311
510
|
### Pluggable Storage Adapters
|
|
312
511
|
|
|
313
|
-
Nitro Auth persists the session automatically. By default, it uses
|
|
512
|
+
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
513
|
|
|
315
514
|
#### 1) JS Storage (AsyncStorage, MMKV, etc.)
|
|
316
515
|
|
|
@@ -332,6 +531,9 @@ const mmkvAdapter: JSStorageAdapter = {
|
|
|
332
531
|
AuthService.setJSStorageAdapter(mmkvAdapter);
|
|
333
532
|
```
|
|
334
533
|
|
|
534
|
+
> [!NOTE]
|
|
535
|
+
> Call `setJSStorageAdapter` before your first `useAuth()` or `AuthService` call so cached values are loaded before UI renders.
|
|
536
|
+
|
|
335
537
|
#### 2) Native Storage (Keychain, etc.)
|
|
336
538
|
|
|
337
539
|
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 +546,18 @@ import { KeychainStorage } from "./native/KeychainStorage";
|
|
|
344
546
|
AuthService.setStorageAdapter(KeychainStorage);
|
|
345
547
|
```
|
|
346
548
|
|
|
549
|
+
**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.
|
|
550
|
+
|
|
551
|
+
### Production Readiness
|
|
552
|
+
|
|
553
|
+
Nitro Auth is suitable for production use:
|
|
554
|
+
|
|
555
|
+
- **Google Sign-In**: Full support including One-Tap / Sheet, incremental scopes, and token refresh on iOS, Android, and Web.
|
|
556
|
+
- **Apple Sign-In**: Supported on iOS and Web (not available on Android).
|
|
557
|
+
- **Microsoft (Azure AD / B2C)**: Login, incremental scopes, and token refresh are supported on all platforms. Uses PKCE, state, and nonce for security.
|
|
558
|
+
|
|
559
|
+
**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).
|
|
560
|
+
|
|
347
561
|
### Logging & Debugging
|
|
348
562
|
|
|
349
563
|
Enable verbose logging to see detailed OAuth flow information in the console:
|
|
@@ -369,7 +583,7 @@ const freshToken = await AuthService.getAccessToken();
|
|
|
369
583
|
|
|
370
584
|
### Standardized Error Codes
|
|
371
585
|
|
|
372
|
-
Handle failures reliably with predictable error strings:
|
|
586
|
+
Handle failures reliably with predictable error strings. Some flows can surface provider-specific codes (listed below):
|
|
373
587
|
|
|
374
588
|
```ts
|
|
375
589
|
try {
|
|
@@ -390,6 +604,12 @@ try {
|
|
|
390
604
|
| `network_error` | A network error occurred |
|
|
391
605
|
| `configuration_error` | Missing client IDs or invalid setup |
|
|
392
606
|
| `unsupported_provider` | The provider is not supported on this platform |
|
|
607
|
+
| `invalid_state` | PKCE state mismatch (possible CSRF) |
|
|
608
|
+
| `invalid_nonce` | Nonce mismatch in token response |
|
|
609
|
+
| `token_error` | Token exchange failed |
|
|
610
|
+
| `no_id_token` | No `id_token` in token response |
|
|
611
|
+
| `parse_error` | Failed to parse token response |
|
|
612
|
+
| `refresh_failed` | Refresh token flow failed |
|
|
393
613
|
| `unknown` | An unknown error occurred |
|
|
394
614
|
|
|
395
615
|
### Native Error Metadata
|
|
@@ -412,6 +632,13 @@ try {
|
|
|
412
632
|
}
|
|
413
633
|
```
|
|
414
634
|
|
|
635
|
+
### Troubleshooting
|
|
636
|
+
|
|
637
|
+
- `configuration_error`: verify client IDs, URL schemes, and redirect URIs are set for the current platform.
|
|
638
|
+
- `invalid_state` or `invalid_nonce`: ensure the redirect URI in your provider console matches your app config exactly.
|
|
639
|
+
- `hasPlayServices` is false: prompt the user to install/update Google Play Services or disable One-Tap.
|
|
640
|
+
- Apple web login fails: confirm `appleWebClientId` is set and your domain is registered with Apple.
|
|
641
|
+
|
|
415
642
|
### Automatic Token Refresh
|
|
416
643
|
|
|
417
644
|
The `getAccessToken()` method automatically checks if the current token is expired (or about to expire) and triggers a silent refresh if possible:
|
|
@@ -423,23 +650,6 @@ const { getAccessToken } = useAuth();
|
|
|
423
650
|
const token = await getAccessToken();
|
|
424
651
|
```
|
|
425
652
|
|
|
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
653
|
### Offline Access (Server Auth Code)
|
|
444
654
|
|
|
445
655
|
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 +665,7 @@ if (user?.serverAuthCode) {
|
|
|
455
665
|
|
|
456
666
|
### Custom Storage Adapter
|
|
457
667
|
|
|
458
|
-
By default, Nitro Auth uses
|
|
668
|
+
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
669
|
|
|
460
670
|
> [!IMPORTANT]
|
|
461
671
|
> `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 +722,9 @@ await login("google", {
|
|
|
512
722
|
});
|
|
513
723
|
```
|
|
514
724
|
|
|
725
|
+
> [!NOTE]
|
|
726
|
+
> One-Tap requires Google Play Services. You can check `hasPlayServices` from `useAuth()` and show a fallback UI if needed.
|
|
727
|
+
|
|
515
728
|
### Force Account Picker
|
|
516
729
|
|
|
517
730
|
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 +761,38 @@ This is useful for scenarios where:
|
|
|
548
761
|
| `getAccessToken` | `() => Promise<string?>` | Get current access token (auto-refreshes) |
|
|
549
762
|
| `refreshToken` | `() => Promise<AuthTokens>` | Explicitly refresh and return new tokens |
|
|
550
763
|
|
|
764
|
+
### AuthService
|
|
765
|
+
|
|
766
|
+
| Method | Type | Description |
|
|
767
|
+
| -------------------------- | ----------------------------------------- | -------------------------------------------------- |
|
|
768
|
+
| `login` | `(provider, options?) => Promise<void>` | Start login flow |
|
|
769
|
+
| `logout` | `() => void` | Clear session |
|
|
770
|
+
| `silentRestore` | `() => Promise<void>` | Restore session on startup |
|
|
771
|
+
| `requestScopes` | `(scopes) => Promise<void>` | Request additional OAuth scopes |
|
|
772
|
+
| `revokeScopes` | `(scopes) => Promise<void>` | Revoke previously granted scopes |
|
|
773
|
+
| `getAccessToken` | `() => Promise<string \| undefined>` | Get current access token (auto-refreshes) |
|
|
774
|
+
| `refreshToken` | `() => Promise<AuthTokens>` | Explicitly refresh and return new tokens |
|
|
775
|
+
| `onAuthStateChanged` | `(callback) => () => void` | Subscribe to auth state changes |
|
|
776
|
+
| `onTokensRefreshed` | `(callback) => () => void` | Subscribe to token refresh events |
|
|
777
|
+
| `setLoggingEnabled` | `(enabled: boolean) => void` | Enable or disable verbose logging |
|
|
778
|
+
| `setStorageAdapter` | `(adapter?: AuthStorageAdapter) => void` | Set native storage adapter |
|
|
779
|
+
| `setJSStorageAdapter` | `(adapter?: JSStorageAdapter) => Promise<void>` | Set JS storage adapter |
|
|
780
|
+
|
|
781
|
+
### AuthUser
|
|
782
|
+
|
|
783
|
+
| Field | Type | Description |
|
|
784
|
+
| --------------- | -------------------- | ------------------------------------------------ |
|
|
785
|
+
| `provider` | `"google" \| "apple" \| "microsoft"` | Provider that authenticated the user |
|
|
786
|
+
| `email` | `string?` | User email (if provided) |
|
|
787
|
+
| `name` | `string?` | User display name |
|
|
788
|
+
| `photo` | `string?` | Profile image URL (Google only) |
|
|
789
|
+
| `idToken` | `string?` | OIDC ID token |
|
|
790
|
+
| `accessToken` | `string?` | Access token (if available) |
|
|
791
|
+
| `serverAuthCode`| `string?` | Google server auth code (if configured) |
|
|
792
|
+
| `scopes` | `string[]?` | Granted OAuth scopes |
|
|
793
|
+
| `expirationTime`| `number?` | Token expiration time (ms since epoch) |
|
|
794
|
+
| `underlyingError` | `string?` | Raw native error message |
|
|
795
|
+
|
|
551
796
|
### LoginOptions
|
|
552
797
|
|
|
553
798
|
| Option | Type | Platform | Description |
|
|
@@ -557,13 +802,14 @@ This is useful for scenarios where:
|
|
|
557
802
|
| `useOneTap` | `boolean` | Android | Enable Google One-Tap (Credential Manager) |
|
|
558
803
|
| `useSheet` | `boolean` | iOS | Enable iOS Google Sign-In Sheet |
|
|
559
804
|
| `forceAccountPicker` | `boolean` | All | Always show the account selection screen |
|
|
560
|
-
| `
|
|
805
|
+
| `tenant` | `string` | Microsoft | Azure AD tenant (`common`, `organizations`, etc.) |
|
|
806
|
+
| `prompt` | `string` | Microsoft | Prompt behavior (`login`, `consent`, `select_account`, `none`) |
|
|
561
807
|
|
|
562
808
|
### SocialButton Props
|
|
563
809
|
|
|
564
810
|
| Prop | Type | Default | Description |
|
|
565
811
|
| -------------- | ---------------------------------------------- | ----------- | --------------------------------------------- |
|
|
566
|
-
| `provider` | `"google" \| "apple"`
|
|
812
|
+
| `provider` | `"google" \| "apple" \| "microsoft"` | required | Authentication provider |
|
|
567
813
|
| `variant` | `"primary" \| "outline" \| "white" \| "black"` | `"primary"` | Button style variant |
|
|
568
814
|
| `onPress` | `() => void` | — | Custom handler (disables default login) |
|
|
569
815
|
| `onSuccess` | `(user: AuthUser) => void` | — | Called with user data on success (auto-login) |
|
|
@@ -579,6 +825,7 @@ This is useful for scenarios where:
|
|
|
579
825
|
| ------------------------- | --- | ------- | --- |
|
|
580
826
|
| Google Sign-In | ✅ | ✅ | ✅ |
|
|
581
827
|
| Apple Sign-In | ✅ | ❌ | ✅ |
|
|
828
|
+
| Microsoft Sign-In | ✅ | ✅ | ✅ |
|
|
582
829
|
| Custom OAuth Scopes | ✅ | ✅ | ✅ |
|
|
583
830
|
| Incremental Authorization | ✅ | ✅ | ✅ |
|
|
584
831
|
| 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,15 +38,32 @@ 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;
|
|
45
54
|
|
|
46
55
|
if (options) {
|
|
47
56
|
if (options->scopes) scopes = *options->scopes;
|
|
48
57
|
loginHint = options->loginHint;
|
|
58
|
+
tenant = options->tenant;
|
|
59
|
+
if (options->prompt.has_value()) {
|
|
60
|
+
switch (options->prompt.value()) {
|
|
61
|
+
case MicrosoftPrompt::LOGIN: prompt = "login"; break;
|
|
62
|
+
case MicrosoftPrompt::CONSENT: prompt = "consent"; break;
|
|
63
|
+
case MicrosoftPrompt::SELECT_ACCOUNT: prompt = "select_account"; break;
|
|
64
|
+
case MicrosoftPrompt::NONE: prompt = "none"; break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
49
67
|
useOneTap = options->useOneTap.value_or(false);
|
|
50
68
|
forceAccountPicker = options->forceAccountPicker.value_or(false);
|
|
51
69
|
}
|
|
@@ -58,9 +76,12 @@ std::shared_ptr<Promise<AuthUser>> PlatformAuth::login(AuthProvider provider, co
|
|
|
58
76
|
}
|
|
59
77
|
|
|
60
78
|
jstring jLoginHint = loginHint.has_value() ? make_jstring(loginHint.value()).get() : nullptr;
|
|
79
|
+
jstring jTenant = tenant.has_value() ? make_jstring(tenant.value()).get() : nullptr;
|
|
80
|
+
jstring jPrompt = prompt.has_value() ? make_jstring(prompt.value()).get() : nullptr;
|
|
81
|
+
|
|
61
82
|
jclass adapterClass = env->FindClass("com/auth/AuthAdapter");
|
|
62
83
|
jmethodID loginMethod = env->GetStaticMethodID(adapterClass, "loginSync",
|
|
63
|
-
"(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;
|
|
84
|
+
"(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;ZZLjava/lang/String;Ljava/lang/String;)V");
|
|
64
85
|
env->CallStaticVoidMethod(adapterClass, loginMethod,
|
|
65
86
|
contextPtr,
|
|
66
87
|
make_jstring(providerStr).get(),
|
|
@@ -68,7 +89,9 @@ std::shared_ptr<Promise<AuthUser>> PlatformAuth::login(AuthProvider provider, co
|
|
|
68
89
|
jScopes,
|
|
69
90
|
jLoginHint,
|
|
70
91
|
(jboolean)useOneTap,
|
|
71
|
-
(jboolean)forceAccountPicker
|
|
92
|
+
(jboolean)forceAccountPicker,
|
|
93
|
+
jTenant,
|
|
94
|
+
jPrompt);
|
|
72
95
|
|
|
73
96
|
return promise;
|
|
74
97
|
}
|
|
@@ -179,7 +202,14 @@ extern "C" JNIEXPORT void JNICALL Java_com_auth_AuthAdapter_nativeOnLoginSuccess
|
|
|
179
202
|
|
|
180
203
|
AuthUser user;
|
|
181
204
|
const char* providerCStr = env->GetStringUTFChars(provider, nullptr);
|
|
182
|
-
|
|
205
|
+
std::string providerStr(providerCStr);
|
|
206
|
+
if (providerStr == "google") {
|
|
207
|
+
user.provider = AuthProvider::GOOGLE;
|
|
208
|
+
} else if (providerStr == "microsoft") {
|
|
209
|
+
user.provider = AuthProvider::MICROSOFT;
|
|
210
|
+
} else {
|
|
211
|
+
user.provider = AuthProvider::APPLE;
|
|
212
|
+
}
|
|
183
213
|
env->ReleaseStringUTFChars(provider, providerCStr);
|
|
184
214
|
|
|
185
215
|
if (email) {
|