react-native-nitro-auth 0.5.3 → 0.5.5

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 (59) hide show
  1. package/README.md +60 -30
  2. package/android/build.gradle +2 -5
  3. package/android/src/main/cpp/JniOnLoad.cpp +3 -1
  4. package/android/src/main/cpp/PlatformAuth+Android.cpp +95 -29
  5. package/android/src/main/java/com/auth/AuthAdapter.kt +124 -126
  6. package/android/src/main/java/com/auth/NitroAuthModule.kt +8 -1
  7. package/cpp/AuthCache.cpp +0 -44
  8. package/cpp/AuthCache.hpp +0 -7
  9. package/cpp/HybridAuth.cpp +20 -2
  10. package/cpp/HybridAuth.hpp +1 -0
  11. package/ios/AuthAdapter.swift +64 -28
  12. package/lib/commonjs/Auth.web.js +96 -43
  13. package/lib/commonjs/Auth.web.js.map +1 -1
  14. package/lib/commonjs/index.js +23 -1
  15. package/lib/commonjs/index.js.map +1 -1
  16. package/lib/commonjs/service.js +33 -8
  17. package/lib/commonjs/service.js.map +1 -1
  18. package/lib/commonjs/use-auth.js +51 -54
  19. package/lib/commonjs/use-auth.js.map +1 -1
  20. package/lib/commonjs/utils/auth-error.js +37 -0
  21. package/lib/commonjs/utils/auth-error.js.map +1 -0
  22. package/lib/module/Auth.web.js +96 -43
  23. package/lib/module/Auth.web.js.map +1 -1
  24. package/lib/module/index.js +1 -0
  25. package/lib/module/index.js.map +1 -1
  26. package/lib/module/service.js +33 -8
  27. package/lib/module/service.js.map +1 -1
  28. package/lib/module/use-auth.js +51 -54
  29. package/lib/module/use-auth.js.map +1 -1
  30. package/lib/module/utils/auth-error.js +30 -0
  31. package/lib/module/utils/auth-error.js.map +1 -0
  32. package/lib/typescript/commonjs/Auth.web.d.ts +7 -0
  33. package/lib/typescript/commonjs/Auth.web.d.ts.map +1 -1
  34. package/lib/typescript/commonjs/index.d.ts +1 -0
  35. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  36. package/lib/typescript/commonjs/service.d.ts.map +1 -1
  37. package/lib/typescript/commonjs/use-auth.d.ts +2 -1
  38. package/lib/typescript/commonjs/use-auth.d.ts.map +1 -1
  39. package/lib/typescript/commonjs/utils/auth-error.d.ts +16 -0
  40. package/lib/typescript/commonjs/utils/auth-error.d.ts.map +1 -0
  41. package/lib/typescript/module/Auth.web.d.ts +7 -0
  42. package/lib/typescript/module/Auth.web.d.ts.map +1 -1
  43. package/lib/typescript/module/index.d.ts +1 -0
  44. package/lib/typescript/module/index.d.ts.map +1 -1
  45. package/lib/typescript/module/service.d.ts.map +1 -1
  46. package/lib/typescript/module/use-auth.d.ts +2 -1
  47. package/lib/typescript/module/use-auth.d.ts.map +1 -1
  48. package/lib/typescript/module/utils/auth-error.d.ts +16 -0
  49. package/lib/typescript/module/utils/auth-error.d.ts.map +1 -0
  50. package/nitrogen/generated/android/NitroAuthOnLoad.cpp +22 -17
  51. package/nitrogen/generated/android/NitroAuthOnLoad.hpp +13 -4
  52. package/package.json +7 -7
  53. package/react-native-nitro-auth.podspec +1 -1
  54. package/src/Auth.web.ts +124 -50
  55. package/src/index.ts +1 -0
  56. package/src/service.ts +34 -8
  57. package/src/use-auth.ts +81 -114
  58. package/src/utils/auth-error.ts +49 -0
  59. package/ios/KeychainStore.swift +0 -43
package/README.md CHANGED
@@ -71,11 +71,11 @@ bun add react-native-nitro-auth react-native-nitro-modules
71
71
 
72
72
  ### Requirements
73
73
 
74
- | Dependency | Version |
75
- | ---------------------------- | ----------- |
76
- | `react-native` | `>= 0.75.0` |
77
- | `react-native-nitro-modules` | `>= 0.33.9` |
78
- | `react` | `*` |
74
+ | Dependency | Version |
75
+ | ---------------------------- | ------------ |
76
+ | `react-native` | `>= 0.75.0` |
77
+ | `react-native-nitro-modules` | `>= 0.35.0` |
78
+ | `react` | `*` |
79
79
 
