react-native-nitro-auth 0.1.0

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 (110) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +254 -0
  3. package/android/CMakeLists.txt +37 -0
  4. package/android/build.gradle +99 -0
  5. package/android/gradle.properties +4 -0
  6. package/android/src/main/AndroidManifest.xml +12 -0
  7. package/android/src/main/cpp/JniOnLoad.cpp +7 -0
  8. package/android/src/main/cpp/PlatformAuth+Android.cpp +293 -0
  9. package/android/src/main/java/com/auth/AuthAdapter.kt +286 -0
  10. package/android/src/main/java/com/auth/GoogleSignInActivity.kt +73 -0
  11. package/android/src/main/java/com/auth/NitroAuthModule.kt +19 -0
  12. package/android/src/main/java/com/auth/NitroAuthPackage.kt +16 -0
  13. package/app.plugin.js +64 -0
  14. package/cpp/AuthCache.cpp +105 -0
  15. package/cpp/AuthCache.hpp +20 -0
  16. package/cpp/HybridAuth.cpp +213 -0
  17. package/cpp/HybridAuth.hpp +47 -0
  18. package/cpp/JSONSerializer.hpp +57 -0
  19. package/cpp/PlatformAuth.hpp +25 -0
  20. package/ios/AuthAdapter.swift +200 -0
  21. package/ios/PlatformAuth+iOS.mm +119 -0
  22. package/lib/commonjs/Auth.nitro.js +6 -0
  23. package/lib/commonjs/Auth.nitro.js.map +1 -0
  24. package/lib/commonjs/Auth.web.js +256 -0
  25. package/lib/commonjs/Auth.web.js.map +1 -0
  26. package/lib/commonjs/global.d.js +6 -0
  27. package/lib/commonjs/global.d.js.map +1 -0
  28. package/lib/commonjs/index.js +52 -0
  29. package/lib/commonjs/index.js.map +1 -0
  30. package/lib/commonjs/index.web.js +52 -0
  31. package/lib/commonjs/index.web.js.map +1 -0
  32. package/lib/commonjs/service.js +9 -0
  33. package/lib/commonjs/service.js.map +1 -0
  34. package/lib/commonjs/service.web.js +13 -0
  35. package/lib/commonjs/service.web.js.map +1 -0
  36. package/lib/commonjs/ui/social-button.js +103 -0
  37. package/lib/commonjs/ui/social-button.js.map +1 -0
  38. package/lib/commonjs/ui/social-button.web.js +102 -0
  39. package/lib/commonjs/ui/social-button.web.js.map +1 -0
  40. package/lib/commonjs/use-auth.js +144 -0
  41. package/lib/commonjs/use-auth.js.map +1 -0
  42. package/lib/module/Auth.nitro.js +4 -0
  43. package/lib/module/Auth.nitro.js.map +1 -0
  44. package/lib/module/Auth.web.js +252 -0
  45. package/lib/module/Auth.web.js.map +1 -0
  46. package/lib/module/global.d.js +4 -0
  47. package/lib/module/global.d.js.map +1 -0
  48. package/lib/module/index.js +7 -0
  49. package/lib/module/index.js.map +1 -0
  50. package/lib/module/index.web.js +7 -0
  51. package/lib/module/index.web.js.map +1 -0
  52. package/lib/module/service.js +5 -0
  53. package/lib/module/service.js.map +1 -0
  54. package/lib/module/service.web.js +4 -0
  55. package/lib/module/service.web.js.map +1 -0
  56. package/lib/module/ui/social-button.js +97 -0
  57. package/lib/module/ui/social-button.js.map +1 -0
  58. package/lib/module/ui/social-button.web.js +96 -0
  59. package/lib/module/ui/social-button.web.js.map +1 -0
  60. package/lib/module/use-auth.js +140 -0
  61. package/lib/module/use-auth.js.map +1 -0
  62. package/lib/typescript/Auth.nitro.d.ts +38 -0
  63. package/lib/typescript/Auth.nitro.d.ts.map +1 -0
  64. package/lib/typescript/Auth.web.d.ts +32 -0
  65. package/lib/typescript/Auth.web.d.ts.map +1 -0
  66. package/lib/typescript/index.d.ts +5 -0
  67. package/lib/typescript/index.d.ts.map +1 -0
  68. package/lib/typescript/index.web.d.ts +5 -0
  69. package/lib/typescript/index.web.d.ts.map +1 -0
  70. package/lib/typescript/service.d.ts +3 -0
  71. package/lib/typescript/service.d.ts.map +1 -0
  72. package/lib/typescript/service.web.d.ts +2 -0
  73. package/lib/typescript/service.web.d.ts.map +1 -0
  74. package/lib/typescript/ui/social-button.d.ts +17 -0
  75. package/lib/typescript/ui/social-button.d.ts.map +1 -0
  76. package/lib/typescript/ui/social-button.web.d.ts +17 -0
  77. package/lib/typescript/ui/social-button.web.d.ts.map +1 -0
  78. package/lib/typescript/use-auth.d.ts +15 -0
  79. package/lib/typescript/use-auth.d.ts.map +1 -0
  80. package/nitro.json +15 -0
  81. package/nitrogen/generated/.gitattributes +1 -0
  82. package/nitrogen/generated/android/NitroAuth+autolinking.cmake +81 -0
  83. package/nitrogen/generated/android/NitroAuth+autolinking.gradle +27 -0
  84. package/nitrogen/generated/android/NitroAuthOnLoad.cpp +44 -0
  85. package/nitrogen/generated/android/NitroAuthOnLoad.hpp +25 -0
  86. package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/auth/NitroAuthOnLoad.kt +35 -0
  87. package/nitrogen/generated/ios/NitroAuth+autolinking.rb +60 -0
  88. package/nitrogen/generated/ios/NitroAuth-Swift-Cxx-Bridge.cpp +17 -0
  89. package/nitrogen/generated/ios/NitroAuth-Swift-Cxx-Bridge.hpp +27 -0
  90. package/nitrogen/generated/ios/NitroAuth-Swift-Cxx-Umbrella.hpp +38 -0
  91. package/nitrogen/generated/ios/NitroAuthAutolinking.mm +35 -0
  92. package/nitrogen/generated/ios/NitroAuthAutolinking.swift +12 -0
  93. package/nitrogen/generated/shared/c++/AuthProvider.hpp +76 -0
  94. package/nitrogen/generated/shared/c++/AuthTokens.hpp +84 -0
  95. package/nitrogen/generated/shared/c++/AuthUser.hpp +107 -0
  96. package/nitrogen/generated/shared/c++/HybridAuthSpec.cpp +30 -0
  97. package/nitrogen/generated/shared/c++/HybridAuthSpec.hpp +85 -0
  98. package/nitrogen/generated/shared/c++/LoginOptions.hpp +81 -0
  99. package/package.json +113 -0
  100. package/react-native-nitro-auth.podspec +40 -0
  101. package/src/Auth.nitro.ts +50 -0
  102. package/src/Auth.web.ts +310 -0
  103. package/src/global.d.ts +38 -0
  104. package/src/index.ts +4 -0
  105. package/src/index.web.ts +4 -0
  106. package/src/service.ts +4 -0
  107. package/src/service.web.ts +1 -0
  108. package/src/ui/social-button.tsx +129 -0
  109. package/src/ui/social-button.web.tsx +128 -0
  110. package/src/use-auth.ts +157 -0
