@capgo/capacitor-social-login 8.3.33 → 8.3.34

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
@@ -420,26 +420,80 @@ const res = await SocialLogin.login({
420
420
  });
421
421
  ```
422
422
 
423
- #### Android troubleshooting (SHA-1 and Firebase)
424
-
425
- Many Android "auth 10/16" or blank-response issues come from a mismatched SHA-1 fingerprint. Use the exact build you install on the device to register the SHA-1 in Google Cloud/Firebase:
426
-
427
- 1. Build a signed APK from Android Studio (`Build` `Generate Signed App Bundle / APK`, pick APK) using your release keystore.
428
- 2. Extract the SHA-1 from that final APK:
429
- ```bash
430
- keytool -printcert -jarfile android/app/release/app-release.apk
431
- ```
432
- 3. Add that SHA-1 to your Android OAuth client in [Google Cloud Console](https://console.cloud.google.com/apis/credentials) (package name + SHA-1).
433
- 4. Reinstall the same signed APK on device for testing:
434
- ```bash
435
- adb install android/app/release/app-release.apk
436
- ```
437
- 5. When consuming the plugin result, read tokens from `result.result`:
438
- ```ts
439
- const login = await SocialLogin.login({ provider: 'google' });
440
- const idToken = login.result?.idToken; // not login.idToken
441
- ```
442
- For Firebase Auth, create credentials with that `idToken` (and use the Web Client ID as `webClientId` in `initialize`).
423
+ #### Android troubleshooting (Credential Manager, SHA-1, and Firebase)
424
+
425
+ On Android this plugin uses **Google Credential Manager** (`androidx.credentials` + Sign in with Google), not the legacy `GoogleSignInClient` API. Logcat errors such as `GetCredentialCustomException: [28444] Developer console is not set up correctly` come from that stack.
426
+
427
+ Filter Logcat with `GoogleProvider` or `CapgoSocialLogin` after a failed login. The plugin logs your **package name**, **signing SHA-1**, and a masked **webClientId** to help compare against Google Cloud Console.
428
+
429
+ ##### Required Google Cloud setup (all in the same project)
430
+
431
+ You need **two kinds** of OAuth 2.0 client IDs:
432
+
433
+ | Client type | Used for | Where it goes |
434
+ |-------------|----------|---------------|
435
+ | **Web application** | Server / ID token audience | `webClientId` in `SocialLogin.initialize()` |
436
+ | **Android** (one per signing key) | Proves your APK is allowed to call Google | Google Cloud Console only — **do not** pass this ID to `webClientId` |
437
+
438
+ Common mistake: using the **Android** client ID as `webClientId`. Credential Manager requires the **Web** client ID there. The Android client only needs the correct **package name + SHA-1** registered in the console.
439
+
440
+ Create one Android OAuth client for **each** certificate that signs builds you test:
441
+
442
+ - **Debug** from `./gradlew signingReport` (debug variant)
443
+ - **Release** — from the APK/AAB you actually install (see below)
444
+ - **Play App Signing** — from Play Console → **App integrity** → **App signing key certificate** (required for Play Store builds even if your upload key SHA-1 is already registered)
445
+
446
+ The `applicationId` in `android/app/build.gradle` must match the Android OAuth client package name exactly (including any `.debug` suffix if you use one).
447
+
448
+ If the OAuth consent screen is in **Testing** mode, add every Google account you test with under **Audience → Test users**. Publishing the app to Production is **not** required for `email` / `profile` scopes. **Digital Asset Links** (`assetlinks.json`) are **not** required for Sign in with Google via Credential Manager.
449
+
450
+ Google Cloud changes can take **up to a few hours** to propagate; a device restart alone may not be enough.
451
+
452
+ ##### Error `[28444] Developer console is not set up correctly`
453
+
454
+ This almost always means Google rejected the combination of **installed APK signing certificate**, **package name**, and **webClientId`. Work through this checklist:
455
+
456
+ 1. Confirm `webClientId` is the **Web application** client ID (ends with `.apps.googleusercontent.com`).
457
+ 2. Run the app, reproduce the failure, and read Logcat (`GoogleProvider`) for `signingSha1=` and `package=`.
458
+ 3. In [Google Cloud Console → Credentials](https://console.cloud.google.com/apis/credentials), open your **Android** OAuth client and verify that **exact** package name and SHA-1 are listed.
459
+ 4. If testing a **release** build, register the SHA-1 from that build — not only the debug keystore.
460
+ 5. If the app is distributed via **Play Store**, also register the **Play App Signing** SHA-1.
461
+ 6. Ensure Web and Android clients live in the **same** Google Cloud project.
462
+ 7. If consent screen is in Testing, confirm the Google account is a **test user**.
463
+ 8. Wait and retry after console changes.
464
+
465
+ `USER_CANCELLED` after picking an account on a misconfigured debug build can still be a SHA-1 / client-ID mismatch — fix the console setup above first.
466
+
467
+ ##### Extract SHA-1 from the build you install
468
+
469
+ Debug / local builds:
470
+
471
+ ```bash
472
+ cd android && ./gradlew signingReport
473
+ ```
474
+
475
+ Signed release APK:
476
+
477
+ ```bash
478
+ keytool -printcert -jarfile android/app/release/app-release.apk
479
+ ```
480
+
481
+ Then add that SHA-1 to an Android OAuth client (package name + SHA-1) in Google Cloud Console, reinstall the **same** signed APK, and test again:
482
+
483
+ ```bash
484
+ adb install android/app/release/app-release.apk
485
+ ```
486
+
487
+ ##### Reading the login result (Firebase and backends)
488
+
489
+ Tokens are nested under `result`:
490
+
491
+ ```ts
492
+ const login = await SocialLogin.login({ provider: 'google' });
493
+ const idToken = login.result?.idToken; // not login.idToken
494
+ ```
495
+
496
+ For Firebase Auth, create credentials with that `idToken` and use the **Web** Client ID as `webClientId` in `initialize`.
443
497
 
444
498
  ### iOS configuration
445
499
 
@@ -690,6 +744,12 @@ const config: CapacitorConfig = {
690
744
 
691
745
  Then run `npx cap sync`. The plugin uses stub classes instead of the real Facebook SDK, so no Facebook dependencies or permissions are included in your build.
692
746
 
747
+ ### Google Sign-In `[28444] Developer console is not set up correctly` (Android)
748
+
749
+ On Android, this error comes from **Google Credential Manager** when the installed APK's signing certificate, package name, or `webClientId` does not match Google Cloud Console.
750
+
751
+ See [Android troubleshooting (Credential Manager, SHA-1, and Firebase)](#android-troubleshooting-credential-manager-sha-1-and-firebase) for the full checklist. After a failed login, filter Logcat for `GoogleProvider` — the plugin prints `package`, `signingSha1`, and `webClientId` to compare with your OAuth clients.
752
+
693
753
  ### Google Sign-In with Family Link Supervised Accounts
694
754
 
695
755
  **Problem**: When users try to sign in with Google accounts supervised by Family Link, login fails with:
@@ -5,6 +5,10 @@ import android.app.PendingIntent;
5
5
  import android.content.Context;
6
6
  import android.content.Intent;
7
7
  import android.content.IntentSender;
8
+ import android.content.pm.PackageInfo;
9
+ import android.content.pm.PackageManager;
10
+ import android.content.pm.Signature;
11
+ import android.os.Build;
8
12
  import android.util.Log;
9
13
  import androidx.annotation.NonNull;
10
14
  import androidx.concurrent.futures.CallbackToFutureAdapter;
@@ -32,6 +36,8 @@ import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential;
32
36
  import com.google.common.util.concurrent.ListenableFuture;
33
37
  import ee.forgr.capacitor.social.login.helpers.SocialProvider;
34
38
  import java.io.IOException;
39
+ import java.security.MessageDigest;
40
+ import java.security.NoSuchAlgorithmException;
35
41
  import java.util.ArrayList;
36
42
  import java.util.HashSet;
37
43
  import java.util.List;
@@ -83,6 +89,75 @@ public class GoogleProvider implements SocialProvider {
83
89
  private GoogleProviderLoginType mode = GoogleProviderLoginType.ONLINE;
84
90
  private String hostedDomain = null;
85
91
 
92
+ private static String maskClientId(String clientId) {
93
+ if (clientId == null || clientId.isEmpty()) {
94
+ return "(not set)";
95
+ }
96
+ if (clientId.length() <= 16) {
97
+ return clientId;
98
+ }
99
+ return "..." + clientId.substring(clientId.length() - 20);
100
+ }
101
+
102
+ private static String getSigningCertificateSha1(Context context) {
103
+ try {
104
+ PackageManager pm = context.getPackageManager();
105
+ String packageName = context.getPackageName();
106
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
107
+ PackageInfo info = pm.getPackageInfo(packageName, PackageManager.GET_SIGNING_CERTIFICATES);
108
+ if (info.signingInfo != null) {
109
+ Signature[] signatures = info.signingInfo.getApkContentsSigners();
110
+ if (signatures != null && signatures.length > 0) {
111
+ return sha1Fingerprint(signatures[0]);
112
+ }
113
+ }
114
+ } else {
115
+ @SuppressWarnings("deprecation")
116
+ PackageInfo info = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
117
+ @SuppressWarnings("deprecation")
118
+ Signature[] signatures = info.signatures;
119
+ if (signatures != null && signatures.length > 0) {
120
+ return sha1Fingerprint(signatures[0]);
121
+ }
122
+ }
123
+ } catch (PackageManager.NameNotFoundException e) {
124
+ Log.w(LOG_TAG, "Could not read app signing certificate", e);
125
+ }
126
+ return null;
127
+ }
128
+
129
+ private static String sha1Fingerprint(Signature signature) {
130
+ try {
131
+ MessageDigest md = MessageDigest.getInstance("SHA-1");
132
+ byte[] digest = md.digest(signature.toByteArray());
133
+ StringBuilder sb = new StringBuilder();
134
+ for (int i = 0; i < digest.length; i++) {
135
+ if (i > 0) {
136
+ sb.append(':');
137
+ }
138
+ sb.append(String.format("%02X", digest[i]));
139
+ }
140
+ return sb.toString();
141
+ } catch (NoSuchAlgorithmException e) {
142
+ return null;
143
+ }
144
+ }
145
+
146
+ private void logGoogleCloudDiagnostics(String phase) {
147
+ String sha1 = getSigningCertificateSha1(context);
148
+ Log.i(
149
+ LOG_TAG,
150
+ String.format(
151
+ "Google %s: package=%s signingSha1=%s webClientId=%s mode=%s",
152
+ phase,
153
+ context.getPackageName(),
154
+ sha1 != null ? sha1 : "unknown",
155
+ maskClientId(clientId),
156
+ mode != null ? mode.name() : "unknown"
157
+ )
158
+ );
159
+ }
160
+
86
161
  public enum GoogleProviderLoginType {
87
162
  ONLINE,
88
163
  OFFLINE
@@ -102,6 +177,7 @@ public class GoogleProvider implements SocialProvider {
102
177
  this.clientId = clientId;
103
178
  this.mode = mode;
104
179
  this.hostedDomain = hostedDomain;
180
+ logGoogleCloudDiagnostics("initialize");
105
181
 
106
182
  String data = context.getSharedPreferences(SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE).getString(GOOGLE_DATA_PREFERENCE, null);
107
183
 
@@ -301,6 +377,8 @@ public class GoogleProvider implements SocialProvider {
301
377
  return;
302
378
  }
303
379
 
380
+ logGoogleCloudDiagnostics("login");
381
+
304
382
  String nonce = config.optString("nonce");
305
383
  JSONObject options = call.getObject("options", new JSObject());
306
384
  boolean bottomUi = false;
@@ -346,8 +424,19 @@ public class GoogleProvider implements SocialProvider {
346
424
  // Build credential request
347
425
  GetCredentialRequest.Builder requestBuilder = new GetCredentialRequest.Builder();
348
426
 
427
+ Log.i(
428
+ LOG_TAG,
429
+ String.format(
430
+ "Google login request: ui=%s filterByAuthorizedAccounts=%s autoSelectEnabled=%s forcePrompt=%s nonceSet=%s",
431
+ bottomUi ? "bottom" : "standard",
432
+ filterByAuthorizedAccounts,
433
+ autoSelectEnabled,
434
+ forcePrompt,
435
+ !nonce.isEmpty()
436
+ )
437
+ );
438
+
349
439
  if (bottomUi) {
350
- Log.e(LOG_TAG, "use bottomUi");
351
440
  GetGoogleIdOption.Builder googleIdOptionBuilder = new GetGoogleIdOption.Builder().setServerClientId(this.clientId);
352
441
  // Handle bottom UI specific options
353
442
  if (forcePrompt) {
@@ -642,8 +731,43 @@ public class GoogleProvider implements SocialProvider {
642
731
  }
643
732
  }
644
733
 
734
+ private boolean isDeveloperConsoleMisconfiguration(String message) {
735
+ return message.contains("Developer console") || message.contains("28444") || message.contains("10:");
736
+ }
737
+
738
+ private void logDeveloperConsoleMisconfigurationHelp(String errorMessage) {
739
+ String sha1 = getSigningCertificateSha1(context);
740
+ Log.e(
741
+ LOG_TAG,
742
+ String.format(
743
+ "Google Credential Manager rejected this app (%s). package=%s signingSha1=%s webClientId=%s. " +
744
+ "Checklist: (1) webClientId must be a Web OAuth client ID, not Android; " +
745
+ "(2) create an Android OAuth client with this exact package + SHA-1 in the same GCP project; " +
746
+ "(3) add Play App Signing SHA-1 for Play Store builds; " +
747
+ "(4) add test users if OAuth consent screen is in Testing; " +
748
+ "(5) allow several hours for console changes to propagate.",
749
+ errorMessage,
750
+ context.getPackageName(),
751
+ sha1 != null ? sha1 : "unknown",
752
+ maskClientId(clientId)
753
+ )
754
+ );
755
+ }
756
+
645
757
  private void handleSignInError(GetCredentialException e, PluginCall call, JSONObject config) {
646
- Log.e(LOG_TAG, "Google Sign-In failed", e);
758
+ String errorMessage = e.getMessage() != null ? e.getMessage() : e.getClass().getSimpleName();
759
+ Log.e(LOG_TAG, "Google Sign-In failed: " + errorMessage, e);
760
+
761
+ if (isDeveloperConsoleMisconfiguration(errorMessage)) {
762
+ logDeveloperConsoleMisconfigurationHelp(errorMessage);
763
+ call.reject(
764
+ "Google Sign-In failed: Google Cloud OAuth is not configured for this installed build (" +
765
+ errorMessage +
766
+ "). Check Logcat tag GoogleProvider for package, signingSha1, and webClientId, then see the Android troubleshooting section in the plugin README."
767
+ );
768
+ return;
769
+ }
770
+
647
771
  boolean isBottomUi = false;
648
772
  JSONObject options = call.getObject("options", new JSObject());
649
773
  if (options.has("style")) {
@@ -24,7 +24,7 @@ import org.json.JSONObject;
24
24
  @CapacitorPlugin(name = "SocialLogin")
25
25
  public class SocialLoginPlugin extends Plugin {
26
26
 
27
- private final String pluginVersion = "8.3.33";
27
+ private final String pluginVersion = "8.3.34";
28
28
 
29
29
  public static String LOG_TAG = "CapgoSocialLogin";
30
30
  public HashMap<String, SocialProvider> socialProviderHashMap = new HashMap<>();
@@ -16,7 +16,7 @@ import GoogleSignIn
16
16
  */
17
17
  @objc(SocialLoginPlugin)
18
18
  public class SocialLoginPlugin: CAPPlugin, CAPBridgedPlugin {
19
- private let pluginVersion: String = "8.3.33"
19
+ private let pluginVersion: String = "8.3.34"
20
20
  public let identifier = "SocialLoginPlugin"
21
21
  public let jsName = "SocialLogin"
22
22
  private static let userCancelledCode = "USER_CANCELLED"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-social-login",
3
- "version": "8.3.33",
3
+ "version": "8.3.34",
4
4
  "description": "All social logins in one plugin",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",