80
80
  For Expo projects, rebuild native code after installation:
81
81
 
@@ -670,17 +670,25 @@ const freshToken = await AuthService.getAccessToken();
670
670
 
671
671
  ### Standardized Error Codes
672
672
 
673
- Handle failures reliably with predictable error strings. Some flows can surface provider-specific codes (listed below):
673
+ All errors thrown by `AuthService` and `useAuth` are `AuthError` instances with a type-safe `code` field (always a valid `AuthErrorCode`) and an optional `underlyingMessage` with the raw provider string when it differs from the code.
674
674
 
675
675
  ```ts
676
+ import { AuthError } from "react-native-nitro-auth";
677
+
676
678
  try {
677
679
  await login("google");
678
680
  } catch (e) {
679
- const error = e as Error;
680
- if (error.message === "cancelled") {
681
- // User closed the popup/picker
682
- } else if (error.message === "network_error") {
683
- // Connection issues
681
+ if (e instanceof AuthError) {
682
+ switch (e.code) {
683
+ case "cancelled":
684
+ // User closed the popup/picker
685
+ break;
686
+ case "network_error":
687
+ // Connection issues
688
+ break;
689
+ default:
690
+ console.error(e.code, e.underlyingMessage);
691
+ }
684
692
  }
685
693
  }
686
694
  ```
