@trainon-inc/capacitor-clerk-native 1.23.0 → 1.24.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.
package/android/build.gradle
CHANGED
|
@@ -51,6 +51,10 @@ dependencies {
|
|
|
51
51
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
|
52
52
|
implementation project(':capacitor-android')
|
|
53
53
|
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
|
54
|
+
|
|
55
|
+
// OkHttp for making HTTP requests to Clerk API
|
|
56
|
+
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
|
|
57
|
+
|
|
54
58
|
testImplementation "junit:junit:$junitVersion"
|
|
55
59
|
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
|
|
56
60
|
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
|
|
@@ -1,92 +1,558 @@
|
|
|
1
1
|
package com.trainon.capacitor.clerk;
|
|
2
2
|
|
|
3
|
+
import android.content.Context;
|
|
4
|
+
import android.content.SharedPreferences;
|
|
5
|
+
import android.util.Log;
|
|
6
|
+
|
|
7
|
+
import com.getcapacitor.JSObject;
|
|
3
8
|
import com.getcapacitor.Plugin;
|
|
4
9
|
import com.getcapacitor.PluginCall;
|
|
5
10
|
import com.getcapacitor.PluginMethod;
|
|
6
11
|
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
7
12
|
|
|
13
|
+
import org.json.JSONArray;
|
|
14
|
+
import org.json.JSONException;
|
|
15
|
+
import org.json.JSONObject;
|
|
16
|
+
|
|
17
|
+
import java.io.IOException;
|
|
18
|
+
import java.util.concurrent.ExecutorService;
|
|
19
|
+
import java.util.concurrent.Executors;
|
|
20
|
+
|
|
21
|
+
import okhttp3.MediaType;
|
|
22
|
+
import okhttp3.OkHttpClient;
|
|
23
|
+
import okhttp3.Request;
|
|
24
|
+
import okhttp3.RequestBody;
|
|
25
|
+
import okhttp3.Response;
|
|
26
|
+
|
|
8
27
|
/**
|
|
9
|
-
* Android
|
|
10
|
-
*
|
|
11
|
-
* On Android, authentication is handled by the web Clerk provider (@clerk/clerk-react)
|
|
12
|
-
* because Android WebViews work well with web-based auth (unlike iOS which has cookie issues).
|
|
28
|
+
* Android implementation of Clerk Native plugin.
|
|
13
29
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
30
|
+
* Makes HTTP requests directly to Clerk's Frontend API, bypassing WebView origin restrictions.
|
|
31
|
+
* This allows the custom Clerk proxy domain to work on Android.
|
|
16
32
|
*/
|
|
17
33
|
@CapacitorPlugin(name = "ClerkNative")
|
|
18
34
|
public class ClerkNativePlugin extends Plugin {
|
|
19
35
|
|
|
20
|
-
private static final String
|
|
36
|
+
private static final String TAG = "ClerkNativePlugin";
|
|
37
|
+
private static final String PREFS_NAME = "ClerkNativePrefs";
|
|
38
|
+
private static final String PREF_SESSION_TOKEN = "session_token";
|
|
39
|
+
private static final String PREF_CLIENT_TOKEN = "client_token";
|
|
40
|
+
private static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
|
|
41
|
+
|
|
42
|
+
private OkHttpClient client;
|
|
43
|
+
private ExecutorService executor;
|
|
44
|
+
private String publishableKey;
|
|
45
|
+
private String clerkDomain;
|
|
46
|
+
private String clientToken;
|
|
47
|
+
private String sessionToken;
|
|
48
|
+
private JSONObject currentUser;
|
|
49
|
+
|
|
50
|
+
@Override
|
|
51
|
+
public void load() {
|
|
52
|
+
super.load();
|
|
53
|
+
client = new OkHttpClient();
|
|
54
|
+
executor = Executors.newSingleThreadExecutor();
|
|
55
|
+
|
|
56
|
+
// Load saved tokens
|
|
57
|
+
SharedPreferences prefs = getContext().getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
|
58
|
+
clientToken = prefs.getString(PREF_CLIENT_TOKEN, null);
|
|
59
|
+
sessionToken = prefs.getString(PREF_SESSION_TOKEN, null);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private void saveTokens() {
|
|
63
|
+
SharedPreferences prefs = getContext().getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
|
64
|
+
SharedPreferences.Editor editor = prefs.edit();
|
|
65
|
+
editor.putString(PREF_CLIENT_TOKEN, clientToken);
|
|
66
|
+
editor.putString(PREF_SESSION_TOKEN, sessionToken);
|
|
67
|
+
editor.apply();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private void clearTokens() {
|
|
71
|
+
clientToken = null;
|
|
72
|
+
sessionToken = null;
|
|
73
|
+
currentUser = null;
|
|
74
|
+
SharedPreferences prefs = getContext().getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
|
75
|
+
prefs.edit().clear().apply();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private String getClerkApiUrl(String path) {
|
|
79
|
+
return "https://" + clerkDomain + path;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private Request.Builder createRequestBuilder(String url) {
|
|
83
|
+
Request.Builder builder = new Request.Builder()
|
|
84
|
+
.url(url)
|
|
85
|
+
.header("Content-Type", "application/json")
|
|
86
|
+
.header("Authorization", publishableKey)
|
|
87
|
+
.header("Origin", "https://app.trainonapp.com") // Spoof origin to match web
|
|
88
|
+
.header("User-Agent", "Mozilla/5.0 (Linux; Android) ClerkNative/1.0");
|
|
89
|
+
|
|
90
|
+
if (clientToken != null) {
|
|
91
|
+
builder.header("Cookie", "__client=" + clientToken);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return builder;
|
|
95
|
+
}
|
|
21
96
|
|
|
22
97
|
@PluginMethod
|
|
23
98
|
public void configure(PluginCall call) {
|
|
24
|
-
|
|
99
|
+
publishableKey = call.getString("publishableKey");
|
|
100
|
+
|
|
101
|
+
if (publishableKey == null || publishableKey.isEmpty()) {
|
|
102
|
+
call.reject("Publishable key is required");
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Extract domain from publishable key (base64 decode)
|
|
107
|
+
try {
|
|
108
|
+
String decoded = new String(android.util.Base64.decode(
|
|
109
|
+
publishableKey.replace("pk_live_", "").replace("pk_test_", ""),
|
|
110
|
+
android.util.Base64.DEFAULT
|
|
111
|
+
));
|
|
112
|
+
// Remove trailing $ if present
|
|
113
|
+
clerkDomain = decoded.replace("$", "").trim();
|
|
114
|
+
Log.d(TAG, "Configured with domain: " + clerkDomain);
|
|
115
|
+
} catch (Exception e) {
|
|
116
|
+
// Fallback to default domain
|
|
117
|
+
clerkDomain = "clerk.trainonapp.com";
|
|
118
|
+
Log.w(TAG, "Could not decode domain from key, using default: " + clerkDomain);
|
|
119
|
+
}
|
|
120
|
+
|
|
25
121
|
call.resolve();
|
|
26
122
|
}
|
|
27
123
|
|
|
28
124
|
@PluginMethod
|
|
29
125
|
public void load(PluginCall call) {
|
|
30
|
-
|
|
126
|
+
executor.execute(() -> {
|
|
127
|
+
try {
|
|
128
|
+
// First, get or create a client
|
|
129
|
+
if (clientToken == null) {
|
|
130
|
+
createClient(call);
|
|
131
|
+
} else {
|
|
132
|
+
// Verify existing session
|
|
133
|
+
fetchClientAndUser(call);
|
|
134
|
+
}
|
|
135
|
+
} catch (Exception e) {
|
|
136
|
+
Log.e(TAG, "Load error", e);
|
|
137
|
+
call.reject("Failed to load: " + e.getMessage());
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
private void createClient(PluginCall call) throws IOException, JSONException {
|
|
143
|
+
String url = getClerkApiUrl("/v1/client?_clerk_js_version=5.117.0");
|
|
144
|
+
|
|
145
|
+
Request request = createRequestBuilder(url)
|
|
146
|
+
.post(RequestBody.create("{}", JSON))
|
|
147
|
+
.build();
|
|
148
|
+
|
|
149
|
+
try (Response response = client.newCall(request).execute()) {
|
|
150
|
+
String body = response.body().string();
|
|
151
|
+
Log.d(TAG, "Create client response: " + response.code());
|
|
152
|
+
|
|
153
|
+
if (!response.isSuccessful()) {
|
|
154
|
+
call.reject("Failed to create client: " + response.code() + " - " + body);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
JSONObject json = new JSONObject(body);
|
|
159
|
+
JSONObject responseObj = json.optJSONObject("response");
|
|
160
|
+
|
|
161
|
+
if (responseObj != null) {
|
|
162
|
+
clientToken = responseObj.optString("id", null);
|
|
163
|
+
|
|
164
|
+
// Check for active session
|
|
165
|
+
JSONArray sessions = responseObj.optJSONArray("sessions");
|
|
166
|
+
if (sessions != null && sessions.length() > 0) {
|
|
167
|
+
JSONObject session = sessions.getJSONObject(0);
|
|
168
|
+
sessionToken = session.optString("last_active_token", null);
|
|
169
|
+
currentUser = session.optJSONObject("user");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
saveTokens();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
JSObject result = new JSObject();
|
|
176
|
+
if (currentUser != null) {
|
|
177
|
+
result.put("user", convertUser(currentUser));
|
|
178
|
+
} else {
|
|
179
|
+
result.put("user", JSObject.NULL);
|
|
180
|
+
}
|
|
181
|
+
call.resolve(result);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private void fetchClientAndUser(PluginCall call) throws IOException, JSONException {
|
|
186
|
+
String url = getClerkApiUrl("/v1/client?_clerk_js_version=5.117.0");
|
|
187
|
+
|
|
188
|
+
Request request = createRequestBuilder(url)
|
|
189
|
+
.get()
|
|
190
|
+
.build();
|
|
191
|
+
|
|
192
|
+
try (Response response = client.newCall(request).execute()) {
|
|
193
|
+
String body = response.body().string();
|
|
194
|
+
Log.d(TAG, "Fetch client response: " + response.code());
|
|
195
|
+
|
|
196
|
+
if (!response.isSuccessful()) {
|
|
197
|
+
// Token might be invalid, clear and retry
|
|
198
|
+
clearTokens();
|
|
199
|
+
createClient(call);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
JSONObject json = new JSONObject(body);
|
|
204
|
+
JSONObject responseObj = json.optJSONObject("response");
|
|
205
|
+
|
|
206
|
+
if (responseObj != null) {
|
|
207
|
+
// Check for active session
|
|
208
|
+
JSONArray sessions = responseObj.optJSONArray("sessions");
|
|
209
|
+
if (sessions != null && sessions.length() > 0) {
|
|
210
|
+
JSONObject session = sessions.getJSONObject(0);
|
|
211
|
+
JSONObject lastToken = session.optJSONObject("last_active_token");
|
|
212
|
+
if (lastToken != null) {
|
|
213
|
+
sessionToken = lastToken.optString("jwt", null);
|
|
214
|
+
}
|
|
215
|
+
currentUser = session.optJSONObject("user");
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
saveTokens();
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
JSObject result = new JSObject();
|
|
222
|
+
if (currentUser != null) {
|
|
223
|
+
result.put("user", convertUser(currentUser));
|
|
224
|
+
} else {
|
|
225
|
+
result.put("user", JSObject.NULL);
|
|
226
|
+
}
|
|
227
|
+
call.resolve(result);
|
|
228
|
+
}
|
|
31
229
|
}
|
|
32
230
|
|
|
33
231
|
@PluginMethod
|
|
34
|
-
public void
|
|
35
|
-
call.
|
|
232
|
+
public void signInWithPassword(PluginCall call) {
|
|
233
|
+
String email = call.getString("email");
|
|
234
|
+
String password = call.getString("password");
|
|
235
|
+
|
|
236
|
+
if (email == null || password == null) {
|
|
237
|
+
call.reject("Email and password are required");
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
executor.execute(() -> {
|
|
242
|
+
try {
|
|
243
|
+
// Step 1: Create sign-in attempt
|
|
244
|
+
String createUrl = getClerkApiUrl("/v1/client/sign_ins?_clerk_js_version=5.117.0");
|
|
245
|
+
|
|
246
|
+
JSONObject createBody = new JSONObject();
|
|
247
|
+
createBody.put("identifier", email);
|
|
248
|
+
|
|
249
|
+
Request createRequest = createRequestBuilder(createUrl)
|
|
250
|
+
.post(RequestBody.create(createBody.toString(), JSON))
|
|
251
|
+
.build();
|
|
252
|
+
|
|
253
|
+
String signInId;
|
|
254
|
+
try (Response response = client.newCall(createRequest).execute()) {
|
|
255
|
+
String body = response.body().string();
|
|
256
|
+
Log.d(TAG, "Create sign-in response: " + response.code());
|
|
257
|
+
|
|
258
|
+
if (!response.isSuccessful()) {
|
|
259
|
+
call.reject("Failed to start sign in: " + body);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
JSONObject json = new JSONObject(body);
|
|
264
|
+
JSONObject responseObj = json.optJSONObject("response");
|
|
265
|
+
signInId = responseObj.optString("id");
|
|
266
|
+
|
|
267
|
+
// Update client token from response
|
|
268
|
+
JSONObject clientObj = json.optJSONObject("client");
|
|
269
|
+
if (clientObj != null) {
|
|
270
|
+
clientToken = clientObj.optString("id", clientToken);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Step 2: Attempt with password
|
|
275
|
+
String attemptUrl = getClerkApiUrl("/v1/client/sign_ins/" + signInId + "/attempt_first_factor?_clerk_js_version=5.117.0");
|
|
276
|
+
|
|
277
|
+
JSONObject attemptBody = new JSONObject();
|
|
278
|
+
attemptBody.put("strategy", "password");
|
|
279
|
+
attemptBody.put("password", password);
|
|
280
|
+
|
|
281
|
+
Request attemptRequest = createRequestBuilder(attemptUrl)
|
|
282
|
+
.post(RequestBody.create(attemptBody.toString(), JSON))
|
|
283
|
+
.build();
|
|
284
|
+
|
|
285
|
+
try (Response response = client.newCall(attemptRequest).execute()) {
|
|
286
|
+
String body = response.body().string();
|
|
287
|
+
Log.d(TAG, "Attempt password response: " + response.code());
|
|
288
|
+
|
|
289
|
+
if (!response.isSuccessful()) {
|
|
290
|
+
JSONObject errorJson = new JSONObject(body);
|
|
291
|
+
JSONArray errors = errorJson.optJSONArray("errors");
|
|
292
|
+
if (errors != null && errors.length() > 0) {
|
|
293
|
+
String message = errors.getJSONObject(0).optString("message", "Sign in failed");
|
|
294
|
+
call.reject(message);
|
|
295
|
+
} else {
|
|
296
|
+
call.reject("Sign in failed: " + body);
|
|
297
|
+
}
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
JSONObject json = new JSONObject(body);
|
|
302
|
+
|
|
303
|
+
// Extract session and user from response
|
|
304
|
+
JSONObject clientObj = json.optJSONObject("client");
|
|
305
|
+
if (clientObj != null) {
|
|
306
|
+
clientToken = clientObj.optString("id", clientToken);
|
|
307
|
+
|
|
308
|
+
JSONArray sessions = clientObj.optJSONArray("sessions");
|
|
309
|
+
if (sessions != null && sessions.length() > 0) {
|
|
310
|
+
JSONObject session = sessions.getJSONObject(0);
|
|
311
|
+
JSONObject lastToken = session.optJSONObject("last_active_token");
|
|
312
|
+
if (lastToken != null) {
|
|
313
|
+
sessionToken = lastToken.optString("jwt", null);
|
|
314
|
+
}
|
|
315
|
+
currentUser = session.optJSONObject("user");
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
saveTokens();
|
|
320
|
+
|
|
321
|
+
JSObject result = new JSObject();
|
|
322
|
+
if (currentUser != null) {
|
|
323
|
+
result.put("user", convertUser(currentUser));
|
|
324
|
+
} else {
|
|
325
|
+
result.put("user", JSObject.NULL);
|
|
326
|
+
}
|
|
327
|
+
call.resolve(result);
|
|
328
|
+
}
|
|
329
|
+
} catch (Exception e) {
|
|
330
|
+
Log.e(TAG, "Sign in error", e);
|
|
331
|
+
call.reject("Sign in failed: " + e.getMessage());
|
|
332
|
+
}
|
|
333
|
+
});
|
|
36
334
|
}
|
|
37
335
|
|
|
38
336
|
@PluginMethod
|
|
39
|
-
public void
|
|
40
|
-
|
|
337
|
+
public void getUser(PluginCall call) {
|
|
338
|
+
JSObject result = new JSObject();
|
|
339
|
+
if (currentUser != null) {
|
|
340
|
+
try {
|
|
341
|
+
result.put("user", convertUser(currentUser));
|
|
342
|
+
} catch (JSONException e) {
|
|
343
|
+
result.put("user", JSObject.NULL);
|
|
344
|
+
}
|
|
345
|
+
} else {
|
|
346
|
+
result.put("user", JSObject.NULL);
|
|
347
|
+
}
|
|
348
|
+
call.resolve(result);
|
|
41
349
|
}
|
|
42
350
|
|
|
43
351
|
@PluginMethod
|
|
44
|
-
public void
|
|
45
|
-
|
|
352
|
+
public void getToken(PluginCall call) {
|
|
353
|
+
JSObject result = new JSObject();
|
|
354
|
+
result.put("token", sessionToken != null ? sessionToken : JSObject.NULL);
|
|
355
|
+
call.resolve(result);
|
|
46
356
|
}
|
|
47
357
|
|
|
48
358
|
@PluginMethod
|
|
49
|
-
public void
|
|
50
|
-
|
|
359
|
+
public void signOut(PluginCall call) {
|
|
360
|
+
if (clientToken == null) {
|
|
361
|
+
clearTokens();
|
|
362
|
+
call.resolve();
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
executor.execute(() -> {
|
|
367
|
+
try {
|
|
368
|
+
String url = getClerkApiUrl("/v1/client/sessions?_clerk_js_version=5.117.0");
|
|
369
|
+
|
|
370
|
+
Request request = createRequestBuilder(url)
|
|
371
|
+
.delete()
|
|
372
|
+
.build();
|
|
373
|
+
|
|
374
|
+
try (Response response = client.newCall(request).execute()) {
|
|
375
|
+
Log.d(TAG, "Sign out response: " + response.code());
|
|
376
|
+
// Clear tokens regardless of response
|
|
377
|
+
clearTokens();
|
|
378
|
+
call.resolve();
|
|
379
|
+
}
|
|
380
|
+
} catch (Exception e) {
|
|
381
|
+
Log.e(TAG, "Sign out error", e);
|
|
382
|
+
clearTokens();
|
|
383
|
+
call.resolve(); // Still resolve since we cleared local state
|
|
384
|
+
}
|
|
385
|
+
});
|
|
51
386
|
}
|
|
52
387
|
|
|
53
388
|
@PluginMethod
|
|
54
|
-
public void
|
|
55
|
-
call.
|
|
389
|
+
public void signInWithEmail(PluginCall call) {
|
|
390
|
+
String email = call.getString("email");
|
|
391
|
+
|
|
392
|
+
if (email == null) {
|
|
393
|
+
call.reject("Email is required");
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
executor.execute(() -> {
|
|
398
|
+
try {
|
|
399
|
+
String createUrl = getClerkApiUrl("/v1/client/sign_ins?_clerk_js_version=5.117.0");
|
|
400
|
+
|
|
401
|
+
JSONObject createBody = new JSONObject();
|
|
402
|
+
createBody.put("identifier", email);
|
|
403
|
+
|
|
404
|
+
Request request = createRequestBuilder(createUrl)
|
|
405
|
+
.post(RequestBody.create(createBody.toString(), JSON))
|
|
406
|
+
.build();
|
|
407
|
+
|
|
408
|
+
try (Response response = client.newCall(request).execute()) {
|
|
409
|
+
String body = response.body().string();
|
|
410
|
+
Log.d(TAG, "Sign in with email response: " + response.code());
|
|
411
|
+
|
|
412
|
+
if (!response.isSuccessful()) {
|
|
413
|
+
call.reject("Failed to start sign in: " + body);
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// TODO: Prepare email code flow
|
|
418
|
+
// For now, return that code is required
|
|
419
|
+
JSObject result = new JSObject();
|
|
420
|
+
result.put("requiresCode", true);
|
|
421
|
+
call.resolve(result);
|
|
422
|
+
}
|
|
423
|
+
} catch (Exception e) {
|
|
424
|
+
Log.e(TAG, "Sign in with email error", e);
|
|
425
|
+
call.reject("Failed: " + e.getMessage());
|
|
426
|
+
}
|
|
427
|
+
});
|
|
56
428
|
}
|
|
57
429
|
|
|
58
430
|
@PluginMethod
|
|
59
|
-
public void
|
|
60
|
-
|
|
431
|
+
public void verifyEmailCode(PluginCall call) {
|
|
432
|
+
// TODO: Implement email code verification
|
|
433
|
+
call.reject("Email code verification not yet implemented on Android");
|
|
61
434
|
}
|
|
62
435
|
|
|
63
436
|
@PluginMethod
|
|
64
|
-
public void
|
|
65
|
-
call.
|
|
437
|
+
public void signUp(PluginCall call) {
|
|
438
|
+
String email = call.getString("emailAddress");
|
|
439
|
+
String password = call.getString("password");
|
|
440
|
+
String firstName = call.getString("firstName");
|
|
441
|
+
String lastName = call.getString("lastName");
|
|
442
|
+
|
|
443
|
+
if (email == null || password == null) {
|
|
444
|
+
call.reject("Email and password are required");
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
executor.execute(() -> {
|
|
449
|
+
try {
|
|
450
|
+
String url = getClerkApiUrl("/v1/client/sign_ups?_clerk_js_version=5.117.0");
|
|
451
|
+
|
|
452
|
+
JSONObject body = new JSONObject();
|
|
453
|
+
body.put("email_address", email);
|
|
454
|
+
body.put("password", password);
|
|
455
|
+
if (firstName != null) body.put("first_name", firstName);
|
|
456
|
+
if (lastName != null) body.put("last_name", lastName);
|
|
457
|
+
|
|
458
|
+
Request request = createRequestBuilder(url)
|
|
459
|
+
.post(RequestBody.create(body.toString(), JSON))
|
|
460
|
+
.build();
|
|
461
|
+
|
|
462
|
+
try (Response response = client.newCall(request).execute()) {
|
|
463
|
+
String responseBody = response.body().string();
|
|
464
|
+
Log.d(TAG, "Sign up response: " + response.code());
|
|
465
|
+
|
|
466
|
+
if (!response.isSuccessful()) {
|
|
467
|
+
JSONObject errorJson = new JSONObject(responseBody);
|
|
468
|
+
JSONArray errors = errorJson.optJSONArray("errors");
|
|
469
|
+
if (errors != null && errors.length() > 0) {
|
|
470
|
+
String message = errors.getJSONObject(0).optString("message", "Sign up failed");
|
|
471
|
+
call.reject(message);
|
|
472
|
+
} else {
|
|
473
|
+
call.reject("Sign up failed: " + responseBody);
|
|
474
|
+
}
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
JSONObject json = new JSONObject(responseBody);
|
|
479
|
+
|
|
480
|
+
// Check if email verification is required
|
|
481
|
+
JSONObject responseObj = json.optJSONObject("response");
|
|
482
|
+
boolean requiresVerification = false;
|
|
483
|
+
if (responseObj != null) {
|
|
484
|
+
JSONArray verifications = responseObj.optJSONArray("verifications");
|
|
485
|
+
if (verifications != null) {
|
|
486
|
+
requiresVerification = true;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
JSObject result = new JSObject();
|
|
491
|
+
result.put("requiresVerification", requiresVerification);
|
|
492
|
+
if (currentUser != null) {
|
|
493
|
+
result.put("user", convertUser(currentUser));
|
|
494
|
+
} else {
|
|
495
|
+
result.put("user", JSObject.NULL);
|
|
496
|
+
}
|
|
497
|
+
call.resolve(result);
|
|
498
|
+
}
|
|
499
|
+
} catch (Exception e) {
|
|
500
|
+
Log.e(TAG, "Sign up error", e);
|
|
501
|
+
call.reject("Sign up failed: " + e.getMessage());
|
|
502
|
+
}
|
|
503
|
+
});
|
|
66
504
|
}
|
|
67
505
|
|
|
68
506
|
@PluginMethod
|
|
69
|
-
public void
|
|
70
|
-
call.reject(
|
|
507
|
+
public void verifySignUpEmail(PluginCall call) {
|
|
508
|
+
call.reject("Sign up email verification not yet implemented on Android");
|
|
71
509
|
}
|
|
72
510
|
|
|
73
511
|
@PluginMethod
|
|
74
512
|
public void updateUser(PluginCall call) {
|
|
75
|
-
call.reject(
|
|
513
|
+
call.reject("Update user not yet implemented on Android");
|
|
76
514
|
}
|
|
77
515
|
|
|
78
516
|
@PluginMethod
|
|
79
517
|
public void requestPasswordReset(PluginCall call) {
|
|
80
|
-
call.reject(
|
|
518
|
+
call.reject("Password reset not yet implemented on Android");
|
|
81
519
|
}
|
|
82
520
|
|
|
83
521
|
@PluginMethod
|
|
84
522
|
public void resetPassword(PluginCall call) {
|
|
85
|
-
call.reject(
|
|
523
|
+
call.reject("Password reset not yet implemented on Android");
|
|
86
524
|
}
|
|
87
525
|
|
|
88
526
|
@PluginMethod
|
|
89
527
|
public void refreshSession(PluginCall call) {
|
|
90
|
-
call.reject(
|
|
528
|
+
call.reject("Refresh session not yet implemented on Android");
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
private JSObject convertUser(JSONObject clerkUser) throws JSONException {
|
|
532
|
+
JSObject user = new JSObject();
|
|
533
|
+
user.put("id", clerkUser.optString("id", null));
|
|
534
|
+
user.put("firstName", clerkUser.optString("first_name", null));
|
|
535
|
+
user.put("lastName", clerkUser.optString("last_name", null));
|
|
536
|
+
user.put("imageUrl", clerkUser.optString("image_url", null));
|
|
537
|
+
user.put("username", clerkUser.optString("username", null));
|
|
538
|
+
|
|
539
|
+
// Get primary email
|
|
540
|
+
JSONArray emailAddresses = clerkUser.optJSONArray("email_addresses");
|
|
541
|
+
if (emailAddresses != null && emailAddresses.length() > 0) {
|
|
542
|
+
String primaryEmailId = clerkUser.optString("primary_email_address_id", null);
|
|
543
|
+
for (int i = 0; i < emailAddresses.length(); i++) {
|
|
544
|
+
JSONObject emailObj = emailAddresses.getJSONObject(i);
|
|
545
|
+
if (primaryEmailId != null && primaryEmailId.equals(emailObj.optString("id"))) {
|
|
546
|
+
user.put("emailAddress", emailObj.optString("email_address", null));
|
|
547
|
+
break;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
// Fallback to first email if no primary found
|
|
551
|
+
if (user.optString("emailAddress", null) == null) {
|
|
552
|
+
user.put("emailAddress", emailAddresses.getJSONObject(0).optString("email_address", null));
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
return user;
|
|
91
557
|
}
|
|
92
558
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trainon-inc/capacitor-clerk-native",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.24.0",
|
|
4
4
|
"description": "Capacitor plugin for Clerk native authentication using bridge pattern to integrate Clerk iOS/Android SDKs with CocoaPods/Gradle",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|