@trainon-inc/capacitor-clerk-native 1.22.0 → 1.23.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/README.md
CHANGED
|
@@ -339,19 +339,21 @@ function Profile() {
|
|
|
339
339
|
└─────────────────────────────────────────────────┘
|
|
340
340
|
```
|
|
341
341
|
|
|
342
|
-
### Android Architecture
|
|
342
|
+
### Android Architecture (Web Provider)
|
|
343
343
|
|
|
344
344
|
```
|
|
345
345
|
┌─────────────────────────────────────────────────┐
|
|
346
346
|
│ JavaScript/React (Capacitor WebView) │
|
|
347
|
-
│ - Uses
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
│
|
|
354
|
-
│ -
|
|
347
|
+
│ - Uses @clerk/clerk-react (web provider) │
|
|
348
|
+
│ - Full Clerk functionality via web SDK │
|
|
349
|
+
└─────────────────────────────────────────────────┘
|
|
350
|
+
(No native plugin needed for auth)
|
|
351
|
+
|
|
352
|
+
┌─────────────────────────────────────────────────┐
|
|
353
|
+
│ ClerkNativePlugin (Gradle Module) - Stub │
|
|
354
|
+
│ - Exists for Capacitor plugin registration │
|
|
355
|
+
│ - Returns "use web provider" for all methods │
|
|
356
|
+
│ - No native Clerk SDK dependency │
|
|
355
357
|
└─────────────────────────────────────────────────┘
|
|
356
358
|
```
|
|
357
359
|
|
|
@@ -414,80 +416,51 @@ function Profile() {
|
|
|
414
416
|
|
|
415
417
|
## Android Setup
|
|
416
418
|
|
|
417
|
-
Android
|
|
418
|
-
|
|
419
|
-
### Prerequisites
|
|
420
|
-
|
|
421
|
-
- ✅ A [Clerk account](https://dashboard.clerk.com/sign-up)
|
|
422
|
-
- ✅ A Clerk application set up in the dashboard
|
|
423
|
-
- ✅ **Native API enabled** in Clerk Dashboard → Settings → Native Applications
|
|
424
|
-
- ✅ Android Studio with Gradle 8.7+ and AGP 8.5+
|
|
425
|
-
- ✅ JDK 17 or higher
|
|
426
|
-
|
|
427
|
-
### 1. Register Your Android App with Clerk
|
|
428
|
-
|
|
429
|
-
1. Go to the [**Native Applications**](https://dashboard.clerk.com/last-active?path=native-applications) page in Clerk Dashboard
|
|
430
|
-
2. Click **"Add Application"**
|
|
431
|
-
3. Select **Android** tab
|
|
432
|
-
4. Enter your Android app details:
|
|
433
|
-
- **Package Name**: Your app's package name (e.g., `com.trainon.member`)
|
|
434
|
-
- **SHA256 Fingerprint**: Your app's signing certificate fingerprint (get it with `./gradlew signingReport`)
|
|
435
|
-
|
|
436
|
-
### 2. Sync Capacitor
|
|
437
|
-
|
|
438
|
-
After installing the plugin, sync your Android project:
|
|
419
|
+
**Important**: On Android, this plugin provides a stub implementation. Android WebViews work well with web-based authentication (unlike iOS which has cookie issues), so **Android should use the web Clerk provider (`@clerk/clerk-react`)** instead of the native plugin.
|
|
439
420
|
|
|
440
|
-
|
|
441
|
-
npx cap sync android
|
|
442
|
-
```
|
|
421
|
+
### Why Web Provider for Android?
|
|
443
422
|
|
|
444
|
-
|
|
445
|
-
-
|
|
446
|
-
-
|
|
447
|
-
-
|
|
423
|
+
- ✅ Android WebViews handle cookies correctly - no authentication issues
|
|
424
|
+
- ✅ The Clerk Android SDK is still in early stages (v0.1.x) with evolving APIs
|
|
425
|
+
- ✅ Using `@clerk/clerk-react` provides a stable, well-tested experience
|
|
426
|
+
- ✅ Simpler setup - no native configuration required
|
|
448
427
|
|
|
449
|
-
###
|
|
428
|
+
### Recommended App Setup
|
|
450
429
|
|
|
451
|
-
|
|
430
|
+
Configure your app to use the native plugin only on iOS:
|
|
452
431
|
|
|
453
432
|
```typescript
|
|
454
|
-
import {
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
433
|
+
import { Capacitor } from "@capacitor/core";
|
|
434
|
+
import { ClerkProvider as WebClerkProvider } from "@clerk/clerk-react";
|
|
435
|
+
import { ClerkProvider as NativeClerkProvider } from "@trainon-inc/capacitor-clerk-native";
|
|
436
|
+
|
|
437
|
+
// Use native Clerk only on iOS (due to WebView cookie issues)
|
|
438
|
+
// Android WebViews work fine with web Clerk
|
|
439
|
+
const isIOS = Capacitor.getPlatform() === "ios";
|
|
440
|
+
const ClerkProvider = isIOS ? NativeClerkProvider : WebClerkProvider;
|
|
441
|
+
|
|
442
|
+
export function App() {
|
|
443
|
+
const clerkProps = isIOS
|
|
444
|
+
? { publishableKey: "pk_test_..." }
|
|
445
|
+
: {
|
|
446
|
+
publishableKey: "pk_test_...",
|
|
447
|
+
signInFallbackRedirectUrl: "/home",
|
|
448
|
+
signUpFallbackRedirectUrl: "/home",
|
|
449
|
+
};
|
|
460
450
|
|
|
461
|
-
|
|
462
|
-
|
|
451
|
+
return (
|
|
452
|
+
<ClerkProvider {...clerkProps}>
|
|
453
|
+
<YourApp />
|
|
454
|
+
</ClerkProvider>
|
|
455
|
+
);
|
|
456
|
+
}
|
|
463
457
|
```
|
|
464
458
|
|
|
465
|
-
###
|
|
466
|
-
|
|
467
|
-
The Android implementation supports all authentication methods:
|
|
468
|
-
|
|
469
|
-
| Method | Description |
|
|
470
|
-
|--------|-------------|
|
|
471
|
-
| `configure()` | Initialize Clerk with publishable key |
|
|
472
|
-
| `load()` | Load Clerk and check for existing session |
|
|
473
|
-
| `signInWithPassword()` | Sign in with email and password |
|
|
474
|
-
| `signInWithEmail()` | Start email code sign in flow |
|
|
475
|
-
| `verifyEmailCode()` | Verify email code |
|
|
476
|
-
| `signUp()` | Create a new account |
|
|
477
|
-
| `verifySignUpEmail()` | Verify sign up email |
|
|
478
|
-
| `getUser()` | Get current user |
|
|
479
|
-
| `getToken()` | Get authentication token |
|
|
480
|
-
| `signOut()` | Sign out current user |
|
|
481
|
-
| `updateUser()` | Update user profile |
|
|
482
|
-
| `requestPasswordReset()` | Request password reset |
|
|
483
|
-
| `resetPassword()` | Reset password with code |
|
|
484
|
-
| `refreshSession()` | Refresh session token |
|
|
485
|
-
|
|
486
|
-
### 5. Build Requirements
|
|
459
|
+
### Build Requirements
|
|
487
460
|
|
|
488
461
|
- **Gradle**: 8.11.1+
|
|
489
|
-
- **Android Gradle Plugin**: 8.
|
|
490
|
-
- **Java
|
|
462
|
+
- **Android Gradle Plugin**: 8.5.0+
|
|
463
|
+
- **Java**: 17+
|
|
491
464
|
- **Min SDK**: 23 (Android 6.0)
|
|
492
465
|
- **Target SDK**: 35 (Android 15)
|
|
493
466
|
|
|
@@ -499,14 +472,14 @@ Could not resolve project :trainon-inc-capacitor-clerk-native
|
|
|
499
472
|
No matching variant of project was found. No variants exist.
|
|
500
473
|
```
|
|
501
474
|
|
|
502
|
-
**Solution**:
|
|
475
|
+
**Solution**: Update to the latest plugin version:
|
|
503
476
|
```bash
|
|
504
477
|
npm update @trainon-inc/capacitor-clerk-native
|
|
505
478
|
npx cap sync android
|
|
506
479
|
```
|
|
507
480
|
|
|
508
481
|
#### "invalid source release: 21" error
|
|
509
|
-
The plugin uses Java 17
|
|
482
|
+
The plugin uses Java 17. Ensure your Android Studio uses JDK 17+:
|
|
510
483
|
- **File → Project Structure → SDK Location → Gradle JDK** → Select JDK 17+
|
|
511
484
|
|
|
512
485
|
#### Gradle sync fails
|
|
@@ -514,12 +487,6 @@ The plugin uses Java 17 by default. Ensure your Android Studio uses JDK 17+:
|
|
|
514
487
|
- Invalidate caches: **File → Invalidate Caches / Restart**
|
|
515
488
|
- Delete `.gradle` folder and re-sync
|
|
516
489
|
|
|
517
|
-
#### Network errors
|
|
518
|
-
Ensure INTERNET permission is in your `AndroidManifest.xml`:
|
|
519
|
-
```xml
|
|
520
|
-
<uses-permission android:name="android.permission.INTERNET" />
|
|
521
|
-
```
|
|
522
|
-
|
|
523
490
|
## Contributing
|
|
524
491
|
|
|
525
492
|
Contributions are welcome! This plugin was created to solve a real problem we encountered, and we'd love to make it better.
|
package/android/build.gradle
CHANGED
|
@@ -3,7 +3,6 @@ ext {
|
|
|
3
3
|
androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.0'
|
|
4
4
|
androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.2.1'
|
|
5
5
|
androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.6.1'
|
|
6
|
-
kotlinVersion = project.hasProperty('kotlinVersion') ? rootProject.ext.kotlinVersion : '1.9.25'
|
|
7
6
|
}
|
|
8
7
|
|
|
9
8
|
buildscript {
|
|
@@ -13,12 +12,10 @@ buildscript {
|
|
|
13
12
|
}
|
|
14
13
|
dependencies {
|
|
15
14
|
classpath 'com.android.tools.build:gradle:8.7.2'
|
|
16
|
-
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.25"
|
|
17
15
|
}
|
|
18
16
|
}
|
|
19
17
|
|
|
20
18
|
apply plugin: 'com.android.library'
|
|
21
|
-
apply plugin: 'kotlin-android'
|
|
22
19
|
|
|
23
20
|
android {
|
|
24
21
|
namespace "com.trainon.capacitor.clerk"
|
|
@@ -43,9 +40,6 @@ android {
|
|
|
43
40
|
sourceCompatibility JavaVersion.VERSION_17
|
|
44
41
|
targetCompatibility JavaVersion.VERSION_17
|
|
45
42
|
}
|
|
46
|
-
kotlinOptions {
|
|
47
|
-
jvmTarget = '17'
|
|
48
|
-
}
|
|
49
43
|
}
|
|
50
44
|
|
|
51
45
|
repositories {
|
|
@@ -57,15 +51,6 @@ dependencies {
|
|
|
57
51
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
|
58
52
|
implementation project(':capacitor-android')
|
|
59
53
|
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
|
60
|
-
|
|
61
|
-
// Kotlin
|
|
62
|
-
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
|
|
63
|
-
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
|
|
64
|
-
|
|
65
|
-
// Clerk Android SDK
|
|
66
|
-
implementation "com.clerk:clerk-android:0.8.0"
|
|
67
|
-
|
|
68
|
-
// Testing
|
|
69
54
|
testImplementation "junit:junit:$junitVersion"
|
|
70
55
|
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
|
|
71
56
|
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
package com.trainon.capacitor.clerk;
|
|
2
|
+
|
|
3
|
+
import com.getcapacitor.Plugin;
|
|
4
|
+
import com.getcapacitor.PluginCall;
|
|
5
|
+
import com.getcapacitor.PluginMethod;
|
|
6
|
+
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Android stub for Clerk Native plugin.
|
|
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).
|
|
13
|
+
*
|
|
14
|
+
* This plugin exists to satisfy Capacitor's plugin registration but all methods
|
|
15
|
+
* will return errors indicating to use the web provider instead.
|
|
16
|
+
*/
|
|
17
|
+
@CapacitorPlugin(name = "ClerkNative")
|
|
18
|
+
public class ClerkNativePlugin extends Plugin {
|
|
19
|
+
|
|
20
|
+
private static final String USE_WEB_MESSAGE = "Android uses web Clerk provider. Configure your app to use @clerk/clerk-react on Android.";
|
|
21
|
+
|
|
22
|
+
@PluginMethod
|
|
23
|
+
public void configure(PluginCall call) {
|
|
24
|
+
// Allow configure to succeed silently - the web provider will handle auth
|
|
25
|
+
call.resolve();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@PluginMethod
|
|
29
|
+
public void load(PluginCall call) {
|
|
30
|
+
call.reject(USE_WEB_MESSAGE);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@PluginMethod
|
|
34
|
+
public void signInWithEmail(PluginCall call) {
|
|
35
|
+
call.reject(USE_WEB_MESSAGE);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@PluginMethod
|
|
39
|
+
public void verifyEmailCode(PluginCall call) {
|
|
40
|
+
call.reject(USE_WEB_MESSAGE);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@PluginMethod
|
|
44
|
+
public void signInWithPassword(PluginCall call) {
|
|
45
|
+
call.reject(USE_WEB_MESSAGE);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@PluginMethod
|
|
49
|
+
public void signUp(PluginCall call) {
|
|
50
|
+
call.reject(USE_WEB_MESSAGE);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@PluginMethod
|
|
54
|
+
public void verifySignUpEmail(PluginCall call) {
|
|
55
|
+
call.reject(USE_WEB_MESSAGE);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@PluginMethod
|
|
59
|
+
public void getUser(PluginCall call) {
|
|
60
|
+
call.reject(USE_WEB_MESSAGE);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@PluginMethod
|
|
64
|
+
public void getToken(PluginCall call) {
|
|
65
|
+
call.reject(USE_WEB_MESSAGE);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@PluginMethod
|
|
69
|
+
public void signOut(PluginCall call) {
|
|
70
|
+
call.reject(USE_WEB_MESSAGE);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@PluginMethod
|
|
74
|
+
public void updateUser(PluginCall call) {
|
|
75
|
+
call.reject(USE_WEB_MESSAGE);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@PluginMethod
|
|
79
|
+
public void requestPasswordReset(PluginCall call) {
|
|
80
|
+
call.reject(USE_WEB_MESSAGE);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@PluginMethod
|
|
84
|
+
public void resetPassword(PluginCall call) {
|
|
85
|
+
call.reject(USE_WEB_MESSAGE);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@PluginMethod
|
|
89
|
+
public void refreshSession(PluginCall call) {
|
|
90
|
+
call.reject(USE_WEB_MESSAGE);
|
|
91
|
+
}
|
|
92
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trainon-inc/capacitor-clerk-native",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.23.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",
|
|
@@ -1,509 +0,0 @@
|
|
|
1
|
-
package com.trainon.capacitor.clerk
|
|
2
|
-
|
|
3
|
-
import android.util.Log
|
|
4
|
-
import com.clerk.android.Clerk
|
|
5
|
-
import com.clerk.android.models.Session
|
|
6
|
-
import com.clerk.android.models.SignIn
|
|
7
|
-
import com.clerk.android.models.SignUp
|
|
8
|
-
import com.clerk.android.models.User
|
|
9
|
-
import com.getcapacitor.JSObject
|
|
10
|
-
import com.getcapacitor.Plugin
|
|
11
|
-
import com.getcapacitor.PluginCall
|
|
12
|
-
import com.getcapacitor.PluginMethod
|
|
13
|
-
import com.getcapacitor.annotation.CapacitorPlugin
|
|
14
|
-
import kotlinx.coroutines.CoroutineScope
|
|
15
|
-
import kotlinx.coroutines.Dispatchers
|
|
16
|
-
import kotlinx.coroutines.SupervisorJob
|
|
17
|
-
import kotlinx.coroutines.launch
|
|
18
|
-
|
|
19
|
-
@CapacitorPlugin(name = "ClerkNative")
|
|
20
|
-
class ClerkNativePlugin : Plugin() {
|
|
21
|
-
|
|
22
|
-
private val TAG = "ClerkNativePlugin"
|
|
23
|
-
private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
|
24
|
-
private var isConfigured = false
|
|
25
|
-
|
|
26
|
-
@PluginMethod
|
|
27
|
-
fun configure(call: PluginCall) {
|
|
28
|
-
val publishableKey = call.getString("publishableKey")
|
|
29
|
-
|
|
30
|
-
if (publishableKey.isNullOrEmpty()) {
|
|
31
|
-
call.reject("Must provide publishableKey")
|
|
32
|
-
return
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
try {
|
|
36
|
-
// Initialize Clerk with the publishable key
|
|
37
|
-
Clerk.configure(context, publishableKey)
|
|
38
|
-
isConfigured = true
|
|
39
|
-
Log.d(TAG, "Clerk configured successfully")
|
|
40
|
-
call.resolve()
|
|
41
|
-
} catch (e: Exception) {
|
|
42
|
-
Log.e(TAG, "Failed to configure Clerk", e)
|
|
43
|
-
call.reject("Failed to configure Clerk: ${e.message}")
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
@PluginMethod
|
|
48
|
-
fun load(call: PluginCall) {
|
|
49
|
-
if (!isConfigured) {
|
|
50
|
-
call.reject("Clerk not configured. Call configure() first.")
|
|
51
|
-
return
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
scope.launch {
|
|
55
|
-
try {
|
|
56
|
-
val user = Clerk.user
|
|
57
|
-
val result = JSObject()
|
|
58
|
-
if (user != null) {
|
|
59
|
-
result.put("user", userToJSObject(user))
|
|
60
|
-
} else {
|
|
61
|
-
result.put("user", null)
|
|
62
|
-
}
|
|
63
|
-
call.resolve(result)
|
|
64
|
-
} catch (e: Exception) {
|
|
65
|
-
Log.e(TAG, "Failed to load Clerk", e)
|
|
66
|
-
call.reject("Failed to load Clerk: ${e.message}")
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
@PluginMethod
|
|
72
|
-
fun signInWithEmail(call: PluginCall) {
|
|
73
|
-
val email = call.getString("email")
|
|
74
|
-
|
|
75
|
-
if (email.isNullOrEmpty()) {
|
|
76
|
-
call.reject("Must provide email")
|
|
77
|
-
return
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
scope.launch {
|
|
81
|
-
try {
|
|
82
|
-
val signIn = SignIn.create(identifier = email)
|
|
83
|
-
signIn.onSuccess { si ->
|
|
84
|
-
// Prepare email code verification
|
|
85
|
-
si.prepareFirstFactor(
|
|
86
|
-
strategy = SignIn.PrepareFirstFactorStrategy.EmailCode(
|
|
87
|
-
emailAddressId = si.supportedFirstFactors
|
|
88
|
-
?.filterIsInstance<SignIn.Factor.EmailCode>()
|
|
89
|
-
?.firstOrNull()
|
|
90
|
-
?.emailAddressId ?: ""
|
|
91
|
-
)
|
|
92
|
-
).onSuccess {
|
|
93
|
-
val result = JSObject()
|
|
94
|
-
result.put("requiresCode", true)
|
|
95
|
-
call.resolve(result)
|
|
96
|
-
}.onFailure { error ->
|
|
97
|
-
call.reject("Failed to prepare email code: ${error.message}")
|
|
98
|
-
}
|
|
99
|
-
}.onFailure { error ->
|
|
100
|
-
call.reject("Sign in with email failed: ${error.message}")
|
|
101
|
-
}
|
|
102
|
-
} catch (e: Exception) {
|
|
103
|
-
Log.e(TAG, "Sign in with email failed", e)
|
|
104
|
-
call.reject("Sign in with email failed: ${e.message}")
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
@PluginMethod
|
|
110
|
-
fun verifyEmailCode(call: PluginCall) {
|
|
111
|
-
val code = call.getString("code")
|
|
112
|
-
|
|
113
|
-
if (code.isNullOrEmpty()) {
|
|
114
|
-
call.reject("Must provide code")
|
|
115
|
-
return
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
scope.launch {
|
|
119
|
-
try {
|
|
120
|
-
val signIn = Clerk.client?.signIn
|
|
121
|
-
if (signIn == null) {
|
|
122
|
-
call.reject("No active sign in session")
|
|
123
|
-
return@launch
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
signIn.attemptFirstFactor(
|
|
127
|
-
strategy = SignIn.AttemptFirstFactorStrategy.EmailCode(code = code)
|
|
128
|
-
).onSuccess { si ->
|
|
129
|
-
if (si.status == SignIn.Status.COMPLETE) {
|
|
130
|
-
// Set active session
|
|
131
|
-
Clerk.client?.activeSessions?.firstOrNull()?.let { session ->
|
|
132
|
-
Clerk.setActive(sessionId = session.id)
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
val user = Clerk.user
|
|
136
|
-
val result = JSObject()
|
|
137
|
-
result.put("user", user?.let { userToJSObject(it) })
|
|
138
|
-
call.resolve(result)
|
|
139
|
-
} else {
|
|
140
|
-
call.reject("Verification incomplete")
|
|
141
|
-
}
|
|
142
|
-
}.onFailure { error ->
|
|
143
|
-
call.reject("Email code verification failed: ${error.message}")
|
|
144
|
-
}
|
|
145
|
-
} catch (e: Exception) {
|
|
146
|
-
Log.e(TAG, "Email code verification failed", e)
|
|
147
|
-
call.reject("Email code verification failed: ${e.message}")
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
@PluginMethod
|
|
153
|
-
fun signInWithPassword(call: PluginCall) {
|
|
154
|
-
val email = call.getString("email")
|
|
155
|
-
val password = call.getString("password")
|
|
156
|
-
|
|
157
|
-
if (email.isNullOrEmpty() || password.isNullOrEmpty()) {
|
|
158
|
-
call.reject("Must provide email and password")
|
|
159
|
-
return
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
scope.launch {
|
|
163
|
-
try {
|
|
164
|
-
val signIn = SignIn.create(identifier = email)
|
|
165
|
-
signIn.onSuccess { si ->
|
|
166
|
-
si.attemptFirstFactor(
|
|
167
|
-
strategy = SignIn.AttemptFirstFactorStrategy.Password(password = password)
|
|
168
|
-
).onSuccess { completedSignIn ->
|
|
169
|
-
if (completedSignIn.status == SignIn.Status.COMPLETE) {
|
|
170
|
-
// Set active session
|
|
171
|
-
completedSignIn.createdSessionId?.let { sessionId ->
|
|
172
|
-
Clerk.setActive(sessionId = sessionId)
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Get user after sign in
|
|
176
|
-
val user = Clerk.user
|
|
177
|
-
val result = JSObject()
|
|
178
|
-
result.put("user", user?.let { userToJSObject(it) })
|
|
179
|
-
call.resolve(result)
|
|
180
|
-
} else {
|
|
181
|
-
call.reject("Sign in incomplete")
|
|
182
|
-
}
|
|
183
|
-
}.onFailure { error ->
|
|
184
|
-
call.reject("Sign in failed: ${error.message}")
|
|
185
|
-
}
|
|
186
|
-
}.onFailure { error ->
|
|
187
|
-
call.reject("Sign in failed: ${error.message}")
|
|
188
|
-
}
|
|
189
|
-
} catch (e: Exception) {
|
|
190
|
-
Log.e(TAG, "Sign in with password failed", e)
|
|
191
|
-
call.reject("Sign in with password failed: ${e.message}")
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
@PluginMethod
|
|
197
|
-
fun signUp(call: PluginCall) {
|
|
198
|
-
val emailAddress = call.getString("emailAddress")
|
|
199
|
-
val password = call.getString("password")
|
|
200
|
-
val firstName = call.getString("firstName")
|
|
201
|
-
val lastName = call.getString("lastName")
|
|
202
|
-
|
|
203
|
-
if (emailAddress.isNullOrEmpty() || password.isNullOrEmpty()) {
|
|
204
|
-
call.reject("Must provide emailAddress and password")
|
|
205
|
-
return
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
scope.launch {
|
|
209
|
-
try {
|
|
210
|
-
val signUp = SignUp.create(
|
|
211
|
-
emailAddress = emailAddress,
|
|
212
|
-
password = password,
|
|
213
|
-
firstName = firstName,
|
|
214
|
-
lastName = lastName
|
|
215
|
-
)
|
|
216
|
-
|
|
217
|
-
signUp.onSuccess { su ->
|
|
218
|
-
when (su.status) {
|
|
219
|
-
SignUp.Status.COMPLETE -> {
|
|
220
|
-
// Sign up complete, set active session
|
|
221
|
-
su.createdSessionId?.let { sessionId ->
|
|
222
|
-
Clerk.setActive(sessionId = sessionId)
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
val user = Clerk.user
|
|
226
|
-
val result = JSObject()
|
|
227
|
-
result.put("user", user?.let { userToJSObject(it) })
|
|
228
|
-
result.put("requiresVerification", false)
|
|
229
|
-
call.resolve(result)
|
|
230
|
-
}
|
|
231
|
-
SignUp.Status.MISSING_REQUIREMENTS -> {
|
|
232
|
-
// Needs email verification
|
|
233
|
-
su.prepareEmailAddressVerification().onSuccess {
|
|
234
|
-
val result = JSObject()
|
|
235
|
-
result.put("user", null)
|
|
236
|
-
result.put("requiresVerification", true)
|
|
237
|
-
call.resolve(result)
|
|
238
|
-
}.onFailure { error ->
|
|
239
|
-
call.reject("Failed to prepare email verification: ${error.message}")
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
else -> {
|
|
243
|
-
call.reject("Sign up incomplete: ${su.status}")
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}.onFailure { error ->
|
|
247
|
-
call.reject("Sign up failed: ${error.message}")
|
|
248
|
-
}
|
|
249
|
-
} catch (e: Exception) {
|
|
250
|
-
Log.e(TAG, "Sign up failed", e)
|
|
251
|
-
call.reject("Sign up failed: ${e.message}")
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
@PluginMethod
|
|
257
|
-
fun verifySignUpEmail(call: PluginCall) {
|
|
258
|
-
val code = call.getString("code")
|
|
259
|
-
|
|
260
|
-
if (code.isNullOrEmpty()) {
|
|
261
|
-
call.reject("Must provide code")
|
|
262
|
-
return
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
scope.launch {
|
|
266
|
-
try {
|
|
267
|
-
val signUp = Clerk.client?.signUp
|
|
268
|
-
if (signUp == null) {
|
|
269
|
-
call.reject("No active sign up session")
|
|
270
|
-
return@launch
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
signUp.attemptEmailAddressVerification(code = code)
|
|
274
|
-
.onSuccess { su ->
|
|
275
|
-
if (su.status == SignUp.Status.COMPLETE) {
|
|
276
|
-
su.createdSessionId?.let { sessionId ->
|
|
277
|
-
Clerk.setActive(sessionId = sessionId)
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
val user = Clerk.user
|
|
281
|
-
val result = JSObject()
|
|
282
|
-
result.put("user", user?.let { userToJSObject(it) })
|
|
283
|
-
call.resolve(result)
|
|
284
|
-
} else {
|
|
285
|
-
call.reject("Verification incomplete")
|
|
286
|
-
}
|
|
287
|
-
}.onFailure { error ->
|
|
288
|
-
call.reject("Email verification failed: ${error.message}")
|
|
289
|
-
}
|
|
290
|
-
} catch (e: Exception) {
|
|
291
|
-
Log.e(TAG, "Email verification failed", e)
|
|
292
|
-
call.reject("Email verification failed: ${e.message}")
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
@PluginMethod
|
|
298
|
-
fun getUser(call: PluginCall) {
|
|
299
|
-
scope.launch {
|
|
300
|
-
try {
|
|
301
|
-
val user = Clerk.user
|
|
302
|
-
val result = JSObject()
|
|
303
|
-
if (user != null) {
|
|
304
|
-
result.put("user", userToJSObject(user))
|
|
305
|
-
} else {
|
|
306
|
-
result.put("user", null)
|
|
307
|
-
}
|
|
308
|
-
call.resolve(result)
|
|
309
|
-
} catch (e: Exception) {
|
|
310
|
-
Log.e(TAG, "Get user failed", e)
|
|
311
|
-
call.reject("Get user failed: ${e.message}")
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
@PluginMethod
|
|
317
|
-
fun getToken(call: PluginCall) {
|
|
318
|
-
scope.launch {
|
|
319
|
-
try {
|
|
320
|
-
val session = Clerk.session
|
|
321
|
-
if (session == null) {
|
|
322
|
-
val result = JSObject()
|
|
323
|
-
result.put("token", null)
|
|
324
|
-
call.resolve(result)
|
|
325
|
-
return@launch
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
session.getToken().onSuccess { tokenResult ->
|
|
329
|
-
val result = JSObject()
|
|
330
|
-
result.put("token", tokenResult?.jwt)
|
|
331
|
-
call.resolve(result)
|
|
332
|
-
}.onFailure { error ->
|
|
333
|
-
call.reject("Get token failed: ${error.message}")
|
|
334
|
-
}
|
|
335
|
-
} catch (e: Exception) {
|
|
336
|
-
Log.e(TAG, "Get token failed", e)
|
|
337
|
-
call.reject("Get token failed: ${e.message}")
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
@PluginMethod
|
|
343
|
-
fun signOut(call: PluginCall) {
|
|
344
|
-
scope.launch {
|
|
345
|
-
try {
|
|
346
|
-
val session = Clerk.session
|
|
347
|
-
if (session != null) {
|
|
348
|
-
session.revoke().onSuccess {
|
|
349
|
-
call.resolve()
|
|
350
|
-
}.onFailure { error ->
|
|
351
|
-
call.reject("Sign out failed: ${error.message}")
|
|
352
|
-
}
|
|
353
|
-
} else {
|
|
354
|
-
// No active session, consider it signed out
|
|
355
|
-
call.resolve()
|
|
356
|
-
}
|
|
357
|
-
} catch (e: Exception) {
|
|
358
|
-
Log.e(TAG, "Sign out failed", e)
|
|
359
|
-
call.reject("Sign out failed: ${e.message}")
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
@PluginMethod
|
|
365
|
-
fun updateUser(call: PluginCall) {
|
|
366
|
-
val firstName = call.getString("firstName")
|
|
367
|
-
val lastName = call.getString("lastName")
|
|
368
|
-
|
|
369
|
-
scope.launch {
|
|
370
|
-
try {
|
|
371
|
-
val user = Clerk.user
|
|
372
|
-
if (user == null) {
|
|
373
|
-
call.reject("No user signed in")
|
|
374
|
-
return@launch
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
user.update(
|
|
378
|
-
firstName = firstName,
|
|
379
|
-
lastName = lastName
|
|
380
|
-
).onSuccess { updatedUser ->
|
|
381
|
-
val result = JSObject()
|
|
382
|
-
result.put("user", userToJSObject(updatedUser))
|
|
383
|
-
call.resolve(result)
|
|
384
|
-
}.onFailure { error ->
|
|
385
|
-
call.reject("Update user failed: ${error.message}")
|
|
386
|
-
}
|
|
387
|
-
} catch (e: Exception) {
|
|
388
|
-
Log.e(TAG, "Update user failed", e)
|
|
389
|
-
call.reject("Update user failed: ${e.message}")
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
@PluginMethod
|
|
395
|
-
fun requestPasswordReset(call: PluginCall) {
|
|
396
|
-
val email = call.getString("email")
|
|
397
|
-
|
|
398
|
-
if (email.isNullOrEmpty()) {
|
|
399
|
-
call.reject("Must provide email")
|
|
400
|
-
return
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
scope.launch {
|
|
404
|
-
try {
|
|
405
|
-
val signIn = SignIn.create(identifier = email)
|
|
406
|
-
signIn.onSuccess { si ->
|
|
407
|
-
si.prepareFirstFactor(
|
|
408
|
-
strategy = SignIn.PrepareFirstFactorStrategy.ResetPasswordEmailCode(
|
|
409
|
-
emailAddressId = si.supportedFirstFactors
|
|
410
|
-
?.filterIsInstance<SignIn.Factor.ResetPasswordEmailCode>()
|
|
411
|
-
?.firstOrNull()
|
|
412
|
-
?.emailAddressId ?: ""
|
|
413
|
-
)
|
|
414
|
-
).onSuccess {
|
|
415
|
-
call.resolve()
|
|
416
|
-
}.onFailure { error ->
|
|
417
|
-
call.reject("Failed to request password reset: ${error.message}")
|
|
418
|
-
}
|
|
419
|
-
}.onFailure { error ->
|
|
420
|
-
call.reject("Failed to request password reset: ${error.message}")
|
|
421
|
-
}
|
|
422
|
-
} catch (e: Exception) {
|
|
423
|
-
Log.e(TAG, "Request password reset failed", e)
|
|
424
|
-
call.reject("Request password reset failed: ${e.message}")
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
@PluginMethod
|
|
430
|
-
fun resetPassword(call: PluginCall) {
|
|
431
|
-
val code = call.getString("code")
|
|
432
|
-
val newPassword = call.getString("newPassword")
|
|
433
|
-
|
|
434
|
-
if (code.isNullOrEmpty() || newPassword.isNullOrEmpty()) {
|
|
435
|
-
call.reject("Must provide code and newPassword")
|
|
436
|
-
return
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
scope.launch {
|
|
440
|
-
try {
|
|
441
|
-
val signIn = Clerk.client?.signIn
|
|
442
|
-
if (signIn == null) {
|
|
443
|
-
call.reject("No active sign in session")
|
|
444
|
-
return@launch
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
signIn.attemptFirstFactor(
|
|
448
|
-
strategy = SignIn.AttemptFirstFactorStrategy.ResetPasswordEmailCode(code = code)
|
|
449
|
-
).onSuccess { si ->
|
|
450
|
-
si.resetPassword(newPassword = newPassword).onSuccess { completedSignIn ->
|
|
451
|
-
if (completedSignIn.status == SignIn.Status.COMPLETE) {
|
|
452
|
-
completedSignIn.createdSessionId?.let { sessionId ->
|
|
453
|
-
Clerk.setActive(sessionId = sessionId)
|
|
454
|
-
}
|
|
455
|
-
call.resolve()
|
|
456
|
-
} else {
|
|
457
|
-
call.reject("Password reset incomplete")
|
|
458
|
-
}
|
|
459
|
-
}.onFailure { error ->
|
|
460
|
-
call.reject("Failed to reset password: ${error.message}")
|
|
461
|
-
}
|
|
462
|
-
}.onFailure { error ->
|
|
463
|
-
call.reject("Failed to verify reset code: ${error.message}")
|
|
464
|
-
}
|
|
465
|
-
} catch (e: Exception) {
|
|
466
|
-
Log.e(TAG, "Reset password failed", e)
|
|
467
|
-
call.reject("Reset password failed: ${e.message}")
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
@PluginMethod
|
|
473
|
-
fun refreshSession(call: PluginCall) {
|
|
474
|
-
scope.launch {
|
|
475
|
-
try {
|
|
476
|
-
val session = Clerk.session
|
|
477
|
-
if (session == null) {
|
|
478
|
-
val result = JSObject()
|
|
479
|
-
result.put("token", null)
|
|
480
|
-
call.resolve(result)
|
|
481
|
-
return@launch
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
// Force refresh the token
|
|
485
|
-
session.getToken(forceRefresh = true).onSuccess { tokenResult ->
|
|
486
|
-
val result = JSObject()
|
|
487
|
-
result.put("token", tokenResult?.jwt)
|
|
488
|
-
call.resolve(result)
|
|
489
|
-
}.onFailure { error ->
|
|
490
|
-
call.reject("Refresh session failed: ${error.message}")
|
|
491
|
-
}
|
|
492
|
-
} catch (e: Exception) {
|
|
493
|
-
Log.e(TAG, "Refresh session failed", e)
|
|
494
|
-
call.reject("Refresh session failed: ${e.message}")
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
private fun userToJSObject(user: User): JSObject {
|
|
500
|
-
val jsObject = JSObject()
|
|
501
|
-
jsObject.put("id", user.id)
|
|
502
|
-
jsObject.put("firstName", user.firstName)
|
|
503
|
-
jsObject.put("lastName", user.lastName)
|
|
504
|
-
jsObject.put("emailAddress", user.primaryEmailAddress?.emailAddress)
|
|
505
|
-
jsObject.put("imageUrl", user.imageUrl)
|
|
506
|
-
jsObject.put("username", user.username)
|
|
507
|
-
return jsObject
|
|
508
|
-
}
|
|
509
|
-
}
|