react-native-nitro-auth 0.5.1 → 0.5.4

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 (126) hide show
  1. package/README.md +362 -190
  2. package/android/build.gradle +2 -5
  3. package/android/src/main/cpp/PlatformAuth+Android.cpp +84 -18
  4. package/android/src/main/java/com/auth/AuthAdapter.kt +82 -182
  5. package/android/src/main/java/com/auth/NitroAuthPackage.kt +1 -1
  6. package/app.plugin.js +2 -9
  7. package/cpp/AuthCache.cpp +0 -134
  8. package/cpp/AuthCache.hpp +0 -7
  9. package/cpp/HybridAuth.cpp +57 -63
  10. package/cpp/HybridAuth.hpp +3 -4
  11. package/ios/AuthAdapter.swift +23 -25
  12. package/lib/commonjs/Auth.web.js +523 -201
  13. package/lib/commonjs/Auth.web.js.map +1 -1
  14. package/lib/commonjs/index.js +0 -12
  15. package/lib/commonjs/index.js.map +1 -1
  16. package/lib/commonjs/index.web.js +0 -12
  17. package/lib/commonjs/index.web.js.map +1 -1
  18. package/lib/commonjs/js-storage-adapter.js +2 -0
  19. package/lib/commonjs/js-storage-adapter.js.map +1 -0
  20. package/lib/commonjs/service.js +9 -86
  21. package/lib/commonjs/service.js.map +1 -1
  22. package/lib/commonjs/service.web.js +1 -5
  23. package/lib/commonjs/service.web.js.map +1 -1
  24. package/lib/commonjs/ui/social-button.js +44 -29
  25. package/lib/commonjs/ui/social-button.js.map +1 -1
  26. package/lib/commonjs/ui/social-button.web.js +44 -29
  27. package/lib/commonjs/ui/social-button.web.js.map +1 -1
  28. package/lib/commonjs/use-auth.js +56 -42
  29. package/lib/commonjs/use-auth.js.map +1 -1
  30. package/lib/commonjs/utils/logger.js +12 -4
  31. package/lib/commonjs/utils/logger.js.map +1 -1
  32. package/lib/module/Auth.web.js +523 -201
  33. package/lib/module/Auth.web.js.map +1 -1
  34. package/lib/module/index.js +0 -1
  35. package/lib/module/index.js.map +1 -1
  36. package/lib/module/index.web.js +0 -1
  37. package/lib/module/index.web.js.map +1 -1
  38. package/lib/module/js-storage-adapter.js +2 -0
  39. package/lib/module/js-storage-adapter.js.map +1 -0
  40. package/lib/module/service.js +9 -86
  41. package/lib/module/service.js.map +1 -1
  42. package/lib/module/service.web.js +1 -5
  43. package/lib/module/service.web.js.map +1 -1
  44. package/lib/module/ui/social-button.js +44 -29
  45. package/lib/module/ui/social-button.js.map +1 -1
  46. package/lib/module/ui/social-button.web.js +44 -29
  47. package/lib/module/ui/social-button.web.js.map +1 -1
  48. package/lib/module/use-auth.js +56 -42
  49. package/lib/module/use-auth.js.map +1 -1
  50. package/lib/module/utils/logger.js +12 -4
  51. package/lib/module/utils/logger.js.map +1 -1
  52. package/lib/typescript/commonjs/Auth.nitro.d.ts +3 -3
  53. package/lib/typescript/commonjs/Auth.nitro.d.ts.map +1 -1
  54. package/lib/typescript/commonjs/Auth.web.d.ts +25 -6
  55. package/lib/typescript/commonjs/Auth.web.d.ts.map +1 -1
  56. package/lib/typescript/commonjs/index.d.ts +1 -2
  57. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  58. package/lib/typescript/commonjs/index.web.d.ts +0 -1
  59. package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
  60. package/lib/typescript/commonjs/js-storage-adapter.d.ts +6 -0
  61. package/lib/typescript/commonjs/js-storage-adapter.d.ts.map +1 -0
  62. package/lib/typescript/commonjs/service.d.ts +1 -8
  63. package/lib/typescript/commonjs/service.d.ts.map +1 -1
  64. package/lib/typescript/commonjs/service.web.d.ts +1 -8
  65. package/lib/typescript/commonjs/service.web.d.ts.map +1 -1
  66. package/lib/typescript/commonjs/ui/social-button.d.ts +6 -6
  67. package/lib/typescript/commonjs/ui/social-button.d.ts.map +1 -1
  68. package/lib/typescript/commonjs/ui/social-button.web.d.ts +6 -6
  69. package/lib/typescript/commonjs/ui/social-button.web.d.ts.map +1 -1
  70. package/lib/typescript/commonjs/use-auth.d.ts +4 -4
  71. package/lib/typescript/commonjs/use-auth.d.ts.map +1 -1
  72. package/lib/typescript/commonjs/utils/logger.d.ts +4 -4
  73. package/lib/typescript/commonjs/utils/logger.d.ts.map +1 -1
  74. package/lib/typescript/module/Auth.nitro.d.ts +3 -3
  75. package/lib/typescript/module/Auth.nitro.d.ts.map +1 -1
  76. package/lib/typescript/module/Auth.web.d.ts +25 -6
  77. package/lib/typescript/module/Auth.web.d.ts.map +1 -1
  78. package/lib/typescript/module/index.d.ts +1 -2
  79. package/lib/typescript/module/index.d.ts.map +1 -1
  80. package/lib/typescript/module/index.web.d.ts +0 -1
  81. package/lib/typescript/module/index.web.d.ts.map +1 -1
  82. package/lib/typescript/module/js-storage-adapter.d.ts +6 -0
  83. package/lib/typescript/module/js-storage-adapter.d.ts.map +1 -0
  84. package/lib/typescript/module/service.d.ts +1 -8
  85. package/lib/typescript/module/service.d.ts.map +1 -1
  86. package/lib/typescript/module/service.web.d.ts +1 -8
  87. package/lib/typescript/module/service.web.d.ts.map +1 -1
  88. package/lib/typescript/module/ui/social-button.d.ts +6 -6
  89. package/lib/typescript/module/ui/social-button.d.ts.map +1 -1
  90. package/lib/typescript/module/ui/social-button.web.d.ts +6 -6
  91. package/lib/typescript/module/ui/social-button.web.d.ts.map +1 -1
  92. package/lib/typescript/module/use-auth.d.ts +4 -4
  93. package/lib/typescript/module/use-auth.d.ts.map +1 -1
  94. package/lib/typescript/module/utils/logger.d.ts +4 -4
  95. package/lib/typescript/module/utils/logger.d.ts.map +1 -1
  96. package/nitrogen/generated/android/NitroAuth+autolinking.cmake +0 -1
  97. package/nitrogen/generated/shared/c++/AuthTokens.hpp +5 -1
  98. package/nitrogen/generated/shared/c++/AuthUser.hpp +5 -1
  99. package/nitrogen/generated/shared/c++/HybridAuthSpec.cpp +0 -1
  100. package/nitrogen/generated/shared/c++/HybridAuthSpec.hpp +0 -5
  101. package/package.json +11 -8
  102. package/react-native-nitro-auth.podspec +1 -1
  103. package/src/Auth.nitro.ts +4 -3
  104. package/src/Auth.web.ts +700 -246
  105. package/src/global.d.ts +0 -1
  106. package/src/index.ts +1 -2
  107. package/src/index.web.ts +0 -1
  108. package/src/js-storage-adapter.ts +5 -0
  109. package/src/service.ts +13 -106
  110. package/src/service.web.ts +0 -7
  111. package/src/ui/social-button.tsx +66 -43
  112. package/src/ui/social-button.web.tsx +67 -44
  113. package/src/use-auth.ts +116 -72
  114. package/src/utils/logger.ts +12 -4
  115. package/ios/KeychainStore.swift +0 -43
  116. package/lib/commonjs/AuthStorage.nitro.js +0 -6
  117. package/lib/commonjs/AuthStorage.nitro.js.map +0 -1
  118. package/lib/module/AuthStorage.nitro.js +0 -4
  119. package/lib/module/AuthStorage.nitro.js.map +0 -1
  120. package/lib/typescript/commonjs/AuthStorage.nitro.d.ts +0 -26
  121. package/lib/typescript/commonjs/AuthStorage.nitro.d.ts.map +0 -1
  122. package/lib/typescript/module/AuthStorage.nitro.d.ts +0 -26
  123. package/lib/typescript/module/AuthStorage.nitro.d.ts.map +0 -1
  124. package/nitrogen/generated/shared/c++/HybridAuthStorageAdapterSpec.cpp +0 -23
  125. package/nitrogen/generated/shared/c++/HybridAuthStorageAdapterSpec.hpp +0 -65
  126. package/src/AuthStorage.nitro.ts +0 -26
