react-native-nitro-auth 0.5.4 → 0.5.6

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 (100) hide show
  1. package/README.md +82 -47
  2. package/android/proguard-rules.pro +7 -1
  3. package/android/src/main/AndroidManifest.xml +12 -0
  4. package/android/src/main/cpp/JniOnLoad.cpp +3 -1
  5. package/android/src/main/cpp/PlatformAuth+Android.cpp +271 -78
  6. package/android/src/main/java/com/auth/AuthAdapter.kt +293 -238
  7. package/android/src/main/java/com/auth/GoogleSignInActivity.kt +9 -5
  8. package/android/src/main/java/com/auth/NitroAuthModule.kt +8 -1
  9. package/cpp/HybridAuth.cpp +79 -64
  10. package/cpp/HybridAuth.hpp +9 -7
  11. package/cpp/JSONSerializer.hpp +3 -0
  12. package/ios/AuthAdapter.swift +226 -79
  13. package/ios/PlatformAuth+iOS.mm +10 -3
  14. package/lib/commonjs/Auth.web.js +50 -10
  15. package/lib/commonjs/Auth.web.js.map +1 -1
  16. package/lib/commonjs/index.js +23 -1
  17. package/lib/commonjs/index.js.map +1 -1
  18. package/lib/commonjs/index.web.js +30 -12
  19. package/lib/commonjs/index.web.js.map +1 -1
  20. package/lib/commonjs/service.js +36 -9
  21. package/lib/commonjs/service.js.map +1 -1
  22. package/lib/commonjs/service.web.js +65 -13
  23. package/lib/commonjs/service.web.js.map +1 -1
  24. package/lib/commonjs/ui/social-button.js +19 -14
  25. package/lib/commonjs/ui/social-button.js.map +1 -1
  26. package/lib/commonjs/ui/social-button.web.js +16 -10
  27. package/lib/commonjs/ui/social-button.web.js.map +1 -1
  28. package/lib/commonjs/use-auth.js +22 -25
  29. package/lib/commonjs/use-auth.js.map +1 -1
  30. package/lib/commonjs/utils/auth-error.js +37 -0
  31. package/lib/commonjs/utils/auth-error.js.map +1 -0
  32. package/lib/commonjs/utils/logger.js +1 -0
  33. package/lib/commonjs/utils/logger.js.map +1 -1
  34. package/lib/module/Auth.web.js +50 -10
  35. package/lib/module/Auth.web.js.map +1 -1
  36. package/lib/module/global.d.js.map +1 -1
  37. package/lib/module/index.js +1 -0
  38. package/lib/module/index.js.map +1 -1
  39. package/lib/module/index.web.js +2 -1
  40. package/lib/module/index.web.js.map +1 -1
  41. package/lib/module/service.js +36 -9
  42. package/lib/module/service.js.map +1 -1
  43. package/lib/module/service.web.js +65 -13
  44. package/lib/module/service.web.js.map +1 -1
  45. package/lib/module/ui/social-button.js +19 -14
  46. package/lib/module/ui/social-button.js.map +1 -1
  47. package/lib/module/ui/social-button.web.js +16 -10
  48. package/lib/module/ui/social-button.web.js.map +1 -1
  49. package/lib/module/use-auth.js +22 -25
  50. package/lib/module/use-auth.js.map +1 -1
  51. package/lib/module/utils/auth-error.js +30 -0
  52. package/lib/module/utils/auth-error.js.map +1 -0
  53. package/lib/module/utils/logger.js +1 -0
  54. package/lib/module/utils/logger.js.map +1 -1
  55. package/lib/typescript/commonjs/Auth.web.d.ts +5 -1
  56. package/lib/typescript/commonjs/Auth.web.d.ts.map +1 -1
  57. package/lib/typescript/commonjs/index.d.ts +1 -0
  58. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  59. package/lib/typescript/commonjs/index.web.d.ts +2 -1
  60. package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
  61. package/lib/typescript/commonjs/service.d.ts.map +1 -1
  62. package/lib/typescript/commonjs/service.web.d.ts +2 -18
  63. package/lib/typescript/commonjs/service.web.d.ts.map +1 -1
  64. package/lib/typescript/commonjs/ui/social-button.d.ts.map +1 -1
  65. package/lib/typescript/commonjs/ui/social-button.web.d.ts.map +1 -1
  66. package/lib/typescript/commonjs/use-auth.d.ts +2 -1
  67. package/lib/typescript/commonjs/use-auth.d.ts.map +1 -1
  68. package/lib/typescript/commonjs/utils/auth-error.d.ts +16 -0
  69. package/lib/typescript/commonjs/utils/auth-error.d.ts.map +1 -0
  70. package/lib/typescript/commonjs/utils/logger.d.ts.map +1 -1
  71. package/lib/typescript/module/Auth.web.d.ts +5 -1
  72. package/lib/typescript/module/Auth.web.d.ts.map +1 -1
  73. package/lib/typescript/module/index.d.ts +1 -0
  74. package/lib/typescript/module/index.d.ts.map +1 -1
  75. package/lib/typescript/module/index.web.d.ts +2 -1
  76. package/lib/typescript/module/index.web.d.ts.map +1 -1
  77. package/lib/typescript/module/service.d.ts.map +1 -1
  78. package/lib/typescript/module/service.web.d.ts +2 -18
  79. package/lib/typescript/module/service.web.d.ts.map +1 -1
  80. package/lib/typescript/module/ui/social-button.d.ts.map +1 -1
  81. package/lib/typescript/module/ui/social-button.web.d.ts.map +1 -1
  82. package/lib/typescript/module/use-auth.d.ts +2 -1
  83. package/lib/typescript/module/use-auth.d.ts.map +1 -1
  84. package/lib/typescript/module/utils/auth-error.d.ts +16 -0
  85. package/lib/typescript/module/utils/auth-error.d.ts.map +1 -0
  86. package/lib/typescript/module/utils/logger.d.ts.map +1 -1
  87. package/nitrogen/generated/android/NitroAuthOnLoad.cpp +22 -17
  88. package/nitrogen/generated/android/NitroAuthOnLoad.hpp +13 -4
  89. package/package.json +8 -10
  90. package/src/Auth.web.ts +77 -11
  91. package/src/global.d.ts +0 -11
  92. package/src/index.ts +5 -0
  93. package/src/index.web.ts +6 -1
  94. package/src/service.ts +37 -9
  95. package/src/service.web.ts +84 -15
  96. package/src/ui/social-button.tsx +21 -9
  97. package/src/ui/social-button.web.tsx +17 -4
  98. package/src/use-auth.ts +29 -67
  99. package/src/utils/auth-error.ts +49 -0
  100. package/src/utils/logger.ts +1 -0
