@capgo/capacitor-social-login 1.2.5 → 6.0.1
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/CapgoCapacitorSocialLogin.podspec +4 -3
- package/Package.swift +3 -3
- package/README.md +19 -12
- package/android/build.gradle +10 -10
- package/android/src/main/java/ee/forgr/capacitor/social/login/GoogleProvider.java +107 -62
- package/android/src/main/java/ee/forgr/capacitor/social/login/SocialLoginPlugin.java +2 -1
- package/dist/docs.json +20 -4
- package/dist/esm/apple-provider.d.ts +17 -0
- package/dist/esm/apple-provider.js +83 -0
- package/dist/esm/apple-provider.js.map +1 -0
- package/dist/esm/base.d.ts +7 -0
- package/dist/esm/base.js +31 -0
- package/dist/esm/base.js.map +1 -0
- package/dist/esm/definitions.d.ts +18 -8
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/facebook-provider.d.ts +15 -0
- package/dist/esm/facebook-provider.js +94 -0
- package/dist/esm/facebook-provider.js.map +1 -0
- package/dist/esm/google-provider.d.ts +28 -0
- package/dist/esm/google-provider.js +350 -0
- package/dist/esm/google-provider.js.map +1 -0
- package/dist/esm/web.d.ts +4 -24
- package/dist/esm/web.js +28 -571
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +436 -428
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +436 -428
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/SocialLoginPlugin/GoogleProvider.swift +9 -3
- package/ios/Sources/SocialLoginPlugin/SocialLoginPlugin.swift +2 -5
- package/package.json +7 -8
|
@@ -11,11 +11,12 @@ Pod::Spec.new do |s|
|
|
|
11
11
|
s.author = package['author']
|
|
12
12
|
s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
|
|
13
13
|
s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
|
|
14
|
+
s.exclude_files = '**/node_modules/**/*', '**/examples/**/*'
|
|
14
15
|
s.ios.deployment_target = '14.0'
|
|
15
16
|
s.dependency 'Capacitor'
|
|
16
|
-
s.dependency 'FBSDKCoreKit', '17.
|
|
17
|
-
s.dependency 'FBSDKLoginKit', '17.
|
|
17
|
+
s.dependency 'FBSDKCoreKit', '17.4.0'
|
|
18
|
+
s.dependency 'FBSDKLoginKit', '17.4.0'
|
|
18
19
|
s.dependency 'GoogleSignIn', '~> 8.0.0'
|
|
19
|
-
s.dependency 'Alamofire'
|
|
20
|
+
s.dependency 'Alamofire', '~> 5.10.2'
|
|
20
21
|
s.swift_version = '5.1'
|
|
21
22
|
end
|
package/Package.swift
CHANGED
|
@@ -3,16 +3,16 @@ import PackageDescription
|
|
|
3
3
|
|
|
4
4
|
let package = Package(
|
|
5
5
|
name: "CapgoCapacitorSocialLogin",
|
|
6
|
-
platforms: [.iOS(.
|
|
6
|
+
platforms: [.iOS(.v13)],
|
|
7
7
|
products: [
|
|
8
8
|
.library(
|
|
9
9
|
name: "CapgoCapacitorSocialLogin",
|
|
10
10
|
targets: ["SocialLoginPlugin"])
|
|
11
11
|
],
|
|
12
12
|
dependencies: [
|
|
13
|
-
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "
|
|
13
|
+
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "6.2.1"),
|
|
14
14
|
// FBSDKCoreKit and FBSDKLoginKit
|
|
15
|
-
.package(url: "https://github.com/facebook/facebook-ios-sdk.git", .upToNextMajor(from: "
|
|
15
|
+
.package(url: "https://github.com/facebook/facebook-ios-sdk.git", .upToNextMajor(from: "17.4.0")),
|
|
16
16
|
// Add Google Sign-In dependency
|
|
17
17
|
.package(url: "https://github.com/google/GoogleSignIn-iOS.git", .upToNextMajor(from: "8.0.0")),
|
|
18
18
|
// Alamofire
|
package/README.md
CHANGED
|
@@ -197,6 +197,8 @@ const res = await SocialLogin.login({
|
|
|
197
197
|
|
|
198
198
|
### Android configuration
|
|
199
199
|
|
|
200
|
+
The implemention use the new library of Google who use Google account at Os level, make sure your device does have at least one google account connected
|
|
201
|
+
|
|
200
202
|
Directly call the `initialize` method with the `google` provider
|
|
201
203
|
|
|
202
204
|
```typescript
|
|
@@ -221,7 +223,7 @@ Call the `initialize` method with the `google` provider
|
|
|
221
223
|
await SocialLogin.initialize({
|
|
222
224
|
google: {
|
|
223
225
|
iOSClientId: 'your-client-id', // the iOS client id
|
|
224
|
-
iOSServerClientId: 'your-server-client-id', // the iOS server client id (
|
|
226
|
+
iOSServerClientId: 'your-server-client-id', // the iOS server client id (required in mode offline)
|
|
225
227
|
},
|
|
226
228
|
});
|
|
227
229
|
const res = await SocialLogin.login({
|
|
@@ -232,6 +234,10 @@ const res = await SocialLogin.login({
|
|
|
232
234
|
});
|
|
233
235
|
```
|
|
234
236
|
|
|
237
|
+
### Web
|
|
238
|
+
|
|
239
|
+
Initialize method create a script tag with google lib, we canot knwo when it's ready so be sure to do it early in web otherwise it will fail
|
|
240
|
+
|
|
235
241
|
## API
|
|
236
242
|
|
|
237
243
|
<docgen-index>
|
|
@@ -351,11 +357,11 @@ Refresh the access token
|
|
|
351
357
|
|
|
352
358
|
#### InitializeOptions
|
|
353
359
|
|
|
354
|
-
| Prop | Type
|
|
355
|
-
| -------------- |
|
|
356
|
-
| **`facebook`** | <code>{ appId: string; clientToken: string; }</code>
|
|
357
|
-
| **`google`** | <code>{ iOSClientId?: string; iOSServerClientId?: string; webClientId?: string; mode?: 'online' \| 'offline'; }</code> |
|
|
358
|
-
| **`apple`** | <code>{ clientId?: string; redirectUrl?: string; }</code>
|
|
360
|
+
| Prop | Type |
|
|
361
|
+
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
362
|
+
| **`facebook`** | <code>{ appId: string; clientToken: string; }</code> |
|
|
363
|
+
| **`google`** | <code>{ iOSClientId?: string; iOSServerClientId?: string; webClientId?: string; mode?: 'online' \| 'offline'; hostedDomain?: string; }</code> |
|
|
364
|
+
| **`apple`** | <code>{ clientId?: string; redirectUrl?: string; }</code> |
|
|
359
365
|
|
|
360
366
|
|
|
361
367
|
#### FacebookLoginResponse
|
|
@@ -420,12 +426,13 @@ Refresh the access token
|
|
|
420
426
|
|
|
421
427
|
#### GoogleLoginOptions
|
|
422
428
|
|
|
423
|
-
| Prop | Type
|
|
424
|
-
| ----------------------- |
|
|
425
|
-
| **`scopes`** | <code>string[]</code>
|
|
426
|
-
| **`nonce`** | <code>string</code>
|
|
427
|
-
| **`forceRefreshToken`** | <code>boolean</code>
|
|
428
|
-
| **`
|
|
429
|
+
| Prop | Type | Description | Default |
|
|
430
|
+
| ----------------------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------- | ----------------------- |
|
|
431
|
+
| **`scopes`** | <code>string[]</code> | Specifies the scopes required for accessing Google APIs The default is defined in the configuration. | |
|
|
432
|
+
| **`nonce`** | <code>string</code> | Nonce | |
|
|
433
|
+
| **`forceRefreshToken`** | <code>boolean</code> | Force refresh token (only for Android) | <code>false</code> |
|
|
434
|
+
| **`forcePrompt`** | <code>boolean</code> | Force account selection prompt (iOS) | <code>false</code> |
|
|
435
|
+
| **`style`** | <code>'bottom' \| 'standard'</code> | Style | <code>'standard'</code> |
|
|
429
436
|
|
|
430
437
|
|
|
431
438
|
#### AppleProviderOptions
|
package/android/build.gradle
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
ext {
|
|
2
2
|
junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
|
|
3
|
-
androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.
|
|
4
|
-
androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.
|
|
5
|
-
androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.
|
|
3
|
+
androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.6.1'
|
|
4
|
+
androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.1.5'
|
|
5
|
+
androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.5.1'
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
buildscript {
|
|
@@ -11,7 +11,7 @@ buildscript {
|
|
|
11
11
|
mavenCentral()
|
|
12
12
|
}
|
|
13
13
|
dependencies {
|
|
14
|
-
classpath 'com.android.tools.build:gradle:8.
|
|
14
|
+
classpath 'com.android.tools.build:gradle:8.2.1'
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -19,10 +19,10 @@ apply plugin: 'com.android.library'
|
|
|
19
19
|
|
|
20
20
|
android {
|
|
21
21
|
namespace "ee.forgr.capacitor.social.login"
|
|
22
|
-
compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion :
|
|
22
|
+
compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 34
|
|
23
23
|
defaultConfig {
|
|
24
|
-
minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion :
|
|
25
|
-
targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion :
|
|
24
|
+
minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 22
|
|
25
|
+
targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 34
|
|
26
26
|
versionCode 1
|
|
27
27
|
versionName "1.0"
|
|
28
28
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
|
@@ -37,8 +37,8 @@ android {
|
|
|
37
37
|
abortOnError false
|
|
38
38
|
}
|
|
39
39
|
compileOptions {
|
|
40
|
-
sourceCompatibility JavaVersion.
|
|
41
|
-
targetCompatibility JavaVersion.
|
|
40
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
41
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
|
|
@@ -52,7 +52,7 @@ dependencies {
|
|
|
52
52
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
|
53
53
|
implementation project(':capacitor-android')
|
|
54
54
|
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
|
55
|
-
implementation 'com.facebook.android:facebook-login:18.0.
|
|
55
|
+
implementation 'com.facebook.android:facebook-login:18.0.2'
|
|
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.3.0"
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
package ee.forgr.capacitor.social.login;
|
|
2
2
|
|
|
3
|
-
import android.accounts.Account;
|
|
4
3
|
import android.app.Activity;
|
|
5
4
|
import android.app.PendingIntent;
|
|
6
5
|
import android.content.Context;
|
|
7
6
|
import android.content.Intent;
|
|
8
7
|
import android.content.IntentSender;
|
|
9
|
-
import android.text.TextUtils;
|
|
10
8
|
import android.util.Log;
|
|
11
9
|
import androidx.annotation.NonNull;
|
|
12
10
|
import androidx.concurrent.futures.CallbackToFutureAdapter;
|
|
@@ -22,13 +20,12 @@ import androidx.credentials.exceptions.GetCredentialException;
|
|
|
22
20
|
import androidx.credentials.exceptions.NoCredentialException;
|
|
23
21
|
import com.getcapacitor.JSObject;
|
|
24
22
|
import com.getcapacitor.PluginCall;
|
|
25
|
-
import com.google.android.gms.auth.GoogleAuthException;
|
|
26
|
-
import com.google.android.gms.auth.GoogleAuthUtil;
|
|
27
23
|
import com.google.android.gms.auth.api.identity.AuthorizationRequest;
|
|
28
24
|
import com.google.android.gms.auth.api.identity.AuthorizationResult;
|
|
29
25
|
import com.google.android.gms.auth.api.identity.Identity;
|
|
30
26
|
import com.google.android.gms.common.api.ApiException;
|
|
31
27
|
import com.google.android.gms.common.api.Scope;
|
|
28
|
+
import com.google.android.libraries.identity.googleid.GetGoogleIdOption;
|
|
32
29
|
import com.google.android.libraries.identity.googleid.GetSignInWithGoogleOption;
|
|
33
30
|
import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential;
|
|
34
31
|
import com.google.common.util.concurrent.ListenableFuture;
|
|
@@ -37,12 +34,11 @@ import java.io.IOException;
|
|
|
37
34
|
import java.util.ArrayList;
|
|
38
35
|
import java.util.HashSet;
|
|
39
36
|
import java.util.List;
|
|
37
|
+
import java.util.Objects;
|
|
40
38
|
import java.util.Set;
|
|
41
|
-
import java.util.concurrent.Callable;
|
|
42
39
|
import java.util.concurrent.Executor;
|
|
43
40
|
import java.util.concurrent.ExecutorService;
|
|
44
41
|
import java.util.concurrent.Executors;
|
|
45
|
-
import java.util.concurrent.Future;
|
|
46
42
|
import java.util.concurrent.TimeUnit;
|
|
47
43
|
import okhttp3.Call;
|
|
48
44
|
import okhttp3.Callback;
|
|
@@ -71,11 +67,12 @@ public class GoogleProvider implements SocialProvider {
|
|
|
71
67
|
private CredentialManager credentialManager;
|
|
72
68
|
private String clientId;
|
|
73
69
|
private String[] scopes;
|
|
74
|
-
private List<CallbackToFutureAdapter.Completer<AuthorizationResult>> futuresList = new ArrayList<>(FUTURE_LIST_LENGTH);
|
|
70
|
+
private final List<CallbackToFutureAdapter.Completer<AuthorizationResult>> futuresList = new ArrayList<>(FUTURE_LIST_LENGTH);
|
|
75
71
|
|
|
76
72
|
private String idToken = null;
|
|
77
73
|
private String accessToken = null;
|
|
78
74
|
private GoogleProviderLoginType mode = GoogleProviderLoginType.ONLINE;
|
|
75
|
+
private String hostedDomain = null;
|
|
79
76
|
|
|
80
77
|
public enum GoogleProviderLoginType {
|
|
81
78
|
ONLINE,
|
|
@@ -91,10 +88,11 @@ public class GoogleProvider implements SocialProvider {
|
|
|
91
88
|
}
|
|
92
89
|
}
|
|
93
90
|
|
|
94
|
-
public void initialize(String clientId, GoogleProviderLoginType mode) {
|
|
91
|
+
public void initialize(String clientId, GoogleProviderLoginType mode, String hostedDomain) {
|
|
95
92
|
this.credentialManager = CredentialManager.create(activity);
|
|
96
93
|
this.clientId = clientId;
|
|
97
94
|
this.mode = mode;
|
|
95
|
+
this.hostedDomain = hostedDomain;
|
|
98
96
|
|
|
99
97
|
String data = context.getSharedPreferences(SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE).getString(GOOGLE_DATA_PREFERENCE, null);
|
|
100
98
|
|
|
@@ -200,7 +198,7 @@ public class GoogleProvider implements SocialProvider {
|
|
|
200
198
|
return;
|
|
201
199
|
}
|
|
202
200
|
|
|
203
|
-
|
|
201
|
+
int expressInInt;
|
|
204
202
|
try {
|
|
205
203
|
expressInInt = Integer.parseInt(expiresIn);
|
|
206
204
|
} catch (Exception e) {
|
|
@@ -261,9 +259,9 @@ public class GoogleProvider implements SocialProvider {
|
|
|
261
259
|
}
|
|
262
260
|
|
|
263
261
|
public String arrayFind(String[] array, String search) {
|
|
264
|
-
for (
|
|
265
|
-
if (
|
|
266
|
-
return
|
|
262
|
+
for (String s : array) {
|
|
263
|
+
if (s.equals(search)) {
|
|
264
|
+
return s;
|
|
267
265
|
}
|
|
268
266
|
}
|
|
269
267
|
return null;
|
|
@@ -281,66 +279,90 @@ public class GoogleProvider implements SocialProvider {
|
|
|
281
279
|
return;
|
|
282
280
|
}
|
|
283
281
|
|
|
284
|
-
String nonce =
|
|
282
|
+
String nonce = config.optString("nonce");
|
|
283
|
+
JSONObject options = call.getObject("options", new JSObject());
|
|
284
|
+
boolean bottomUi = false;
|
|
285
|
+
boolean forcePrompt = false;
|
|
286
|
+
boolean filterByAuthorizedAccounts = false;
|
|
287
|
+
boolean autoSelectEnabled = false;
|
|
285
288
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
for (int i = 0; i < scopesArray.length(); i++) {
|
|
293
|
-
uniqueScopes.add(scopesArray.optString(i));
|
|
289
|
+
try {
|
|
290
|
+
if (options != null) {
|
|
291
|
+
bottomUi = options.has("style") && Objects.equals(options.getString("style"), "bottom");
|
|
292
|
+
filterByAuthorizedAccounts = options.has("filterByAuthorizedAccounts") && options.getBoolean("filterByAuthorizedAccounts");
|
|
293
|
+
autoSelectEnabled = options.has("autoSelectEnabled") && options.getBoolean("autoSelectEnabled");
|
|
294
|
+
forcePrompt = options.has("forcePrompt") && options.getBoolean("forcePrompt");
|
|
294
295
|
}
|
|
295
|
-
|
|
296
|
+
} catch (JSONException e) {
|
|
297
|
+
Log.e(LOG_TAG, "Error parsing options", e);
|
|
298
|
+
call.reject("Error parsing options: " + e.getMessage());
|
|
299
|
+
return;
|
|
296
300
|
}
|
|
301
|
+
|
|
302
|
+
// Handle scopes
|
|
303
|
+
JSONArray scopesArray = config.optJSONArray("scopes");
|
|
304
|
+
Set<String> uniqueScopes = new HashSet<>();
|
|
305
|
+
|
|
306
|
+
// Add default scopes
|
|
307
|
+
uniqueScopes.add("https://www.googleapis.com/auth/userinfo.email");
|
|
308
|
+
uniqueScopes.add("https://www.googleapis.com/auth/userinfo.profile");
|
|
309
|
+
uniqueScopes.add("openid");
|
|
310
|
+
|
|
311
|
+
// Add custom scopes if provided
|
|
297
312
|
if (scopesArray != null) {
|
|
298
313
|
if (!(this.activity instanceof ModifiedMainActivityForSocialLoginPlugin)) {
|
|
299
314
|
call.reject("You CANNOT use scopes without modifying the main activity. Please follow the docs!");
|
|
300
315
|
return;
|
|
301
316
|
}
|
|
302
|
-
|
|
303
|
-
this.scopes = new String[scopesArray.length()];
|
|
304
317
|
for (int i = 0; i < scopesArray.length(); i++) {
|
|
305
|
-
|
|
318
|
+
uniqueScopes.add(scopesArray.optString(i));
|
|
306
319
|
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
this.scopes = uniqueScopes.toArray(new String[0]);
|
|
307
323
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
324
|
+
// Build credential request
|
|
325
|
+
GetCredentialRequest.Builder requestBuilder = new GetCredentialRequest.Builder();
|
|
326
|
+
|
|
327
|
+
if (bottomUi) {
|
|
328
|
+
Log.e(LOG_TAG, "use bottomUi");
|
|
329
|
+
GetGoogleIdOption.Builder googleIdOptionBuilder = new GetGoogleIdOption.Builder().setServerClientId(this.clientId);
|
|
330
|
+
// Handle bottom UI specific options
|
|
331
|
+
if (forcePrompt) {
|
|
332
|
+
filterByAuthorizedAccounts = false;
|
|
333
|
+
autoSelectEnabled = false;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (!nonce.isEmpty()) {
|
|
337
|
+
googleIdOptionBuilder.setNonce(nonce);
|
|
313
338
|
}
|
|
314
|
-
if (
|
|
315
|
-
|
|
316
|
-
System.arraycopy(this.scopes, 0, newScopes, 0, this.scopes.length);
|
|
317
|
-
newScopes[this.scopes.length] = "https://www.googleapis.com/auth/userinfo.profile";
|
|
318
|
-
this.scopes = newScopes;
|
|
339
|
+
if (filterByAuthorizedAccounts) {
|
|
340
|
+
googleIdOptionBuilder.setFilterByAuthorizedAccounts(true);
|
|
319
341
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
newScopes[this.scopes.length] = "openid";
|
|
324
|
-
this.scopes = newScopes;
|
|
342
|
+
|
|
343
|
+
if (autoSelectEnabled) {
|
|
344
|
+
googleIdOptionBuilder.setAutoSelectEnabled(true);
|
|
325
345
|
}
|
|
346
|
+
|
|
347
|
+
GetGoogleIdOption googleIdOptionFiltered = googleIdOptionBuilder.build();
|
|
348
|
+
requestBuilder.addCredentialOption(googleIdOptionFiltered);
|
|
326
349
|
} else {
|
|
327
|
-
//
|
|
328
|
-
|
|
329
|
-
"https://www.googleapis.com/auth/userinfo.profile",
|
|
330
|
-
"https://www.googleapis.com/auth/userinfo.email",
|
|
331
|
-
"openid"
|
|
332
|
-
};
|
|
333
|
-
}
|
|
350
|
+
// For standard UI, we don't use these options
|
|
351
|
+
GetSignInWithGoogleOption.Builder googleIdOptionBuilder = new GetSignInWithGoogleOption.Builder(this.clientId);
|
|
334
352
|
|
|
335
|
-
|
|
353
|
+
if (!nonce.isEmpty()) {
|
|
354
|
+
googleIdOptionBuilder.setNonce(nonce);
|
|
355
|
+
}
|
|
356
|
+
if (this.hostedDomain != null && !this.hostedDomain.isEmpty()) {
|
|
357
|
+
googleIdOptionBuilder.setHostedDomainFilter(this.hostedDomain);
|
|
358
|
+
}
|
|
336
359
|
|
|
337
|
-
|
|
338
|
-
googleIdOptionBuilder.setNonce(nonce);
|
|
360
|
+
requestBuilder.addCredentialOption(googleIdOptionBuilder.build());
|
|
339
361
|
}
|
|
340
362
|
|
|
341
|
-
|
|
342
|
-
GetCredentialRequest filteredRequest = new GetCredentialRequest.Builder().addCredentialOption(googleIdOptionFiltered).build();
|
|
363
|
+
GetCredentialRequest filteredRequest = requestBuilder.build();
|
|
343
364
|
|
|
365
|
+
// Execute credential request
|
|
344
366
|
Executor executor = Executors.newSingleThreadExecutor();
|
|
345
367
|
credentialManager.getCredentialAsync(
|
|
346
368
|
context,
|
|
@@ -354,8 +376,8 @@ public class GoogleProvider implements SocialProvider {
|
|
|
354
376
|
}
|
|
355
377
|
|
|
356
378
|
@Override
|
|
357
|
-
public void onError(GetCredentialException e) {
|
|
358
|
-
handleSignInError(e, call);
|
|
379
|
+
public void onError(@NonNull GetCredentialException e) {
|
|
380
|
+
handleSignInError(e, call, config);
|
|
359
381
|
}
|
|
360
382
|
}
|
|
361
383
|
);
|
|
@@ -394,8 +416,8 @@ public class GoogleProvider implements SocialProvider {
|
|
|
394
416
|
|
|
395
417
|
ListenableFuture<AuthorizationResult> future = CallbackToFutureAdapter.getFuture(completer -> {
|
|
396
418
|
List<Scope> scopes = new ArrayList<>(this.scopes.length);
|
|
397
|
-
for (
|
|
398
|
-
scopes.add(new Scope(
|
|
419
|
+
for (String scope : this.scopes) {
|
|
420
|
+
scopes.add(new Scope(scope));
|
|
399
421
|
}
|
|
400
422
|
AuthorizationRequest.Builder authorizationRequestBuilder = AuthorizationRequest.builder().setRequestedScopes(scopes);
|
|
401
423
|
// .requestOfflineAccess(this.clientId)
|
|
@@ -489,8 +511,12 @@ public class GoogleProvider implements SocialProvider {
|
|
|
489
511
|
JSObject resultObj = new JSObject();
|
|
490
512
|
|
|
491
513
|
JSONObject options = call.getObject("options", new JSObject());
|
|
492
|
-
Boolean forceRefreshToken =
|
|
493
|
-
|
|
514
|
+
Boolean forceRefreshToken = false;
|
|
515
|
+
try {
|
|
516
|
+
forceRefreshToken = options != null && options.has("forceRefreshToken") && options.getBoolean("forceRefreshToken");
|
|
517
|
+
} catch (JSONException e) {
|
|
518
|
+
Log.e(LOG_TAG, "Error parsing forceRefreshToken option", e);
|
|
519
|
+
}
|
|
494
520
|
|
|
495
521
|
GoogleIdTokenCredential googleIdTokenCredential = GoogleIdTokenCredential.createFrom(credential.getData());
|
|
496
522
|
ListenableFuture<AuthorizationResult> future = getAuthorizationResult(forceRefreshToken);
|
|
@@ -577,10 +603,29 @@ public class GoogleProvider implements SocialProvider {
|
|
|
577
603
|
}
|
|
578
604
|
}
|
|
579
605
|
|
|
580
|
-
private void handleSignInError(GetCredentialException e, PluginCall call) {
|
|
606
|
+
private void handleSignInError(GetCredentialException e, PluginCall call, JSONObject config) {
|
|
581
607
|
Log.e(LOG_TAG, "Google Sign-In failed", e);
|
|
582
|
-
|
|
583
|
-
|
|
608
|
+
boolean isBottomUi = false;
|
|
609
|
+
JSONObject options = call.getObject("options", new JSObject());
|
|
610
|
+
if (options.has("style")) {
|
|
611
|
+
try {
|
|
612
|
+
isBottomUi = options.getString("style").equals("bottom");
|
|
613
|
+
} catch (JSONException ex) {
|
|
614
|
+
// do nothing
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
if (e instanceof NoCredentialException && isBottomUi) {
|
|
618
|
+
Log.e(LOG_TAG, "No Google accounts available or miss configuration using bottomUi, auto switch to standard UI");
|
|
619
|
+
// During the get credential flow, this is returned when no viable credential is available for the the user. This can be caused by various scenarios such as that the user doesn't have any credential or the user doesn't grant consent to using any available credential. Upon this exception, your app should navigate to use the regular app sign-up or sign-in screen.
|
|
620
|
+
// https://developer.android.com/reference/androidx/credentials/exceptions/NoCredentialException
|
|
621
|
+
try {
|
|
622
|
+
options.put("style", "standard");
|
|
623
|
+
call.getData().put("options", options);
|
|
624
|
+
} catch (JSONException ex) {
|
|
625
|
+
call.reject("Google Sign-In failed: " + ex.getMessage());
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
login(call, config);
|
|
584
629
|
} else {
|
|
585
630
|
call.reject("Google Sign-In failed: " + e.getMessage());
|
|
586
631
|
}
|
|
@@ -650,7 +695,7 @@ public class GoogleProvider implements SocialProvider {
|
|
|
650
695
|
}
|
|
651
696
|
|
|
652
697
|
@Override
|
|
653
|
-
public void onError(ClearCredentialException e) {
|
|
698
|
+
public void onError(@NonNull ClearCredentialException e) {
|
|
654
699
|
Log.e(LOG_TAG, "Failed to clear credential state", e);
|
|
655
700
|
handler.onError(e);
|
|
656
701
|
}
|
|
@@ -59,6 +59,7 @@ public class SocialLoginPlugin extends Plugin {
|
|
|
59
59
|
call.reject("google.clientId is null or empty");
|
|
60
60
|
return;
|
|
61
61
|
}
|
|
62
|
+
String hostedDomain = google.getString("hostedDomain");
|
|
62
63
|
String modestr = google.getString("mode", "online");
|
|
63
64
|
GoogleProvider.GoogleProviderLoginType mode = null;
|
|
64
65
|
switch (modestr) {
|
|
@@ -72,7 +73,7 @@ public class SocialLoginPlugin extends Plugin {
|
|
|
72
73
|
call.reject("google.mode != (online || offline)");
|
|
73
74
|
return;
|
|
74
75
|
}
|
|
75
|
-
googleProvider.initialize(googleClientId, mode);
|
|
76
|
+
googleProvider.initialize(googleClientId, mode, hostedDomain);
|
|
76
77
|
this.socialProviderHashMap.put("google", googleProvider);
|
|
77
78
|
}
|
|
78
79
|
|
package/dist/docs.json
CHANGED
|
@@ -168,7 +168,7 @@
|
|
|
168
168
|
"tags": [],
|
|
169
169
|
"docs": "",
|
|
170
170
|
"complexTypes": [],
|
|
171
|
-
"type": "{ iOSClientId?: string | undefined; iOSServerClientId?: string | undefined; webClientId?: string | undefined; mode?: 'online' | 'offline' | undefined; } | undefined"
|
|
171
|
+
"type": "{ iOSClientId?: string | undefined; iOSServerClientId?: string | undefined; webClientId?: string | undefined; mode?: 'online' | 'offline' | undefined; hostedDomain?: string | undefined; } | undefined"
|
|
172
172
|
},
|
|
173
173
|
{
|
|
174
174
|
"name": "apple",
|
|
@@ -478,10 +478,10 @@
|
|
|
478
478
|
"type": "boolean | undefined"
|
|
479
479
|
},
|
|
480
480
|
{
|
|
481
|
-
"name": "
|
|
481
|
+
"name": "forcePrompt",
|
|
482
482
|
"tags": [
|
|
483
483
|
{
|
|
484
|
-
"text": "
|
|
484
|
+
"text": "forces the account selection prompt to appear on iOS",
|
|
485
485
|
"name": "description"
|
|
486
486
|
},
|
|
487
487
|
{
|
|
@@ -489,9 +489,25 @@
|
|
|
489
489
|
"name": "default"
|
|
490
490
|
}
|
|
491
491
|
],
|
|
492
|
-
"docs": "
|
|
492
|
+
"docs": "Force account selection prompt (iOS)",
|
|
493
493
|
"complexTypes": [],
|
|
494
494
|
"type": "boolean | undefined"
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
"name": "style",
|
|
498
|
+
"tags": [
|
|
499
|
+
{
|
|
500
|
+
"text": "style",
|
|
501
|
+
"name": "description"
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
"text": "'standard'",
|
|
505
|
+
"name": "default"
|
|
506
|
+
}
|
|
507
|
+
],
|
|
508
|
+
"docs": "Style",
|
|
509
|
+
"complexTypes": [],
|
|
510
|
+
"type": "'bottom' | 'standard' | undefined"
|
|
495
511
|
}
|
|
496
512
|
]
|
|
497
513
|
},
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { BaseSocialLogin } from './base';
|
|
2
|
+
import type { AppleProviderOptions, AuthorizationCode, LoginResult } from './definitions';
|
|
3
|
+
export declare class AppleSocialLogin extends BaseSocialLogin {
|
|
4
|
+
private clientId;
|
|
5
|
+
private redirectUrl;
|
|
6
|
+
private scriptLoaded;
|
|
7
|
+
private scriptUrl;
|
|
8
|
+
initialize(clientId: string | null, redirectUrl: string | null | undefined): Promise<void>;
|
|
9
|
+
login(options: AppleProviderOptions): Promise<LoginResult>;
|
|
10
|
+
logout(): Promise<void>;
|
|
11
|
+
isLoggedIn(): Promise<{
|
|
12
|
+
isLoggedIn: boolean;
|
|
13
|
+
}>;
|
|
14
|
+
getAuthorizationCode(): Promise<AuthorizationCode>;
|
|
15
|
+
refresh(): Promise<void>;
|
|
16
|
+
private loadAppleScript;
|
|
17
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { BaseSocialLogin } from './base';
|
|
2
|
+
export class AppleSocialLogin extends BaseSocialLogin {
|
|
3
|
+
constructor() {
|
|
4
|
+
super(...arguments);
|
|
5
|
+
this.clientId = null;
|
|
6
|
+
this.redirectUrl = null;
|
|
7
|
+
this.scriptLoaded = false;
|
|
8
|
+
this.scriptUrl = 'https://appleid.cdn-apple.com/appleauth/static/jsapi/appleid/1/en_US/appleid.auth.js';
|
|
9
|
+
}
|
|
10
|
+
async initialize(clientId, redirectUrl) {
|
|
11
|
+
this.clientId = clientId;
|
|
12
|
+
this.redirectUrl = redirectUrl || null;
|
|
13
|
+
if (clientId) {
|
|
14
|
+
await this.loadAppleScript();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
async login(options) {
|
|
18
|
+
if (!this.clientId) {
|
|
19
|
+
throw new Error('Apple Client ID not set. Call initialize() first.');
|
|
20
|
+
}
|
|
21
|
+
if (!this.scriptLoaded) {
|
|
22
|
+
throw new Error('Apple Sign-In script not loaded.');
|
|
23
|
+
}
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
var _a;
|
|
26
|
+
AppleID.auth.init({
|
|
27
|
+
clientId: this.clientId,
|
|
28
|
+
scope: ((_a = options.scopes) === null || _a === void 0 ? void 0 : _a.join(' ')) || 'name email',
|
|
29
|
+
redirectURI: this.redirectUrl || window.location.href,
|
|
30
|
+
state: options.state,
|
|
31
|
+
nonce: options.nonce,
|
|
32
|
+
usePopup: true,
|
|
33
|
+
});
|
|
34
|
+
AppleID.auth
|
|
35
|
+
.signIn()
|
|
36
|
+
.then((res) => {
|
|
37
|
+
var _a, _b, _c, _d, _e;
|
|
38
|
+
const result = {
|
|
39
|
+
profile: {
|
|
40
|
+
user: res.user || '',
|
|
41
|
+
email: ((_a = res.user) === null || _a === void 0 ? void 0 : _a.email) || null,
|
|
42
|
+
givenName: ((_c = (_b = res.user) === null || _b === void 0 ? void 0 : _b.name) === null || _c === void 0 ? void 0 : _c.firstName) || null,
|
|
43
|
+
familyName: ((_e = (_d = res.user) === null || _d === void 0 ? void 0 : _d.name) === null || _e === void 0 ? void 0 : _e.lastName) || null,
|
|
44
|
+
},
|
|
45
|
+
accessToken: {
|
|
46
|
+
token: res.authorization.id_token || '',
|
|
47
|
+
},
|
|
48
|
+
idToken: res.authorization.code || null,
|
|
49
|
+
};
|
|
50
|
+
resolve({ provider: 'apple', result });
|
|
51
|
+
})
|
|
52
|
+
.catch((error) => {
|
|
53
|
+
reject(error);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
async logout() {
|
|
58
|
+
// Apple doesn't provide a logout method for web
|
|
59
|
+
console.log('Apple logout: Session should be managed on the client side');
|
|
60
|
+
}
|
|
61
|
+
async isLoggedIn() {
|
|
62
|
+
// Apple doesn't provide a method to check login status on web
|
|
63
|
+
console.log('Apple login status should be managed on the client side');
|
|
64
|
+
return { isLoggedIn: false };
|
|
65
|
+
}
|
|
66
|
+
async getAuthorizationCode() {
|
|
67
|
+
// Apple authorization code should be obtained during login
|
|
68
|
+
console.log('Apple authorization code should be stored during login');
|
|
69
|
+
throw new Error('Apple authorization code not available');
|
|
70
|
+
}
|
|
71
|
+
async refresh() {
|
|
72
|
+
// Apple doesn't provide a refresh method for web
|
|
73
|
+
console.log('Apple refresh not available on web');
|
|
74
|
+
}
|
|
75
|
+
async loadAppleScript() {
|
|
76
|
+
if (this.scriptLoaded)
|
|
77
|
+
return;
|
|
78
|
+
return this.loadScript(this.scriptUrl).then(() => {
|
|
79
|
+
this.scriptLoaded = true;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=apple-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apple-provider.js","sourceRoot":"","sources":["../../src/apple-provider.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseSocialLogin } from './base';\nimport type { AppleProviderOptions, AppleProviderResponse, AuthorizationCode, LoginResult } from './definitions';\n\ndeclare const AppleID: any;\n\nexport class AppleSocialLogin extends BaseSocialLogin {\n private clientId: string | null = null;\n private redirectUrl: string | null = null;\n private scriptLoaded = false;\n private scriptUrl = 'https://appleid.cdn-apple.com/appleauth/static/jsapi/appleid/1/en_US/appleid.auth.js';\n\n async initialize(clientId: string | null, redirectUrl: string | null | undefined): Promise<void> {\n this.clientId = clientId;\n this.redirectUrl = redirectUrl || null;\n\n if (clientId) {\n await this.loadAppleScript();\n }\n }\n\n async login(options: AppleProviderOptions): Promise<LoginResult> {\n if (!this.clientId) {\n throw new Error('Apple Client ID not set. Call initialize() first.');\n }\n\n if (!this.scriptLoaded) {\n throw new Error('Apple Sign-In script not loaded.');\n }\n\n return new Promise((resolve, reject) => {\n AppleID.auth.init({\n clientId: this.clientId!,\n scope: options.scopes?.join(' ') || 'name email',\n redirectURI: this.redirectUrl || window.location.href,\n state: options.state,\n nonce: options.nonce,\n usePopup: true,\n });\n\n AppleID.auth\n .signIn()\n .then((res: any) => {\n const result: AppleProviderResponse = {\n profile: {\n user: res.user || '',\n email: res.user?.email || null,\n givenName: res.user?.name?.firstName || null,\n familyName: res.user?.name?.lastName || null,\n },\n accessToken: {\n token: res.authorization.id_token || '',\n },\n idToken: res.authorization.code || null,\n };\n resolve({ provider: 'apple', result });\n })\n .catch((error: any) => {\n reject(error);\n });\n });\n }\n\n async logout(): Promise<void> {\n // Apple doesn't provide a logout method for web\n console.log('Apple logout: Session should be managed on the client side');\n }\n\n async isLoggedIn(): Promise<{ isLoggedIn: boolean }> {\n // Apple doesn't provide a method to check login status on web\n console.log('Apple login status should be managed on the client side');\n return { isLoggedIn: false };\n }\n\n async getAuthorizationCode(): Promise<AuthorizationCode> {\n // Apple authorization code should be obtained during login\n console.log('Apple authorization code should be stored during login');\n throw new Error('Apple authorization code not available');\n }\n\n async refresh(): Promise<void> {\n // Apple doesn't provide a refresh method for web\n console.log('Apple refresh not available on web');\n }\n\n private async loadAppleScript(): Promise<void> {\n if (this.scriptLoaded) return;\n\n return this.loadScript(this.scriptUrl).then(() => {\n this.scriptLoaded = true;\n });\n }\n}\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { WebPlugin } from '@capacitor/core';
|
|
2
|
+
export declare class BaseSocialLogin extends WebPlugin {
|
|
3
|
+
protected static readonly OAUTH_STATE_KEY = "social_login_oauth_pending";
|
|
4
|
+
constructor();
|
|
5
|
+
protected parseJwt(token: string): any;
|
|
6
|
+
protected loadScript(src: string): Promise<void>;
|
|
7
|
+
}
|