package/cpp/AuthCache.cpp CHANGED
@@ -1,10 +1,5 @@
1
1
  #include "AuthCache.hpp"
2
2
 
3
- #ifdef __APPLE__
4
- #include <CoreFoundation/CoreFoundation.h>
5
- #include <Security/Security.h>
6
- #endif
7
-
8
3
  #ifdef __ANDROID__
9
4
  #include <jni.h>
10
5
  #include <fbjni/fbjni.h>
@@ -12,121 +7,9 @@
12
7
 
13
8
  namespace margelo::nitro::NitroAuth {
14
9
 
15
- #ifdef __APPLE__
16
- static CFStringRef kService = CFSTR("react-native-nitro-auth");
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
- }
51
-
52
- void AuthCache::setUserJson(const std::string& json) {
53
- CFMutableDictionaryRef query = createKeychainQuery();
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);
67
- }
68
-
69
- std::optional<std::string> AuthCache::getUserJson() {
70
- CFMutableDictionaryRef query = createKeychainQuery();
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
- }
86
- return std::nullopt;
87
- }
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;
95
- }
96
-
97
- void AuthCache::clear() {
98
- CFMutableDictionaryRef query = createKeychainQuery();
99
- SecItemDelete(query);
100
- CFRelease(query);
101
- }
102
- #endif
103
-
104
10
  #ifdef __ANDROID__
