@capgo/capacitor-social-login 8.3.33 → 8.3.35
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 +87 -23
- 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
|
@@ -31,6 +31,7 @@ Use the Auth Connect preset wrapper (`SocialLoginAuthConnect`) to log in with `a
|
|
|
31
31
|
|
|
32
32
|
- Compatibility guide: https://github.com/Cap-go/capacitor-social-login/blob/main/docs/auth_connect_compatibility.md
|
|
33
33
|
- Migration guide: https://github.com/Cap-go/capacitor-social-login/blob/main/MIGRATION_AUTH_CONNECT.md
|
|
34
|
+
- Keycloak setup: https://github.com/Cap-go/capacitor-social-login/blob/main/docs/setup_keycloak.md
|
|
34
35
|
|
|
35
36
|
## Documentation
|
|
36
37
|
|
|
@@ -420,26 +421,80 @@ const res = await SocialLogin.login({
|
|
|
420
421
|
});
|
|
421
422
|
```
|
|
422
423
|
|
|
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
|
-
|
|
424
|
+
#### Android troubleshooting (Credential Manager, SHA-1, and Firebase)
|
|
425
|
+
|
|
426
|
+
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.
|
|
427
|
+
|
|
428
|
+
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.
|
|
429
|
+
|
|
430
|
+
##### Required Google Cloud setup (all in the same project)
|
|
431
|
+
|
|
432
|
+
You need **two kinds** of OAuth 2.0 client IDs:
|
|
433
|
+
|
|
434
|
+
| Client type | Used for | Where it goes |
|
|
435
|
+
|-------------|----------|---------------|
|
|
436
|
+
| **Web application** | Server / ID token audience | `webClientId` in `SocialLogin.initialize()` |
|
|
437
|
+
| **Android** (one per signing key) | Proves your APK is allowed to call Google | Google Cloud Console only — **do not** pass this ID to `webClientId` |
|
|
438
|
+
|
|
439
|
+
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.
|
|
440
|
+
|
|
441
|
+
Create one Android OAuth client for **each** certificate that signs builds you test:
|
|
442
|
+
|
|
443
|
+
- **Debug** — from `./gradlew signingReport` (debug variant)
|
|
444
|
+
- **Release** — from the APK/AAB you actually install (see below)
|
|
445
|
+
- **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)
|
|
446
|
+
|
|
447
|
+
The `applicationId` in `android/app/build.gradle` must match the Android OAuth client package name exactly (including any `.debug` suffix if you use one).
|
|
448
|
+
|
|
449
|
+
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.
|
|
450
|
+
|
|
451
|
+
Google Cloud changes can take **up to a few hours** to propagate; a device restart alone may not be enough.
|
|
452
|
+
|
|
453
|
+
##### Error `[28444] Developer console is not set up correctly`
|
|
454
|
+
|
|
455
|
+
This almost always means Google rejected the combination of **installed APK signing certificate**, **package name**, and **webClientId`. Work through this checklist:
|
|
456
|
+
|
|
457
|
+
1. Confirm `webClientId` is the **Web application** client ID (ends with `.apps.googleusercontent.com`).
|
|
458
|
+
2. Run the app, reproduce the failure, and read Logcat (`GoogleProvider`) for `signingSha1=` and `package=`.
|
|
459
|
+
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.
|
|
460
|
+
4. If testing a **release** build, register the SHA-1 from that build — not only the debug keystore.
|
|
461
|
+
5. If the app is distributed via **Play Store**, also register the **Play App Signing** SHA-1.
|
|
462
|
+
6. Ensure Web and Android clients live in the **same** Google Cloud project.
|
|
463
|
+
7. If consent screen is in Testing, confirm the Google account is a **test user**.
|
|
464
|
+
8. Wait and retry after console changes.
|
|
465
|
+
|
|
466
|
+
`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.
|
|
467
|
+
|
|
468
|
+
##### Extract SHA-1 from the build you install
|
|
469
|
+
|
|
470
|
+
Debug / local builds:
|
|
471
|
+
|
|
472
|
+
```bash
|
|
473
|
+
cd android && ./gradlew signingReport
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
Signed release APK:
|
|
477
|
+
|
|
478
|
+
```bash
|
|
479
|
+
keytool -printcert -jarfile android/app/release/app-release.apk
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
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:
|
|
483
|
+
|
|
484
|
+
```bash
|
|
485
|
+
adb install android/app/release/app-release.apk
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
##### Reading the login result (Firebase and backends)
|
|
489
|
+
|
|
490
|
+
Tokens are nested under `result`:
|
|
491
|
+
|
|
492
|
+
```ts
|
|
493
|
+
const login = await SocialLogin.login({ provider: 'google' });
|
|
494
|
+
const idToken = login.result?.idToken; // not login.idToken
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
For Firebase Auth, create credentials with that `idToken` and use the **Web** Client ID as `webClientId` in `initialize`.
|
|
443
498
|
|
|
444
499
|
### iOS configuration
|
|
445
500
|
|
|
@@ -484,7 +539,9 @@ On Web, Google `refresh()` is not implemented, even when using `mode: 'online'`.
|
|
|
484
539
|
|
|
485
540
|
## OAuth2 (Generic)
|
|
486
541
|
|
|
487
|
-
The plugin supports generic OAuth2 authentication, allowing you to integrate with any OAuth2-compliant provider (GitHub, Azure AD, Auth0, Okta, custom servers, etc.). You can configure multiple OAuth2 providers simultaneously.
|
|
542
|
+
The plugin supports generic OAuth2 authentication, allowing you to integrate with any OAuth2-compliant provider (GitHub, Azure AD, Auth0, Okta, Keycloak, custom servers, etc.). You can configure multiple OAuth2 providers simultaneously.
|
|
543
|
+
|
|
544
|
+
For Keycloak, use the generic OAuth2 provider with your realm issuer URL. See the [Keycloak setup guide](./docs/setup_keycloak.md).
|
|
488
545
|
|
|
489
546
|
### Multi-Provider Configuration
|
|
490
547
|
|
|
@@ -623,8 +680,9 @@ await SocialLogin.refresh({
|
|
|
623
680
|
| Option | Type | Required | Description |
|
|
624
681
|
|--------|------|----------|-------------|
|
|
625
682
|
| `appId` | string | Yes | OAuth2 Client ID |
|
|
626
|
-
| `
|
|
627
|
-
| `
|
|
683
|
+
| `issuerUrl` | string | No* | OpenID Connect issuer URL for discovery (*Use this or an authorization endpoint) |
|
|
684
|
+
| `authorizationBaseUrl` / `authorizationEndpoint` | string | No* | Authorization endpoint URL aliases for the same setting (*Use one alias or `issuerUrl`) |
|
|
685
|
+
| `accessTokenEndpoint` / `tokenEndpoint` | string | No* | Token endpoint URL aliases for the same setting (*Required for code flow without `issuerUrl`) |
|
|
628
686
|
| `redirectUrl` | string | Yes | Callback URL for OAuth redirect |
|
|
629
687
|
| `responseType` | 'code' \| 'token' | No | OAuth flow type (default: 'code') |
|
|
630
688
|
| `pkceEnabled` | boolean | No | Enable PKCE (default: true) |
|
|
@@ -690,6 +748,12 @@ const config: CapacitorConfig = {
|
|
|
690
748
|
|
|
691
749
|
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
750
|
|
|
751
|
+
### Google Sign-In `[28444] Developer console is not set up correctly` (Android)
|
|
752
|
+
|
|
753
|
+
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.
|
|
754
|
+
|
|
755
|
+
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.
|
|
756
|
+
|
|
693
757
|
### Google Sign-In with Family Link Supervised Accounts
|
|
694
758
|
|
|
695
759
|
**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.35";
|
|
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.35"
|
|
20
20
|
public let identifier = "SocialLoginPlugin"
|
|
21
21
|
public let jsName = "SocialLogin"
|
|
22
22
|
private static let userCancelledCode = "USER_CANCELLED"
|