@capgo/capacitor-social-login 7.19.1 → 7.20.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.
@@ -14,6 +14,8 @@ Pod::Spec.new do |s|
14
14
  s.exclude_files = '**/node_modules/**/*', '**/examples/**/*'
15
15
  s.ios.deployment_target = '14.0'
16
16
  s.dependency 'Capacitor'
17
+ # Provider dependencies (conditionally included via hook script)
18
+ # Hook script modifies these lines based on capacitor.config.ts
17
19
  s.dependency 'FBSDKCoreKit', '18.0.0'
18
20
  s.dependency 'FBSDKLoginKit', '18.0.0'
19
21
  s.dependency 'GoogleSignIn', '~> 9.0.0'
package/README.md CHANGED
@@ -37,6 +37,68 @@ npm install @capgo/capacitor-social-login
37
37
  npx cap sync
38
38
  ```
39
39
 
40
+ ## Dynamic Provider Dependencies
41
+
42
+ You can configure which providers to include to reduce app size. This is especially useful if you only need specific providers.
43
+
44
+ ### Configuration
45
+
46
+ Add provider configuration to your `capacitor.config.ts`:
47
+
48
+ ```typescript
49
+ import type { CapacitorConfig } from '@capacitor/cli';
50
+
51
+ const config: CapacitorConfig = {
52
+ appId: 'com.example.app',
53
+ appName: 'MyApp',
54
+ webDir: 'dist',
55
+ plugins: {
56
+ SocialLogin: {
57
+ providers: {
58
+ google: true, // true = enabled (bundled), false = disabled (not bundled)
59
+ facebook: true, // Use false to reduce app size
60
+ apple: true, // Apple uses system APIs, no external deps
61
+ twitter: false // false = disabled (not bundled)
62
+ }
63
+ }
64
+ }
65
+ };
66
+
67
+ export default config;
68
+ ```
69
+
70
+ ### Provider Configuration
71
+
72
+ - **`true`** (default): Provider is enabled - dependencies are bundled in final APK/IPA
73
+ - **`false`**: Provider is disabled - dependencies are not bundled in final APK/IPA
74
+
75
+ ### Notes
76
+
77
+ - Changes require running `npx cap sync` to take effect
78
+ - If configuration is not provided, all providers default to `true` (enabled, backward compatible)
79
+ - **Important**: Disabling a provider (`false`) will make it unavailable at runtime, regardless of whether it actually adds any dependencies. The provider will be disabled even if it uses only system APIs.
80
+ - This configuration only affects iOS and Android platforms; it does not affect the web platform.
81
+ - **Important**: Using `false` means the dependency won't be bundled, but the plugin code still compiles against it. Ensure the consuming app includes the dependency if needed.
82
+ - Apple Sign-In on Android uses OAuth flow without external SDK dependencies
83
+ - Twitter uses standard OAuth 2.0 flow without external SDK dependencies
84
+
85
+ ### Example: Reduce App Size
86
+
87
+ To only include Google Sign-In and disable others:
88
+
89
+ ```typescript
90
+ plugins: {
91
+ SocialLogin: {
92
+ providers: {
93
+ google: true, // Enabled
94
+ facebook: false, // Disabled (not bundled)
95
+ apple: true, // Enabled
96
+ twitter: false // Disabled (not bundled)
97
+ }
98
+ }
99
+ }
100
+ ```
101
+
40
102
  ## Apple
41
103
 
42
104
  [How to get the credentials](https://github.com/Cap-go/capacitor-social-login/blob/main/docs/setup_apple.md)
@@ -49,19 +49,60 @@ repositories {
49
49
 
50
50
 
51
51
  dependencies {
52
+ // Common dependencies (always included)
52
53
  implementation fileTree(dir: 'libs', include: ['*.jar'])
53
54
  implementation project(':capacitor-android')
54
55
  implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
55
- implementation 'com.facebook.android:facebook-login:18.1.3'
56
56
  implementation 'com.squareup.okhttp3:okhttp:4.12.0'
57
57
  implementation 'com.auth0.android:jwtdecode:2.0.2'
58
58
  implementation "androidx.credentials:credentials:1.5.0"
59
- implementation 'com.google.android.gms:play-services-auth:21.4.0'
60
- implementation "androidx.credentials:credentials-play-services-auth:1.5.0"
61
- implementation "com.google.android.libraries.identity.googleid:googleid:1.1.1"
62
- implementation 'com.google.androidbrowserhelper:androidbrowserhelper:2.5.0'
59
+ implementation 'androidx.concurrent:concurrent-futures:1.3.0'
60
+
61
+ // Read provider configuration from gradle.properties (set by hook script)
62
+ def googleDependencyType = project.findProperty('socialLogin.google.dependencyType') ?: 'implementation'
63
+ def includeGoogle = project.findProperty('socialLogin.google.include') ?: 'true'
64
+
65
+ def facebookDependencyType = project.findProperty('socialLogin.facebook.dependencyType') ?: 'implementation'
66
+ def includeFacebook = project.findProperty('socialLogin.facebook.include') ?: 'true'
67
+
68
+ def appleDependencyType = project.findProperty('socialLogin.apple.dependencyType') ?: 'implementation'
69
+ def includeApple = project.findProperty('socialLogin.apple.include') ?: 'true'
70
+
71
+ def twitterDependencyType = project.findProperty('socialLogin.twitter.dependencyType') ?: 'implementation'
72
+ def includeTwitter = project.findProperty('socialLogin.twitter.include') ?: 'true'
73
+
74
+ // Google dependencies
75
+ if (googleDependencyType == 'compileOnly') {
76
+ compileOnly 'com.google.android.gms:play-services-auth:21.4.0'
77
+ compileOnly "androidx.credentials:credentials-play-services-auth:1.5.0"
78
+ compileOnly "com.google.android.libraries.identity.googleid:googleid:1.1.1"
79
+ compileOnly 'com.google.androidbrowserhelper:androidbrowserhelper:2.5.0'
80
+ } else if (includeGoogle == 'true') {
81
+ implementation 'com.google.android.gms:play-services-auth:21.4.0'
82
+ implementation "androidx.credentials:credentials-play-services-auth:1.5.0"
83
+ implementation "com.google.android.libraries.identity.googleid:googleid:1.1.1"
84
+ implementation 'com.google.androidbrowserhelper:androidbrowserhelper:2.5.0'
85
+ }
86
+
87
+ // Facebook dependencies
88
+ if (facebookDependencyType == 'compileOnly') {
89
+ compileOnly 'com.facebook.android:facebook-login:18.1.3'
90
+ } else if (includeFacebook == 'true') {
91
+ implementation 'com.facebook.android:facebook-login:18.1.3'
92
+ }
93
+
94
+ // Apple dependencies
95
+ if (appleDependencyType == 'compileOnly') {
96
+ compileOnly "androidx.browser:browser:1.9.0"
97
+ } else if (includeApple == 'true') {
98
+ implementation "androidx.browser:browser:1.9.0"
99
+ }
100
+
101
+ // Twitter dependencies (uses OAuth flow, no external SDK)
102
+ // Twitter uses standard OAuth 2.0 flow without external dependencies
103
+
104
+ // Test dependencies (always included)
63
105
  testImplementation "junit:junit:$junitVersion"
64
106
  androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
65
107
  androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
66
- implementation 'androidx.concurrent:concurrent-futures:1.3.0'
67
108
  }
@@ -148,8 +148,13 @@ public class AppleProvider implements SocialProvider {
148
148
  public void login(PluginCall call, JSONObject config) {
149
149
  if (this.lastcall != null) {
150
150
  call.reject("Last call is not null");
151
+ return;
151
152
  }
152
153
 
154
+ // Save the call reference immediately so it's always available
155
+ this.lastcall = call;
156
+ call.setKeepAlive(true);
157
+
153
158
  // Check if Broadcast Channel is enabled
154
159
  boolean useBroadcastChannel = config.optBoolean("useBroadcastChannel", this.useBroadcastChannel);
155
160
 
@@ -206,12 +211,11 @@ public class AppleProvider implements SocialProvider {
206
211
  }
207
212
 
208
213
  if (context == null || activity == null) {
209
- call.reject("Context or Activity is null");
214
+ this.lastcall.reject("Context or Activity is null");
215
+ this.lastcall = null;
210
216
  return;
211
217
  }
212
218
 
213
- this.lastcall = call;
214
- call.setKeepAlive(true);
215
219
  activity.runOnUiThread(() -> setupBroadcastChannelWebview(context, activity, call, appleAuthURLFull));
216
220
  }
217
221
 
@@ -256,12 +260,11 @@ public class AppleProvider implements SocialProvider {
256
260
  }
257
261
 
258
262
  if (context == null || activity == null) {
259
- call.reject("Context or Activity is null");
263
+ this.lastcall.reject("Context or Activity is null");
264
+ this.lastcall = null;
260
265
  return;
261
266
  }
262
267
 
263
- this.lastcall = call;
264
- call.setKeepAlive(true);
265
268
  activity.runOnUiThread(() -> setupWebview(context, activity, call, appleAuthURLFull));
266
269
  }
267
270
 
@@ -310,6 +313,11 @@ public class AppleProvider implements SocialProvider {
310
313
  }
311
314
 
312
315
  public void handleUrl(String url) {
316
+ if (this.lastcall == null) {
317
+ Log.e(SocialLoginPlugin.LOG_TAG, "handleUrl called but lastcall is null");
318
+ return;
319
+ }
320
+
313
321
  Uri uri = Uri.parse(url);
314
322
  String success = uri.getQueryParameter("success");
315
323
  if ("true".equals(success)) {
@@ -351,6 +359,7 @@ public class AppleProvider implements SocialProvider {
351
359
  } else {
352
360
  // Legacy mode: exchange the authorization code for tokens
353
361
  requestForAccessToken(appleAuthCode, appleClientSecret);
362
+ return; // Don't clear lastcall here, it will be cleared in the callback
354
363
  }
355
364
  }
356
365
  } else {
@@ -377,8 +386,12 @@ public class AppleProvider implements SocialProvider {
377
386
  new Callback() {
378
387
  @Override
379
388
  public void onFailure(@NonNull Call call, @NonNull IOException e) {
380
- AppleProvider.this.lastcall.reject("Cannot get access_token", e);
381
- AppleProvider.this.lastcall = null;
389
+ if (AppleProvider.this.lastcall != null) {
390
+ AppleProvider.this.lastcall.reject("Cannot get access_token", e);
391
+ AppleProvider.this.lastcall = null;
392
+ } else {
393
+ Log.e(SocialLoginPlugin.LOG_TAG, "Cannot get access_token: lastcall is null. Error: " + e.getMessage(), e);
394
+ }
382
395
  }
383
396
 
384
397
  @Override
@@ -407,11 +420,22 @@ public class AppleProvider implements SocialProvider {
407
420
  appleResponse.put("provider", "apple");
408
421
  appleResponse.put("result", result);
409
422
 
410
- AppleProvider.this.lastcall.resolve(appleResponse);
411
- AppleProvider.this.lastcall = null;
423
+ if (AppleProvider.this.lastcall != null) {
424
+ AppleProvider.this.lastcall.resolve(appleResponse);
425
+ AppleProvider.this.lastcall = null;
426
+ } else {
427
+ Log.e(
428
+ SocialLoginPlugin.LOG_TAG,
429
+ "Cannot resolve access_token response: lastcall is null. Response: " + appleResponse.toString()
430
+ );
431
+ }
412
432
  } catch (Exception e) {
413
- AppleProvider.this.lastcall.reject("Cannot get access_token", e);
414
- AppleProvider.this.lastcall = null;
433
+ if (AppleProvider.this.lastcall != null) {
434
+ AppleProvider.this.lastcall.reject("Cannot get access_token", e);
435
+ AppleProvider.this.lastcall = null;
436
+ } else {
437
+ Log.e(SocialLoginPlugin.LOG_TAG, "Cannot get access_token: lastcall is null. Error: " + e.getMessage(), e);
438
+ }
415
439
  } finally {
416
440
  response.close();
417
441
  }
@@ -488,6 +512,12 @@ public class AppleProvider implements SocialProvider {
488
512
  Uri uri = Uri.parse(url);
489
513
  String success = uri.getQueryParameter("success");
490
514
 
515
+ if (lastcall == null) {
516
+ Log.e(SocialLoginPlugin.LOG_TAG, "setupBroadcastChannelWebview: lastcall is null");
517
+ dialog.dismiss();
518
+ return true;
519
+ }
520
+
491
521
  if ("true".equals(success)) {
492
522
  String accessToken = uri.getQueryParameter("access_token");
493
523
  if (accessToken != null) {
@@ -635,6 +665,11 @@ public class AppleProvider implements SocialProvider {
635
665
 
636
666
  // Handle authentication messages
637
667
  if ("auth".equals(channel)) {
668
+ if (lastcall == null) {
669
+ Log.e(SocialLoginPlugin.LOG_TAG, "BroadcastChannelInterface.postMessage: lastcall is null");
670
+ return;
671
+ }
672
+
638
673
  String type = messageData.getString("type");
639
674
  if ("success".equals(type)) {
640
675
  // Handle successful authentication
@@ -653,18 +688,24 @@ public class AppleProvider implements SocialProvider {
653
688
  response.put("result", result);
654
689
 
655
690
  lastcall.resolve(response);
691
+ lastcall = null;
656
692
  } catch (JSONException e) {
657
693
  Log.e(SocialLoginPlugin.LOG_TAG, "Cannot create response", e);
658
694
  lastcall.reject("Cannot create response", e);
695
+ lastcall = null;
659
696
  }
660
697
  } else if ("error".equals(type)) {
661
698
  String error = messageData.optString("error", "Authentication failed");
662
699
  lastcall.reject(error);
700
+ lastcall = null;
663
701
  }
664
702
  }
665
703
  } catch (JSONException e) {
666
704
  Log.e("BroadcastChannel", "Error parsing message", e);
667
- lastcall.reject("Error parsing authentication message", e);
705
+ if (lastcall != null) {
706
+ lastcall.reject("Error parsing authentication message", e);
707
+ lastcall = null;
708
+ }
668
709
  }
669
710
  }
670
711
  }
@@ -9,6 +9,7 @@ import com.getcapacitor.Plugin;
9
9
  import com.getcapacitor.PluginCall;
10
10
  import com.getcapacitor.PluginMethod;
11
11
  import com.getcapacitor.annotation.CapacitorPlugin;
12
+ import ee.forgr.capacitor.social.login.helpers.DependencyAvailabilityChecker;
12
13
  import ee.forgr.capacitor.social.login.helpers.SocialProvider;
13
14
  import java.util.HashMap;
14
15
  import org.json.JSONArray;
@@ -18,7 +19,7 @@ import org.json.JSONObject;
18
19
  @CapacitorPlugin(name = "SocialLogin")
19
20
  public class SocialLoginPlugin extends Plugin {
20
21
 
21
- private final String pluginVersion = "7.19.1";
22
+ private final String pluginVersion = "7.20.0";
22
23
 
23
24
  public static String LOG_TAG = "CapgoSocialLogin";
24
25
 
@@ -26,6 +27,9 @@ public class SocialLoginPlugin extends Plugin {
26
27
 
27
28
  @PluginMethod
28
29
  public void initialize(PluginCall call) {
30
+ // Set plugin instance for config access
31
+ DependencyAvailabilityChecker.setPluginInstance(this);
32
+
29
33
  if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
30
34
  call.reject("Your android device is too old");
31
35
  return;
@@ -33,6 +37,15 @@ public class SocialLoginPlugin extends Plugin {
33
37
 
34
38
  JSObject apple = call.getObject("apple");
35
39
  if (apple != null) {
40
+ // Check if Apple dependencies are available
41
+ if (!DependencyAvailabilityChecker.isProviderAvailable("apple")) {
42
+ call.reject(
43
+ "Apple Sign-In provider is disabled. " +
44
+ "Dependencies are not available. Ensure JWT decode and CustomTabs dependencies are included in your app's build.gradle"
45
+ );
46
+ return;
47
+ }
48
+
36
49
  String androidAppleRedirect = apple.getString("redirectUrl");
37
50
  if (androidAppleRedirect == null || androidAppleRedirect.isEmpty()) {
38
51
  call.reject("apple.android.redirectUrl is null or empty");
@@ -89,6 +102,15 @@ public class SocialLoginPlugin extends Plugin {
89
102
 
90
103
  JSObject facebook = call.getObject("facebook");
91
104
  if (facebook != null) {
105
+ // Check if Facebook dependencies are available
106
+ if (!DependencyAvailabilityChecker.isProviderAvailable("facebook")) {
107
+ call.reject(
108
+ "Facebook provider is disabled. " +
109
+ "Dependencies are not available. Ensure Facebook Login dependencies are included in your app's build.gradle"
110
+ );
111
+ return;
112
+ }
113
+
92
114
  String facebookAppId = facebook.getString("appId");
93
115
  String facebookClientToken = facebook.getString("clientToken");
94
116
  if (facebookAppId == null || facebookAppId.isEmpty()) {
@@ -111,6 +133,15 @@ public class SocialLoginPlugin extends Plugin {
111
133
 
112
134
  JSObject twitter = call.getObject("twitter");
113
135
  if (twitter != null) {
136
+ // Check if Twitter dependencies are available
137
+ if (!DependencyAvailabilityChecker.isProviderAvailable("twitter")) {
138
+ call.reject(
139
+ "Twitter provider is disabled. " +
140
+ "Dependencies are not available. Ensure OkHttp dependencies are included in your app's build.gradle"
141
+ );
142
+ return;
143
+ }
144
+
114
145
  String twitterClientId = twitter.getString("clientId");
115
146
  String twitterRedirect = twitter.getString("redirectUrl");
116
147
  if (twitterClientId == null || twitterClientId.isEmpty()) {
@@ -145,7 +176,18 @@ public class SocialLoginPlugin extends Plugin {
145
176
 
146
177
  SocialProvider provider = this.socialProviderHashMap.get(providerStr);
147
178
  if (provider == null) {
148
- call.reject(String.format("Cannot find provider '%s'", providerStr));
179
+ // Check if provider is disabled (dependencies not available)
180
+ if (!DependencyAvailabilityChecker.isProviderAvailable(providerStr)) {
181
+ call.reject(
182
+ String.format(
183
+ "Provider '%s' is disabled. Dependencies are not available. " +
184
+ "Ensure required dependencies are included in your app's build.gradle",
185
+ providerStr
186
+ )
187
+ );
188
+ } else {
189
+ call.reject(String.format("Cannot find provider '%s'. Provider was not initialized.", providerStr));
190
+ }
149
191
  return;
150
192
  }
151
193
 
@@ -161,7 +203,18 @@ public class SocialLoginPlugin extends Plugin {
161
203
 
162
204
  SocialProvider provider = this.socialProviderHashMap.get(providerStr);
163
205
  if (provider == null) {
164
- call.reject(String.format("Cannot find provider '%s'", providerStr));
206
+ // Check if provider is disabled (dependencies not available)
207
+ if (!DependencyAvailabilityChecker.isProviderAvailable(providerStr)) {
208
+ call.reject(
209
+ String.format(
210
+ "Provider '%s' is disabled. Dependencies are not available. " +
211
+ "Ensure required dependencies are included in your app's build.gradle",
212
+ providerStr
213
+ )
214
+ );
215
+ } else {
216
+ call.reject(String.format("Cannot find provider '%s'. Provider was not initialized.", providerStr));
217
+ }
165
218
  return;
166
219
  }
167
220
 
@@ -177,7 +230,18 @@ public class SocialLoginPlugin extends Plugin {
177
230
 
178
231
  SocialProvider provider = this.socialProviderHashMap.get(providerStr);
179
232
  if (provider == null) {
180
- call.reject(String.format("Cannot find provider '%s'", providerStr));
233
+ // Check if provider is disabled (dependencies not available)
234
+ if (!DependencyAvailabilityChecker.isProviderAvailable(providerStr)) {
235
+ call.reject(
236
+ String.format(
237
+ "Provider '%s' is disabled. Dependencies are not available. " +
238
+ "Ensure required dependencies are included in your app's build.gradle",
239
+ providerStr
240
+ )
241
+ );
242
+ } else {
243
+ call.reject(String.format("Cannot find provider '%s'. Provider was not initialized.", providerStr));
244
+ }
181
245
  return;
182
246
  }
183
247
 
@@ -193,7 +257,18 @@ public class SocialLoginPlugin extends Plugin {
193
257
 
194
258
  SocialProvider provider = this.socialProviderHashMap.get(providerStr);
195
259
  if (provider == null) {
196
- call.reject(String.format("Cannot find provider '%s'", providerStr));
260
+ // Check if provider is disabled (dependencies not available)
261
+ if (!DependencyAvailabilityChecker.isProviderAvailable(providerStr)) {
262
+ call.reject(
263
+ String.format(
264
+ "Provider '%s' is disabled. Dependencies are not available. " +
265
+ "Ensure required dependencies are included in your app's build.gradle",
266
+ providerStr
267
+ )
268
+ );
269
+ } else {
270
+ call.reject(String.format("Cannot find provider '%s'. Provider was not initialized.", providerStr));
271
+ }
197
272
  return;
198
273
  }
199
274
 
@@ -209,7 +284,18 @@ public class SocialLoginPlugin extends Plugin {
209
284
 
210
285
  SocialProvider provider = this.socialProviderHashMap.get(providerStr);
211
286
  if (provider == null) {
212
- call.reject(String.format("Cannot find provider '%s'", providerStr));
287
+ // Check if provider is disabled (dependencies not available)
288
+ if (!DependencyAvailabilityChecker.isProviderAvailable(providerStr)) {
289
+ call.reject(
290
+ String.format(
291
+ "Provider '%s' is disabled. Dependencies are not available. " +
292
+ "Ensure required dependencies are included in your app's build.gradle",
293
+ providerStr
294
+ )
295
+ );
296
+ } else {
297
+ call.reject(String.format("Cannot find provider '%s'. Provider was not initialized.", providerStr));
298
+ }
213
299
  return;
214
300
  }
215
301