@@ -701,24 +709,24 @@ try {
701
709
 
702
710
  ### Native Error Metadata
703
711
 
704
- For more detailed debugging, Nitro Auth captures raw provider/native details in `underlyingError` where available:
712
+ `AuthError` carries the raw provider/native message in `underlyingMessage` when the platform error didn't map to a known code:
705
713
 
706
714
  ```ts
707
- // From authenticated user (on success)
708
- const { user } = useAuth();
709
- if (user?.underlyingError) {
710
- console.warn("Auth warning:", user.underlyingError);
711
- }
715
+ import { AuthError } from "react-native-nitro-auth";
712
716
 
713
- // From error (on failure)
714
717
  try {
715
718
  await login("google");
716
719
  } catch (e) {
717
- const error = e as Error & { underlyingError?: string };
718
- console.log("Native error:", error.underlyingError);
720
+ if (e instanceof AuthError) {
721
+ // e.code is always a valid AuthErrorCode
722
+ // e.underlyingMessage is the original native string (or undefined if it equalled the code)
723
+ console.log(e.code, e.underlyingMessage);
724
+ }
719
725
  }
720
726
  ```
721
727
 
728
+ The `AuthUser.underlyingError` field carries a raw warning string when the provider returns one alongside a successful result.
729
+
722
730
  ### Troubleshooting
723
731
 
724
732
  - `configuration_error`: verify client IDs, URL schemes, and redirect URIs are set for the current platform.
@@ -797,6 +805,9 @@ This is useful for scenarios where:
797
805
  ```ts
798
806
  import {
799
807
  AuthService,
808
+ AuthError,
809
+ isAuthErrorCode,
810
+ toAuthErrorCode,
800
811
  SocialButton,
801
812
  useAuth,
802
813
  type UseAuthReturn,
@@ -869,7 +880,7 @@ declare function useAuth(): UseAuthReturn;
869
880
  | `user` | `AuthUser \| undefined` | Current in-memory user |
870
881
  | `scopes` | `string[]` | Current granted scopes |
871
882
  | `loading` | `boolean` | `true` while an auth operation is in-flight |
872
- | `error` | `Error \| undefined` | Last operation error |
883
+ | `error` | `AuthError \| undefined` | Last operation error (typed, safe to switch on `.code`) |
873
884
  | `hasPlayServices` | `boolean` | Android Play Services availability |
874
885
  | `login` | `(provider: AuthProvider, options?: LoginOptions) => Promise<void>` | Starts provider login |
875
886
  | `logout` | `() => void` | Clears current session |
@@ -965,17 +976,36 @@ There is no public `setStorageAdapter`/`setJSStorageAdapter` API in this package
965
976
  | `nitroAuthWebStorage` | `"session" \| "local" \| "memory"` | `"session"` | Storage for non-sensitive web cache |
966
977
  | `nitroAuthPersistTokensOnWeb` | `boolean` | `false` | Persist sensitive tokens on web storage instead of memory |
967
978
 
968
- ### Error semantics
979
+ ### `AuthError`
980
+
981
+ All thrown errors from `AuthService` and `useAuth` are `AuthError` instances.
982
+
983
+ ```ts
984
+ class AuthError extends Error {
985
+ readonly code: AuthErrorCode; // Always a valid AuthErrorCode — safe to switch on
986
+ readonly underlyingMessage?: string; // Raw native/platform string when it differs from code
987
+ static from(e: unknown): AuthError; // Wraps any value; passes AuthError instances through
988
+ }
969
989
 
970
- Errors are surfaced as `Error` with `message` as a normalized code when possible, and `underlyingError` with provider/native details.
990
+ function isAuthErrorCode(value: string): value is AuthErrorCode;
991
+ function toAuthErrorCode(raw: string): AuthErrorCode; // Returns "unknown" for unrecognized strings
992
+ ```
971
993
 
972
- | Normalized message | Meaning |
973
- | --------------------- | ---------------------------------------------- |
974
- | `cancelled` | User cancelled popup/login flow |
975
- | `timeout` | Provider popup did not complete before timeout |
976
- | `popup_blocked` | Browser blocked popup opening |
977
- | `network_error` | Network failure |
978
- | `configuration_error` | Missing/invalid provider configuration |
994
+ | `code` | Meaning |
995
+ | ---------------------- | ---------------------------------------------- |
996
+ | `cancelled` | User cancelled the login flow |
997
+ | `timeout` | Provider popup did not complete before timeout |
998
+ | `popup_blocked` | Browser blocked popup opening |
999
+ | `network_error` | Network failure |
1000
+ | `configuration_error` | Missing/invalid provider configuration |
1001
+ | `unsupported_provider` | Provider not supported on this platform |
1002
+ | `invalid_state` | PKCE state mismatch (possible CSRF) |
1003
+ | `invalid_nonce` | Nonce mismatch in token response |
1004
+ | `token_error` | Token exchange failed |
1005
+ | `no_id_token` | No `id_token` in token response |
1006
+ | `parse_error` | Failed to parse token response |
1007
+ | `refresh_failed` | Refresh token flow failed |
1008
+ | `unknown` | Unrecognized error |
979
1009
 
980
1010
  ## Quality Gates
981
1011
 
@@ -93,7 +93,7 @@ dependencies {
93
93
  implementation project(":react-native-nitro-modules")
94
94
 
95
95
  // Google Sign-In SDK (full scope support)
96
- implementation "com.google.android.gms:play-services-auth:21.2.0"
96
+ implementation "com.google.android.gms:play-services-auth:21.5.1"
97
97
 
98
98
  // Activity result APIs
99
99
  implementation "androidx.activity:activity-ktx:1.9.3"
@@ -104,8 +104,5 @@ dependencies {
104
104
  // Google Credential Manager (One-Tap / Passkeys)
105
105
  implementation "androidx.credentials:credentials:1.5.0"
106
106
  implementation "androidx.credentials:credentials-play-services-auth:1.5.0"
107
- implementation "com.google.android.libraries.identity.googleid:googleid:1.1.1"
108
-
109
- // Secure storage (EncryptedSharedPreferences)
110
- implementation "androidx.security:security-crypto:1.0.0"
107
+ implementation "com.google.android.libraries.identity.googleid:googleid:1.2.0"
111
108
  }
@@ -4,5 +4,7 @@
4
4
 
5
5
  extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
6
6
  (void)reserved;
7
- return margelo::nitro::NitroAuth::initialize(vm);
7
+ return facebook::jni::initialize(vm, []() {
8
+ margelo::nitro::NitroAuth::registerAllNatives();
9
+ });
8
10
  }
@@ -6,6 +6,8 @@
6
6
  #include <fbjni/fbjni.h>
7
7
  #include <NitroModules/NitroLogger.hpp>
8
8
  #include <NitroModules/Promise.hpp>
9
+ #include <exception>
10
+ #include <stdexcept>
9
11
 
10
12
  namespace margelo::nitro::NitroAuth {
11
13
 
@@ -24,6 +26,33 @@ static std::shared_ptr<Promise<AuthUser>> gScopesPromise;
24
26
  static std::shared_ptr<Promise<AuthTokens>> gRefreshPromise;
25
27
  static std::shared_ptr<Promise<std::optional<AuthUser>>> gSilentPromise;
26
28
  static std::mutex gMutex;
29
+ static jclass gAuthAdapterClass = nullptr;
30
+ static jmethodID gLoginMethod = nullptr;
31
+ static jmethodID gRequestScopesMethod = nullptr;
32
+
33
+ static void ensureAuthAdapterMethods(JNIEnv* env) {
34
+ if (gAuthAdapterClass != nullptr && gLoginMethod != nullptr && gRequestScopesMethod != nullptr) {
35
+ return;
36
+ }
37
+
38
+ jclass localAdapterClass = env->FindClass("com/auth/AuthAdapter");
39
+ if (localAdapterClass == nullptr) {
40
+ throw std::runtime_error("Unable to resolve com/auth/AuthAdapter");
41
+ }
42
+ gAuthAdapterClass = static_cast<jclass>(env->NewGlobalRef(localAdapterClass));
43
+ env->DeleteLocalRef(localAdapterClass);
44
+
45
+ gLoginMethod = env->GetStaticMethodID(
46
+ gAuthAdapterClass,
47
+ "loginSync",
48
+ "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;ZZZLjava/lang/String;Ljava/lang/String;)V"
49
+ );
50
+ gRequestScopesMethod = env->GetStaticMethodID(
51
+ gAuthAdapterClass,
52
+ "requestScopesSync",
53
+ "(Landroid/content/Context;[Ljava/lang/String;)V"
54
+ );
55
+ }
27
56
 
28
57
  std::shared_ptr<Promise<AuthUser>> PlatformAuth::login(AuthProvider provider, const std::optional<LoginOptions>& options) {
29
58
  auto promise = Promise<AuthUser>::create();
@@ -35,6 +64,9 @@ std::shared_ptr<Promise<AuthUser>> PlatformAuth::login(AuthProvider provider, co
35
64
 
36
65
  {
37
66
  std::lock_guard<std::mutex> lock(gMutex);
67
+ if (gLoginPromise) {
68
+ gLoginPromise->reject(std::make_exception_ptr(std::runtime_error("Login request superseded by a newer request")));
69
+ }
38
70
  gLoginPromise = promise;
39
71
  }
40
72
 
@@ -71,30 +103,47 @@ std::shared_ptr<Promise<AuthUser>> PlatformAuth::login(AuthProvider provider, co
71
103
  }
72
104
 
73
105
  JNIEnv* env = Environment::current();
106
+ try {
107
+ ensureAuthAdapterMethods(env);
108
+ } catch (...) {
109
+ promise->reject(std::current_exception());
110
+ return promise;
111
+ }
74
112
  jclass stringClass = env->FindClass("java/lang/String");
75
113
  jobjectArray jScopes = env->NewObjectArray(scopes.size(), stringClass, nullptr);
76
114
  for (size_t i = 0; i < scopes.size(); i++) {
77
115
  env->SetObjectArrayElement(jScopes, i, make_jstring(scopes[i]).get());
78
116
  }
79
117
 
80
- jstring jLoginHint = loginHint.has_value() ? make_jstring(loginHint.value()).get() : nullptr;
81
- jstring jTenant = tenant.has_value() ? make_jstring(tenant.value()).get() : nullptr;
82
- jstring jPrompt = prompt.has_value() ? make_jstring(prompt.value()).get() : nullptr;
83
-
84
- jclass adapterClass = env->FindClass("com/auth/AuthAdapter");
85
- jmethodID loginMethod = env->GetStaticMethodID(adapterClass, "loginSync",
86
- "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;ZZZLjava/lang/String;Ljava/lang/String;)V");
87
- env->CallStaticVoidMethod(adapterClass, loginMethod,
118
+ local_ref<JString> providerRef = make_jstring(providerStr);
119
+ local_ref<JString> loginHintRef;
120
+ local_ref<JString> tenantRef;
121
+ local_ref<JString> promptRef;
122
+
123
+ if (loginHint.has_value()) {
124
+ loginHintRef = make_jstring(loginHint.value());
125
+ }
126
+ if (tenant.has_value()) {
127
+ tenantRef = make_jstring(tenant.value());
128
+ }
129
+ if (prompt.has_value()) {
130
+ promptRef = make_jstring(prompt.value());
131
+ }
132
+
133
+ env->CallStaticVoidMethod(gAuthAdapterClass, gLoginMethod,
88
134
  contextPtr,
89
- make_jstring(providerStr).get(),
135
+ providerRef.get(),
90
136
  nullptr,
91
137
  jScopes,
92
- jLoginHint,
138
+ loginHintRef.get(),
93
139
  (jboolean)useOneTap,
94
140
  (jboolean)forceAccountPicker,
95
141
  (jboolean)useLegacyGoogleSignIn,
96
- jTenant,
97
- jPrompt);
142
+ tenantRef.get(),
143
+ promptRef.get());
144
+
145
+ env->DeleteLocalRef(jScopes);
146
+ env->DeleteLocalRef(stringClass);
98
147
 
99
148
  return promise;
100
149
  }
@@ -109,20 +158,28 @@ std::shared_ptr<Promise<AuthUser>> PlatformAuth::requestScopes(const std::vector
109
158
 
110
159
  {
111
160
  std::lock_guard<std::mutex> lock(gMutex);
161
+ if (gScopesPromise) {
162
+ gScopesPromise->reject(std::make_exception_ptr(std::runtime_error("Scope request superseded by a newer request")));
163
+ }
112
164
  gScopesPromise = promise;
113
165
  }
114
166
 
115
167
  JNIEnv* env = Environment::current();
168
+ try {
169
+ ensureAuthAdapterMethods(env);
170
+ } catch (...) {
171
+ promise->reject(std::current_exception());
172
+ return promise;
173
+ }
116
174
  jclass stringClass = env->FindClass("java/lang/String");
117
175
  jobjectArray jScopes = env->NewObjectArray(scopes.size(), stringClass, nullptr);
118
176
  for (size_t i = 0; i < scopes.size(); i++) {
119
177
  env->SetObjectArrayElement(jScopes, i, make_jstring(scopes[i]).get());
120
178
  }
121
179
 
122
- jclass adapterClass = env->FindClass("com/auth/AuthAdapter");
123
- jmethodID requestMethod = env->GetStaticMethodID(adapterClass, "requestScopesSync",
124
- "(Landroid/content/Context;[Ljava/lang/String;)V");
125
- env->CallStaticVoidMethod(adapterClass, requestMethod, contextPtr, jScopes);
180
+ env->CallStaticVoidMethod(gAuthAdapterClass, gRequestScopesMethod, contextPtr, jScopes);
181
+ env->DeleteLocalRef(jScopes);
182
+ env->DeleteLocalRef(stringClass);
126
183
  return promise;
127
184
  }
128
185
 
@@ -136,6 +193,9 @@ std::shared_ptr<Promise<AuthTokens>> PlatformAuth::refreshToken() {
136
193
 
137
194
  {
138
195
  std::lock_guard<std::mutex> lock(gMutex);
196
+ if (gRefreshPromise) {
197
+ gRefreshPromise->reject(std::make_exception_ptr(std::runtime_error("Refresh request superseded by a newer request")));
198
+ }
139
199
  gRefreshPromise = promise;
140
200
  }
141
201
 
@@ -155,6 +215,9 @@ std::shared_ptr<Promise<std::optional<AuthUser>>> PlatformAuth::silentRestore()
155
215
 
156
216
  {
157
217
  std::lock_guard<std::mutex> lock(gMutex);
218
+ if (gSilentPromise) {
219
+ gSilentPromise->reject(std::make_exception_ptr(std::runtime_error("Silent restore superseded by a newer request")));
220
+ }
158
221
  gSilentPromise = promise;
159
222
  }
160
223
 
@@ -182,8 +245,8 @@ void PlatformAuth::logout() {
182
245
  logoutMethod(JAuthAdapter::javaClassStatic(), static_ref_cast<JContext>(jContext));
183
246
  }
184
247
 
185
- extern "C" JNIEXPORT void JNICALL Java_com_auth_AuthAdapter_nativeInitialize(JNIEnv* env, jclass, jobject context) {
186
- AuthCache::setAndroidContext(env->NewGlobalRef(context));
248
+ extern "C" JNIEXPORT void JNICALL Java_com_auth_AuthAdapter_nativeInitialize(JNIEnv*, jclass, jobject context) {
249
+ AuthCache::setAndroidContext(context);
187
250
  }
188
251
 
189
252
  extern "C" JNIEXPORT void JNICALL Java_com_auth_AuthAdapter_nativeOnLoginSuccess(
@@ -253,6 +316,7 @@ extern "C" JNIEXPORT void JNICALL Java_com_auth_AuthAdapter_nativeOnLoginSuccess
253
316
  const char* s = env->GetStringUTFChars(jstr, nullptr);
254
317
  scopeVec.push_back(std::string(s));
255
318
  env->ReleaseStringUTFChars(jstr, s);
319
+ env->DeleteLocalRef(jstr);
256
320
  }
257
321
  user.scopes = scopeVec;
258
322
  }
@@ -260,6 +324,7 @@ extern "C" JNIEXPORT void JNICALL Java_com_auth_AuthAdapter_nativeOnLoginSuccess
260
324
  jclass longClass = env->FindClass("java/lang/Long");
261
325
  jmethodID longValueMethod = env->GetMethodID(longClass, "longValue", "()J");
262
326
  user.expirationTime = (double)env->CallLongMethod(expirationTime, longValueMethod);
327
+ env->DeleteLocalRef(longClass);
263
328
  }
264
329
 
265
330
  if (loginPromise) loginPromise->resolve(user);
@@ -286,19 +351,21 @@ extern "C" JNIEXPORT void JNICALL Java_com_auth_AuthAdapter_nativeOnLoginError(
286
351
  const char* errorCStr = env->GetStringUTFChars(error, nullptr);
287
352
  std::string errorStr(errorCStr);
288
353
  env->ReleaseStringUTFChars(error, errorCStr);
289
-
290
- std::string finalError = errorStr;
354
+
355
+ // errorStr is the structured AuthErrorCode (e.g. "cancelled", "network_error").
356
+ // underlyingError is a raw platform message for debugging — it must not replace the code.
291
357
  if (underlyingError) {
292
358
  const char* uCStr = env->GetStringUTFChars(underlyingError, nullptr);
293
- finalError = std::string(uCStr);
294
359
  env->ReleaseStringUTFChars(underlyingError, uCStr);
360
+ // underlyingError is intentionally discarded here; the structured code is sufficient
361
+ // for consumers. If richer debugging is needed, add it to the AuthUser.underlyingError field.
295
362
  }
296
363
 
297
- if (loginPromise) loginPromise->reject(std::make_exception_ptr(std::runtime_error(finalError)));
298
- if (scopesPromise) scopesPromise->reject(std::make_exception_ptr(std::runtime_error(finalError)));
364
+ if (loginPromise) loginPromise->reject(std::make_exception_ptr(std::runtime_error(errorStr)));
365
+ if (scopesPromise) scopesPromise->reject(std::make_exception_ptr(std::runtime_error(errorStr)));
299
366
  if (silentPromise) {
300
367
  if (errorStr == "No session") silentPromise->resolve(std::nullopt);
301
- else silentPromise->reject(std::make_exception_ptr(std::runtime_error(finalError)));
368
+ else silentPromise->reject(std::make_exception_ptr(std::runtime_error(errorStr)));
302
369
  }
303
370
  }
304
371
 
@@ -328,6 +395,7 @@ extern "C" JNIEXPORT void JNICALL Java_com_auth_AuthAdapter_nativeOnRefreshSucce
328
395
  jclass longClass = env->FindClass("java/lang/Long");
329
396
  jmethodID longValueMethod = env->GetMethodID(longClass, "longValue", "()J");
330
397
  tokens.expirationTime = (double)env->CallLongMethod(expirationTime, longValueMethod);
398
+ env->DeleteLocalRef(longClass);
331
399
  }
332
400
  refreshPromise->resolve(tokens);
333
401
  }
@@ -343,17 +411,15 @@ extern "C" JNIEXPORT void JNICALL Java_com_auth_AuthAdapter_nativeOnRefreshError
343
411
  gRefreshPromise = nullptr;
344
412
  }
345
413
  if (refreshPromise) {
346
- std::string finalError;
347
414
  const char* errorCStr = env->GetStringUTFChars(error, nullptr);
348
- finalError = std::string(errorCStr);
415
+ std::string errorStr(errorCStr);
349
416
  env->ReleaseStringUTFChars(error, errorCStr);
350
-
417
+
351
418
  if (underlyingError) {
352
419
  const char* uCStr = env->GetStringUTFChars(underlyingError, nullptr);
353
- finalError = std::string(uCStr);
354
420
  env->ReleaseStringUTFChars(underlyingError, uCStr);
355
421
  }
356
- refreshPromise->reject(std::make_exception_ptr(std::runtime_error(finalError)));
422
+ refreshPromise->reject(std::make_exception_ptr(std::runtime_error(errorStr)));
357
423
  }
358
424
  }
359
425