@@ -0,0 +1,293 @@
1
+ #include "PlatformAuth.hpp"
2
+ #include "AuthUser.hpp"
3
+ #include "AuthTokens.hpp"
4
+ #include "AuthCache.hpp"
5
+ #include <fbjni/fbjni.h>
6
+ #include <NitroModules/NitroLogger.hpp>
7
+ #include <NitroModules/Promise.hpp>
8
+
9
+ namespace margelo::nitro::NitroAuth {
10
+
11
+ using namespace facebook::jni;
12
+
13
+ struct JContext : JavaClass<JContext> {
14
+ static constexpr auto kJavaDescriptor = "Landroid/content/Context;";
15
+ };
16
+
17
+ struct JAuthAdapter : JavaClass<JAuthAdapter> {
18
+ static constexpr auto kJavaDescriptor = "Lcom/auth/AuthAdapter;";
19
+ };
20
+
21
+ static std::shared_ptr<Promise<AuthUser>> gLoginPromise;
22
+ static std::shared_ptr<Promise<AuthUser>> gScopesPromise;
23
+ static std::shared_ptr<Promise<AuthTokens>> gRefreshPromise;
24
+ static std::shared_ptr<Promise<std::optional<AuthUser>>> gSilentPromise;
25
+ static std::mutex gMutex;
26
+
27
+ std::shared_ptr<Promise<AuthUser>> PlatformAuth::login(AuthProvider provider, const std::vector<std::string>& scopes, const std::optional<std::string>& loginHint) {
28
+ auto promise = Promise<AuthUser>::create();
29
+ auto contextPtr = static_cast<jobject>(AuthCache::getAndroidContext());
30
+ if (!contextPtr) {
31
+ promise->reject(std::make_exception_ptr(std::runtime_error("Android Context not initialized")));
32
+ return promise;
33
+ }
34
+
35
+ {
36
+ std::lock_guard<std::mutex> lock(gMutex);
37
+ gLoginPromise = promise;
38
+ }
39
+
40
+ std::string providerStr = (provider == AuthProvider::GOOGLE) ? "google" : "apple";
41
+ JNIEnv* env = Environment::current();
42
+ jclass stringClass = env->FindClass("java/lang/String");
43
+ jobjectArray jScopes = env->NewObjectArray(scopes.size(), stringClass, nullptr);
44
+ for (size_t i = 0; i < scopes.size(); i++) {
45
+ env->SetObjectArrayElement(jScopes, i, make_jstring(scopes[i]).get());
46
+ }
47
+
48
+ jstring jLoginHint = loginHint.has_value() ? make_jstring(loginHint.value()).get() : nullptr;
49
+ jclass adapterClass = env->FindClass("com/auth/AuthAdapter");
50
+ jmethodID loginMethod = env->GetStaticMethodID(adapterClass, "loginSync",
51
+ "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V");
52
+ env->CallStaticVoidMethod(adapterClass, loginMethod,
53
+ contextPtr,
54
+ make_jstring(providerStr).get(),
55
+ nullptr,
56
+ jScopes,
57
+ jLoginHint);
58
+
59
+ return promise;
60
+ }
61
+
62
+ std::shared_ptr<Promise<AuthUser>> PlatformAuth::requestScopes(const std::vector<std::string>& scopes) {
63
+ auto promise = Promise<AuthUser>::create();
64
+ auto contextPtr = static_cast<jobject>(AuthCache::getAndroidContext());
65
+ if (!contextPtr) {
66
+ promise->reject(std::make_exception_ptr(std::runtime_error("Android Context not initialized")));
67
+ return promise;
68
+ }
69
+
70
+ {
71
+ std::lock_guard<std::mutex> lock(gMutex);
72
+ gScopesPromise = promise;
73
+ }
74
+
75
+ JNIEnv* env = Environment::current();
76
+ jclass stringClass = env->FindClass("java/lang/String");
77
+ jobjectArray jScopes = env->NewObjectArray(scopes.size(), stringClass, nullptr);
78
+ for (size_t i = 0; i < scopes.size(); i++) {
79
+ env->SetObjectArrayElement(jScopes, i, make_jstring(scopes[i]).get());
80
+ }
81
+
82
+ jclass adapterClass = env->FindClass("com/auth/AuthAdapter");
83
+ jmethodID requestMethod = env->GetStaticMethodID(adapterClass, "requestScopesSync",
84
+ "(Landroid/content/Context;[Ljava/lang/String;)V");
85
+ env->CallStaticVoidMethod(adapterClass, requestMethod, contextPtr, jScopes);
86
+ return promise;
87
+ }
88
+
89
+ std::shared_ptr<Promise<AuthTokens>> PlatformAuth::refreshToken() {
90
+ auto promise = Promise<AuthTokens>::create();
91
+ auto contextPtr = static_cast<jobject>(AuthCache::getAndroidContext());
92
+ if (!contextPtr) {
93
+ promise->reject(std::make_exception_ptr(std::runtime_error("Android Context not initialized")));
94
+ return promise;
95
+ }
96
+
97
+ {
98
+ std::lock_guard<std::mutex> lock(gMutex);
99
+ gRefreshPromise = promise;
100
+ }
101
+
102
+ auto jContext = wrap_alias(contextPtr);
103
+ static auto refreshMethod = JAuthAdapter::javaClassStatic()->getStaticMethod<void(alias_ref<JContext>)>("refreshTokenSync");
104
+ refreshMethod(JAuthAdapter::javaClassStatic(), static_ref_cast<JContext>(jContext));
105
+ return promise;
106
+ }
107
+
108
+ std::shared_ptr<Promise<std::optional<AuthUser>>> PlatformAuth::silentRestore() {
109
+ auto promise = Promise<std::optional<AuthUser>>::create();
110
+ auto contextPtr = static_cast<jobject>(AuthCache::getAndroidContext());
111
+ if (!contextPtr) {
112
+ promise->resolve(std::nullopt);
113
+ return promise;
114
+ }
115
+
116
+ {
117
+ std::lock_guard<std::mutex> lock(gMutex);
118
+ gSilentPromise = promise;
119
+ }
120
+
121
+ auto jContext = wrap_alias(contextPtr);
122
+ static auto restoreMethod = JAuthAdapter::javaClassStatic()->getStaticMethod<void(alias_ref<JContext>)>("restoreSession");
123
+ restoreMethod(JAuthAdapter::javaClassStatic(), static_ref_cast<JContext>(jContext));
124
+ return promise;
125
+ }
126
+
127
+ bool PlatformAuth::hasPlayServices() {
128
+ auto contextPtr = static_cast<jobject>(AuthCache::getAndroidContext());
129
+ if (!contextPtr) return false;
130
+
131
+ auto jContext = wrap_alias(contextPtr);
132
+ static auto hasPlayMethod = JAuthAdapter::javaClassStatic()->getStaticMethod<jboolean(alias_ref<JContext>)>("hasPlayServices");
133
+ return hasPlayMethod(JAuthAdapter::javaClassStatic(), static_ref_cast<JContext>(jContext));
134
+ }
135
+
136
+ void PlatformAuth::logout() {
137
+ auto contextPtr = static_cast<jobject>(AuthCache::getAndroidContext());
138
+ if (!contextPtr) return;
139
+
140
+ auto jContext = wrap_alias(contextPtr);
141
+ static auto logoutMethod = JAuthAdapter::javaClassStatic()->getStaticMethod<void(alias_ref<JContext>)>("logoutSync");
142
+ logoutMethod(JAuthAdapter::javaClassStatic(), static_ref_cast<JContext>(jContext));
143
+ }
144
+
145
+ extern "C" JNIEXPORT void JNICALL Java_com_auth_AuthAdapter_nativeInitialize(JNIEnv* env, jclass, jobject context) {
146
+ AuthCache::setAndroidContext(env->NewGlobalRef(context));
147
+ }
148
+
149
+ extern "C" JNIEXPORT void JNICALL Java_com_auth_AuthAdapter_nativeOnLoginSuccess(
150
+ JNIEnv* env, jclass,
151
+ jstring provider, jstring email, jstring name, jstring photo, jstring idToken, jstring accessToken, jobjectArray scopes, jobject expirationTime) {
152
+
153
+ std::shared_ptr<Promise<AuthUser>> loginPromise;
154
+ std::shared_ptr<Promise<AuthUser>> scopesPromise;
155
+ std::shared_ptr<Promise<std::optional<AuthUser>>> silentPromise;
156
+ {
157
+ std::lock_guard<std::mutex> lock(gMutex);
158
+ loginPromise = gLoginPromise;
159
+ gLoginPromise = nullptr;
160
+ scopesPromise = gScopesPromise;
161
+ gScopesPromise = nullptr;
162
+ silentPromise = gSilentPromise;
163
+ gSilentPromise = nullptr;
164
+ }
165
+
166
+ AuthUser user;
167
+ const char* providerCStr = env->GetStringUTFChars(provider, nullptr);
168
+ user.provider = (std::string(providerCStr) == "google") ? AuthProvider::GOOGLE : AuthProvider::APPLE;
169
+ env->ReleaseStringUTFChars(provider, providerCStr);
170
+
171
+ if (email) {
172
+ const char* s = env->GetStringUTFChars(email, nullptr);
173
+ user.email = std::string(s);
174
+ env->ReleaseStringUTFChars(email, s);
175
+ }
176
+ if (name) {
177
+ const char* s = env->GetStringUTFChars(name, nullptr);
178
+ user.name = std::string(s);
179
+ env->ReleaseStringUTFChars(name, s);
180
+ }
181
+ if (photo) {
182
+ const char* s = env->GetStringUTFChars(photo, nullptr);
183
+ user.photo = std::string(s);
184
+ env->ReleaseStringUTFChars(photo, s);
185
+ }
186
+ if (idToken) {
187
+ const char* s = env->GetStringUTFChars(idToken, nullptr);
188
+ user.idToken = std::string(s);
189
+ env->ReleaseStringUTFChars(idToken, s);
190
+ }
191
+ if (accessToken) {
192
+ const char* s = env->GetStringUTFChars(accessToken, nullptr);
193
+ user.accessToken = std::string(s);
194
+ env->ReleaseStringUTFChars(accessToken, s);
195
+ }
196
+ if (scopes) {
197
+ int len = env->GetArrayLength(scopes);
198
+ std::vector<std::string> scopeVec;
199
+ for (int i = 0; i < len; i++) {
200
+ jstring jstr = (jstring)env->GetObjectArrayElement(scopes, i);
201
+ const char* s = env->GetStringUTFChars(jstr, nullptr);
202
+ scopeVec.push_back(std::string(s));
203
+ env->ReleaseStringUTFChars(jstr, s);
204
+ }
205
+ user.scopes = scopeVec;
206
+ }
207
+ if (expirationTime) {
208
+ jclass longClass = env->FindClass("java/lang/Long");
209
+ jmethodID longValueMethod = env->GetMethodID(longClass, "longValue", "()J");
210
+ user.expirationTime = (double)env->CallLongMethod(expirationTime, longValueMethod);
211
+ }
212
+ if (loginPromise) loginPromise->resolve(user);
213
+ if (scopesPromise) scopesPromise->resolve(user);
214
+ if (silentPromise) silentPromise->resolve(user);
215
+ }
216
+
217
+ extern "C" JNIEXPORT void JNICALL Java_com_auth_AuthAdapter_nativeOnLoginError(
218
+ JNIEnv* env, jclass, jstring error) {
219
+
220
+ std::shared_ptr<Promise<AuthUser>> loginPromise;
221
+ std::shared_ptr<Promise<AuthUser>> scopesPromise;
222
+ std::shared_ptr<Promise<std::optional<AuthUser>>> silentPromise;
223
+ {
224
+ std::lock_guard<std::mutex> lock(gMutex);
225
+ loginPromise = gLoginPromise;
226
+ gLoginPromise = nullptr;
227
+ scopesPromise = gScopesPromise;
228
+ gScopesPromise = nullptr;
229
+ silentPromise = gSilentPromise;
230
+ gSilentPromise = nullptr;
231
+ }
232
+
233
+ const char* errorCStr = env->GetStringUTFChars(error, nullptr);
234
+ std::string errorStr(errorCStr);
235
+ env->ReleaseStringUTFChars(error, errorCStr);
236
+
237
+ if (loginPromise) loginPromise->reject(std::make_exception_ptr(std::runtime_error(errorStr)));
238
+ if (scopesPromise) scopesPromise->reject(std::make_exception_ptr(std::runtime_error(errorStr)));
239
+ if (silentPromise) {
240
+ if (errorStr == "No session") silentPromise->resolve(std::nullopt);
241
+ else silentPromise->reject(std::make_exception_ptr(std::runtime_error(errorStr)));
242
+ }
243
+ }
244
+
245
+ extern "C" JNIEXPORT void JNICALL Java_com_auth_AuthAdapter_nativeOnRefreshSuccess(
246
+ JNIEnv* env, jclass, jstring idToken, jstring accessToken, jobject expirationTime) {
247
+
248
+ std::shared_ptr<Promise<AuthTokens>> refreshPromise;
249
+ {
250
+ std::lock_guard<std::mutex> lock(gMutex);
251
+ refreshPromise = gRefreshPromise;
252
+ gRefreshPromise = nullptr;
253
+ }
254
+
255
+ if (refreshPromise) {
256
+ AuthTokens tokens;
257
+ if (idToken) {
258
+ const char* s = env->GetStringUTFChars(idToken, nullptr);
259
+ tokens.idToken = std::string(s);
260
+ env->ReleaseStringUTFChars(idToken, s);
261
+ }
262
+ if (accessToken) {
263
+ const char* s = env->GetStringUTFChars(accessToken, nullptr);
264
+ tokens.accessToken = std::string(s);
265
+ env->ReleaseStringUTFChars(accessToken, s);
266
+ }
267
+ if (expirationTime) {
268
+ jclass longClass = env->FindClass("java/lang/Long");
269
+ jmethodID longValueMethod = env->GetMethodID(longClass, "longValue", "()J");
270
+ tokens.expirationTime = (double)env->CallLongMethod(expirationTime, longValueMethod);
271
+ }
272
+ refreshPromise->resolve(tokens);
273
+ }
274
+ }
275
+
276
+ extern "C" JNIEXPORT void JNICALL Java_com_auth_AuthAdapter_nativeOnRefreshError(
277
+ JNIEnv* env, jclass, jstring error) {
278
+
279
+ std::shared_ptr<Promise<AuthTokens>> refreshPromise;
280
+ {
281
+ std::lock_guard<std::mutex> lock(gMutex);
282
+ refreshPromise = gRefreshPromise;
283
+ gRefreshPromise = nullptr;
284
+ }
285
+ if (refreshPromise) {
286
+ const char* errorCStr = env->GetStringUTFChars(error, nullptr);
287
+ std::string errorStr(errorCStr);
288
+ env->ReleaseStringUTFChars(error, errorCStr);
289
+ refreshPromise->reject(std::make_exception_ptr(std::runtime_error(errorStr)));
290
+ }
291
+ }
292
+
293
+ } // namespace margelo::nitro::NitroAuth
@@ -0,0 +1,286 @@
1
+ package com.auth
2
+
3
+ import android.app.Activity
4
+ import android.app.Application
5
+ import android.content.Context
6
+ import android.os.Bundle
7
+ import android.util.Log
8
+ import com.google.android.gms.auth.api.signin.GoogleSignIn
9
+ import com.google.android.gms.auth.api.signin.GoogleSignInAccount
10
+ import com.google.android.gms.auth.api.signin.GoogleSignInClient
11
+ import com.google.android.gms.auth.api.signin.GoogleSignInOptions
12
+ import com.google.android.gms.common.GoogleApiAvailability
13
+ import com.google.android.gms.common.ConnectionResult
14
+ import com.google.android.gms.common.api.Scope
15
+ import java.lang.ref.WeakReference
16
+
17
+ object AuthAdapter : Application.ActivityLifecycleCallbacks {
18
+ private const val TAG = "AuthAdapter"
19
+ private const val PREF_NAME = "nitro_auth"
20
+
21
+ private var appContext: Context? = null
22
+ private var currentActivityRef: WeakReference<Activity>? = null
23
+ private var googleSignInClient: GoogleSignInClient? = null
24
+ private var pendingScopes: List<String> = emptyList()
25
+
26
+ @JvmStatic
27
+ private external fun nativeInitialize(context: Context)
28
+
29
+ @JvmStatic
30
+ private external fun nativeOnLoginSuccess(
31
+ provider: String,
32
+ email: String?,
33
+ name: String?,
34
+ photo: String?,
35
+ idToken: String?,
36
+ accessToken: String?,
37
+ scopes: Array<String>?,
38
+ expirationTime: Long?
39
+ )
40
+
41
+ @JvmStatic
42
+ private external fun nativeOnLoginError(error: String)
43
+
44
+ @JvmStatic
45
+ private external fun nativeOnRefreshSuccess(idToken: String?, accessToken: String?, expirationTime: Long?)
46
+
47
+ @JvmStatic
48
+ private external fun nativeOnRefreshError(error: String)
49
+
50
+ fun initialize(context: Context) {
51
+ appContext = context.applicationContext
52
+ (appContext as? Application)?.registerActivityLifecycleCallbacks(this)
53
+ try {
54
+ System.loadLibrary("NitroAuth")
55
+ nativeInitialize(appContext!!)
56
+ } catch (e: Exception) {
57
+ Log.e(TAG, "Failed to load NitroAuth library", e)
58
+ }
59
+ }
60
+
61
+ override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
62
+ currentActivityRef = WeakReference(activity)
63
+ }
64
+ override fun onActivityStarted(activity: Activity) {
65
+ currentActivityRef = WeakReference(activity)
66
+ }
67
+ override fun onActivityResumed(activity: Activity) {
68
+ currentActivityRef = WeakReference(activity)
69
+ }
70
+ override fun onActivityPaused(activity: Activity) {}
71
+ override fun onActivityStopped(activity: Activity) {}
72
+ override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
73
+ override fun onActivityDestroyed(activity: Activity) {
74
+ if (currentActivityRef?.get() == activity) {
75
+ currentActivityRef = null
76
+ }
77
+ }
78
+
79
+ fun onSignInSuccess(account: GoogleSignInAccount, scopes: List<String>) {
80
+ val ctx = appContext ?: return
81
+ saveUser(ctx, "google", account.email, account.displayName,
82
+ account.photoUrl?.toString(), account.idToken, scopes)
83
+ nativeOnLoginSuccess("google", account.email, account.displayName,
84
+ account.photoUrl?.toString(), account.idToken, null, scopes.toTypedArray(), null)
85
+ }
86
+
87
+ fun onSignInError(errorCode: Int, message: String?) {
88
+ val mappedError = when (errorCode) {
89
+ 12501 -> "cancelled"
90
+ 7 -> "network_error"
91
+ 8, 10 -> "configuration_error"
92
+ else -> message ?: "unknown"
93
+ }
94
+ nativeOnLoginError(mappedError)
95
+ }
96
+
97
+ @JvmStatic
98
+ fun loginSync(context: Context, provider: String, googleClientId: String?, scopes: Array<String>?, loginHint: String?) {
99
+ if (provider == "apple") {
100
+ nativeOnLoginError("Apple Sign-In is not supported on Android.")
101
+ return
102
+ }
103
+
104
+ if (provider != "google") {
105
+ nativeOnLoginError("Unsupported provider: $provider")
106
+ return
107
+ }
108
+
109
+ val ctx = appContext ?: context.applicationContext
110
+ val clientId = googleClientId ?: getClientIdFromResources(ctx)
111
+ if (clientId == null) {
112
+ nativeOnLoginError("Google Client ID is required. Set it in app.json plugins.")
113
+ return
114
+ }
115
+
116
+ val requestedScopes = scopes?.toList() ?: listOf("email", "profile")
117
+ pendingScopes = requestedScopes
118
+
119
+ val intent = GoogleSignInActivity.createIntent(ctx, clientId, requestedScopes.toTypedArray(), loginHint)
120
+ ctx.startActivity(intent)
121
+ }
122
+
123
+ @JvmStatic
124
+ fun requestScopesSync(context: Context, scopes: Array<String>) {
125
+ val ctx = appContext ?: context.applicationContext
126
+ val account = GoogleSignIn.getLastSignedInAccount(ctx)
127
+ if (account == null) {
128
+ nativeOnLoginError("No user logged in")
129
+ return
130
+ }
131
+
132
+ val newScopes = scopes.map { Scope(it) }
133
+ if (GoogleSignIn.hasPermissions(account, *newScopes.toTypedArray())) {
134
+ onSignInSuccess(account, (pendingScopes + scopes.toList()).distinct())
135
+ return
136
+ }
137
+
138
+ val clientId = getClientIdFromResources(ctx)
139
+ if (clientId == null) {
140
+ nativeOnLoginError("Google Client ID not configured")
141
+ return
142
+ }
143
+
144
+ val allScopes = (pendingScopes + scopes.toList()).distinct()
145
+ val intent = GoogleSignInActivity.createIntent(ctx, clientId, allScopes.toTypedArray(), account.email)
146
+ ctx.startActivity(intent)
147
+ }
148
+
149
+ @JvmStatic
150
+ fun refreshTokenSync(context: Context) {
151
+ val ctx = appContext ?: context.applicationContext
152
+ if (googleSignInClient == null) {
153
+ val account = GoogleSignIn.getLastSignedInAccount(ctx)
154
+ if (account == null) {
155
+ nativeOnRefreshError("No user logged in")
156
+ return
157
+ }
158
+ val clientId = getClientIdFromResources(ctx)
159
+ if (clientId == null) {
160
+ nativeOnRefreshError("Google Client ID not configured")
161
+ return
162
+ }
163
+ val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
164
+ .requestIdToken(clientId)
165
+ .requestEmail()
166
+ .build()
167
+ googleSignInClient = GoogleSignIn.getClient(ctx, gso)
168
+ }
169
+
170
+ googleSignInClient!!.silentSignIn().addOnCompleteListener { task ->
171
+ if (task.isSuccessful) {
172
+ val account = task.result
173
+ nativeOnRefreshSuccess(account?.idToken, null, null)
174
+ } else {
175
+ nativeOnRefreshError(task.exception?.message ?: "Silent sign-in failed")
176
+ }
177
+ }
178
+ }
179
+
180
+ @JvmStatic
181
+ fun hasPlayServices(context: Context): Boolean {
182
+ val ctx = context.applicationContext ?: appContext ?: return false
183
+ val availability = GoogleApiAvailability.getInstance()
184
+ val result = availability.isGooglePlayServicesAvailable(ctx)
185
+ return result == ConnectionResult.SUCCESS
186
+ }
187
+
188
+ @JvmStatic
189
+ fun logoutSync(context: Context) {
190
+ val ctx = appContext ?: context.applicationContext
191
+ val clientId = getClientIdFromResources(ctx)
192
+ if (clientId != null) {
193
+ val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
194
+ .requestIdToken(clientId)
195
+ .requestEmail()
196
+ .build()
197
+ GoogleSignIn.getClient(ctx, gso).signOut()
198
+ }
199
+ clearUser(ctx)
200
+ }
201
+
202
+ @JvmStatic
203
+ fun revokeAccessSync(context: Context) {
204
+ val ctx = appContext ?: context.applicationContext
205
+ val clientId = getClientIdFromResources(ctx)
206
+ if (clientId != null) {
207
+ val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
208
+ .requestIdToken(clientId)
209
+ .requestEmail()
210
+ .build()
211
+ GoogleSignIn.getClient(ctx, gso).revokeAccess()
212
+ }
213
+ clearUser(ctx)
214
+ }
215
+
216
+ private fun getClientIdFromResources(context: Context): String? {
217
+ val resId = context.resources.getIdentifier("nitro_auth_google_client_id", "string", context.packageName)
218
+ return if (resId != 0) context.getString(resId) else null
219
+ }
220
+
221
+ @JvmStatic
222
+ fun getUserJson(context: Context): String? {
223
+ val pref = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
224
+ return pref.getString("user_json", null)
225
+ }
226
+
227
+ @JvmStatic
228
+ fun setUserJson(context: Context, json: String) {
229
+ val pref = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
230
+ pref.edit().putString("user_json", json).apply()
231
+ }
232
+
233
+ @JvmStatic
234
+ fun clearUser(context: Context) {
235
+ val pref = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
236
+ pref.edit().clear().apply()
237
+ }
238
+
239
+ @JvmStatic
240
+ fun restoreSession(context: Context) {
241
+ val ctx = context.applicationContext ?: appContext ?: context
242
+ val account = GoogleSignIn.getLastSignedInAccount(ctx)
243
+ if (account != null) {
244
+ nativeOnLoginSuccess("google", account.email, account.displayName,
245
+ account.photoUrl?.toString(), account.idToken, null,
246
+ account.grantedScopes?.map { it.scopeUri }?.toTypedArray(), null)
247
+ } else {
248
+ val json = getUserJson(ctx)
249
+ if (json != null) {
250
+ val provider = if (json.contains("\"provider\":\"google\"")) "google" else "apple"
251
+ val email = extractJsonValue(json, "email")
252
+ val name = extractJsonValue(json, "name")
253
+ val photo = extractJsonValue(json, "photo")
254
+ val idToken = extractJsonValue(json, "idToken")
255
+ nativeOnLoginSuccess(provider, email, name, photo, idToken, null, null, null)
256
+ } else {
257
+ nativeOnLoginError("No session")
258
+ }
259
+ }
260
+ }
261
+
262
+ private fun extractJsonValue(json: String, key: String): String? {
263
+ val pattern = "\"$key\":\"([^\"]*)\""
264
+ val regex = Regex(pattern)
265
+ return regex.find(json)?.groupValues?.get(1)
266
+ }
267
+
268
+ private fun saveUser(context: Context, provider: String, email: String?, name: String?,
269
+ photo: String?, idToken: String?, scopes: List<String>?) {
270
+ val pref = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
271
+ val json = StringBuilder()
272
+ json.append("{")
273
+ json.append("\"provider\":\"$provider\"")
274
+ if (email != null) json.append(",\"email\":\"$email\"")
275
+ if (name != null) json.append(",\"name\":\"$name\"")
276
+ if (photo != null) json.append(",\"photo\":\"$photo\"")
277
+ if (idToken != null) json.append(",\"idToken\":\"$idToken\"")
278
+ if (scopes != null) {
279
+ json.append(",\"scopes\":[")
280
+ json.append(scopes.joinToString(",") { "\"$it\"" })
281
+ json.append("]")
282
+ }
283
+ json.append("}")
284
+ pref.edit().putString("user_json", json.toString()).apply()
285
+ }
286
+ }
@@ -0,0 +1,73 @@
1
+ package com.auth
2
+
3
+ import android.app.Activity
4
+ import android.content.Context
5
+ import android.content.Intent
6
+ import android.os.Bundle
7
+ import android.util.Log
8
+ import androidx.activity.ComponentActivity
9
+ import androidx.activity.result.contract.ActivityResultContracts
10
+ import com.google.android.gms.auth.api.signin.GoogleSignIn
11
+ import com.google.android.gms.auth.api.signin.GoogleSignInOptions
12
+ import com.google.android.gms.common.api.ApiException
13
+ import com.google.android.gms.common.api.Scope
14
+
15
+ class GoogleSignInActivity : ComponentActivity() {
16
+ companion object {
17
+ private const val TAG = "GoogleSignInActivity"
18
+ private const val EXTRA_CLIENT_ID = "client_id"
19
+ private const val EXTRA_SCOPES = "scopes"
20
+ private const val EXTRA_LOGIN_HINT = "login_hint"
21
+
22
+ fun createIntent(context: Context, clientId: String, scopes: Array<String>, loginHint: String?): Intent {
23
+ return Intent(context, GoogleSignInActivity::class.java).apply {
24
+ putExtra(EXTRA_CLIENT_ID, clientId)
25
+ putExtra(EXTRA_SCOPES, scopes)
26
+ putExtra(EXTRA_LOGIN_HINT, loginHint)
27
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
28
+ }
29
+ }
30
+ }
31
+
32
+ private val signInLauncher = registerForActivityResult(
33
+ ActivityResultContracts.StartActivityForResult()
34
+ ) { result ->
35
+ try {
36
+ val task = GoogleSignIn.getSignedInAccountFromIntent(result.data)
37
+ val account = task.getResult(ApiException::class.java)
38
+ val scopes = intent.getStringArrayExtra(EXTRA_SCOPES)?.toList() ?: emptyList()
39
+ AuthAdapter.onSignInSuccess(account, scopes)
40
+ } catch (e: ApiException) {
41
+ AuthAdapter.onSignInError(e.statusCode, e.message)
42
+ }
43
+ finish()
44
+ }
45
+
46
+ override fun onCreate(savedInstanceState: Bundle?) {
47
+ super.onCreate(savedInstanceState)
48
+ val clientId = intent.getStringExtra(EXTRA_CLIENT_ID)
49
+ val scopes = intent.getStringArrayExtra(EXTRA_SCOPES) ?: arrayOf("email", "profile")
50
+ val loginHint = intent.getStringExtra(EXTRA_LOGIN_HINT)
51
+
52
+ if (clientId == null) {
53
+ AuthAdapter.onSignInError(8, "Missing client ID")
54
+ finish()
55
+ return
56
+ }
57
+
58
+ val gsoBuilder = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
59
+ .requestIdToken(clientId)
60
+ .requestEmail()
61
+
62
+ scopes.forEach { scopeStr ->
63
+ if (scopeStr != "email" && scopeStr != "profile" && scopeStr != "openid") {
64
+ gsoBuilder.requestScopes(Scope(scopeStr))
65
+ }
66
+ }
67
+
68
+ if (loginHint != null) gsoBuilder.setAccountName(loginHint)
69
+
70
+ val client = GoogleSignIn.getClient(this, gsoBuilder.build())
71
+ signInLauncher.launch(client.signInIntent)
72
+ }
73
+ }
@@ -0,0 +1,19 @@
1
+ package com.auth
2
+
3
+ import android.util.Log
4
+ import com.facebook.react.bridge.ReactApplicationContext
5
+ import com.facebook.react.bridge.ReactContextBaseJavaModule
6
+
7
+ class NitroAuthModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
8
+ override fun getName(): String = "NitroAuthModule"
9
+
10
+ init {
11
+ try {
12
+ AuthAdapter.initialize(reactContext)
13
+ com.margelo.nitro.com.auth.NitroAuthOnLoad.initializeNative()
14
+ Log.d("NitroAuthModule", "NitroAuth initialized")
15
+ } catch (e: Exception) {
16
+ Log.e("NitroAuthModule", "Failed to initialize NitroAuth", e)
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,16 @@
1
+ package com.auth
2
+
3
+ import com.facebook.react.ReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.uimanager.ViewManager
7
+
8
+ class NitroAuthPackage : ReactPackage {
9
+ override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
10
+ return listOf(NitroAuthModule(reactContext))
11
+ }
12
+
13
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
14
+ return emptyList()
15
+ }
16
+ }