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.
Files changed (124) hide show
  1. package/README.md +374 -189
  2. package/android/src/main/cpp/PlatformAuth+Android.cpp +4 -1
  3. package/android/src/main/java/com/auth/AuthAdapter.kt +99 -182
  4. package/android/src/main/java/com/auth/GoogleSignInActivity.kt +2 -1
  5. package/android/src/main/java/com/auth/NitroAuthPackage.kt +2 -0
  6. package/app.plugin.js +2 -9
  7. package/cpp/AuthCache.cpp +12 -102
  8. package/cpp/HybridAuth.cpp +37 -61
  9. package/cpp/HybridAuth.hpp +2 -4
  10. package/ios/AuthAdapter.swift +21 -25
  11. package/lib/commonjs/Auth.web.js +433 -164
  12. package/lib/commonjs/Auth.web.js.map +1 -1
  13. package/lib/commonjs/index.js +0 -12
  14. package/lib/commonjs/index.js.map +1 -1
  15. package/lib/commonjs/index.web.js +0 -12
  16. package/lib/commonjs/index.web.js.map +1 -1
  17. package/lib/commonjs/js-storage-adapter.js +2 -0
  18. package/lib/commonjs/js-storage-adapter.js.map +1 -0
  19. package/lib/commonjs/service.js +7 -84
  20. package/lib/commonjs/service.js.map +1 -1
  21. package/lib/commonjs/service.web.js +1 -5
  22. package/lib/commonjs/service.web.js.map +1 -1
  23. package/lib/commonjs/ui/social-button.js +44 -29
  24. package/lib/commonjs/ui/social-button.js.map +1 -1
  25. package/lib/commonjs/ui/social-button.web.js +44 -29
  26. package/lib/commonjs/ui/social-button.web.js.map +1 -1
  27. package/lib/commonjs/use-auth.js +8 -2
  28. package/lib/commonjs/use-auth.js.map +1 -1
  29. package/lib/commonjs/utils/logger.js +12 -4
  30. package/lib/commonjs/utils/logger.js.map +1 -1
  31. package/lib/module/Auth.web.js +433 -164
  32. package/lib/module/Auth.web.js.map +1 -1
  33. package/lib/module/index.js +0 -1
  34. package/lib/module/index.js.map +1 -1
  35. package/lib/module/index.web.js +0 -1
  36. package/lib/module/index.web.js.map +1 -1
  37. package/lib/module/js-storage-adapter.js +2 -0
  38. package/lib/module/js-storage-adapter.js.map +1 -0
  39. package/lib/module/service.js +7 -84
  40. package/lib/module/service.js.map +1 -1
  41. package/lib/module/service.web.js +1 -5
  42. package/lib/module/service.web.js.map +1 -1
  43. package/lib/module/ui/social-button.js +44 -29
  44. package/lib/module/ui/social-button.js.map +1 -1
  45. package/lib/module/ui/social-button.web.js +44 -29
  46. package/lib/module/ui/social-button.web.js.map +1 -1
  47. package/lib/module/use-auth.js +8 -2
  48. package/lib/module/use-auth.js.map +1 -1
  49. package/lib/module/utils/logger.js +12 -4
  50. package/lib/module/utils/logger.js.map +1 -1
  51. package/lib/typescript/commonjs/Auth.nitro.d.ts +5 -3
  52. package/lib/typescript/commonjs/Auth.nitro.d.ts.map +1 -1
  53. package/lib/typescript/commonjs/Auth.web.d.ts +18 -6
  54. package/lib/typescript/commonjs/Auth.web.d.ts.map +1 -1
  55. package/lib/typescript/commonjs/index.d.ts +1 -2
  56. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  57. package/lib/typescript/commonjs/index.web.d.ts +0 -1
  58. package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
  59. package/lib/typescript/commonjs/js-storage-adapter.d.ts +6 -0
  60. package/lib/typescript/commonjs/js-storage-adapter.d.ts.map +1 -0
  61. package/lib/typescript/commonjs/service.d.ts +1 -8
  62. package/lib/typescript/commonjs/service.d.ts.map +1 -1
  63. package/lib/typescript/commonjs/service.web.d.ts +1 -8
  64. package/lib/typescript/commonjs/service.web.d.ts.map +1 -1
  65. package/lib/typescript/commonjs/ui/social-button.d.ts +6 -6
  66. package/lib/typescript/commonjs/ui/social-button.d.ts.map +1 -1
  67. package/lib/typescript/commonjs/ui/social-button.web.d.ts +6 -6
  68. package/lib/typescript/commonjs/ui/social-button.web.d.ts.map +1 -1
  69. package/lib/typescript/commonjs/use-auth.d.ts +4 -4
  70. package/lib/typescript/commonjs/use-auth.d.ts.map +1 -1
  71. package/lib/typescript/commonjs/utils/logger.d.ts +4 -4
  72. package/lib/typescript/commonjs/utils/logger.d.ts.map +1 -1
  73. package/lib/typescript/module/Auth.nitro.d.ts +5 -3
  74. package/lib/typescript/module/Auth.nitro.d.ts.map +1 -1
  75. package/lib/typescript/module/Auth.web.d.ts +18 -6
  76. package/lib/typescript/module/Auth.web.d.ts.map +1 -1
  77. package/lib/typescript/module/index.d.ts +1 -2
  78. package/lib/typescript/module/index.d.ts.map +1 -1
  79. package/lib/typescript/module/index.web.d.ts +0 -1
  80. package/lib/typescript/module/index.web.d.ts.map +1 -1
  81. package/lib/typescript/module/js-storage-adapter.d.ts +6 -0
  82. package/lib/typescript/module/js-storage-adapter.d.ts.map +1 -0
  83. package/lib/typescript/module/service.d.ts +1 -8
  84. package/lib/typescript/module/service.d.ts.map +1 -1
  85. package/lib/typescript/module/service.web.d.ts +1 -8
  86. package/lib/typescript/module/service.web.d.ts.map +1 -1
  87. package/lib/typescript/module/ui/social-button.d.ts +6 -6
  88. package/lib/typescript/module/ui/social-button.d.ts.map +1 -1
  89. package/lib/typescript/module/ui/social-button.web.d.ts +6 -6
  90. package/lib/typescript/module/ui/social-button.web.d.ts.map +1 -1
  91. package/lib/typescript/module/use-auth.d.ts +4 -4
  92. package/lib/typescript/module/use-auth.d.ts.map +1 -1
  93. package/lib/typescript/module/utils/logger.d.ts +4 -4
  94. package/lib/typescript/module/utils/logger.d.ts.map +1 -1
  95. package/nitrogen/generated/android/NitroAuth+autolinking.cmake +0 -1
  96. package/nitrogen/generated/shared/c++/AuthTokens.hpp +5 -1
  97. package/nitrogen/generated/shared/c++/AuthUser.hpp +5 -1
  98. package/nitrogen/generated/shared/c++/HybridAuthSpec.cpp +0 -1
  99. package/nitrogen/generated/shared/c++/HybridAuthSpec.hpp +0 -5
  100. package/nitrogen/generated/shared/c++/LoginOptions.hpp +5 -1
  101. package/package.json +13 -10
  102. package/src/Auth.nitro.ts +6 -3
  103. package/src/Auth.web.ts +582 -202
  104. package/src/global.d.ts +0 -1
  105. package/src/index.ts +1 -2
  106. package/src/index.web.ts +0 -1
  107. package/src/js-storage-adapter.ts +5 -0
  108. package/src/service.ts +11 -104
  109. package/src/service.web.ts +0 -7
  110. package/src/ui/social-button.tsx +66 -43
  111. package/src/ui/social-button.web.tsx +67 -44
  112. package/src/use-auth.ts +18 -6
  113. package/src/utils/logger.ts +12 -4
  114. package/lib/commonjs/AuthStorage.nitro.js +0 -6
  115. package/lib/commonjs/AuthStorage.nitro.js.map +0 -1
  116. package/lib/module/AuthStorage.nitro.js +0 -4
  117. package/lib/module/AuthStorage.nitro.js.map +0 -1
  118. package/lib/typescript/commonjs/AuthStorage.nitro.d.ts +0 -26
  119. package/lib/typescript/commonjs/AuthStorage.nitro.d.ts.map +0 -1
  120. package/lib/typescript/module/AuthStorage.nitro.d.ts +0 -26
  121. package/lib/typescript/module/AuthStorage.nitro.d.ts.map +0 -1
  122. package/nitrogen/generated/shared/c++/HybridAuthStorageAdapterSpec.cpp +0 -23
  123. package/nitrogen/generated/shared/c++/HybridAuthStorageAdapterSpec.hpp +0 -65
  124. 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;ZZLjava/lang/String;Ljava/lang/String;)V");
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 fun getPrefs(context: Context): SharedPreferences {
54
- return try {
55
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
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
- val ctx = appContext ?: return
143
- saveUser(ctx, "google", account.email, account.displayName,
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(), null)
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(context: Context, provider: String, googleClientId: String?, scopes: Array<String>?, loginHint: String?, useOneTap: Boolean, forceAccountPicker: Boolean = false, tenant: String? = null, prompt: String? = null) {
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 (useOneTap && !forceAccountPicker) {
187
- loginOneTap(context, clientId, requestedScopes)
188
- } else {
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
- val ctx = appContext
385
- if (ctx != null) {
386
- saveUser(ctx, "microsoft", email, name, null, idToken, refreshToken, pendingMicrosoftScopes)
387
- if (refreshToken.isNotEmpty()) {
388
- saveMicrosoftRefreshToken(ctx, refreshToken)
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(context: Context, refreshToken: String) {
411
- val prefs = getPrefs(context)
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(context: Context): String? {
416
- val prefs = getPrefs(context)
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(context: Context, clientId: String, scopes: List<String>) {
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(false)
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(context: Context, clientId: String, scopes: List<String>) {
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(ctx, clientId, scopes.toTypedArray(), null)
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
- null
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
- val userJson = getUserJson(ctx)
563
- if (userJson != null && userJson.contains("\"provider\":\"microsoft\"")) {
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(acc?.idToken, null, null)
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 userJson = getUserJson(ctx)
604
- if (userJson != null && userJson.contains("\"provider\":\"microsoft\"")) {
605
- val refreshToken = getMicrosoftRefreshToken(ctx)
606
- if (refreshToken != null) {
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
- clearUser(ctx)
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
- clearUser(ctx)
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(), null)
657
+ account.grantedScopes?.map { it.scopeUri }?.toTypedArray(), expirationTime)
683
658
  } else {
684
- val json = getUserJson(ctx)
685
- if (json != null) {
686
- val provider = try {
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(context, newRefreshToken)
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(context, newRefreshToken)
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
  }
@@ -1,3 +1,5 @@
1
+ @file:Suppress("DEPRECATION")
2
+
1
3
  package com.auth
2
4
 
3
5
  import android.app.Activity
@@ -84,4 +86,3 @@ class GoogleSignInActivity : ComponentActivity() {
84
86
  }
85
87
  }
86
88
  }
87
-
@@ -1,3 +1,5 @@
1
+ @file:Suppress("DEPRECATION", "OVERRIDE_DEPRECATION")
2
+
1
3
  package com.auth
2
4
 
3
5
  import com.facebook.react.ReactPackage
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);