react-native-nitro-auth 0.5.0 → 0.5.3
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 +374 -189
- package/android/src/main/cpp/PlatformAuth+Android.cpp +4 -1
- package/android/src/main/java/com/auth/AuthAdapter.kt +99 -182
- package/android/src/main/java/com/auth/GoogleSignInActivity.kt +2 -1
- package/android/src/main/java/com/auth/NitroAuthPackage.kt +2 -0
- package/app.plugin.js +2 -9
- package/cpp/AuthCache.cpp +12 -102
- package/cpp/HybridAuth.cpp +37 -61
- package/cpp/HybridAuth.hpp +2 -4
- package/ios/AuthAdapter.swift +21 -25
- package/lib/commonjs/Auth.web.js +433 -164
- package/lib/commonjs/Auth.web.js.map +1 -1
- package/lib/commonjs/index.js +0 -12
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +0 -12
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/js-storage-adapter.js +2 -0
- package/lib/commonjs/js-storage-adapter.js.map +1 -0
- package/lib/commonjs/service.js +7 -84
- package/lib/commonjs/service.js.map +1 -1
- package/lib/commonjs/service.web.js +1 -5
- package/lib/commonjs/service.web.js.map +1 -1
- package/lib/commonjs/ui/social-button.js +44 -29
- package/lib/commonjs/ui/social-button.js.map +1 -1
- package/lib/commonjs/ui/social-button.web.js +44 -29
- package/lib/commonjs/ui/social-button.web.js.map +1 -1
- package/lib/commonjs/use-auth.js +8 -2
- package/lib/commonjs/use-auth.js.map +1 -1
- package/lib/commonjs/utils/logger.js +12 -4
- package/lib/commonjs/utils/logger.js.map +1 -1
- package/lib/module/Auth.web.js +433 -164
- package/lib/module/Auth.web.js.map +1 -1
- package/lib/module/index.js +0 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +0 -1
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/js-storage-adapter.js +2 -0
- package/lib/module/js-storage-adapter.js.map +1 -0
- package/lib/module/service.js +7 -84
- package/lib/module/service.js.map +1 -1
- package/lib/module/service.web.js +1 -5
- package/lib/module/service.web.js.map +1 -1
- package/lib/module/ui/social-button.js +44 -29
- package/lib/module/ui/social-button.js.map +1 -1
- package/lib/module/ui/social-button.web.js +44 -29
- package/lib/module/ui/social-button.web.js.map +1 -1
- package/lib/module/use-auth.js +8 -2
- package/lib/module/use-auth.js.map +1 -1
- package/lib/module/utils/logger.js +12 -4
- package/lib/module/utils/logger.js.map +1 -1
- package/lib/typescript/commonjs/Auth.nitro.d.ts +5 -3
- package/lib/typescript/commonjs/Auth.nitro.d.ts.map +1 -1
- package/lib/typescript/commonjs/Auth.web.d.ts +18 -6
- package/lib/typescript/commonjs/Auth.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +1 -2
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.web.d.ts +0 -1
- package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/js-storage-adapter.d.ts +6 -0
- package/lib/typescript/commonjs/js-storage-adapter.d.ts.map +1 -0
- package/lib/typescript/commonjs/service.d.ts +1 -8
- package/lib/typescript/commonjs/service.d.ts.map +1 -1
- package/lib/typescript/commonjs/service.web.d.ts +1 -8
- package/lib/typescript/commonjs/service.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/social-button.d.ts +6 -6
- package/lib/typescript/commonjs/ui/social-button.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/social-button.web.d.ts +6 -6
- package/lib/typescript/commonjs/ui/social-button.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/use-auth.d.ts +4 -4
- package/lib/typescript/commonjs/use-auth.d.ts.map +1 -1
- package/lib/typescript/commonjs/utils/logger.d.ts +4 -4
- package/lib/typescript/commonjs/utils/logger.d.ts.map +1 -1
- package/lib/typescript/module/Auth.nitro.d.ts +5 -3
- package/lib/typescript/module/Auth.nitro.d.ts.map +1 -1
- package/lib/typescript/module/Auth.web.d.ts +18 -6
- package/lib/typescript/module/Auth.web.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +1 -2
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/index.web.d.ts +0 -1
- package/lib/typescript/module/index.web.d.ts.map +1 -1
- package/lib/typescript/module/js-storage-adapter.d.ts +6 -0
- package/lib/typescript/module/js-storage-adapter.d.ts.map +1 -0
- package/lib/typescript/module/service.d.ts +1 -8
- package/lib/typescript/module/service.d.ts.map +1 -1
- package/lib/typescript/module/service.web.d.ts +1 -8
- package/lib/typescript/module/service.web.d.ts.map +1 -1
- package/lib/typescript/module/ui/social-button.d.ts +6 -6
- package/lib/typescript/module/ui/social-button.d.ts.map +1 -1
- package/lib/typescript/module/ui/social-button.web.d.ts +6 -6
- package/lib/typescript/module/ui/social-button.web.d.ts.map +1 -1
- package/lib/typescript/module/use-auth.d.ts +4 -4
- package/lib/typescript/module/use-auth.d.ts.map +1 -1
- package/lib/typescript/module/utils/logger.d.ts +4 -4
- package/lib/typescript/module/utils/logger.d.ts.map +1 -1
- package/nitrogen/generated/android/NitroAuth+autolinking.cmake +0 -1
- package/nitrogen/generated/shared/c++/AuthTokens.hpp +5 -1
- package/nitrogen/generated/shared/c++/AuthUser.hpp +5 -1
- package/nitrogen/generated/shared/c++/HybridAuthSpec.cpp +0 -1
- package/nitrogen/generated/shared/c++/HybridAuthSpec.hpp +0 -5
- package/nitrogen/generated/shared/c++/LoginOptions.hpp +5 -1
- package/package.json +13 -10
- package/src/Auth.nitro.ts +6 -3
- package/src/Auth.web.ts +582 -202
- package/src/global.d.ts +0 -1
- package/src/index.ts +1 -2
- package/src/index.web.ts +0 -1
- package/src/js-storage-adapter.ts +5 -0
- package/src/service.ts +11 -104
- package/src/service.web.ts +0 -7
- package/src/ui/social-button.tsx +66 -43
- package/src/ui/social-button.web.tsx +67 -44
- package/src/use-auth.ts +18 -6
- package/src/utils/logger.ts +12 -4
- package/lib/commonjs/AuthStorage.nitro.js +0 -6
- package/lib/commonjs/AuthStorage.nitro.js.map +0 -1
- package/lib/module/AuthStorage.nitro.js +0 -4
- package/lib/module/AuthStorage.nitro.js.map +0 -1
- package/lib/typescript/commonjs/AuthStorage.nitro.d.ts +0 -26
- package/lib/typescript/commonjs/AuthStorage.nitro.d.ts.map +0 -1
- package/lib/typescript/module/AuthStorage.nitro.d.ts +0 -26
- package/lib/typescript/module/AuthStorage.nitro.d.ts.map +0 -1
- package/nitrogen/generated/shared/c++/HybridAuthStorageAdapterSpec.cpp +0 -23
- package/nitrogen/generated/shared/c++/HybridAuthStorageAdapterSpec.hpp +0 -65
- package/src/AuthStorage.nitro.ts +0 -26
|
@@ -51,6 +51,7 @@ std::shared_ptr<Promise<AuthUser>> PlatformAuth::login(AuthProvider provider, co
|
|
|
51
51
|
std::optional<std::string> prompt;
|
|
52
52
|
bool useOneTap = false;
|
|
53
53
|
bool forceAccountPicker = false;
|
|
54
|
+
bool useLegacyGoogleSignIn = false;
|
|
54
55
|
|
|
55
56
|
if (options) {
|
|
56
57
|
if (options->scopes) scopes = *options->scopes;
|
|
@@ -66,6 +67,7 @@ std::shared_ptr<Promise<AuthUser>> PlatformAuth::login(AuthProvider provider, co
|
|
|
66
67
|
}
|
|
67
68
|
useOneTap = options->useOneTap.value_or(false);
|
|
68
69
|
forceAccountPicker = options->forceAccountPicker.value_or(false);
|
|
70
|
+
useLegacyGoogleSignIn = options->useLegacyGoogleSignIn.value_or(false);
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
JNIEnv* env = Environment::current();
|
|
@@ -81,7 +83,7 @@ std::shared_ptr<Promise<AuthUser>> PlatformAuth::login(AuthProvider provider, co
|
|
|
81
83
|
|
|
82
84
|
jclass adapterClass = env->FindClass("com/auth/AuthAdapter");
|
|
83
85
|
jmethodID loginMethod = env->GetStaticMethodID(adapterClass, "loginSync",
|
|
84
|
-
"(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");
|
|
85
87
|
env->CallStaticVoidMethod(adapterClass, loginMethod,
|
|
86
88
|
contextPtr,
|
|
87
89
|
make_jstring(providerStr).get(),
|
|
@@ -90,6 +92,7 @@ std::shared_ptr<Promise<AuthUser>> PlatformAuth::login(AuthProvider provider, co
|
|
|
90
92
|
jLoginHint,
|
|
91
93
|
(jboolean)useOneTap,
|
|
92
94
|
(jboolean)forceAccountPicker,
|
|
95
|
+
(jboolean)useLegacyGoogleSignIn,
|
|
93
96
|
jTenant,
|
|
94
97
|
jPrompt);
|
|
95
98
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
@file:Suppress("DEPRECATION")
|
|
2
|
+
|
|
1
3
|
package com.auth
|
|
2
4
|
|
|
3
5
|
import android.content.Context
|
|
4
|
-
import android.content.SharedPreferences
|
|
5
6
|
import android.os.Bundle
|
|
6
|
-
import android.os.Build
|
|
7
7
|
import android.util.Log
|
|
8
8
|
import com.google.android.gms.auth.api.signin.GoogleSignIn
|
|
9
9
|
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
|
|
@@ -25,17 +25,12 @@ import android.app.Application
|
|
|
25
25
|
import android.content.Intent
|
|
26
26
|
import android.net.Uri
|
|
27
27
|
import androidx.browser.customtabs.CustomTabsIntent
|
|
28
|
-
import androidx.security.crypto.EncryptedSharedPreferences
|
|
29
|
-
import androidx.security.crypto.MasterKeys
|
|
30
28
|
import java.util.UUID
|
|
31
|
-
import org.json.JSONArray
|
|
32
29
|
import org.json.JSONObject
|
|
33
30
|
import android.util.Base64
|
|
34
31
|
|
|
35
32
|
object AuthAdapter {
|
|
36
33
|
private const val TAG = "AuthAdapter"
|
|
37
|
-
private const val PREF_NAME = "nitro_auth"
|
|
38
|
-
private const val SECURE_PREF_NAME = "nitro_auth_secure"
|
|
39
34
|
|
|
40
35
|
private var appContext: Context? = null
|
|
41
36
|
private var currentActivity: Activity? = null
|
|
@@ -49,47 +44,10 @@ object AuthAdapter {
|
|
|
49
44
|
private var pendingMicrosoftTenant: String? = null
|
|
50
45
|
private var pendingMicrosoftClientId: String? = null
|
|
51
46
|
private var pendingMicrosoftB2cDomain: String? = null
|
|
52
|
-
|
|
53
|
-
private
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
|
|
57
|
-
val securePrefs = EncryptedSharedPreferences.create(
|
|
58
|
-
SECURE_PREF_NAME,
|
|
59
|
-
masterKeyAlias,
|
|
60
|
-
context,
|
|
61
|
-
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
|
62
|
-
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
|
63
|
-
)
|
|
64
|
-
migrateLegacyPrefsIfNeeded(context, securePrefs)
|
|
65
|
-
securePrefs
|
|
66
|
-
} else {
|
|
67
|
-
context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
|
68
|
-
}
|
|
69
|
-
} catch (e: Exception) {
|
|
70
|
-
Log.w(TAG, "Failed to initialize encrypted storage, falling back to SharedPreferences", e)
|
|
71
|
-
context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
private fun migrateLegacyPrefsIfNeeded(context: Context, securePrefs: SharedPreferences) {
|
|
76
|
-
val legacyPrefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
|
77
|
-
if (legacyPrefs.all.isEmpty() || securePrefs.all.isNotEmpty()) {
|
|
78
|
-
return
|
|
79
|
-
}
|
|
80
|
-
val editor = securePrefs.edit()
|
|
81
|
-
for ((key, value) in legacyPrefs.all) {
|
|
82
|
-
when (value) {
|
|
83
|
-
is String -> editor.putString(key, value)
|
|
84
|
-
is Boolean -> editor.putBoolean(key, value)
|
|
85
|
-
is Int -> editor.putInt(key, value)
|
|
86
|
-
is Long -> editor.putLong(key, value)
|
|
87
|
-
is Float -> editor.putFloat(key, value)
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
editor.apply()
|
|
91
|
-
legacyPrefs.edit().clear().apply()
|
|
92
|
-
}
|
|
47
|
+
|
|
48
|
+
private var inMemoryMicrosoftRefreshToken: String? = null
|
|
49
|
+
private var inMemoryMicrosoftScopes: List<String> =
|
|
50
|
+
listOf("openid", "email", "profile", "offline_access", "User.Read")
|
|
93
51
|
|
|
94
52
|
@JvmStatic
|
|
95
53
|
private external fun nativeInitialize(context: Context)
|
|
@@ -139,11 +97,10 @@ object AuthAdapter {
|
|
|
139
97
|
}
|
|
140
98
|
|
|
141
99
|
fun onSignInSuccess(account: GoogleSignInAccount, scopes: List<String>) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
account.photoUrl?.toString(), account.idToken, account.serverAuthCode, scopes)
|
|
100
|
+
appContext ?: return
|
|
101
|
+
val expirationTime = getJwtExpirationTimeMs(account.idToken)
|
|
145
102
|
nativeOnLoginSuccess("google", account.email, account.displayName,
|
|
146
|
-
account.photoUrl?.toString(), account.idToken, null, account.serverAuthCode, scopes.toTypedArray(),
|
|
103
|
+
account.photoUrl?.toString(), account.idToken, null, account.serverAuthCode, scopes.toTypedArray(), expirationTime)
|
|
147
104
|
}
|
|
148
105
|
|
|
149
106
|
fun onSignInError(errorCode: Int, message: String?) {
|
|
@@ -157,7 +114,18 @@ object AuthAdapter {
|
|
|
157
114
|
}
|
|
158
115
|
|
|
159
116
|
@JvmStatic
|
|
160
|
-
fun loginSync(
|
|
117
|
+
fun loginSync(
|
|
118
|
+
context: Context,
|
|
119
|
+
provider: String,
|
|
120
|
+
googleClientId: String?,
|
|
121
|
+
scopes: Array<String>?,
|
|
122
|
+
loginHint: String?,
|
|
123
|
+
useOneTap: Boolean,
|
|
124
|
+
forceAccountPicker: Boolean = false,
|
|
125
|
+
useLegacyGoogleSignIn: Boolean = false,
|
|
126
|
+
tenant: String? = null,
|
|
127
|
+
prompt: String? = null
|
|
128
|
+
) {
|
|
161
129
|
if (provider == "apple") {
|
|
162
130
|
nativeOnLoginError("unsupported_provider", "Apple Sign-In is not supported on Android.")
|
|
163
131
|
return
|
|
@@ -183,13 +151,12 @@ object AuthAdapter {
|
|
|
183
151
|
val requestedScopes = scopes?.toList() ?: listOf("email", "profile")
|
|
184
152
|
pendingScopes = requestedScopes
|
|
185
153
|
|
|
186
|
-
if (
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
val intent = GoogleSignInActivity.createIntent(ctx, clientId, requestedScopes.toTypedArray(), loginHint, forceAccountPicker)
|
|
190
|
-
intent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
191
|
-
ctx.startActivity(intent)
|
|
154
|
+
if (useLegacyGoogleSignIn) {
|
|
155
|
+
loginLegacy(context, clientId, requestedScopes, loginHint, forceAccountPicker)
|
|
156
|
+
return
|
|
192
157
|
}
|
|
158
|
+
|
|
159
|
+
loginOneTap(context, clientId, requestedScopes, loginHint, forceAccountPicker, useOneTap)
|
|
193
160
|
}
|
|
194
161
|
|
|
195
162
|
private fun loginMicrosoft(context: Context, scopes: Array<String>?, loginHint: String?, tenant: String?, prompt: String?) {
|
|
@@ -381,12 +348,11 @@ object AuthAdapter {
|
|
|
381
348
|
val email = claims["preferred_username"] ?: claims["email"]
|
|
382
349
|
val name = claims["name"]
|
|
383
350
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
}
|
|
351
|
+
if (refreshToken.isNotEmpty()) {
|
|
352
|
+
inMemoryMicrosoftRefreshToken = refreshToken
|
|
353
|
+
}
|
|
354
|
+
inMemoryMicrosoftScopes = pendingMicrosoftScopes.ifEmpty {
|
|
355
|
+
listOf("openid", "email", "profile", "offline_access", "User.Read")
|
|
390
356
|
}
|
|
391
357
|
|
|
392
358
|
clearPkceState()
|
|
@@ -407,14 +373,12 @@ object AuthAdapter {
|
|
|
407
373
|
}
|
|
408
374
|
}
|
|
409
375
|
|
|
410
|
-
private fun saveMicrosoftRefreshToken(
|
|
411
|
-
|
|
412
|
-
prefs.edit().putString("microsoft_refresh_token", refreshToken).apply()
|
|
376
|
+
private fun saveMicrosoftRefreshToken(refreshToken: String) {
|
|
377
|
+
inMemoryMicrosoftRefreshToken = refreshToken
|
|
413
378
|
}
|
|
414
379
|
|
|
415
|
-
private fun getMicrosoftRefreshToken(
|
|
416
|
-
|
|
417
|
-
return prefs.getString("microsoft_refresh_token", null)
|
|
380
|
+
private fun getMicrosoftRefreshToken(): String? {
|
|
381
|
+
return inMemoryMicrosoftRefreshToken
|
|
418
382
|
}
|
|
419
383
|
|
|
420
384
|
private fun clearPkceState() {
|
|
@@ -443,6 +407,15 @@ object AuthAdapter {
|
|
|
443
407
|
}
|
|
444
408
|
}
|
|
445
409
|
|
|
410
|
+
private fun getJwtExpirationTimeMs(idToken: String?): Long? {
|
|
411
|
+
if (idToken.isNullOrEmpty()) {
|
|
412
|
+
return null
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
val expSeconds = decodeJwt(idToken)["exp"]?.toLongOrNull() ?: return null
|
|
416
|
+
return expSeconds * 1000
|
|
417
|
+
}
|
|
418
|
+
|
|
446
419
|
private fun getMicrosoftClientIdFromResources(context: Context): String? {
|
|
447
420
|
val resId = context.resources.getIdentifier("nitro_auth_microsoft_client_id", "string", context.packageName)
|
|
448
421
|
return if (resId != 0) context.getString(resId) else null
|
|
@@ -470,18 +443,25 @@ object AuthAdapter {
|
|
|
470
443
|
}
|
|
471
444
|
}
|
|
472
445
|
|
|
473
|
-
private fun loginOneTap(
|
|
446
|
+
private fun loginOneTap(
|
|
447
|
+
context: Context,
|
|
448
|
+
clientId: String,
|
|
449
|
+
scopes: List<String>,
|
|
450
|
+
loginHint: String?,
|
|
451
|
+
forceAccountPicker: Boolean,
|
|
452
|
+
useOneTap: Boolean
|
|
453
|
+
) {
|
|
474
454
|
val activity = currentActivity ?: context as? Activity
|
|
475
455
|
if (activity == null) {
|
|
476
456
|
Log.w(TAG, "No Activity context available for One-Tap, falling back to legacy")
|
|
477
|
-
return loginLegacy(context, clientId, scopes)
|
|
457
|
+
return loginLegacy(context, clientId, scopes, loginHint, forceAccountPicker)
|
|
478
458
|
}
|
|
479
459
|
|
|
480
460
|
val credentialManager = CredentialManager.create(activity)
|
|
481
461
|
val googleIdOption = GetGoogleIdOption.Builder()
|
|
482
462
|
.setFilterByAuthorizedAccounts(false)
|
|
483
463
|
.setServerClientId(clientId)
|
|
484
|
-
.setAutoSelectEnabled(
|
|
464
|
+
.setAutoSelectEnabled(useOneTap && !forceAccountPicker)
|
|
485
465
|
.build()
|
|
486
466
|
|
|
487
467
|
val request = GetCredentialRequest.Builder()
|
|
@@ -494,14 +474,26 @@ object AuthAdapter {
|
|
|
494
474
|
handleCredentialResponse(result, scopes)
|
|
495
475
|
} catch (e: Exception) {
|
|
496
476
|
Log.w(TAG, "One-Tap failed, falling back to legacy: ${e.message}")
|
|
497
|
-
loginLegacy(context, clientId, scopes)
|
|
477
|
+
loginLegacy(context, clientId, scopes, loginHint, forceAccountPicker)
|
|
498
478
|
}
|
|
499
479
|
}
|
|
500
480
|
}
|
|
501
481
|
|
|
502
|
-
private fun loginLegacy(
|
|
482
|
+
private fun loginLegacy(
|
|
483
|
+
context: Context,
|
|
484
|
+
clientId: String,
|
|
485
|
+
scopes: List<String>,
|
|
486
|
+
loginHint: String?,
|
|
487
|
+
forceAccountPicker: Boolean
|
|
488
|
+
) {
|
|
503
489
|
val ctx = appContext ?: context.applicationContext
|
|
504
|
-
val intent = GoogleSignInActivity.createIntent(
|
|
490
|
+
val intent = GoogleSignInActivity.createIntent(
|
|
491
|
+
ctx,
|
|
492
|
+
clientId,
|
|
493
|
+
scopes.toTypedArray(),
|
|
494
|
+
loginHint,
|
|
495
|
+
forceAccountPicker
|
|
496
|
+
)
|
|
505
497
|
intent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
506
498
|
ctx.startActivity(intent)
|
|
507
499
|
}
|
|
@@ -522,6 +514,7 @@ object AuthAdapter {
|
|
|
522
514
|
}
|
|
523
515
|
|
|
524
516
|
if (googleIdTokenCredential != null) {
|
|
517
|
+
val expirationTime = getJwtExpirationTimeMs(googleIdTokenCredential.idToken)
|
|
525
518
|
nativeOnLoginSuccess(
|
|
526
519
|
"google",
|
|
527
520
|
googleIdTokenCredential.id,
|
|
@@ -531,7 +524,7 @@ object AuthAdapter {
|
|
|
531
524
|
null,
|
|
532
525
|
null,
|
|
533
526
|
scopes.toTypedArray(),
|
|
534
|
-
|
|
527
|
+
expirationTime
|
|
535
528
|
)
|
|
536
529
|
} else {
|
|
537
530
|
Log.w(TAG, "Unsupported credential type: ${credential.type}")
|
|
@@ -559,12 +552,8 @@ object AuthAdapter {
|
|
|
559
552
|
ctx.startActivity(intent)
|
|
560
553
|
return
|
|
561
554
|
}
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
val currentScopes = extractScopesFromUserJson(userJson)
|
|
565
|
-
val defaultMicrosoftScopes = listOf("openid", "email", "profile", "offline_access", "User.Read")
|
|
566
|
-
val existing = if (currentScopes.isEmpty()) defaultMicrosoftScopes else currentScopes
|
|
567
|
-
val mergedScopes = (existing + scopes.toList()).distinct()
|
|
555
|
+
if (inMemoryMicrosoftRefreshToken != null) {
|
|
556
|
+
val mergedScopes = (inMemoryMicrosoftScopes + scopes.toList()).distinct()
|
|
568
557
|
val tenant = getMicrosoftTenantFromResources(ctx)
|
|
569
558
|
loginMicrosoft(ctx, mergedScopes.toTypedArray(), null, tenant, null)
|
|
570
559
|
return
|
|
@@ -593,20 +582,21 @@ object AuthAdapter {
|
|
|
593
582
|
googleSignInClient!!.silentSignIn().addOnCompleteListener { task ->
|
|
594
583
|
if (task.isSuccessful) {
|
|
595
584
|
val acc = task.result
|
|
596
|
-
nativeOnRefreshSuccess(
|
|
585
|
+
nativeOnRefreshSuccess(
|
|
586
|
+
acc?.idToken,
|
|
587
|
+
null,
|
|
588
|
+
getJwtExpirationTimeMs(acc?.idToken),
|
|
589
|
+
)
|
|
597
590
|
} else {
|
|
598
591
|
nativeOnRefreshError("network_error", task.exception?.message ?: "Silent sign-in failed")
|
|
599
592
|
}
|
|
600
593
|
}
|
|
601
594
|
return
|
|
602
595
|
}
|
|
603
|
-
val
|
|
604
|
-
if (
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
refreshMicrosoftTokenForRefresh(ctx, refreshToken)
|
|
608
|
-
return
|
|
609
|
-
}
|
|
596
|
+
val refreshToken = getMicrosoftRefreshToken()
|
|
597
|
+
if (refreshToken != null) {
|
|
598
|
+
refreshMicrosoftTokenForRefresh(ctx, refreshToken)
|
|
599
|
+
return
|
|
610
600
|
}
|
|
611
601
|
nativeOnRefreshError("unknown", "No user logged in")
|
|
612
602
|
}
|
|
@@ -631,7 +621,8 @@ object AuthAdapter {
|
|
|
631
621
|
.build()
|
|
632
622
|
GoogleSignIn.getClient(ctx, gso).signOut()
|
|
633
623
|
}
|
|
634
|
-
|
|
624
|
+
inMemoryMicrosoftRefreshToken = null
|
|
625
|
+
inMemoryMicrosoftScopes = listOf("openid", "email", "profile", "offline_access", "User.Read")
|
|
635
626
|
}
|
|
636
627
|
|
|
637
628
|
@JvmStatic
|
|
@@ -646,7 +637,8 @@ object AuthAdapter {
|
|
|
646
637
|
.build()
|
|
647
638
|
GoogleSignIn.getClient(ctx, gso).revokeAccess()
|
|
648
639
|
}
|
|
649
|
-
|
|
640
|
+
inMemoryMicrosoftRefreshToken = null
|
|
641
|
+
inMemoryMicrosoftScopes = listOf("openid", "email", "profile", "offline_access", "User.Read")
|
|
650
642
|
}
|
|
651
643
|
|
|
652
644
|
private fun getClientIdFromResources(context: Context): String? {
|
|
@@ -654,66 +646,19 @@ object AuthAdapter {
|
|
|
654
646
|
return if (resId != 0) context.getString(resId) else null
|
|
655
647
|
}
|
|
656
648
|
|
|
657
|
-
@JvmStatic
|
|
658
|
-
fun getUserJson(context: Context): String? {
|
|
659
|
-
val pref = getPrefs(context)
|
|
660
|
-
return pref.getString("user_json", null)
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
@JvmStatic
|
|
664
|
-
fun setUserJson(context: Context, json: String) {
|
|
665
|
-
val pref = getPrefs(context)
|
|
666
|
-
pref.edit().putString("user_json", json).apply()
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
@JvmStatic
|
|
670
|
-
fun clearUser(context: Context) {
|
|
671
|
-
val pref = getPrefs(context)
|
|
672
|
-
pref.edit().clear().apply()
|
|
673
|
-
}
|
|
674
|
-
|
|
675
649
|
@JvmStatic
|
|
676
650
|
fun restoreSession(context: Context) {
|
|
677
651
|
val ctx = context.applicationContext ?: appContext ?: context
|
|
678
652
|
val account = GoogleSignIn.getLastSignedInAccount(ctx)
|
|
679
653
|
if (account != null) {
|
|
654
|
+
val expirationTime = getJwtExpirationTimeMs(account.idToken)
|
|
680
655
|
nativeOnLoginSuccess("google", account.email, account.displayName,
|
|
681
656
|
account.photoUrl?.toString(), account.idToken, null, account.serverAuthCode,
|
|
682
|
-
account.grantedScopes?.map { it.scopeUri }?.toTypedArray(),
|
|
657
|
+
account.grantedScopes?.map { it.scopeUri }?.toTypedArray(), expirationTime)
|
|
683
658
|
} else {
|
|
684
|
-
val
|
|
685
|
-
if (
|
|
686
|
-
|
|
687
|
-
val parsed = JSONObject(json)
|
|
688
|
-
parsed.optString("provider")
|
|
689
|
-
} catch (_: Exception) {
|
|
690
|
-
""
|
|
691
|
-
}
|
|
692
|
-
val effectiveProvider = when (provider) {
|
|
693
|
-
"google" -> "google"
|
|
694
|
-
"microsoft" -> "microsoft"
|
|
695
|
-
"apple" -> "apple"
|
|
696
|
-
else -> "apple"
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
if (effectiveProvider == "microsoft") {
|
|
700
|
-
val refreshToken = getMicrosoftRefreshToken(ctx)
|
|
701
|
-
if (refreshToken != null) {
|
|
702
|
-
refreshMicrosoftToken(ctx, refreshToken)
|
|
703
|
-
} else {
|
|
704
|
-
val email = extractJsonValue(json, "email")
|
|
705
|
-
val name = extractJsonValue(json, "name")
|
|
706
|
-
val idToken = extractJsonValue(json, "idToken")
|
|
707
|
-
nativeOnLoginSuccess(effectiveProvider, email, name, null, idToken, null, null, null, null)
|
|
708
|
-
}
|
|
709
|
-
} else {
|
|
710
|
-
val email = extractJsonValue(json, "email")
|
|
711
|
-
val name = extractJsonValue(json, "name")
|
|
712
|
-
val photo = extractJsonValue(json, "photo")
|
|
713
|
-
val idToken = extractJsonValue(json, "idToken")
|
|
714
|
-
val serverAuthCode = extractJsonValue(json, "serverAuthCode")
|
|
715
|
-
nativeOnLoginSuccess(effectiveProvider, email, name, photo, idToken, null, serverAuthCode, null, null)
|
|
716
|
-
}
|
|
659
|
+
val refreshToken = getMicrosoftRefreshToken()
|
|
660
|
+
if (refreshToken != null) {
|
|
661
|
+
refreshMicrosoftToken(ctx, refreshToken)
|
|
717
662
|
} else {
|
|
718
663
|
nativeOnLoginError("unknown", "No session")
|
|
719
664
|
}
|
|
@@ -770,9 +715,11 @@ object AuthAdapter {
|
|
|
770
715
|
val name = claims["name"]
|
|
771
716
|
|
|
772
717
|
if (newRefreshToken.isNotEmpty()) {
|
|
773
|
-
saveMicrosoftRefreshToken(
|
|
718
|
+
saveMicrosoftRefreshToken(newRefreshToken)
|
|
719
|
+
}
|
|
720
|
+
inMemoryMicrosoftScopes = pendingMicrosoftScopes.ifEmpty {
|
|
721
|
+
listOf("openid", "email", "profile", "offline_access", "User.Read")
|
|
774
722
|
}
|
|
775
|
-
saveUser(context, "microsoft", email, name, null, newIdToken, newRefreshToken, null)
|
|
776
723
|
|
|
777
724
|
nativeOnLoginSuccess("microsoft", email, name, null, newIdToken, newAccessToken, null, null, expirationTime)
|
|
778
725
|
} else {
|
|
@@ -828,9 +775,11 @@ object AuthAdapter {
|
|
|
828
775
|
val email = claims["preferred_username"] ?: claims["email"]
|
|
829
776
|
val name = claims["name"]
|
|
830
777
|
if (newRefreshToken.isNotEmpty()) {
|
|
831
|
-
saveMicrosoftRefreshToken(
|
|
778
|
+
saveMicrosoftRefreshToken(newRefreshToken)
|
|
779
|
+
}
|
|
780
|
+
inMemoryMicrosoftScopes = pendingMicrosoftScopes.ifEmpty {
|
|
781
|
+
listOf("openid", "email", "profile", "offline_access", "User.Read")
|
|
832
782
|
}
|
|
833
|
-
saveUser(context, "microsoft", email, name, null, newIdToken, newRefreshToken, null)
|
|
834
783
|
nativeOnRefreshSuccess(
|
|
835
784
|
newIdToken.ifEmpty { null },
|
|
836
785
|
newAccessToken.ifEmpty { null },
|
|
@@ -848,36 +797,4 @@ object AuthAdapter {
|
|
|
848
797
|
}
|
|
849
798
|
}
|
|
850
799
|
|
|
851
|
-
private fun extractJsonValue(json: String, key: String): String? {
|
|
852
|
-
return try {
|
|
853
|
-
val value = JSONObject(json).optString(key, "")
|
|
854
|
-
if (value.isEmpty()) null else value
|
|
855
|
-
} catch (_: Exception) {
|
|
856
|
-
null
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
private fun extractScopesFromUserJson(json: String): List<String> {
|
|
861
|
-
return try {
|
|
862
|
-
val jsonObj = JSONObject(json)
|
|
863
|
-
val arr = jsonObj.optJSONArray("scopes") ?: return emptyList()
|
|
864
|
-
(0 until arr.length()).mapNotNull { i -> arr.optString(i).takeIf { it.isNotEmpty() } }
|
|
865
|
-
} catch (_: Exception) {
|
|
866
|
-
emptyList()
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
private fun saveUser(context: Context, provider: String, email: String?, name: String?,
|
|
871
|
-
photo: String?, idToken: String?, serverAuthCode: String?, scopes: List<String>?) {
|
|
872
|
-
val pref = getPrefs(context)
|
|
873
|
-
val json = JSONObject()
|
|
874
|
-
json.put("provider", provider)
|
|
875
|
-
if (email != null) json.put("email", email)
|
|
876
|
-
if (name != null) json.put("name", name)
|
|
877
|
-
if (photo != null) json.put("photo", photo)
|
|
878
|
-
if (idToken != null) json.put("idToken", idToken)
|
|
879
|
-
if (serverAuthCode != null) json.put("serverAuthCode", serverAuthCode)
|
|
880
|
-
if (scopes != null) json.put("scopes", JSONArray(scopes))
|
|
881
|
-
pref.edit().putString("user_json", json.toString()).apply()
|
|
882
|
-
}
|
|
883
800
|
}
|
package/app.plugin.js
CHANGED
|
@@ -6,7 +6,6 @@ const {
|
|
|
6
6
|
AndroidConfig,
|
|
7
7
|
createRunOncePlugin,
|
|
8
8
|
} = require("@expo/config-plugins");
|
|
9
|
-
|
|
10
9
|
const pkg = require("./package.json");
|
|
11
10
|
|
|
12
11
|
const withNitroAuth = (config, props = {}) => {
|
|
@@ -157,9 +156,7 @@ const withNitroAuth = (config, props = {}) => {
|
|
|
157
156
|
],
|
|
158
157
|
};
|
|
159
158
|
const existingMsalActivity = application.activity.find(
|
|
160
|
-
(a) =>
|
|
161
|
-
a.$?.["android:name"] ===
|
|
162
|
-
"com.auth.MicrosoftAuthActivity",
|
|
159
|
+
(a) => a.$?.["android:name"] === "com.auth.MicrosoftAuthActivity",
|
|
163
160
|
);
|
|
164
161
|
if (!existingMsalActivity) {
|
|
165
162
|
application.activity.push(msalActivity);
|
|
@@ -172,8 +169,4 @@ const withNitroAuth = (config, props = {}) => {
|
|
|
172
169
|
return config;
|
|
173
170
|
};
|
|
174
171
|
|
|
175
|
-
module.exports = createRunOncePlugin(
|
|
176
|
-
withNitroAuth,
|
|
177
|
-
pkg.name,
|
|
178
|
-
pkg.version,
|
|
179
|
-
);
|
|
172
|
+
module.exports = createRunOncePlugin(withNitroAuth, pkg.name, pkg.version);
|