@@ -21,13 +21,15 @@ class GoogleSignInActivity : ComponentActivity() {
21
21
  private const val EXTRA_SCOPES = "scopes"
22
22
  private const val EXTRA_LOGIN_HINT = "login_hint"
23
23
  private const val EXTRA_FORCE_PICKER = "force_picker"
24
-
25
- fun createIntent(context: Context, clientId: String, scopes: Array<String>, loginHint: String?, forcePicker: Boolean = false): Intent {
24
+ private const val EXTRA_ORIGIN = "origin"
25
+
26
+ fun createIntent(context: Context, clientId: String, scopes: Array<String>, loginHint: String?, forcePicker: Boolean = false, origin: String = "login"): Intent {
26
27
  return Intent(context, GoogleSignInActivity::class.java).apply {
27
28
  putExtra(EXTRA_CLIENT_ID, clientId)
28
29
  putExtra(EXTRA_SCOPES, scopes)
29
30
  putExtra(EXTRA_LOGIN_HINT, loginHint)
30
31
  putExtra(EXTRA_FORCE_PICKER, forcePicker)
32
+ putExtra(EXTRA_ORIGIN, origin)
31
33
  addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
32
34
  }
33
35
  }
@@ -36,13 +38,14 @@ class GoogleSignInActivity : ComponentActivity() {
36
38
  private val signInLauncher = registerForActivityResult(
37
39
  ActivityResultContracts.StartActivityForResult()
38
40
  ) { result ->
41
+ val origin = intent.getStringExtra(EXTRA_ORIGIN) ?: "login"
39
42
  try {
40
43
  val task = GoogleSignIn.getSignedInAccountFromIntent(result.data)
41
44
  val account = task.getResult(ApiException::class.java)
42
45
  val scopes = intent.getStringArrayExtra(EXTRA_SCOPES)?.toList() ?: emptyList()
43
- AuthAdapter.onSignInSuccess(account, scopes)
46
+ AuthAdapter.onSignInSuccess(account, scopes, origin)
44
47
  } catch (e: ApiException) {
45
- AuthAdapter.onSignInError(e.statusCode, e.message)
48
+ AuthAdapter.onSignInError(e.statusCode, e.message, origin)
46
49
  }
47
50
  finish()
48
51
  }
@@ -54,8 +57,9 @@ class GoogleSignInActivity : ComponentActivity() {
54
57
  val loginHint = intent.getStringExtra(EXTRA_LOGIN_HINT)
55
58
  val forcePicker = intent.getBooleanExtra(EXTRA_FORCE_PICKER, false)
56
59
 
60
+ val origin = intent.getStringExtra(EXTRA_ORIGIN) ?: "login"
57
61
  if (clientId == null) {
58
- AuthAdapter.onSignInError(8, "Missing client ID")
62
+ AuthAdapter.onSignInError(8, "Missing client ID", origin)
59
63
  finish()
60
64
  return
61
65
  }
@@ -3,17 +3,24 @@ package com.auth
3
3
  import android.util.Log
4
4
  import com.facebook.react.bridge.ReactApplicationContext
5
5
  import com.facebook.react.bridge.ReactContextBaseJavaModule
6
+ import com.margelo.nitro.com.auth.NitroAuthOnLoad
6
7
 
7
8
  class NitroAuthModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
8
9
  override fun getName(): String = "NitroAuthModule"
9
10
 
10
11
  init {
11
12
  try {
13
+ // Load the native library first so that AuthAdapter's JNI methods are resolvable.
14
+ NitroAuthOnLoad.initializeNative()
12
15
  AuthAdapter.initialize(reactContext)
13
- com.margelo.nitro.com.auth.NitroAuthOnLoad.initializeNative()
14
16
  Log.d("NitroAuthModule", "NitroAuth initialized")
15
17
  } catch (e: Exception) {
16
18
  Log.e("NitroAuthModule", "Failed to initialize NitroAuth", e)
17
19
  }
18
20
  }
21
+
22
+ override fun invalidate() {
23
+ super.invalidate()
24
+ AuthAdapter.dispose()
25
+ }
19
26
  }
@@ -1,23 +1,21 @@
1
1
  #include "HybridAuth.hpp"
2
2
  #include "PlatformAuth.hpp"
3
- #include <NitroModules/NitroLogger.hpp>
3
+ #include <algorithm>
4
4
  #include <chrono>
5
5
 
6
6
  namespace margelo::nitro::NitroAuth {
7
-
8
- bool HybridAuth::sLoggingEnabled = false;
9
7
 
10
8
  HybridAuth::HybridAuth() : HybridObject(TAG) {
11
9
  // In-memory only - no internal persistence.
12
10
  }
13
11
 
14
12
  std::optional<AuthUser> HybridAuth::getCurrentUser() {
15
- std::lock_guard<std::mutex> lock(_mutex);
13
+ std::lock_guard<std::recursive_mutex> lock(_mutex);
16
14
  return _currentUser;
17
15
  }
18
16
 
19
17
  std::vector<std::string> HybridAuth::getGrantedScopes() {
20
- std::lock_guard<std::mutex> lock(_mutex);
18
+ std::lock_guard<std::recursive_mutex> lock(_mutex);
21
19
  return _grantedScopes;
22
20
  }
23
21
 
@@ -29,7 +27,7 @@ void HybridAuth::notifyAuthStateChanged() {
29
27
  std::optional<AuthUser> user;
30
28
  std::vector<std::function<void(const std::optional<AuthUser>&)>> listeners;
31
29
  {
32
- std::lock_guard<std::mutex> lock(_mutex);
30
+ std::lock_guard<std::recursive_mutex> lock(_mutex);
33
31
  user = _currentUser;
34
32
  for (auto const& [id, listener] : _listeners) {
35
33
  listeners.push_back(listener);
@@ -41,30 +39,38 @@ void HybridAuth::notifyAuthStateChanged() {
41
39
  }
42
40
 
43
41
  std::function<void()> HybridAuth::onAuthStateChanged(const std::function<void(const std::optional<AuthUser>&)>& callback) {
44
- std::lock_guard<std::mutex> lock(_mutex);
45
- int id = _nextListenerId++;
42
+ std::lock_guard<std::recursive_mutex> lock(_mutex);
43
+ uint64_t id = _nextListenerId++;
46
44
  _listeners[id] = callback;
47
45
 
48
- return [this, id]() {
49
- std::lock_guard<std::mutex> lock(_mutex);
50
- _listeners.erase(id);
46
+ auto weak = weak_from_this();
47
+ return [weak, id]() {
48
+ auto self = weak.lock();
49
+ if (!self) return;
50
+ auto* auth = dynamic_cast<HybridAuth*>(self.get());
51
+ std::lock_guard<std::recursive_mutex> lock(auth->_mutex);
52
+ auth->_listeners.erase(id);
51
53
  };
52
54
  }
53
55
 
54
56
  std::function<void()> HybridAuth::onTokensRefreshed(const std::function<void(const AuthTokens&)>& callback) {
55
- std::lock_guard<std::mutex> lock(_mutex);
56
- int id = _nextTokenListenerId++;
57
+ std::lock_guard<std::recursive_mutex> lock(_mutex);
58
+ uint64_t id = _nextTokenListenerId++;
57
59
  _tokenListeners[id] = callback;
58
-
59
- return [this, id]() {
60
- std::lock_guard<std::mutex> lock(_mutex);
61
- _tokenListeners.erase(id);
60
+
61
+ auto weak = weak_from_this();
62
+ return [weak, id]() {
63
+ auto self = weak.lock();
64
+ if (!self) return;
65
+ auto* auth = dynamic_cast<HybridAuth*>(self.get());
66
+ std::lock_guard<std::recursive_mutex> lock(auth->_mutex);
67
+ auth->_tokenListeners.erase(id);
62
68
  };
63
69
  }
64
70
 
65
71
  void HybridAuth::logout() {
66
72
  {
67
- std::lock_guard<std::mutex> lock(_mutex);
73
+ std::lock_guard<std::recursive_mutex> lock(_mutex);
68
74
  _currentUser = std::nullopt;
69
75
  _grantedScopes.clear();
70
76
  }
@@ -75,22 +81,24 @@ void HybridAuth::logout() {
75
81
  std::shared_ptr<Promise<void>> HybridAuth::silentRestore() {
76
82
  auto promise = Promise<void>::create();
77
83
  auto silentPromise = PlatformAuth::silentRestore();
78
- silentPromise->addOnResolvedListener([this, promise](const std::optional<AuthUser>& user) {
84
+ auto self = shared_from_this();
85
+ silentPromise->addOnResolvedListener([self, promise](const std::optional<AuthUser>& user) {
86
+ auto* auth = dynamic_cast<HybridAuth*>(self.get());
79
87
  {
80
- std::lock_guard<std::mutex> lock(_mutex);
81
- _currentUser = user;
88
+ std::lock_guard<std::recursive_mutex> lock(auth->_mutex);
89
+ auth->_currentUser = user;
82
90
  if (user) {
83
91
  if (user->scopes) {
84
- _grantedScopes = *user->scopes;
92
+ auth->_grantedScopes = *user->scopes;
85
93
  } else {
86
- _grantedScopes.clear();
94
+ auth->_grantedScopes.clear();
87
95
  }
88
96
  } else {
89
- _grantedScopes.clear();
97
+ auth->_grantedScopes.clear();
90
98
  }
91
99
  }
92
100
  // Always resolve - no session is not an error, just means user is logged out
93
- notifyAuthStateChanged();
101
+ auth->notifyAuthStateChanged();
94
102
  promise->resolve();
95
103
  });
96
104
 
@@ -104,25 +112,27 @@ std::shared_ptr<Promise<void>> HybridAuth::silentRestore() {
104
112
  std::shared_ptr<Promise<void>> HybridAuth::login(AuthProvider provider, const std::optional<LoginOptions>& options) {
105
113
  auto promise = Promise<void>::create();
106
114
 
115
+ auto self = shared_from_this();
107
116
  auto loginPromise = PlatformAuth::login(provider, options);
108
- loginPromise->addOnResolvedListener([this, promise, options](const AuthUser& user) {
117
+ loginPromise->addOnResolvedListener([self, promise, options](const AuthUser& user) {
118
+ auto* auth = dynamic_cast<HybridAuth*>(self.get());
109
119
  {
110
- std::lock_guard<std::mutex> lock(_mutex);
111
- _currentUser = user;
120
+ std::lock_guard<std::recursive_mutex> lock(auth->_mutex);
121
+ auth->_currentUser = user;
112
122
  if (user.scopes && !user.scopes->empty()) {
113
- _grantedScopes = *user.scopes;
123
+ auth->_grantedScopes = *user.scopes;
114
124
  } else if (options && options->scopes && !options->scopes->empty()) {
115
- _grantedScopes = *options->scopes;
125
+ auth->_grantedScopes = *options->scopes;
116
126
  } else {
117
- _grantedScopes.clear();
127
+ auth->_grantedScopes.clear();
118
128
  }
119
- if (_currentUser) {
120
- _currentUser->scopes = _grantedScopes.empty()
129
+ if (auth->_currentUser) {
130
+ auth->_currentUser->scopes = auth->_grantedScopes.empty()
121
131
  ? std::nullopt
122
- : std::make_optional(_grantedScopes);
132
+ : std::make_optional(auth->_grantedScopes);
123
133
  }
124
134
  }
125
- notifyAuthStateChanged();
135
+ auth->notifyAuthStateChanged();
126
136
  promise->resolve();
127
137
  });
128
138
 
@@ -134,19 +144,21 @@ std::shared_ptr<Promise<void>> HybridAuth::login(AuthProvider provider, const st
134
144
 
135
145
  std::shared_ptr<Promise<void>> HybridAuth::requestScopes(const std::vector<std::string>& scopes) {
136
146
  auto promise = Promise<void>::create();
147
+ auto self = shared_from_this();
137
148
  auto requestPromise = PlatformAuth::requestScopes(scopes);
138
- requestPromise->addOnResolvedListener([this, promise, scopes](const AuthUser& user) {
149
+ requestPromise->addOnResolvedListener([self, promise, scopes](const AuthUser& user) {
150
+ auto* auth = dynamic_cast<HybridAuth*>(self.get());
139
151
  {
140
- std::lock_guard<std::mutex> lock(_mutex);
141
- _currentUser = user;
152
+ std::lock_guard<std::recursive_mutex> lock(auth->_mutex);
153
+ auth->_currentUser = user;
142
154
  for (const auto& scope : scopes) {
143
- if (std::find(_grantedScopes.begin(), _grantedScopes.end(), scope) == _grantedScopes.end()) {
144
- _grantedScopes.push_back(scope);
155
+ if (std::find(auth->_grantedScopes.begin(), auth->_grantedScopes.end(), scope) == auth->_grantedScopes.end()) {
156
+ auth->_grantedScopes.push_back(scope);
145
157
  }
146
158
  }
147
- if (_currentUser) _currentUser->scopes = _grantedScopes;
159
+ if (auth->_currentUser) auth->_currentUser->scopes = auth->_grantedScopes;
148
160
  }
149
- notifyAuthStateChanged();
161
+ auth->notifyAuthStateChanged();
150
162
  promise->resolve();
151
163
  });
152
164
 
@@ -159,7 +171,7 @@ std::shared_ptr<Promise<void>> HybridAuth::requestScopes(const std::vector<std::
159
171
  std::shared_ptr<Promise<void>> HybridAuth::revokeScopes(const std::vector<std::string>& scopes) {
160
172
  auto promise = Promise<void>::create();
161
173
  {
162
- std::lock_guard<std::mutex> lock(_mutex);
174
+ std::lock_guard<std::recursive_mutex> lock(_mutex);
163
175
  _grantedScopes.erase(
164
176
  std::remove_if(_grantedScopes.begin(), _grantedScopes.end(),
165
177
  [&scopes](const std::string& s) {
@@ -180,7 +192,7 @@ std::shared_ptr<Promise<std::optional<std::string>>> HybridAuth::getAccessToken(
180
192
  auto promise = Promise<std::optional<std::string>>::create();
181
193
  bool needsRefresh = false;
182
194
  {
183
- std::lock_guard<std::mutex> lock(_mutex);
195
+ std::lock_guard<std::recursive_mutex> lock(_mutex);
184
196
  if (_currentUser && _currentUser->accessToken) {
185
197
  if (_currentUser->expirationTime) {
186
198
  auto now = std::chrono::system_clock::now().time_since_epoch() / std::chrono::milliseconds(1);
@@ -211,7 +223,7 @@ std::shared_ptr<Promise<std::optional<std::string>>> HybridAuth::getAccessToken(
211
223
  std::shared_ptr<Promise<AuthTokens>> HybridAuth::refreshToken() {
212
224
  std::shared_ptr<Promise<AuthTokens>> promise;
213
225
  {
214
- std::lock_guard<std::mutex> lock(_mutex);
226
+ std::lock_guard<std::recursive_mutex> lock(_mutex);
215
227
  if (_refreshInFlight) {
216
228
  return _refreshInFlight;
217
229
  }
@@ -219,38 +231,41 @@ std::shared_ptr<Promise<AuthTokens>> HybridAuth::refreshToken() {
219
231
  _refreshInFlight = promise;
220
232
  }
221
233
 
234
+ auto self = shared_from_this();
222
235
  auto refreshPromise = PlatformAuth::refreshToken();
223
- refreshPromise->addOnResolvedListener([this, promise](const AuthTokens& tokens) {
236
+ refreshPromise->addOnResolvedListener([self, promise](const AuthTokens& tokens) {
237
+ auto* auth = dynamic_cast<HybridAuth*>(self.get());
224
238
  {
225
- std::lock_guard<std::mutex> lock(_mutex);
226
- if (_currentUser) {
239
+ std::lock_guard<std::recursive_mutex> lock(auth->_mutex);
240
+ if (auth->_currentUser) {
227
241
  if (tokens.accessToken.has_value()) {
228
- _currentUser->accessToken = tokens.accessToken;
242
+ auth->_currentUser->accessToken = tokens.accessToken;
229
243
  }
230
244
  if (tokens.idToken.has_value()) {
231
- _currentUser->idToken = tokens.idToken;
245
+ auth->_currentUser->idToken = tokens.idToken;
232
246
  }
233
247
  if (tokens.refreshToken.has_value()) {
234
- _currentUser->refreshToken = tokens.refreshToken;
248
+ auth->_currentUser->refreshToken = tokens.refreshToken;
235
249
  }
236
250
  if (tokens.expirationTime.has_value()) {
237
- _currentUser->expirationTime = tokens.expirationTime;
251
+ auth->_currentUser->expirationTime = tokens.expirationTime;
238
252
  }
239
253
  }
240
- if (_refreshInFlight == promise) {
241
- _refreshInFlight = nullptr;
254
+ if (auth->_refreshInFlight == promise) {
255
+ auth->_refreshInFlight = nullptr;
242
256
  }
243
257
  }
244
- notifyTokensRefreshed(tokens);
245
- notifyAuthStateChanged();
258
+ auth->notifyTokensRefreshed(tokens);
259
+ auth->notifyAuthStateChanged();
246
260
  promise->resolve(tokens);
247
261
  });
248
-
249
- refreshPromise->addOnRejectedListener([this, promise](const std::exception_ptr& error) {
262
+
263
+ refreshPromise->addOnRejectedListener([self, promise](const std::exception_ptr& error) {
264
+ auto* auth = dynamic_cast<HybridAuth*>(self.get());
250
265
  {
251
- std::lock_guard<std::mutex> lock(_mutex);
252
- if (_refreshInFlight == promise) {
253
- _refreshInFlight = nullptr;
266
+ std::lock_guard<std::recursive_mutex> lock(auth->_mutex);
267
+ if (auth->_refreshInFlight == promise) {
268
+ auth->_refreshInFlight = nullptr;
254
269
  }
255
270
  }
256
271
  promise->reject(error);
@@ -258,14 +273,14 @@ std::shared_ptr<Promise<AuthTokens>> HybridAuth::refreshToken() {
258
273
  return promise;
259
274
  }
260
275
 
261
- void HybridAuth::setLoggingEnabled(bool enabled) {
262
- sLoggingEnabled = enabled;
276
+ void HybridAuth::setLoggingEnabled(bool /* enabled */) {
277
+ // Reserved for future use — logging not yet implemented in C++ layer
263
278
  }
264
279
 
265
280
  void HybridAuth::notifyTokensRefreshed(const AuthTokens& tokens) {
266
281
  std::vector<std::function<void(const AuthTokens&)>> listeners;
267
282
  {
268
- std::lock_guard<std::mutex> lock(_mutex);
283
+ std::lock_guard<std::recursive_mutex> lock(_mutex);
269
284
  for (auto const& [id, listener] : _tokenListeners) {
270
285
  listeners.push_back(listener);
271
286
  }
@@ -4,6 +4,7 @@
4
4
  #include "AuthUser.hpp"
5
5
  #include "LoginOptions.hpp"
6
6
  #include "AuthTokens.hpp"
7
+ #include <cstdint>
7
8
  #include <optional>
8
9
  #include <mutex>
9
10
  #include <memory>
@@ -41,17 +42,18 @@ private:
41
42
  private:
42
43
  std::optional<AuthUser> _currentUser;
43
44
  std::vector<std::string> _grantedScopes;
44
- std::map<int, std::function<void(const std::optional<AuthUser>&)>> _listeners;
45
- int _nextListenerId = 0;
46
-
47
- std::map<int, std::function<void(const AuthTokens&)>> _tokenListeners;
48
- int _nextTokenListenerId = 0;
45
+ std::map<uint64_t, std::function<void(const std::optional<AuthUser>&)>> _listeners;
46
+ uint64_t _nextListenerId = 0;
47
+
48
+ std::map<uint64_t, std::function<void(const AuthTokens&)>> _tokenListeners;
49
+ uint64_t _nextTokenListenerId = 0;
49
50
  std::shared_ptr<Promise<AuthTokens>> _refreshInFlight;
50
51
 
51
- std::mutex _mutex;
52
+ // recursive_mutex: listeners resolved inside a lock scope may re-enter Auth methods
53
+ // that also acquire _mutex, causing deadlock with a non-recursive mutex.
54
+ std::recursive_mutex _mutex;
52
55
 
53
56
  static constexpr auto TAG = "Auth";
54
- static bool sLoggingEnabled;
55
57
  };
56
58
 
57
59
  } // namespace margelo::nitro::NitroAuth
@@ -1,3 +1,6 @@
1
+ // ⚠️ TEST-ONLY: This serializer is used exclusively by unit tests.
2
+ // It does NOT escape strings and does NOT handle all providers.
3
+ // Do NOT use in production code — platform-native JSON APIs are used instead.
1
4
  #pragma once
2
5
 
3
6
  #include "AuthUser.hpp"