@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 +80 -20
- package/android/src/main/java/ee/forgr/capacitor/social/login/GoogleProvider.java +126 -2
- package/android/src/main/java/ee/forgr/capacitor/social/login/SocialLoginPlugin.java +1 -1
- package/ios/Sources/SocialLoginPlugin/SocialLoginPlugin.swift +1 -1
- package/package.json +1 -1
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
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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"
|