react-native-nitro-auth 0.1.5 → 0.3.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 +28 -11
- package/android/build.gradle +1 -1
- package/android/src/main/cpp/PlatformAuth+Android.cpp +22 -7
- package/android/src/main/java/com/auth/AuthAdapter.kt +15 -15
- package/app.plugin.js +7 -5
- package/ios/AuthAdapter.swift +37 -48
- package/ios/PlatformAuth+iOS.mm +32 -24
- package/lib/commonjs/Auth.web.js +14 -21
- package/lib/commonjs/Auth.web.js.map +1 -1
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/use-auth.js +5 -3
- package/lib/commonjs/use-auth.js.map +1 -1
- package/lib/module/Auth.web.js +14 -21
- package/lib/module/Auth.web.js.map +1 -1
- package/lib/module/package.json +1 -0
- package/lib/module/use-auth.js +5 -3
- package/lib/module/use-auth.js.map +1 -1
- package/lib/typescript/{Auth.nitro.d.ts → commonjs/Auth.nitro.d.ts} +4 -0
- package/lib/typescript/commonjs/Auth.nitro.d.ts.map +1 -0
- package/lib/typescript/commonjs/Auth.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/AuthStorage.nitro.d.ts.map +1 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/index.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/package.json +1 -0
- package/lib/typescript/commonjs/service.d.ts.map +1 -0
- package/lib/typescript/commonjs/service.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/social-button.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/social-button.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/use-auth.d.ts.map +1 -0
- package/lib/typescript/commonjs/utils/logger.d.ts.map +1 -0
- package/lib/typescript/module/Auth.nitro.d.ts +48 -0
- package/lib/typescript/module/Auth.nitro.d.ts.map +1 -0
- package/lib/typescript/module/Auth.web.d.ts +40 -0
- package/lib/typescript/module/Auth.web.d.ts.map +1 -0
- package/lib/typescript/module/AuthStorage.nitro.d.ts +19 -0
- package/lib/typescript/module/AuthStorage.nitro.d.ts.map +1 -0
- package/lib/typescript/module/index.d.ts +6 -0
- package/lib/typescript/module/index.d.ts.map +1 -0
- package/lib/typescript/module/index.web.d.ts +6 -0
- package/lib/typescript/module/index.web.d.ts.map +1 -0
- package/lib/typescript/module/package.json +1 -0
- package/lib/typescript/module/service.d.ts +3 -0
- package/lib/typescript/module/service.d.ts.map +1 -0
- package/lib/typescript/module/service.web.d.ts +2 -0
- package/lib/typescript/module/service.web.d.ts.map +1 -0
- package/lib/typescript/module/ui/social-button.d.ts +17 -0
- package/lib/typescript/module/ui/social-button.d.ts.map +1 -0
- package/lib/typescript/module/ui/social-button.web.d.ts +17 -0
- package/lib/typescript/module/ui/social-button.web.d.ts.map +1 -0
- package/lib/typescript/module/use-auth.d.ts +15 -0
- package/lib/typescript/module/use-auth.d.ts.map +1 -0
- package/lib/typescript/module/utils/logger.d.ts +8 -0
- package/lib/typescript/module/utils/logger.d.ts.map +1 -0
- package/nitrogen/generated/android/NitroAuth+autolinking.cmake +1 -1
- package/nitrogen/generated/android/NitroAuth+autolinking.gradle +1 -1
- package/nitrogen/generated/android/NitroAuthOnLoad.cpp +1 -1
- package/nitrogen/generated/android/NitroAuthOnLoad.hpp +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/auth/NitroAuthOnLoad.kt +1 -1
- package/nitrogen/generated/ios/NitroAuth+autolinking.rb +2 -2
- package/nitrogen/generated/ios/NitroAuth-Swift-Cxx-Bridge.cpp +1 -1
- package/nitrogen/generated/ios/NitroAuth-Swift-Cxx-Bridge.hpp +1 -1
- package/nitrogen/generated/ios/NitroAuth-Swift-Cxx-Umbrella.hpp +1 -1
- package/nitrogen/generated/ios/NitroAuthAutolinking.mm +1 -1
- package/nitrogen/generated/ios/NitroAuthAutolinking.swift +5 -1
- package/nitrogen/generated/shared/c++/AuthProvider.hpp +1 -1
- package/nitrogen/generated/shared/c++/AuthTokens.hpp +19 -11
- package/nitrogen/generated/shared/c++/AuthUser.hpp +42 -30
- package/nitrogen/generated/shared/c++/HybridAuthSpec.cpp +1 -1
- package/nitrogen/generated/shared/c++/HybridAuthSpec.hpp +1 -1
- package/nitrogen/generated/shared/c++/HybridAuthStorageAdapterSpec.cpp +1 -1
- package/nitrogen/generated/shared/c++/HybridAuthStorageAdapterSpec.hpp +1 -1
- package/nitrogen/generated/shared/c++/LoginOptions.hpp +24 -12
- package/package.json +5 -4
- package/react-native-nitro-auth.podspec +1 -1
- package/src/Auth.nitro.ts +4 -0
- package/src/Auth.web.ts +17 -20
- package/src/use-auth.ts +5 -3
- package/lib/typescript/Auth.nitro.d.ts.map +0 -1
- package/lib/typescript/Auth.web.d.ts.map +0 -1
- package/lib/typescript/AuthStorage.nitro.d.ts.map +0 -1
- package/lib/typescript/index.d.ts.map +0 -1
- package/lib/typescript/index.web.d.ts.map +0 -1
- package/lib/typescript/service.d.ts.map +0 -1
- package/lib/typescript/service.web.d.ts.map +0 -1
- package/lib/typescript/ui/social-button.d.ts.map +0 -1
- package/lib/typescript/ui/social-button.web.d.ts.map +0 -1
- package/lib/typescript/use-auth.d.ts.map +0 -1
- package/lib/typescript/utils/logger.d.ts.map +0 -1
- /package/lib/typescript/{Auth.web.d.ts → commonjs/Auth.web.d.ts} +0 -0
- /package/lib/typescript/{AuthStorage.nitro.d.ts → commonjs/AuthStorage.nitro.d.ts} +0 -0
- /package/lib/typescript/{index.d.ts → commonjs/index.d.ts} +0 -0
- /package/lib/typescript/{index.web.d.ts → commonjs/index.web.d.ts} +0 -0
- /package/lib/typescript/{service.d.ts → commonjs/service.d.ts} +0 -0
- /package/lib/typescript/{service.web.d.ts → commonjs/service.web.d.ts} +0 -0
- /package/lib/typescript/{ui → commonjs/ui}/social-button.d.ts +0 -0
- /package/lib/typescript/{ui → commonjs/ui}/social-button.web.d.ts +0 -0
- /package/lib/typescript/{use-auth.d.ts → commonjs/use-auth.d.ts} +0 -0
- /package/lib/typescript/{utils → commonjs/utils}/logger.d.ts +0 -0
package/README.md
CHANGED
|
@@ -27,7 +27,8 @@ Nitro Auth is designed to replace legacy modules like `@react-native-google-sign
|
|
|
27
27
|
- **Expo Ready**: Comes with a powerful Config Plugin for zero-config setup.
|
|
28
28
|
- **Cross-Platform**: Unified API for iOS, Android, and Web.
|
|
29
29
|
- **Auto-Refresh**: Synchronous access to tokens with automatic silent refresh.
|
|
30
|
-
- **Google One-Tap**: Modern login experience on Android
|
|
30
|
+
- **Google One-Tap / Sheet**: Modern login experience on Android (Credential Manager) and iOS (Sign-In Sheet).
|
|
31
|
+
- **Error Metadata**: Detailed native error messages for easier debugging.
|
|
31
32
|
- **Custom Storage**: Pluggable storage adapters for secure persistence (e.g., Keychain).
|
|
32
33
|
- **Refresh Interceptors**: Listen to token updates globally.
|
|
33
34
|
|
|
@@ -52,7 +53,8 @@ Add the plugin to `app.json`:
|
|
|
52
53
|
"ios": {
|
|
53
54
|
"googleClientId": "YOUR_IOS_CLIENT_ID.apps.googleusercontent.com",
|
|
54
55
|
"googleServerClientId": "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com",
|
|
55
|
-
"googleUrlScheme": "com.googleusercontent.apps.YOUR_IOS_CLIENT_ID"
|
|
56
|
+
"googleUrlScheme": "com.googleusercontent.apps.YOUR_IOS_CLIENT_ID",
|
|
57
|
+
"appleSignIn": true
|
|
56
58
|
},
|
|
57
59
|
"android": {
|
|
58
60
|
"googleClientId": "YOUR_ANDROID_CLIENT_ID.apps.googleusercontent.com"
|
|
@@ -64,7 +66,8 @@ Add the plugin to `app.json`:
|
|
|
64
66
|
}
|
|
65
67
|
```
|
|
66
68
|
|
|
67
|
-
> [!NOTE] > `
|
|
69
|
+
> [!NOTE] > `appleSignIn` on iOS is `false` by default to avoid unnecessary entitlements. Set it to `true` to enable Apple Sign-In.
|
|
70
|
+
> `googleServerClientId` is only required if you need a `serverAuthCode` for backend integration.
|
|
68
71
|
|
|
69
72
|
### Bare React Native
|
|
70
73
|
|
|
@@ -144,13 +147,24 @@ try {
|
|
|
144
147
|
}
|
|
145
148
|
```
|
|
146
149
|
|
|
147
|
-
|
|
|
148
|
-
|
|
|
149
|
-
| `
|
|
150
|
-
| `network_error` | A network error occurred |
|
|
151
|
-
| `configuration_error` | Missing client IDs or invalid setup |
|
|
150
|
+
| `cancelled` | The user cancelled the sign-in flow |
|
|
151
|
+
| `network_error` | A network error occurred |
|
|
152
|
+
| `configuration_error` | Missing client IDs or invalid setup |
|
|
152
153
|
| `unsupported_provider` | The provider is not supported on this platform |
|
|
153
154
|
|
|
155
|
+
### Native Error Metadata
|
|
156
|
+
|
|
157
|
+
For more detailed debugging, Nitro Auth captures the raw native error message in the `underlyingError` property of the `AuthUser` (on success) or surfaces it in the caught `Error` object:
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
try {
|
|
161
|
+
await login("google");
|
|
162
|
+
} catch (e) {
|
|
163
|
+
// Access raw native error message
|
|
164
|
+
console.log(e.underlyingError);
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
154
168
|
### Automatic Token Refresh
|
|
155
169
|
|
|
156
170
|
The `getAccessToken()` method automatically checks if the current token is expired (or about to expire) and triggers a silent refresh if possible:
|
|
@@ -224,12 +238,15 @@ AuthService.onTokensRefreshed((tokens) => {
|
|
|
224
238
|
});
|
|
225
239
|
```
|
|
226
240
|
|
|
227
|
-
### Google One-Tap
|
|
241
|
+
### Google One-Tap & Sheet
|
|
228
242
|
|
|
229
|
-
Explicitly enable the modern One-Tap flow on Android:
|
|
243
|
+
Explicitly enable the modern One-Tap flow on Android or the Sign-In Sheet on iOS:
|
|
230
244
|
|
|
231
245
|
```ts
|
|
232
|
-
await login("google", {
|
|
246
|
+
await login("google", {
|
|
247
|
+
useOneTap: true, // Android
|
|
248
|
+
useSheet: true, // iOS
|
|
249
|
+
});
|
|
233
250
|
```
|
|
234
251
|
|
|
235
252
|
## API Reference
|
package/android/build.gradle
CHANGED
|
@@ -93,7 +93,7 @@ dependencies {
|
|
|
93
93
|
implementation project(":react-native-nitro-modules")
|
|
94
94
|
|
|
95
95
|
// Google Sign-In SDK (full scope support)
|
|
96
|
-
implementation "com.google.android.gms:play-services-auth:21.
|
|
96
|
+
implementation "com.google.android.gms:play-services-auth:21.5.0"
|
|
97
97
|
|
|
98
98
|
// Activity result APIs
|
|
99
99
|
implementation "androidx.activity:activity-ktx:1.9.3"
|
|
@@ -225,13 +225,14 @@ extern "C" JNIEXPORT void JNICALL Java_com_auth_AuthAdapter_nativeOnLoginSuccess
|
|
|
225
225
|
jmethodID longValueMethod = env->GetMethodID(longClass, "longValue", "()J");
|
|
226
226
|
user.expirationTime = (double)env->CallLongMethod(expirationTime, longValueMethod);
|
|
227
227
|
}
|
|
228
|
+
|
|
228
229
|
if (loginPromise) loginPromise->resolve(user);
|
|
229
230
|
if (scopesPromise) scopesPromise->resolve(user);
|
|
230
231
|
if (silentPromise) silentPromise->resolve(user);
|
|
231
232
|
}
|
|
232
233
|
|
|
233
234
|
extern "C" JNIEXPORT void JNICALL Java_com_auth_AuthAdapter_nativeOnLoginError(
|
|
234
|
-
JNIEnv* env, jclass, jstring error) {
|
|
235
|
+
JNIEnv* env, jclass, jstring error, jstring underlyingError) {
|
|
235
236
|
|
|
236
237
|
std::shared_ptr<Promise<AuthUser>> loginPromise;
|
|
237
238
|
std::shared_ptr<Promise<AuthUser>> scopesPromise;
|
|
@@ -249,12 +250,19 @@ extern "C" JNIEXPORT void JNICALL Java_com_auth_AuthAdapter_nativeOnLoginError(
|
|
|
249
250
|
const char* errorCStr = env->GetStringUTFChars(error, nullptr);
|
|
250
251
|
std::string errorStr(errorCStr);
|
|
251
252
|
env->ReleaseStringUTFChars(error, errorCStr);
|
|
253
|
+
|
|
254
|
+
std::string finalError = errorStr;
|
|
255
|
+
if (underlyingError) {
|
|
256
|
+
const char* uCStr = env->GetStringUTFChars(underlyingError, nullptr);
|
|
257
|
+
finalError = std::string(uCStr);
|
|
258
|
+
env->ReleaseStringUTFChars(underlyingError, uCStr);
|
|
259
|
+
}
|
|
252
260
|
|
|
253
|
-
if (loginPromise) loginPromise->reject(std::make_exception_ptr(std::runtime_error(
|
|
254
|
-
if (scopesPromise) scopesPromise->reject(std::make_exception_ptr(std::runtime_error(
|
|
261
|
+
if (loginPromise) loginPromise->reject(std::make_exception_ptr(std::runtime_error(finalError)));
|
|
262
|
+
if (scopesPromise) scopesPromise->reject(std::make_exception_ptr(std::runtime_error(finalError)));
|
|
255
263
|
if (silentPromise) {
|
|
256
264
|
if (errorStr == "No session") silentPromise->resolve(std::nullopt);
|
|
257
|
-
else silentPromise->reject(std::make_exception_ptr(std::runtime_error(
|
|
265
|
+
else silentPromise->reject(std::make_exception_ptr(std::runtime_error(finalError)));
|
|
258
266
|
}
|
|
259
267
|
}
|
|
260
268
|
|
|
@@ -290,7 +298,7 @@ extern "C" JNIEXPORT void JNICALL Java_com_auth_AuthAdapter_nativeOnRefreshSucce
|
|
|
290
298
|
}
|
|
291
299
|
|
|
292
300
|
extern "C" JNIEXPORT void JNICALL Java_com_auth_AuthAdapter_nativeOnRefreshError(
|
|
293
|
-
JNIEnv* env, jclass, jstring error) {
|
|
301
|
+
JNIEnv* env, jclass, jstring error, jstring underlyingError) {
|
|
294
302
|
|
|
295
303
|
std::shared_ptr<Promise<AuthTokens>> refreshPromise;
|
|
296
304
|
{
|
|
@@ -299,10 +307,17 @@ extern "C" JNIEXPORT void JNICALL Java_com_auth_AuthAdapter_nativeOnRefreshError
|
|
|
299
307
|
gRefreshPromise = nullptr;
|
|
300
308
|
}
|
|
301
309
|
if (refreshPromise) {
|
|
310
|
+
std::string finalError;
|
|
302
311
|
const char* errorCStr = env->GetStringUTFChars(error, nullptr);
|
|
303
|
-
std::string
|
|
312
|
+
finalError = std::string(errorCStr);
|
|
304
313
|
env->ReleaseStringUTFChars(error, errorCStr);
|
|
305
|
-
|
|
314
|
+
|
|
315
|
+
if (underlyingError) {
|
|
316
|
+
const char* uCStr = env->GetStringUTFChars(underlyingError, nullptr);
|
|
317
|
+
finalError = std::string(uCStr);
|
|
318
|
+
env->ReleaseStringUTFChars(underlyingError, uCStr);
|
|
319
|
+
}
|
|
320
|
+
refreshPromise->reject(std::make_exception_ptr(std::runtime_error(finalError)));
|
|
306
321
|
}
|
|
307
322
|
}
|
|
308
323
|
|
|
@@ -47,13 +47,13 @@ object AuthAdapter {
|
|
|
47
47
|
)
|
|
48
48
|
|
|
49
49
|
@JvmStatic
|
|
50
|
-
private external fun nativeOnLoginError(error: String)
|
|
50
|
+
private external fun nativeOnLoginError(error: String, underlyingError: String?)
|
|
51
51
|
|
|
52
52
|
@JvmStatic
|
|
53
53
|
private external fun nativeOnRefreshSuccess(idToken: String?, accessToken: String?, expirationTime: Long?)
|
|
54
54
|
|
|
55
55
|
@JvmStatic
|
|
56
|
-
private external fun nativeOnRefreshError(error: String)
|
|
56
|
+
private external fun nativeOnRefreshError(error: String, underlyingError: String?)
|
|
57
57
|
|
|
58
58
|
fun initialize(context: Context) {
|
|
59
59
|
val app = context.applicationContext as? Application
|
|
@@ -90,27 +90,27 @@ object AuthAdapter {
|
|
|
90
90
|
12501 -> "cancelled"
|
|
91
91
|
7 -> "network_error"
|
|
92
92
|
8, 10 -> "configuration_error"
|
|
93
|
-
else ->
|
|
93
|
+
else -> "unknown"
|
|
94
94
|
}
|
|
95
|
-
nativeOnLoginError(mappedError)
|
|
95
|
+
nativeOnLoginError(mappedError, message)
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
@JvmStatic
|
|
99
99
|
fun loginSync(context: Context, provider: String, googleClientId: String?, scopes: Array<String>?, loginHint: String?, useOneTap: Boolean) {
|
|
100
100
|
if (provider == "apple") {
|
|
101
|
-
nativeOnLoginError("Apple Sign-In is not supported on Android.")
|
|
101
|
+
nativeOnLoginError("unsupported_provider", "Apple Sign-In is not supported on Android.")
|
|
102
102
|
return
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
if (provider != "google") {
|
|
106
|
-
nativeOnLoginError("Unsupported provider: $provider")
|
|
106
|
+
nativeOnLoginError("unsupported_provider", "Unsupported provider: $provider")
|
|
107
107
|
return
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
val ctx = appContext ?: context.applicationContext
|
|
111
111
|
val clientId = googleClientId ?: getClientIdFromResources(ctx)
|
|
112
112
|
if (clientId == null) {
|
|
113
|
-
nativeOnLoginError("Google Client ID is required. Set it in app.json plugins.")
|
|
113
|
+
nativeOnLoginError("configuration_error", "Google Client ID is required. Set it in app.json plugins.")
|
|
114
114
|
return
|
|
115
115
|
}
|
|
116
116
|
|
|
@@ -137,7 +137,7 @@ object AuthAdapter {
|
|
|
137
137
|
val googleIdOption = GetGoogleIdOption.Builder()
|
|
138
138
|
.setFilterByAuthorizedAccounts(false)
|
|
139
139
|
.setServerClientId(clientId)
|
|
140
|
-
.setAutoSelectEnabled(false)
|
|
140
|
+
.setAutoSelectEnabled(false)
|
|
141
141
|
.build()
|
|
142
142
|
|
|
143
143
|
val request = GetCredentialRequest.Builder()
|
|
@@ -191,7 +191,7 @@ object AuthAdapter {
|
|
|
191
191
|
)
|
|
192
192
|
} else {
|
|
193
193
|
Log.w(TAG, "Unsupported credential type: ${credential.type}")
|
|
194
|
-
nativeOnLoginError("Unsupported credential type: ${credential.type}")
|
|
194
|
+
nativeOnLoginError("unknown", "Unsupported credential type: ${credential.type}")
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
197
|
|
|
@@ -200,7 +200,7 @@ object AuthAdapter {
|
|
|
200
200
|
val ctx = appContext ?: context.applicationContext
|
|
201
201
|
val account = GoogleSignIn.getLastSignedInAccount(ctx)
|
|
202
202
|
if (account == null) {
|
|
203
|
-
nativeOnLoginError("No user logged in")
|
|
203
|
+
nativeOnLoginError("unknown", "No user logged in")
|
|
204
204
|
return
|
|
205
205
|
}
|
|
206
206
|
|
|
@@ -212,7 +212,7 @@ object AuthAdapter {
|
|
|
212
212
|
|
|
213
213
|
val clientId = getClientIdFromResources(ctx)
|
|
214
214
|
if (clientId == null) {
|
|
215
|
-
nativeOnLoginError("Google Client ID not configured")
|
|
215
|
+
nativeOnLoginError("configuration_error", "Google Client ID not configured")
|
|
216
216
|
return
|
|
217
217
|
}
|
|
218
218
|
|
|
@@ -227,12 +227,12 @@ object AuthAdapter {
|
|
|
227
227
|
if (googleSignInClient == null) {
|
|
228
228
|
val account = GoogleSignIn.getLastSignedInAccount(ctx)
|
|
229
229
|
if (account == null) {
|
|
230
|
-
nativeOnRefreshError("No user logged in")
|
|
230
|
+
nativeOnRefreshError("unknown", "No user logged in")
|
|
231
231
|
return
|
|
232
232
|
}
|
|
233
233
|
val clientId = getClientIdFromResources(ctx)
|
|
234
234
|
if (clientId == null) {
|
|
235
|
-
nativeOnRefreshError("Google Client ID not configured")
|
|
235
|
+
nativeOnRefreshError("configuration_error", "Google Client ID not configured")
|
|
236
236
|
return
|
|
237
237
|
}
|
|
238
238
|
val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
|
|
@@ -248,7 +248,7 @@ object AuthAdapter {
|
|
|
248
248
|
val account = task.result
|
|
249
249
|
nativeOnRefreshSuccess(account?.idToken, null, null)
|
|
250
250
|
} else {
|
|
251
|
-
nativeOnRefreshError(task.exception?.message ?: "Silent sign-in failed")
|
|
251
|
+
nativeOnRefreshError("network_error", task.exception?.message ?: "Silent sign-in failed")
|
|
252
252
|
}
|
|
253
253
|
}
|
|
254
254
|
}
|
|
@@ -333,7 +333,7 @@ object AuthAdapter {
|
|
|
333
333
|
val serverAuthCode = extractJsonValue(json, "serverAuthCode")
|
|
334
334
|
nativeOnLoginSuccess(provider, email, name, photo, idToken, null, serverAuthCode, null, null)
|
|
335
335
|
} else {
|
|
336
|
-
nativeOnLoginError("No session")
|
|
336
|
+
nativeOnLoginError("unknown", "No session")
|
|
337
337
|
}
|
|
338
338
|
}
|
|
339
339
|
}
|
package/app.plugin.js
CHANGED
|
@@ -36,10 +36,12 @@ const withNitroAuth = (config, props = {}) => {
|
|
|
36
36
|
});
|
|
37
37
|
|
|
38
38
|
// 2. iOS Entitlements
|
|
39
|
-
|
|
40
|
-
config
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
if (ios.appleSignIn === true) {
|
|
40
|
+
config = withEntitlementsPlist(config, (config) => {
|
|
41
|
+
config.modResults["com.apple.developer.applesignin"] = ["Default"];
|
|
42
|
+
return config;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
43
45
|
|
|
44
46
|
// 3. Android Strings (for Google Client ID)
|
|
45
47
|
config = withStringsXml(config, (config) => {
|
|
@@ -63,5 +65,5 @@ const withNitroAuth = (config, props = {}) => {
|
|
|
63
65
|
module.exports = createRunOncePlugin(
|
|
64
66
|
withNitroAuth,
|
|
65
67
|
"react-native-nitro-auth",
|
|
66
|
-
"0.1.
|
|
68
|
+
"0.1.6"
|
|
67
69
|
);
|
package/ios/AuthAdapter.swift
CHANGED
|
@@ -7,7 +7,7 @@ import ObjectiveC
|
|
|
7
7
|
@objc
|
|
8
8
|
public class AuthAdapter: NSObject {
|
|
9
9
|
@objc
|
|
10
|
-
public static func login(provider: String, scopes: [String], loginHint: String?, completion: @escaping (NSDictionary?, String?) -> Void) {
|
|
10
|
+
public static func login(provider: String, scopes: [String], loginHint: String?, useSheet: Bool, completion: @escaping (NSDictionary?, String?) -> Void) {
|
|
11
11
|
if provider == "google" {
|
|
12
12
|
guard let clientId = Bundle.main.object(forInfoDictionaryKey: "GIDClientID") as? String, !clientId.isEmpty else {
|
|
13
13
|
completion(nil, "configuration_error")
|
|
@@ -27,28 +27,10 @@ public class AuthAdapter: NSObject {
|
|
|
27
27
|
GIDSignIn.sharedInstance.configuration = config
|
|
28
28
|
|
|
29
29
|
let additionalScopes = scopes.isEmpty ? nil : scopes
|
|
30
|
+
|
|
31
|
+
// Modern sheet-like API
|
|
30
32
|
GIDSignIn.sharedInstance.signIn(withPresenting: rootVC, hint: loginHint, additionalScopes: additionalScopes) { result, error in
|
|
31
|
-
|
|
32
|
-
completion(nil, AuthAdapter.mapError(error))
|
|
33
|
-
return
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
guard let user = result?.user else {
|
|
37
|
-
completion(nil, "unknown")
|
|
38
|
-
return
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
let data: [String: Any] = [
|
|
42
|
-
"provider": "google",
|
|
43
|
-
"email": user.profile?.email ?? "",
|
|
44
|
-
"name": user.profile?.name ?? "",
|
|
45
|
-
"photo": user.profile?.imageURL(withDimension: 300)?.absoluteString ?? "",
|
|
46
|
-
"idToken": user.idToken?.tokenString ?? "",
|
|
47
|
-
"accessToken": user.accessToken.tokenString,
|
|
48
|
-
"serverAuthCode": result?.serverAuthCode ?? "",
|
|
49
|
-
"expirationTime": (user.accessToken.expirationDate?.timeIntervalSince1970 ?? 0) * 1000
|
|
50
|
-
]
|
|
51
|
-
completion(data as NSDictionary, nil)
|
|
33
|
+
self.handleGoogleResult(result, error: error, completion: completion)
|
|
52
34
|
}
|
|
53
35
|
}
|
|
54
36
|
} else if provider == "apple" {
|
|
@@ -66,6 +48,31 @@ public class AuthAdapter: NSObject {
|
|
|
66
48
|
}
|
|
67
49
|
}
|
|
68
50
|
|
|
51
|
+
private static func handleGoogleResult(_ result: GIDSignInResult?, error: Error?, completion: @escaping (NSDictionary?, String?) -> Void) {
|
|
52
|
+
if let error = error {
|
|
53
|
+
completion(nil, error.localizedDescription)
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
guard let user = result?.user else {
|
|
58
|
+
completion(nil, "unknown")
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let data: [String: Any] = [
|
|
63
|
+
"provider": "google",
|
|
64
|
+
"email": user.profile?.email ?? "",
|
|
65
|
+
"name": user.profile?.name ?? "",
|
|
66
|
+
"photo": user.profile?.imageURL(withDimension: 300)?.absoluteString ?? "",
|
|
67
|
+
"idToken": user.idToken?.tokenString ?? "",
|
|
68
|
+
"accessToken": user.accessToken.tokenString,
|
|
69
|
+
"serverAuthCode": result?.serverAuthCode ?? "",
|
|
70
|
+
"expirationTime": (user.accessToken.expirationDate?.timeIntervalSince1970 ?? 0) * 1000,
|
|
71
|
+
"underlyingError": ""
|
|
72
|
+
]
|
|
73
|
+
completion(data as NSDictionary, nil)
|
|
74
|
+
}
|
|
75
|
+
|
|
69
76
|
static func mapError(_ error: Error) -> String {
|
|
70
77
|
let nsError = error as NSError
|
|
71
78
|
if nsError.domain == "com.google.GIDSignIn" {
|
|
@@ -92,27 +99,7 @@ public class AuthAdapter: NSObject {
|
|
|
92
99
|
}
|
|
93
100
|
|
|
94
101
|
currentUser.addScopes(scopes, presenting: rootVC) { result, error in
|
|
95
|
-
|
|
96
|
-
completion(nil, AuthAdapter.mapError(error))
|
|
97
|
-
return
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
guard let user = result?.user else {
|
|
101
|
-
completion(nil, "unknown")
|
|
102
|
-
return
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
let data: [String: Any] = [
|
|
106
|
-
"provider": "google",
|
|
107
|
-
"email": user.profile?.email ?? "",
|
|
108
|
-
"name": user.profile?.name ?? "",
|
|
109
|
-
"photo": user.profile?.imageURL(withDimension: 300)?.absoluteString ?? "",
|
|
110
|
-
"idToken": user.idToken?.tokenString ?? "",
|
|
111
|
-
"accessToken": user.accessToken.tokenString,
|
|
112
|
-
"serverAuthCode": result?.serverAuthCode ?? "",
|
|
113
|
-
"expirationTime": (user.accessToken.expirationDate?.timeIntervalSince1970 ?? 0) * 1000
|
|
114
|
-
]
|
|
115
|
-
completion(data as NSDictionary, nil)
|
|
102
|
+
self.handleGoogleResult(result, error: error, completion: completion)
|
|
116
103
|
}
|
|
117
104
|
}
|
|
118
105
|
}
|
|
@@ -126,7 +113,7 @@ public class AuthAdapter: NSObject {
|
|
|
126
113
|
|
|
127
114
|
currentUser.refreshTokensIfNeeded { user, error in
|
|
128
115
|
if let error = error {
|
|
129
|
-
completion(nil,
|
|
116
|
+
completion(nil, error.localizedDescription)
|
|
130
117
|
return
|
|
131
118
|
}
|
|
132
119
|
|
|
@@ -138,9 +125,10 @@ public class AuthAdapter: NSObject {
|
|
|
138
125
|
let data: [String: Any] = [
|
|
139
126
|
"accessToken": user.accessToken.tokenString,
|
|
140
127
|
"idToken": user.idToken?.tokenString ?? "",
|
|
141
|
-
"expirationTime": (user.accessToken.expirationDate?.timeIntervalSince1970 ?? 0) * 1000
|
|
128
|
+
"expirationTime": (user.accessToken.expirationDate?.timeIntervalSince1970 ?? 0) * 1000,
|
|
129
|
+
"underlyingError": error?.localizedDescription ?? ""
|
|
142
130
|
]
|
|
143
|
-
completion(data as NSDictionary,
|
|
131
|
+
completion(data as NSDictionary, error?.localizedDescription)
|
|
144
132
|
}
|
|
145
133
|
}
|
|
146
134
|
|
|
@@ -196,13 +184,14 @@ class AppleSignInDelegate: NSObject, ASAuthorizationControllerDelegate {
|
|
|
196
184
|
"provider": "apple",
|
|
197
185
|
"email": email ?? "",
|
|
198
186
|
"name": name,
|
|
199
|
-
"idToken": idToken ?? ""
|
|
187
|
+
"idToken": idToken ?? "",
|
|
188
|
+
"underlyingError": ""
|
|
200
189
|
]
|
|
201
190
|
completion(data as NSDictionary, nil)
|
|
202
191
|
}
|
|
203
192
|
}
|
|
204
193
|
|
|
205
194
|
func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
|
|
206
|
-
completion(nil,
|
|
195
|
+
completion(nil, error.localizedDescription)
|
|
207
196
|
}
|
|
208
197
|
}
|
package/ios/PlatformAuth+iOS.mm
CHANGED
|
@@ -14,6 +14,11 @@
|
|
|
14
14
|
#include "LoginOptions.hpp"
|
|
15
15
|
|
|
16
16
|
namespace margelo::nitro::NitroAuth {
|
|
17
|
+
|
|
18
|
+
inline std::string nsToStd(NSString* _Nullable ns) {
|
|
19
|
+
if (ns == nil) return "";
|
|
20
|
+
return std::string([ns UTF8String]);
|
|
21
|
+
}
|
|
17
22
|
|
|
18
23
|
std::shared_ptr<Promise<AuthUser>> PlatformAuth::login(AuthProvider provider, const std::optional<LoginOptions>& options) {
|
|
19
24
|
auto promise = Promise<AuthUser>::create();
|
|
@@ -33,12 +38,12 @@ std::shared_ptr<Promise<AuthUser>> PlatformAuth::login(AuthProvider provider, co
|
|
|
33
38
|
}
|
|
34
39
|
}
|
|
35
40
|
|
|
36
|
-
|
|
37
|
-
if (
|
|
38
|
-
|
|
41
|
+
BOOL useSheet = NO;
|
|
42
|
+
if (options.has_value() && options->useSheet.has_value()) {
|
|
43
|
+
useSheet = options->useSheet.value();
|
|
39
44
|
}
|
|
40
45
|
|
|
41
|
-
[AuthAdapter loginWithProvider:providerStr scopes:scopesArray loginHint:hintStr completion:^(NSDictionary* _Nullable data, NSString* _Nullable error) {
|
|
46
|
+
[AuthAdapter loginWithProvider:providerStr scopes:scopesArray loginHint:hintStr useSheet:useSheet completion:^(NSDictionary* _Nullable data, NSString* _Nullable error) {
|
|
42
47
|
if (error != nil) {
|
|
43
48
|
promise->reject(std::make_exception_ptr(std::runtime_error([error UTF8String])));
|
|
44
49
|
return;
|
|
@@ -50,13 +55,14 @@ std::shared_ptr<Promise<AuthUser>> PlatformAuth::login(AuthProvider provider, co
|
|
|
50
55
|
|
|
51
56
|
AuthUser user;
|
|
52
57
|
user.provider = provider;
|
|
53
|
-
user.email =
|
|
54
|
-
user.name =
|
|
55
|
-
user.photo =
|
|
56
|
-
user.idToken =
|
|
57
|
-
if ([data objectForKey:@"accessToken"]) user.accessToken =
|
|
58
|
-
if ([data objectForKey:@"serverAuthCode"]) user.serverAuthCode =
|
|
58
|
+
user.email = nsToStd([data objectForKey:@"email"]);
|
|
59
|
+
user.name = nsToStd([data objectForKey:@"name"]);
|
|
60
|
+
user.photo = nsToStd([data objectForKey:@"photo"]);
|
|
61
|
+
user.idToken = nsToStd([data objectForKey:@"idToken"]);
|
|
62
|
+
if ([data objectForKey:@"accessToken"]) user.accessToken = nsToStd([data objectForKey:@"accessToken"]);
|
|
63
|
+
if ([data objectForKey:@"serverAuthCode"]) user.serverAuthCode = nsToStd([data objectForKey:@"serverAuthCode"]);
|
|
59
64
|
if ([data objectForKey:@"expirationTime"]) user.expirationTime = [[data objectForKey:@"expirationTime"] doubleValue];
|
|
65
|
+
if ([data objectForKey:@"underlyingError"]) user.underlyingError = nsToStd([data objectForKey:@"underlyingError"]);
|
|
60
66
|
|
|
61
67
|
promise->resolve(user);
|
|
62
68
|
}];
|
|
@@ -80,13 +86,14 @@ std::shared_ptr<Promise<AuthUser>> PlatformAuth::requestScopes(const std::vector
|
|
|
80
86
|
|
|
81
87
|
AuthUser user;
|
|
82
88
|
user.provider = AuthProvider::GOOGLE;
|
|
83
|
-
user.email =
|
|
84
|
-
user.name =
|
|
85
|
-
user.photo =
|
|
86
|
-
user.idToken =
|
|
87
|
-
if ([data objectForKey:@"accessToken"]) user.accessToken =
|
|
88
|
-
if ([data objectForKey:@"serverAuthCode"]) user.serverAuthCode =
|
|
89
|
+
user.email = nsToStd([data objectForKey:@"email"]);
|
|
90
|
+
user.name = nsToStd([data objectForKey:@"name"]);
|
|
91
|
+
user.photo = nsToStd([data objectForKey:@"photo"]);
|
|
92
|
+
user.idToken = nsToStd([data objectForKey:@"idToken"]);
|
|
93
|
+
if ([data objectForKey:@"accessToken"]) user.accessToken = nsToStd([data objectForKey:@"accessToken"]);
|
|
94
|
+
if ([data objectForKey:@"serverAuthCode"]) user.serverAuthCode = nsToStd([data objectForKey:@"serverAuthCode"]);
|
|
89
95
|
if ([data objectForKey:@"expirationTime"]) user.expirationTime = [[data objectForKey:@"expirationTime"] doubleValue];
|
|
96
|
+
if ([data objectForKey:@"underlyingError"]) user.underlyingError = nsToStd([data objectForKey:@"underlyingError"]);
|
|
90
97
|
promise->resolve(user);
|
|
91
98
|
}];
|
|
92
99
|
return promise;
|
|
@@ -100,8 +107,8 @@ std::shared_ptr<Promise<AuthTokens>> PlatformAuth::refreshToken() {
|
|
|
100
107
|
return;
|
|
101
108
|
}
|
|
102
109
|
AuthTokens tokens;
|
|
103
|
-
if ([data objectForKey:@"accessToken"]) tokens.accessToken =
|
|
104
|
-
if ([data objectForKey:@"idToken"]) tokens.idToken =
|
|
110
|
+
if ([data objectForKey:@"accessToken"]) tokens.accessToken = nsToStd([data objectForKey:@"accessToken"]);
|
|
111
|
+
if ([data objectForKey:@"idToken"]) tokens.idToken = nsToStd([data objectForKey:@"idToken"]);
|
|
105
112
|
if ([data objectForKey:@"expirationTime"]) tokens.expirationTime = [[data objectForKey:@"expirationTime"] doubleValue];
|
|
106
113
|
promise->resolve(tokens);
|
|
107
114
|
}];
|
|
@@ -117,13 +124,14 @@ std::shared_ptr<Promise<std::optional<AuthUser>>> PlatformAuth::silentRestore()
|
|
|
117
124
|
}
|
|
118
125
|
AuthUser user;
|
|
119
126
|
user.provider = [[data objectForKey:@"provider"] isEqualToString:@"google"] ? AuthProvider::GOOGLE : AuthProvider::APPLE;
|
|
120
|
-
user.email =
|
|
121
|
-
user.name =
|
|
122
|
-
user.photo =
|
|
123
|
-
user.idToken =
|
|
124
|
-
if ([data objectForKey:@"accessToken"]) user.accessToken =
|
|
125
|
-
if ([data objectForKey:@"serverAuthCode"]) user.serverAuthCode =
|
|
127
|
+
user.email = nsToStd([data objectForKey:@"email"]);
|
|
128
|
+
user.name = nsToStd([data objectForKey:@"name"]);
|
|
129
|
+
user.photo = nsToStd([data objectForKey:@"photo"]);
|
|
130
|
+
user.idToken = nsToStd([data objectForKey:@"idToken"]);
|
|
131
|
+
if ([data objectForKey:@"accessToken"]) user.accessToken = nsToStd([data objectForKey:@"accessToken"]);
|
|
132
|
+
if ([data objectForKey:@"serverAuthCode"]) user.serverAuthCode = nsToStd([data objectForKey:@"serverAuthCode"]);
|
|
126
133
|
if ([data objectForKey:@"expirationTime"]) user.expirationTime = [[data objectForKey:@"expirationTime"] doubleValue];
|
|
134
|
+
if ([data objectForKey:@"underlyingError"]) user.underlyingError = nsToStd([data objectForKey:@"underlyingError"]);
|
|
127
135
|
promise->resolve(user);
|
|
128
136
|
}];
|
|
129
137
|
return promise;
|
package/lib/commonjs/Auth.web.js
CHANGED
|
@@ -147,24 +147,19 @@ class AuthWeb {
|
|
|
147
147
|
return tokens;
|
|
148
148
|
}
|
|
149
149
|
mapError(error) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
return new Error("cancelled");
|
|
154
|
-
}
|
|
155
|
-
if (msg.includes("network")) {
|
|
156
|
-
return new Error("network_error");
|
|
157
|
-
}
|
|
158
|
-
if (msg.includes("client id") || msg.includes("config")) {
|
|
159
|
-
return new Error("configuration_error");
|
|
160
|
-
}
|
|
161
|
-
return error;
|
|
162
|
-
}
|
|
163
|
-
const msg = String(error).toLowerCase();
|
|
150
|
+
const rawMessage = error instanceof Error ? error.message : String(error);
|
|
151
|
+
const msg = rawMessage.toLowerCase();
|
|
152
|
+
let mappedMsg = rawMessage;
|
|
164
153
|
if (msg.includes("cancel") || msg.includes("popup_closed")) {
|
|
165
|
-
|
|
154
|
+
mappedMsg = "cancelled";
|
|
155
|
+
} else if (msg.includes("network")) {
|
|
156
|
+
mappedMsg = "network_error";
|
|
157
|
+
} else if (msg.includes("client id") || msg.includes("config")) {
|
|
158
|
+
mappedMsg = "configuration_error";
|
|
166
159
|
}
|
|
167
|
-
|
|
160
|
+
const authError = new Error(mappedMsg);
|
|
161
|
+
authError.underlyingError = rawMessage;
|
|
162
|
+
return authError;
|
|
168
163
|
}
|
|
169
164
|
async loginGoogle(scopes, loginHint) {
|
|
170
165
|
const config = getConfig();
|
|
@@ -177,13 +172,11 @@ class AuthWeb {
|
|
|
177
172
|
const authUrl = new URL("https://accounts.google.com/o/oauth2/v2/auth");
|
|
178
173
|
authUrl.searchParams.set("client_id", clientId);
|
|
179
174
|
authUrl.searchParams.set("redirect_uri", redirectUri);
|
|
180
|
-
// Requesting code alongside tokens for server-side verification if needed
|
|
181
175
|
authUrl.searchParams.set("response_type", "id_token token code");
|
|
182
176
|
authUrl.searchParams.set("scope", scopes.join(" "));
|
|
183
177
|
authUrl.searchParams.set("nonce", Math.random().toString(36).slice(2));
|
|
184
|
-
authUrl.searchParams.set("access_type", "offline");
|
|
185
|
-
authUrl.searchParams.set("prompt", "consent");
|
|
186
|
-
|
|
178
|
+
authUrl.searchParams.set("access_type", "offline");
|
|
179
|
+
authUrl.searchParams.set("prompt", "consent");
|
|
187
180
|
if (loginHint) {
|
|
188
181
|
authUrl.searchParams.set("login_hint", loginHint);
|
|
189
182
|
}
|
|
@@ -282,7 +275,7 @@ class AuthWeb {
|
|
|
282
275
|
};
|
|
283
276
|
this.updateUser(user);
|
|
284
277
|
resolve();
|
|
285
|
-
}).catch(
|
|
278
|
+
}).catch(err => reject(this.mapError(err)));
|
|
286
279
|
};
|
|
287
280
|
script.onerror = () => reject(new Error("Failed to load Apple SDK"));
|
|
288
281
|
document.head.appendChild(script);
|