105
11
  using namespace facebook::jni;
106
12
 
107
- struct JContext : JavaClass<JContext> {
108
- static constexpr auto kJavaDescriptor = "Landroid/content/Context;";
109
- };
110
-
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
13
  static facebook::jni::global_ref<jobject> gContext;
131
14
 
132
15
  void AuthCache::setAndroidContext(void* context) {
@@ -136,23 +19,6 @@ void AuthCache::setAndroidContext(void* context) {
136
19
  void* AuthCache::getAndroidContext() {
137
20
  return gContext.get();
138
21
  }
139
-
140
- void AuthCache::setUserJson(const std::string& json) {
141
- if (!gContext) return;
142
- JAuthAdapter::setUserJson(gContext, json);
143
- }
144
-
145
- std::optional<std::string> AuthCache::getUserJson() {
146
- if (!gContext) return std::nullopt;
147
- auto result = JAuthAdapter::getUserJson(gContext);
148
- if (!result) return std::nullopt;
149
- return result->toStdString();
150
- }
151
-
152
- void AuthCache::clear() {
153
- if (!gContext) return;
154
- JAuthAdapter::clearUser(gContext);
155
- }
156
22
  #endif
157
23
 
158
24
  } // namespace margelo::nitro::NitroAuth
package/cpp/AuthCache.hpp CHANGED
@@ -1,16 +1,9 @@
1
1
  #pragma once
2
2
 
3
- #include <string>
4
- #include <optional>
5
-
6
3
  namespace margelo::nitro::NitroAuth {
7
4
 
8
5
  class AuthCache {
9
6
  public:
10
- static void setUserJson(const std::string& json);
11
- static std::optional<std::string> getUserJson();
12
- static void clear();
13
-
14
7
  #ifdef __ANDROID__
15
8
  static void setAndroidContext(void* context);
16
9
  static void* getAndroidContext();
@@ -1,6 +1,4 @@
1
1
  #include "HybridAuth.hpp"
2
- #include "AuthCache.hpp"
3
- #include "JSONSerializer.hpp"
4
2
  #include "PlatformAuth.hpp"
5
3
  #include <NitroModules/NitroLogger.hpp>
6
4
  #include <chrono>
@@ -10,7 +8,7 @@ namespace margelo::nitro::NitroAuth {
10
8
  bool HybridAuth::sLoggingEnabled = false;
11
9
 
12
10
  HybridAuth::HybridAuth() : HybridObject(TAG) {
13
- loadFromCache();
11
+ // In-memory only - no internal persistence.
14
12
  }
15
13
 
16
14
  std::optional<AuthUser> HybridAuth::getCurrentUser() {
@@ -27,41 +25,6 @@ bool HybridAuth::getHasPlayServices() {
27
25
  return PlatformAuth::hasPlayServices();
28
26
  }
29
27
 
30
- void HybridAuth::loadFromCache() {
31
- std::lock_guard<std::mutex> lock(_mutex);
32
- std::optional<std::string> json;
33
-
34
- if (_storageAdapter) {
35
- json = _storageAdapter->load("nitro_auth_user");
36
- } else {
37
- json = AuthCache::getUserJson();
38
- }
39
-
40
- if (json) {
41
- _currentUser = JSONSerializer::deserialize(*json);
42
- if (_currentUser && _currentUser->scopes) {
43
- _grantedScopes = *_currentUser->scopes;
44
- }
45
- }
46
- }
47
-
48
- void HybridAuth::saveToCache(const std::optional<AuthUser>& user) {
49
- if (user) {
50
- auto json = JSONSerializer::serialize(*user);
51
- if (_storageAdapter) {
52
- _storageAdapter->save("nitro_auth_user", json);
53
- } else {
54
- AuthCache::setUserJson(json);
55
- }
56
- } else {
57
- if (_storageAdapter) {
58
- _storageAdapter->remove("nitro_auth_user");
59
- } else {
60
- AuthCache::clear();
61
- }
62
- }
63
- }
64
-
65
28
  void HybridAuth::notifyAuthStateChanged() {
66
29
  std::optional<AuthUser> user;
67
30
  std::vector<std::function<void(const std::optional<AuthUser>&)>> listeners;
@@ -104,7 +67,6 @@ void HybridAuth::logout() {
104
67
  std::lock_guard<std::mutex> lock(_mutex);
105
68
  _currentUser = std::nullopt;
106
69
  _grantedScopes.clear();
107
- saveToCache(std::nullopt);
108
70
  }
109
71
  PlatformAuth::logout();
110
72
  notifyAuthStateChanged();
@@ -114,18 +76,27 @@ std::shared_ptr<Promise<void>> HybridAuth::silentRestore() {
114
76
  auto promise = Promise<void>::create();
115
77
  auto silentPromise = PlatformAuth::silentRestore();
116
78
  silentPromise->addOnResolvedListener([this, promise](const std::optional<AuthUser>& user) {
117
- if (user) {
79
+ {
118
80
  std::lock_guard<std::mutex> lock(_mutex);
119
81
  _currentUser = user;
120
- if (user->scopes) _grantedScopes = *user->scopes;
121
- saveToCache(_currentUser);
82
+ if (user) {
83
+ if (user->scopes) {
84
+ _grantedScopes = *user->scopes;
85
+ } else {
86
+ _grantedScopes.clear();
87
+ }
88
+ } else {
89
+ _grantedScopes.clear();
90
+ }
122
91
  }
92
+ // Always resolve - no session is not an error, just means user is logged out
123
93
  notifyAuthStateChanged();
124
94
  promise->resolve();
125
95
  });
126
96
 
127
- silentPromise->addOnRejectedListener([promise](const std::exception_ptr& error) {
128
- promise->reject(error);
97
+ silentPromise->addOnRejectedListener([promise](const std::exception_ptr&) {
98
+ // Silently ignore errors during restore - user will be logged out
99
+ promise->resolve();
129
100
  });
130
101
  return promise;
131
102
  }
@@ -138,11 +109,18 @@ std::shared_ptr<Promise<void>> HybridAuth::login(AuthProvider provider, const st
138
109
  {
139
110
  std::lock_guard<std::mutex> lock(_mutex);
140
111
  _currentUser = user;
141
- if (options && options->scopes) {
112
+ if (user.scopes && !user.scopes->empty()) {
113
+ _grantedScopes = *user.scopes;
114
+ } else if (options && options->scopes && !options->scopes->empty()) {
142
115
  _grantedScopes = *options->scopes;
116
+ } else {
117
+ _grantedScopes.clear();
118
+ }
119
+ if (_currentUser) {
120
+ _currentUser->scopes = _grantedScopes.empty()
121
+ ? std::nullopt
122
+ : std::make_optional(_grantedScopes);
143
123
  }
144
- if (_currentUser) _currentUser->scopes = _grantedScopes;
145
- saveToCache(_currentUser);
146
124
  }
147
125
  notifyAuthStateChanged();
148
126
  promise->resolve();
@@ -167,7 +145,6 @@ std::shared_ptr<Promise<void>> HybridAuth::requestScopes(const std::vector<std::
167
145
  }
168
146
  }
169
147
  if (_currentUser) _currentUser->scopes = _grantedScopes;
170
- saveToCache(_currentUser);
171
148
  }
172
149
  notifyAuthStateChanged();
173
150
  promise->resolve();
@@ -192,7 +169,6 @@ std::shared_ptr<Promise<void>> HybridAuth::revokeScopes(const std::vector<std::s
192
169
  );
193
170
  if (_currentUser) {
194
171
  _currentUser->scopes = _grantedScopes;
195
- saveToCache(_currentUser);
196
172
  }
197
173
  }
198
174
  notifyAuthStateChanged();
@@ -233,15 +209,36 @@ std::shared_ptr<Promise<std::optional<std::string>>> HybridAuth::getAccessToken(
233
209
  }
234
210
 
235
211
  std::shared_ptr<Promise<AuthTokens>> HybridAuth::refreshToken() {
236
- auto promise = Promise<AuthTokens>::create();
212
+ std::shared_ptr<Promise<AuthTokens>> promise;
213
+ {
214
+ std::lock_guard<std::mutex> lock(_mutex);
215
+ if (_refreshInFlight) {
216
+ return _refreshInFlight;
217
+ }
218
+ promise = Promise<AuthTokens>::create();
219
+ _refreshInFlight = promise;
220
+ }
221
+
237
222
  auto refreshPromise = PlatformAuth::refreshToken();
238
223
  refreshPromise->addOnResolvedListener([this, promise](const AuthTokens& tokens) {
239
224
  {
240
225
  std::lock_guard<std::mutex> lock(_mutex);
241
226
  if (_currentUser) {
242
- _currentUser->accessToken = tokens.accessToken;
243
- _currentUser->idToken = tokens.idToken;
244
- saveToCache(_currentUser);
227
+ if (tokens.accessToken.has_value()) {
228
+ _currentUser->accessToken = tokens.accessToken;
229
+ }
230
+ if (tokens.idToken.has_value()) {
231
+ _currentUser->idToken = tokens.idToken;
232
+ }
233
+ if (tokens.refreshToken.has_value()) {
234
+ _currentUser->refreshToken = tokens.refreshToken;
235
+ }
236
+ if (tokens.expirationTime.has_value()) {
237
+ _currentUser->expirationTime = tokens.expirationTime;
238
+ }
239
+ }
240
+ if (_refreshInFlight == promise) {
241
+ _refreshInFlight = nullptr;
245
242
  }
246
243
  }
247
244
  notifyTokensRefreshed(tokens);
@@ -249,7 +246,13 @@ std::shared_ptr<Promise<AuthTokens>> HybridAuth::refreshToken() {
249
246
  promise->resolve(tokens);
250
247
  });
251
248
 
252
- refreshPromise->addOnRejectedListener([promise](const std::exception_ptr& error) {
249
+ refreshPromise->addOnRejectedListener([this, promise](const std::exception_ptr& error) {
250
+ {
251
+ std::lock_guard<std::mutex> lock(_mutex);
252
+ if (_refreshInFlight == promise) {
253
+ _refreshInFlight = nullptr;
254
+ }
255
+ }
253
256
  promise->reject(error);
254
257
  });
255
258
  return promise;
@@ -259,15 +262,6 @@ void HybridAuth::setLoggingEnabled(bool enabled) {
259
262
  sLoggingEnabled = enabled;
260
263
  }
261
264
 
262
- void HybridAuth::setStorageAdapter(const std::optional<std::shared_ptr<HybridAuthStorageAdapterSpec>>& adapter) {
263
- std::lock_guard<std::mutex> lock(_mutex);
264
- _storageAdapter = adapter.value_or(nullptr);
265
- if (_storageAdapter) {
266
- loadFromCache();
267
- notifyAuthStateChanged();
268
- }
269
- }
270
-
271
265
  void HybridAuth::notifyTokensRefreshed(const AuthTokens& tokens) {
272
266
  std::vector<std::function<void(const AuthTokens&)>> listeners;
273
267
  {
@@ -31,11 +31,10 @@ public:
31
31
  std::function<void()> onAuthStateChanged(const std::function<void(const std::optional<AuthUser>&)>& callback) override;
32
32
  std::function<void()> onTokensRefreshed(const std::function<void(const AuthTokens&)>& callback) override;
33
33
  void setLoggingEnabled(bool enabled) override;
34
- void setStorageAdapter(const std::optional<std::shared_ptr<HybridAuthStorageAdapterSpec>>& adapter) override;
34
+ // Note: setStorageAdapter is kept internally but not exposed in public API
35
+ // Storage is in-memory only by default
35
36
 
36
37
  private:
37
- void loadFromCache();
38
- void saveToCache(const std::optional<AuthUser>& user);
39
38
  void notifyAuthStateChanged();
40
39
  void notifyTokensRefreshed(const AuthTokens& tokens);
41
40
 
@@ -45,9 +44,9 @@ private:
45
44
  std::map<int, std::function<void(const std::optional<AuthUser>&)>> _listeners;
46
45
  int _nextListenerId = 0;
47
46
 
48
- std::shared_ptr<HybridAuthStorageAdapterSpec> _storageAdapter;
49
47
  std::map<int, std::function<void(const AuthTokens&)>> _tokenListeners;
50
48
  int _nextTokenListenerId = 0;
49
+ std::shared_ptr<Promise<AuthTokens>> _refreshInFlight;
51
50
 
52
51
  std::mutex _mutex;
53
52
 
@@ -7,6 +7,11 @@ import CommonCrypto
7
7
 
8
8
  @objc
9
9
  public class AuthAdapter: NSObject {
10
+ private static let defaultMicrosoftScopes = ["openid", "email", "profile", "offline_access", "User.Read"]
11
+ private static var inMemoryMicrosoftRefreshToken: String?
12
+ private static var inMemoryMicrosoftScopes: [String] = defaultMicrosoftScopes
13
+ private static var inMemoryGoogleServerAuthCode: String?
14
+
10
15
  @objc
11
16
  public static func login(provider: String, scopes: [String], loginHint: String?, useSheet: Bool, forceAccountPicker: Bool = false, tenant: String? = nil, prompt: String? = nil, completion: @escaping (NSDictionary?, String?) -> Void) {
12
17
  if provider == "google" {
@@ -258,9 +263,9 @@ public class AuthAdapter: NSObject {
258
263
  let expirationTime = Date().timeIntervalSince1970 * 1000 + expiresIn * 1000
259
264
 
260
265
  if !refreshToken.isEmpty {
261
- KeychainStore.set(refreshToken, for: "nitro_auth_microsoft_refresh_token")
266
+ inMemoryMicrosoftRefreshToken = refreshToken
262
267
  }
263
- UserDefaults.standard.set(scopes, forKey: "nitro_auth_microsoft_scopes")
268
+ inMemoryMicrosoftScopes = scopes.isEmpty ? defaultMicrosoftScopes : scopes
264
269
 
265
270
  let resultData: [String: Any] = [
266
271
  "provider": "microsoft",
@@ -283,6 +288,8 @@ public class AuthAdapter: NSObject {
283
288
  guard parts.count >= 2 else { return [:] }
284
289
 
285
290
  var base64 = parts[1]
291
+ .replacingOccurrences(of: "-", with: "+")
292
+ .replacingOccurrences(of: "_", with: "/")
286
293
  let remainder = base64.count % 4
287
294
  if remainder > 0 {
288
295
  base64 += String(repeating: "=", count: 4 - remainder)
@@ -313,6 +320,9 @@ public class AuthAdapter: NSObject {
313
320
  return
314
321
  }
315
322
 
323
+ let serverAuthCode = result?.serverAuthCode ?? ""
324
+ inMemoryGoogleServerAuthCode = serverAuthCode.isEmpty ? nil : serverAuthCode
325
+
316
326
  let data: [String: Any] = [
317
327
  "provider": "google",
318
328
  "email": user.profile?.email ?? "",
@@ -320,7 +330,7 @@ public class AuthAdapter: NSObject {
320
330
  "photo": user.profile?.imageURL(withDimension: 300)?.absoluteString ?? "",
321
331
  "idToken": user.idToken?.tokenString ?? "",
322
332
  "accessToken": user.accessToken.tokenString,
323
- "serverAuthCode": result?.serverAuthCode ?? "",
333
+ "serverAuthCode": serverAuthCode,
324
334
  "expirationTime": (user.accessToken.expirationDate?.timeIntervalSince1970 ?? 0) * 1000,
325
335
  "underlyingError": ""
326
336
  ]
@@ -353,12 +363,11 @@ public class AuthAdapter: NSObject {
353
363
  }
354
364
  return
355
365
  }
356
- guard getStoredMicrosoftRefreshToken() != nil else {
366
+ guard inMemoryMicrosoftRefreshToken != nil else {
357
367
  completion(nil, "No user logged in")
358
368
  return
359
369
  }
360
- let storedScopes = UserDefaults.standard.array(forKey: "nitro_auth_microsoft_scopes") as? [String] ?? ["openid", "email", "profile", "offline_access", "User.Read"]
361
- let mergedScopes = Array(Set(storedScopes + scopes))
370
+ let mergedScopes = Array(Set(inMemoryMicrosoftScopes + scopes))
362
371
  loginMicrosoft(scopes: mergedScopes, loginHint: nil, tenant: nil, prompt: nil, completion: completion)
363
372
  }
364
373
 
@@ -399,7 +408,7 @@ public class AuthAdapter: NSObject {
399
408
  "photo": user.profile?.imageURL(withDimension: 300)?.absoluteString ?? "",
400
409
  "idToken": user.idToken?.tokenString ?? "",
401
410
  "accessToken": user.accessToken.tokenString,
402
- "serverAuthCode": "",
411
+ "serverAuthCode": inMemoryGoogleServerAuthCode ?? "",
403
412
  "expirationTime": (user.accessToken.expirationDate?.timeIntervalSince1970 ?? 0) * 1000
404
413
  ]
405
414
  completion(data as NSDictionary)
@@ -413,7 +422,7 @@ public class AuthAdapter: NSObject {
413
422
  }
414
423
 
415
424
  private static func tryMicrosoftSilentRefresh(completion: @escaping (NSDictionary?) -> Void) {
416
- guard let refreshToken = getStoredMicrosoftRefreshToken() else {
425
+ guard let refreshToken = inMemoryMicrosoftRefreshToken else {
417
426
  completion(nil)
418
427
  return
419
428
  }
@@ -459,7 +468,7 @@ public class AuthAdapter: NSObject {
459
468
  let expirationTime = Date().timeIntervalSince1970 * 1000 + expiresIn * 1000
460
469
 
461
470
  if !newRefreshToken.isEmpty {
462
- KeychainStore.set(newRefreshToken, for: "nitro_auth_microsoft_refresh_token")
471
+ inMemoryMicrosoftRefreshToken = newRefreshToken
463
472
  }
464
473
 
465
474
  let resultData: [String: Any] = [
@@ -478,7 +487,7 @@ public class AuthAdapter: NSObject {
478
487
  }
479
488
 
480
489
  private static func tryMicrosoftRefreshForTokenRefresh(completion: @escaping (NSDictionary?, String?) -> Void) {
481
- guard let refreshToken = getStoredMicrosoftRefreshToken() else {
490
+ guard let refreshToken = inMemoryMicrosoftRefreshToken else {
482
491
  completion(nil, "No user logged in")
483
492
  return
484
493
  }
@@ -523,7 +532,7 @@ public class AuthAdapter: NSObject {
523
532
  let expiresIn = json["expires_in"] as? Double ?? 0
524
533
  let expirationTime = Date().timeIntervalSince1970 * 1000 + expiresIn * 1000
525
534
  if !newRefreshToken.isEmpty {
526
- KeychainStore.set(newRefreshToken, for: "nitro_auth_microsoft_refresh_token")
535
+ inMemoryMicrosoftRefreshToken = newRefreshToken
527
536
  }
528
537
  let tokensData: [String: Any] = [
529
538
  "accessToken": accessToken,
@@ -551,20 +560,9 @@ public class AuthAdapter: NSObject {
551
560
  @objc
552
561
  public static func logout() {
553
562
  GIDSignIn.sharedInstance.signOut()
554
- KeychainStore.remove("nitro_auth_microsoft_refresh_token")
555
- UserDefaults.standard.removeObject(forKey: "nitro_auth_microsoft_scopes")
556
- }
557
-
558
- private static func getStoredMicrosoftRefreshToken() -> String? {
559
- if let token = KeychainStore.get("nitro_auth_microsoft_refresh_token") {
560
- return token
561
- }
562
- if let legacy = UserDefaults.standard.string(forKey: "nitro_auth_microsoft_refresh_token") {
563
- KeychainStore.set(legacy, for: "nitro_auth_microsoft_refresh_token")
564
- UserDefaults.standard.removeObject(forKey: "nitro_auth_microsoft_refresh_token")
565
- return legacy
566
- }
567
- return nil
563
+ inMemoryMicrosoftRefreshToken = nil
564
+ inMemoryMicrosoftScopes = defaultMicrosoftScopes
565
+ inMemoryGoogleServerAuthCode = nil
568
566
  }
569
567
  }
570
568