react-native-nitro-auth 0.5.1 → 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 +362 -190
- package/android/src/main/java/com/auth/AuthAdapter.kt +55 -169
- package/android/src/main/java/com/auth/NitroAuthPackage.kt +1 -1
- 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 +3 -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 +3 -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/package.json +11 -8
- package/src/Auth.nitro.ts +4 -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
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
package com.auth
|
|
4
4
|
|
|
5
5
|
import android.content.Context
|
|
6
|
-
import android.content.SharedPreferences
|
|
7
6
|
import android.os.Bundle
|
|
8
|
-
import android.os.Build
|
|
9
7
|
import android.util.Log
|
|
10
8
|
import com.google.android.gms.auth.api.signin.GoogleSignIn
|
|
11
9
|
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
|
|
@@ -27,17 +25,12 @@ import android.app.Application
|
|
|
27
25
|
import android.content.Intent
|
|
28
26
|
import android.net.Uri
|
|
29
27
|
import androidx.browser.customtabs.CustomTabsIntent
|
|
30
|
-
import androidx.security.crypto.EncryptedSharedPreferences
|
|
31
|
-
import androidx.security.crypto.MasterKeys
|
|
32
28
|
import java.util.UUID
|
|
33
|
-
import org.json.JSONArray
|
|
34
29
|
import org.json.JSONObject
|
|
35
30
|
import android.util.Base64
|
|
36
31
|
|
|
37
32
|
object AuthAdapter {
|
|
38
33
|
private const val TAG = "AuthAdapter"
|
|
39
|
-
private const val PREF_NAME = "nitro_auth"
|
|
40
|
-
private const val SECURE_PREF_NAME = "nitro_auth_secure"
|
|
41
34
|
|
|
42
35
|
private var appContext: Context? = null
|
|
43
36
|
private var currentActivity: Activity? = null
|
|
@@ -51,47 +44,10 @@ object AuthAdapter {
|
|
|
51
44
|
private var pendingMicrosoftTenant: String? = null
|
|
52
45
|
private var pendingMicrosoftClientId: String? = null
|
|
53
46
|
private var pendingMicrosoftB2cDomain: String? = null
|
|
54
|
-
|
|
55
|
-
private
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
|
|
59
|
-
val securePrefs = EncryptedSharedPreferences.create(
|
|
60
|
-
SECURE_PREF_NAME,
|
|
61
|
-
masterKeyAlias,
|
|
62
|
-
context,
|
|
63
|
-
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
|
64
|
-
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
|
65
|
-
)
|
|
66
|
-
migrateLegacyPrefsIfNeeded(context, securePrefs)
|
|
67
|
-
securePrefs
|
|
68
|
-
} else {
|
|
69
|
-
context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
|
70
|
-
}
|
|
71
|
-
} catch (e: Exception) {
|
|
72
|
-
Log.w(TAG, "Failed to initialize encrypted storage, falling back to SharedPreferences", e)
|
|
73
|
-
context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
private fun migrateLegacyPrefsIfNeeded(context: Context, securePrefs: SharedPreferences) {
|
|
78
|
-
val legacyPrefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
|
79
|
-
if (legacyPrefs.all.isEmpty() || securePrefs.all.isNotEmpty()) {
|
|
80
|
-
return
|
|
81
|
-
}
|
|
82
|
-
val editor = securePrefs.edit()
|
|
83
|
-
for ((key, value) in legacyPrefs.all) {
|
|
84
|
-
when (value) {
|
|
85
|
-
is String -> editor.putString(key, value)
|
|
86
|
-
is Boolean -> editor.putBoolean(key, value)
|
|
87
|
-
is Int -> editor.putInt(key, value)
|
|
88
|
-
is Long -> editor.putLong(key, value)
|
|
89
|
-
is Float -> editor.putFloat(key, value)
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
editor.apply()
|
|
93
|
-
legacyPrefs.edit().clear().apply()
|
|
94
|
-
}
|
|
47
|
+
|
|
48
|
+
private var inMemoryMicrosoftRefreshToken: String? = null
|
|
49
|
+
private var inMemoryMicrosoftScopes: List<String> =
|
|
50
|
+
listOf("openid", "email", "profile", "offline_access", "User.Read")
|
|
95
51
|
|
|
96
52
|
@JvmStatic
|
|
97
53
|
private external fun nativeInitialize(context: Context)
|
|
@@ -141,11 +97,10 @@ object AuthAdapter {
|
|
|
141
97
|
}
|
|
142
98
|
|
|
143
99
|
fun onSignInSuccess(account: GoogleSignInAccount, scopes: List<String>) {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
account.photoUrl?.toString(), account.idToken, account.serverAuthCode, scopes)
|
|
100
|
+
appContext ?: return
|
|
101
|
+
val expirationTime = getJwtExpirationTimeMs(account.idToken)
|
|
147
102
|
nativeOnLoginSuccess("google", account.email, account.displayName,
|
|
148
|
-
account.photoUrl?.toString(), account.idToken, null, account.serverAuthCode, scopes.toTypedArray(),
|
|
103
|
+
account.photoUrl?.toString(), account.idToken, null, account.serverAuthCode, scopes.toTypedArray(), expirationTime)
|
|
149
104
|
}
|
|
150
105
|
|
|
151
106
|
fun onSignInError(errorCode: Int, message: String?) {
|
|
@@ -393,12 +348,11 @@ object AuthAdapter {
|
|
|
393
348
|
val email = claims["preferred_username"] ?: claims["email"]
|
|
394
349
|
val name = claims["name"]
|
|
395
350
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
}
|
|
351
|
+
if (refreshToken.isNotEmpty()) {
|
|
352
|
+
inMemoryMicrosoftRefreshToken = refreshToken
|
|
353
|
+
}
|
|
354
|
+
inMemoryMicrosoftScopes = pendingMicrosoftScopes.ifEmpty {
|
|
355
|
+
listOf("openid", "email", "profile", "offline_access", "User.Read")
|
|
402
356
|
}
|
|
403
357
|
|
|
404
358
|
clearPkceState()
|
|
@@ -419,14 +373,12 @@ object AuthAdapter {
|
|
|
419
373
|
}
|
|
420
374
|
}
|
|
421
375
|
|
|
422
|
-
private fun saveMicrosoftRefreshToken(
|
|
423
|
-
|
|
424
|
-
prefs.edit().putString("microsoft_refresh_token", refreshToken).apply()
|
|
376
|
+
private fun saveMicrosoftRefreshToken(refreshToken: String) {
|
|
377
|
+
inMemoryMicrosoftRefreshToken = refreshToken
|
|
425
378
|
}
|
|
426
379
|
|
|
427
|
-
private fun getMicrosoftRefreshToken(
|
|
428
|
-
|
|
429
|
-
return prefs.getString("microsoft_refresh_token", null)
|
|
380
|
+
private fun getMicrosoftRefreshToken(): String? {
|
|
381
|
+
return inMemoryMicrosoftRefreshToken
|
|
430
382
|
}
|
|
431
383
|
|
|
432
384
|
private fun clearPkceState() {
|
|
@@ -455,6 +407,15 @@ object AuthAdapter {
|
|
|
455
407
|
}
|
|
456
408
|
}
|
|
457
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
|
+
|
|
458
419
|
private fun getMicrosoftClientIdFromResources(context: Context): String? {
|
|
459
420
|
val resId = context.resources.getIdentifier("nitro_auth_microsoft_client_id", "string", context.packageName)
|
|
460
421
|
return if (resId != 0) context.getString(resId) else null
|
|
@@ -553,6 +514,7 @@ object AuthAdapter {
|
|
|
553
514
|
}
|
|
554
515
|
|
|
555
516
|
if (googleIdTokenCredential != null) {
|
|
517
|
+
val expirationTime = getJwtExpirationTimeMs(googleIdTokenCredential.idToken)
|
|
556
518
|
nativeOnLoginSuccess(
|
|
557
519
|
"google",
|
|
558
520
|
googleIdTokenCredential.id,
|
|
@@ -562,7 +524,7 @@ object AuthAdapter {
|
|
|
562
524
|
null,
|
|
563
525
|
null,
|
|
564
526
|
scopes.toTypedArray(),
|
|
565
|
-
|
|
527
|
+
expirationTime
|
|
566
528
|
)
|
|
567
529
|
} else {
|
|
568
530
|
Log.w(TAG, "Unsupported credential type: ${credential.type}")
|
|
@@ -590,12 +552,8 @@ object AuthAdapter {
|
|
|
590
552
|
ctx.startActivity(intent)
|
|
591
553
|
return
|
|
592
554
|
}
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
val currentScopes = extractScopesFromUserJson(userJson)
|
|
596
|
-
val defaultMicrosoftScopes = listOf("openid", "email", "profile", "offline_access", "User.Read")
|
|
597
|
-
val existing = if (currentScopes.isEmpty()) defaultMicrosoftScopes else currentScopes
|
|
598
|
-
val mergedScopes = (existing + scopes.toList()).distinct()
|
|
555
|
+
if (inMemoryMicrosoftRefreshToken != null) {
|
|
556
|
+
val mergedScopes = (inMemoryMicrosoftScopes + scopes.toList()).distinct()
|
|
599
557
|
val tenant = getMicrosoftTenantFromResources(ctx)
|
|
600
558
|
loginMicrosoft(ctx, mergedScopes.toTypedArray(), null, tenant, null)
|
|
601
559
|
return
|
|
@@ -624,20 +582,21 @@ object AuthAdapter {
|
|
|
624
582
|
googleSignInClient!!.silentSignIn().addOnCompleteListener { task ->
|
|
625
583
|
if (task.isSuccessful) {
|
|
626
584
|
val acc = task.result
|
|
627
|
-
nativeOnRefreshSuccess(
|
|
585
|
+
nativeOnRefreshSuccess(
|
|
586
|
+
acc?.idToken,
|
|
587
|
+
null,
|
|
588
|
+
getJwtExpirationTimeMs(acc?.idToken),
|
|
589
|
+
)
|
|
628
590
|
} else {
|
|
629
591
|
nativeOnRefreshError("network_error", task.exception?.message ?: "Silent sign-in failed")
|
|
630
592
|
}
|
|
631
593
|
}
|
|
632
594
|
return
|
|
633
595
|
}
|
|
634
|
-
val
|
|
635
|
-
if (
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
refreshMicrosoftTokenForRefresh(ctx, refreshToken)
|
|
639
|
-
return
|
|
640
|
-
}
|
|
596
|
+
val refreshToken = getMicrosoftRefreshToken()
|
|
597
|
+
if (refreshToken != null) {
|
|
598
|
+
refreshMicrosoftTokenForRefresh(ctx, refreshToken)
|
|
599
|
+
return
|
|
641
600
|
}
|
|
642
601
|
nativeOnRefreshError("unknown", "No user logged in")
|
|
643
602
|
}
|
|
@@ -662,7 +621,8 @@ object AuthAdapter {
|
|
|
662
621
|
.build()
|
|
663
622
|
GoogleSignIn.getClient(ctx, gso).signOut()
|
|
664
623
|
}
|
|
665
|
-
|
|
624
|
+
inMemoryMicrosoftRefreshToken = null
|
|
625
|
+
inMemoryMicrosoftScopes = listOf("openid", "email", "profile", "offline_access", "User.Read")
|
|
666
626
|
}
|
|
667
627
|
|
|
668
628
|
@JvmStatic
|
|
@@ -677,7 +637,8 @@ object AuthAdapter {
|
|
|
677
637
|
.build()
|
|
678
638
|
GoogleSignIn.getClient(ctx, gso).revokeAccess()
|
|
679
639
|
}
|
|
680
|
-
|
|
640
|
+
inMemoryMicrosoftRefreshToken = null
|
|
641
|
+
inMemoryMicrosoftScopes = listOf("openid", "email", "profile", "offline_access", "User.Read")
|
|
681
642
|
}
|
|
682
643
|
|
|
683
644
|
private fun getClientIdFromResources(context: Context): String? {
|
|
@@ -685,66 +646,19 @@ object AuthAdapter {
|
|
|
685
646
|
return if (resId != 0) context.getString(resId) else null
|
|
686
647
|
}
|
|
687
648
|
|
|
688
|
-
@JvmStatic
|
|
689
|
-
fun getUserJson(context: Context): String? {
|
|
690
|
-
val pref = getPrefs(context)
|
|
691
|
-
return pref.getString("user_json", null)
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
@JvmStatic
|
|
695
|
-
fun setUserJson(context: Context, json: String) {
|
|
696
|
-
val pref = getPrefs(context)
|
|
697
|
-
pref.edit().putString("user_json", json).apply()
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
@JvmStatic
|
|
701
|
-
fun clearUser(context: Context) {
|
|
702
|
-
val pref = getPrefs(context)
|
|
703
|
-
pref.edit().clear().apply()
|
|
704
|
-
}
|
|
705
|
-
|
|
706
649
|
@JvmStatic
|
|
707
650
|
fun restoreSession(context: Context) {
|
|
708
651
|
val ctx = context.applicationContext ?: appContext ?: context
|
|
709
652
|
val account = GoogleSignIn.getLastSignedInAccount(ctx)
|
|
710
653
|
if (account != null) {
|
|
654
|
+
val expirationTime = getJwtExpirationTimeMs(account.idToken)
|
|
711
655
|
nativeOnLoginSuccess("google", account.email, account.displayName,
|
|
712
656
|
account.photoUrl?.toString(), account.idToken, null, account.serverAuthCode,
|
|
713
|
-
account.grantedScopes?.map { it.scopeUri }?.toTypedArray(),
|
|
657
|
+
account.grantedScopes?.map { it.scopeUri }?.toTypedArray(), expirationTime)
|
|
714
658
|
} else {
|
|
715
|
-
val
|
|
716
|
-
if (
|
|
717
|
-
|
|
718
|
-
val parsed = JSONObject(json)
|
|
719
|
-
parsed.optString("provider")
|
|
720
|
-
} catch (_: Exception) {
|
|
721
|
-
""
|
|
722
|
-
}
|
|
723
|
-
val effectiveProvider = when (provider) {
|
|
724
|
-
"google" -> "google"
|
|
725
|
-
"microsoft" -> "microsoft"
|
|
726
|
-
"apple" -> "apple"
|
|
727
|
-
else -> "apple"
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
if (effectiveProvider == "microsoft") {
|
|
731
|
-
val refreshToken = getMicrosoftRefreshToken(ctx)
|
|
732
|
-
if (refreshToken != null) {
|
|
733
|
-
refreshMicrosoftToken(ctx, refreshToken)
|
|
734
|
-
} else {
|
|
735
|
-
val email = extractJsonValue(json, "email")
|
|
736
|
-
val name = extractJsonValue(json, "name")
|
|
737
|
-
val idToken = extractJsonValue(json, "idToken")
|
|
738
|
-
nativeOnLoginSuccess(effectiveProvider, email, name, null, idToken, null, null, null, null)
|
|
739
|
-
}
|
|
740
|
-
} else {
|
|
741
|
-
val email = extractJsonValue(json, "email")
|
|
742
|
-
val name = extractJsonValue(json, "name")
|
|
743
|
-
val photo = extractJsonValue(json, "photo")
|
|
744
|
-
val idToken = extractJsonValue(json, "idToken")
|
|
745
|
-
val serverAuthCode = extractJsonValue(json, "serverAuthCode")
|
|
746
|
-
nativeOnLoginSuccess(effectiveProvider, email, name, photo, idToken, null, serverAuthCode, null, null)
|
|
747
|
-
}
|
|
659
|
+
val refreshToken = getMicrosoftRefreshToken()
|
|
660
|
+
if (refreshToken != null) {
|
|
661
|
+
refreshMicrosoftToken(ctx, refreshToken)
|
|
748
662
|
} else {
|
|
749
663
|
nativeOnLoginError("unknown", "No session")
|
|
750
664
|
}
|
|
@@ -801,9 +715,11 @@ object AuthAdapter {
|
|
|
801
715
|
val name = claims["name"]
|
|
802
716
|
|
|
803
717
|
if (newRefreshToken.isNotEmpty()) {
|
|
804
|
-
saveMicrosoftRefreshToken(
|
|
718
|
+
saveMicrosoftRefreshToken(newRefreshToken)
|
|
719
|
+
}
|
|
720
|
+
inMemoryMicrosoftScopes = pendingMicrosoftScopes.ifEmpty {
|
|
721
|
+
listOf("openid", "email", "profile", "offline_access", "User.Read")
|
|
805
722
|
}
|
|
806
|
-
saveUser(context, "microsoft", email, name, null, newIdToken, newRefreshToken, null)
|
|
807
723
|
|
|
808
724
|
nativeOnLoginSuccess("microsoft", email, name, null, newIdToken, newAccessToken, null, null, expirationTime)
|
|
809
725
|
} else {
|
|
@@ -859,9 +775,11 @@ object AuthAdapter {
|
|
|
859
775
|
val email = claims["preferred_username"] ?: claims["email"]
|
|
860
776
|
val name = claims["name"]
|
|
861
777
|
if (newRefreshToken.isNotEmpty()) {
|
|
862
|
-
saveMicrosoftRefreshToken(
|
|
778
|
+
saveMicrosoftRefreshToken(newRefreshToken)
|
|
779
|
+
}
|
|
780
|
+
inMemoryMicrosoftScopes = pendingMicrosoftScopes.ifEmpty {
|
|
781
|
+
listOf("openid", "email", "profile", "offline_access", "User.Read")
|
|
863
782
|
}
|
|
864
|
-
saveUser(context, "microsoft", email, name, null, newIdToken, newRefreshToken, null)
|
|
865
783
|
nativeOnRefreshSuccess(
|
|
866
784
|
newIdToken.ifEmpty { null },
|
|
867
785
|
newAccessToken.ifEmpty { null },
|
|
@@ -879,36 +797,4 @@ object AuthAdapter {
|
|
|
879
797
|
}
|
|
880
798
|
}
|
|
881
799
|
|
|
882
|
-
private fun extractJsonValue(json: String, key: String): String? {
|
|
883
|
-
return try {
|
|
884
|
-
val value = JSONObject(json).optString(key, "")
|
|
885
|
-
if (value.isEmpty()) null else value
|
|
886
|
-
} catch (_: Exception) {
|
|
887
|
-
null
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
private fun extractScopesFromUserJson(json: String): List<String> {
|
|
892
|
-
return try {
|
|
893
|
-
val jsonObj = JSONObject(json)
|
|
894
|
-
val arr = jsonObj.optJSONArray("scopes") ?: return emptyList()
|
|
895
|
-
(0 until arr.length()).mapNotNull { i -> arr.optString(i).takeIf { it.isNotEmpty() } }
|
|
896
|
-
} catch (_: Exception) {
|
|
897
|
-
emptyList()
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
private fun saveUser(context: Context, provider: String, email: String?, name: String?,
|
|
902
|
-
photo: String?, idToken: String?, serverAuthCode: String?, scopes: List<String>?) {
|
|
903
|
-
val pref = getPrefs(context)
|
|
904
|
-
val json = JSONObject()
|
|
905
|
-
json.put("provider", provider)
|
|
906
|
-
if (email != null) json.put("email", email)
|
|
907
|
-
if (name != null) json.put("name", name)
|
|
908
|
-
if (photo != null) json.put("photo", photo)
|
|
909
|
-
if (idToken != null) json.put("idToken", idToken)
|
|
910
|
-
if (serverAuthCode != null) json.put("serverAuthCode", serverAuthCode)
|
|
911
|
-
if (scopes != null) json.put("scopes", JSONArray(scopes))
|
|
912
|
-
pref.edit().putString("user_json", json.toString()).apply()
|
|
913
|
-
}
|
|
914
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);
|
package/cpp/AuthCache.cpp
CHANGED
|
@@ -13,91 +13,21 @@
|
|
|
13
13
|
namespace margelo::nitro::NitroAuth {
|
|
14
14
|
|
|
15
15
|
#ifdef __APPLE__
|
|
16
|
-
static
|
|
17
|
-
static CFStringRef kAccount = CFSTR("nitro_auth_user");
|
|
18
|
-
static CFStringRef kLegacyCacheKey = CFSTR("nitro_auth_user");
|
|
19
|
-
|
|
20
|
-
static CFMutableDictionaryRef createKeychainQuery() {
|
|
21
|
-
CFMutableDictionaryRef query = CFDictionaryCreateMutable(
|
|
22
|
-
kCFAllocatorDefault,
|
|
23
|
-
0,
|
|
24
|
-
&kCFTypeDictionaryKeyCallBacks,
|
|
25
|
-
&kCFTypeDictionaryValueCallBacks
|
|
26
|
-
);
|
|
27
|
-
CFDictionarySetValue(query, kSecClass, kSecClassGenericPassword);
|
|
28
|
-
CFDictionarySetValue(query, kSecAttrService, kService);
|
|
29
|
-
CFDictionarySetValue(query, kSecAttrAccount, kAccount);
|
|
30
|
-
return query;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
static std::optional<std::string> getLegacyUserJson() {
|
|
34
|
-
CFPropertyListRef value = CFPreferencesCopyAppValue(kLegacyCacheKey, kCFPreferencesCurrentApplication);
|
|
35
|
-
if (value && CFGetTypeID(value) == CFStringGetTypeID()) {
|
|
36
|
-
CFStringRef cfStr = static_cast<CFStringRef>(value);
|
|
37
|
-
char buffer[4096];
|
|
38
|
-
if (CFStringGetCString(cfStr, buffer, sizeof(buffer), kCFStringEncodingUTF8)) {
|
|
39
|
-
CFRelease(value);
|
|
40
|
-
return std::string(buffer);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
if (value) CFRelease(value);
|
|
44
|
-
return std::nullopt;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
static void clearLegacyUserJson() {
|
|
48
|
-
CFPreferencesSetAppValue(kLegacyCacheKey, nullptr, kCFPreferencesCurrentApplication);
|
|
49
|
-
CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication);
|
|
50
|
-
}
|
|
16
|
+
static std::string sInMemoryUserJson;
|
|
51
17
|
|
|
52
18
|
void AuthCache::setUserJson(const std::string& json) {
|
|
53
|
-
|
|
54
|
-
SecItemDelete(query);
|
|
55
|
-
|
|
56
|
-
CFDataRef data = CFDataCreate(
|
|
57
|
-
kCFAllocatorDefault,
|
|
58
|
-
reinterpret_cast<const UInt8*>(json.data()),
|
|
59
|
-
static_cast<CFIndex>(json.size())
|
|
60
|
-
);
|
|
61
|
-
CFDictionarySetValue(query, kSecValueData, data);
|
|
62
|
-
CFDictionarySetValue(query, kSecAttrAccessible, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly);
|
|
63
|
-
|
|
64
|
-
SecItemAdd(query, nullptr);
|
|
65
|
-
CFRelease(data);
|
|
66
|
-
CFRelease(query);
|
|
19
|
+
sInMemoryUserJson = json;
|
|
67
20
|
}
|
|
68
21
|
|
|
69
22
|
std::optional<std::string> AuthCache::getUserJson() {
|
|
70
|
-
|
|
71
|
-
CFDictionarySetValue(query, kSecReturnData, kCFBooleanTrue);
|
|
72
|
-
CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitOne);
|
|
73
|
-
|
|
74
|
-
CFTypeRef result = nullptr;
|
|
75
|
-
OSStatus status = SecItemCopyMatching(query, &result);
|
|
76
|
-
CFRelease(query);
|
|
77
|
-
|
|
78
|
-
if (status != errSecSuccess || result == nullptr) {
|
|
79
|
-
if (result) CFRelease(result);
|
|
80
|
-
auto legacy = getLegacyUserJson();
|
|
81
|
-
if (legacy) {
|
|
82
|
-
AuthCache::setUserJson(*legacy);
|
|
83
|
-
clearLegacyUserJson();
|
|
84
|
-
return legacy;
|
|
85
|
-
}
|
|
23
|
+
if (sInMemoryUserJson.empty()) {
|
|
86
24
|
return std::nullopt;
|
|
87
25
|
}
|
|
88
|
-
|
|
89
|
-
CFDataRef data = static_cast<CFDataRef>(result);
|
|
90
|
-
const UInt8* bytes = CFDataGetBytePtr(data);
|
|
91
|
-
const CFIndex length = CFDataGetLength(data);
|
|
92
|
-
std::string value(reinterpret_cast<const char*>(bytes), static_cast<size_t>(length));
|
|
93
|
-
CFRelease(result);
|
|
94
|
-
return value;
|
|
26
|
+
return sInMemoryUserJson;
|
|
95
27
|
}
|
|
96
28
|
|
|
97
29
|
void AuthCache::clear() {
|
|
98
|
-
|
|
99
|
-
SecItemDelete(query);
|
|
100
|
-
CFRelease(query);
|
|
30
|
+
sInMemoryUserJson.clear();
|
|
101
31
|
}
|
|
102
32
|
#endif
|
|
103
33
|
|
|
@@ -108,26 +38,8 @@ struct JContext : JavaClass<JContext> {
|
|
|
108
38
|
static constexpr auto kJavaDescriptor = "Landroid/content/Context;";
|
|
109
39
|
};
|
|
110
40
|
|
|
111
|
-
struct JAuthAdapter : facebook::jni::JavaClass<JAuthAdapter> {
|
|
112
|
-
static constexpr auto kJavaDescriptor = "Lcom/auth/AuthAdapter;";
|
|
113
|
-
|
|
114
|
-
static void setUserJson(facebook::jni::alias_ref<jobject> context, const std::string& json) {
|
|
115
|
-
static auto method = javaClassStatic()->getStaticMethod<void(alias_ref<JContext>, jstring)>("setUserJson");
|
|
116
|
-
method(javaClassStatic(), static_ref_cast<JContext>(context), make_jstring(json).get());
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
static facebook::jni::local_ref<jstring> getUserJson(facebook::jni::alias_ref<jobject> context) {
|
|
120
|
-
static auto method = javaClassStatic()->getStaticMethod<jstring(alias_ref<JContext>)>("getUserJson");
|
|
121
|
-
return method(javaClassStatic(), static_ref_cast<JContext>(context));
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
static void clearUser(facebook::jni::alias_ref<jobject> context) {
|
|
125
|
-
static auto method = javaClassStatic()->getStaticMethod<void(alias_ref<JContext>)>("clearUser");
|
|
126
|
-
method(javaClassStatic(), static_ref_cast<JContext>(context));
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
|
-
|
|
130
41
|
static facebook::jni::global_ref<jobject> gContext;
|
|
42
|
+
static std::string sInMemoryUserJson;
|
|
131
43
|
|
|
132
44
|
void AuthCache::setAndroidContext(void* context) {
|
|
133
45
|
gContext = facebook::jni::make_global(static_cast<jobject>(context));
|
|
@@ -138,20 +50,18 @@ void* AuthCache::getAndroidContext() {
|
|
|
138
50
|
}
|
|
139
51
|
|
|
140
52
|
void AuthCache::setUserJson(const std::string& json) {
|
|
141
|
-
|
|
142
|
-
JAuthAdapter::setUserJson(gContext, json);
|
|
53
|
+
sInMemoryUserJson = json;
|
|
143
54
|
}
|
|
144
55
|
|
|
145
56
|
std::optional<std::string> AuthCache::getUserJson() {
|
|
146
|
-
if (
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
return
|
|
57
|
+
if (sInMemoryUserJson.empty()) {
|
|
58
|
+
return std::nullopt;
|
|
59
|
+
}
|
|
60
|
+
return sInMemoryUserJson;
|
|
150
61
|
}
|
|
151
62
|
|
|
152
63
|
void AuthCache::clear() {
|
|
153
|
-
|
|
154
|
-
JAuthAdapter::clearUser(gContext);
|
|
64
|
+
sInMemoryUserJson.clear();
|
|
155
65
|
}
|
|
156
66
|
#endif
|
|
157
67
|
|