capacitor-native-google-one-tap-signin 0.2.0 → 0.3.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.
- package/CapacitorNativeGoogleOneTapSignin.podspec +16 -15
- package/README.md +61 -5
- package/android/build.gradle +3 -2
- package/android/src/main/java/com/proventask/capacitor/GoogleOneTapAuth/GoogleOneTapAuth.java +137 -67
- package/dist/docs.json +268 -0
- package/dist/esm/GoogleOneTapAuth.d.ts +13 -0
- package/dist/esm/GoogleOneTapAuth.js +72 -0
- package/dist/esm/GoogleOneTapAuth.js.map +1 -0
- package/dist/esm/definitions.d.ts +45 -42
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/helpers.d.ts +2 -0
- package/dist/esm/helpers.js +15 -0
- package/dist/esm/helpers.js.map +1 -0
- package/dist/esm/index.d.ts +1 -2
- package/dist/esm/index.js +1 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/web.d.ts +9 -13
- package/dist/esm/web.js +47 -63
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +232 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +233 -0
- package/dist/plugin.js.map +1 -0
- package/package.json +60 -34
- package/android/.gradle/7.3.3/checksums/checksums.lock +0 -0
- package/android/.gradle/7.3.3/dependencies-accessors/dependencies-accessors.lock +0 -0
- package/android/.gradle/7.3.3/dependencies-accessors/gc.properties +0 -0
- package/android/.gradle/7.3.3/executionHistory/executionHistory.bin +0 -0
- package/android/.gradle/7.3.3/executionHistory/executionHistory.lock +0 -0
- package/android/.gradle/7.3.3/fileChanges/last-build.bin +0 -0
- package/android/.gradle/7.3.3/fileHashes/fileHashes.bin +0 -0
- package/android/.gradle/7.3.3/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/7.3.3/fileHashes/resourceHashesCache.bin +0 -0
- package/android/.gradle/7.3.3/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
- package/android/.gradle/buildOutputCleanup/outputFiles.bin +0 -0
- package/android/.gradle/vcs-1/gc.properties +0 -0
- package/android/.idea/workspace.xml +0 -89
- package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/android/gradle/wrapper/gradle-wrapper.properties +0 -6
- package/android/gradle.properties +0 -16
- package/android/gradlew +0 -160
- package/android/gradlew.bat +0 -90
- package/android/proguard-rules.pro +0 -21
- package/android/settings.gradle +0 -2
- package/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java +0 -26
- package/android/src/test/java/com/getcapacitor/ExampleUnitTest.java +0 -17
- package/ios/Plugin.xcodeproj/project.pbxproj +0 -565
- package/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/ios/Plugin.xcworkspace/contents.xcworkspacedata +0 -10
- package/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
- package/ios/PluginTests/Info.plist +0 -22
- package/ios/PluginTests/PluginTests.swift +0 -35
- package/ios/Podfile +0 -23
- package/ios/Podfile.lock +0 -47
|
@@ -1,16 +1,17 @@
|
|
|
1
|
+
require 'json'
|
|
1
2
|
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = 'CapacitorNativeGoogleOneTapSignin'
|
|
7
|
+
s.version = package['version']
|
|
8
|
+
s.summary = package['description']
|
|
9
|
+
s.license = package['license']
|
|
10
|
+
s.homepage = package['repository']['url']
|
|
11
|
+
s.author = package['author']
|
|
12
|
+
s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
|
|
13
|
+
s.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}'
|
|
14
|
+
s.ios.deployment_target = '13.0'
|
|
15
|
+
s.dependency 'Capacitor'
|
|
16
|
+
s.swift_version = '5.1'
|
|
17
|
+
end
|
package/README.md
CHANGED
|
@@ -2,17 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
Wraps the native android One Tap api for ionic capacitor apps. One Tap Sign In is part of Sign in with Google and offers features as automatic sign-in on return visits.
|
|
4
4
|
|
|
5
|
+
This library intends to provide the best google authentication experience for each platform.
|
|
6
|
+
|
|
5
7
|
<img src="screenshots/one-tap-sign-in-demo-initial-sign-in.jpg" alt="One tap signin screenshot initial sign-in" width=250/>
|
|
6
8
|
<img src="screenshots/one-tap-sign-in-demo-sign-back-in.jpg" alt="One tap signin screenshot sign back in" width=250/>
|
|
7
9
|
<img src="screenshots/one-tap-sign-in-demo-without-filter-by-authorized-acounts.jpg" alt="One tap signin screenshot without filter by authorized accounts" width=250/>
|
|
8
10
|
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
After a successful authentication, the idToken is returned as base64 and as object.
|
|
11
13
|
|
|
12
14
|
The user is automatically signed-in, without a prompt after the first sign-in.
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
The andorid minSdkVersion is 24, however the one-tap library seems to require API version 29. For lower API versions GoogleSignIn is used.
|
|
15
17
|
|
|
18
|
+
# Supported platforms
|
|
19
|
+
Android, iOS, Web.
|
|
16
20
|
|
|
17
21
|
# Demos
|
|
18
22
|
See the `demo` folder.
|
|
@@ -20,7 +24,50 @@ See the `demo` folder.
|
|
|
20
24
|
# Setup
|
|
21
25
|
You need to provide the plug-in a client ID of type "Web application". However you will also need to create a client ID of type "Android" as stated in the [One Tap Get started docu](https://developers.google.cn/identity/one-tap/android/get-started) in the [Google Cloud Console](https://console.cloud.google.com/apis/dashboard).
|
|
22
26
|
|
|
23
|
-
To test it using an emulator, you need to create an emulator with android play services and API version
|
|
27
|
+
To test it using an emulator, you need to create an emulator with android play services and API version 24 or higher. See [my stackoverflow answer](https://stackoverflow.com/questions/71325279/missing-featurename-auth-api-credentials-begin-sign-in-version-6/75285717#75285717). For older API levels like 24, you may need to update Chrome after creating the emulator because the plugin targets JavaScript es2017.
|
|
28
|
+
|
|
29
|
+
# Usage recommendation
|
|
30
|
+
|
|
31
|
+
For web, it will auto-sign in even if the user signs out.
|
|
32
|
+
For android, after sign-out sill be able to add another google account.
|
|
33
|
+
|
|
34
|
+
# Detailed description of different use cases
|
|
35
|
+
|
|
36
|
+
See https://developers.google.com/identity/gsi/web/guides/features
|
|
37
|
+
|
|
38
|
+
# Exposed api
|
|
39
|
+
See `src/definitions.ts` for a complete definition.
|
|
40
|
+
|
|
41
|
+
```TypeScript
|
|
42
|
+
/**
|
|
43
|
+
* For the web platform, starts pre-loading the google one tap JavaScript library.
|
|
44
|
+
* @param options
|
|
45
|
+
*/
|
|
46
|
+
initialize(options: InitializeOptions): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Tries to auto-sign in the user.
|
|
49
|
+
* If there is a single google account and that account has previously signed into the app, then that user is auto signed in. A short popover is displayed during sign-in.
|
|
50
|
+
* If there are multiple google accounts and more than one have previously signed into the app then a user selection screen is shown.
|
|
51
|
+
* If there is no active google session or if no user session has logged in previously in the app or if the user has opt out of One Tap, the auto sign-in will fail.
|
|
52
|
+
* See https://developers.google.com/identity/gsi/web/guides/features
|
|
53
|
+
*/
|
|
54
|
+
tryAutoSignIn(): Promise<SignInResult>;
|
|
55
|
+
/**
|
|
56
|
+
*
|
|
57
|
+
* @param parentElementId
|
|
58
|
+
* @param options
|
|
59
|
+
* @param gsiButtonConfiguration Not all button configuration options are supported on android.
|
|
60
|
+
*/
|
|
61
|
+
renderSignInButton(parentElementId: string, options: RenderSignInButtonOptions, gsiButtonConfiguration?: google.GsiButtonConfiguration): Promise<SignInResult>;
|
|
62
|
+
/**
|
|
63
|
+
* Ends the session.
|
|
64
|
+
*/
|
|
65
|
+
signOut(): Promise<SignOutResult>;
|
|
66
|
+
/**
|
|
67
|
+
* Gets the last user defined or auto-created nonce.
|
|
68
|
+
*/
|
|
69
|
+
getNonce(): string;
|
|
70
|
+
```
|
|
24
71
|
|
|
25
72
|
# Design decisions
|
|
26
73
|
Promises will not be rejected for anticipated unsuccessful control flow. For example, if the one tap auto-login does not succeed with the reason `opt_out_or_no_session` then this is not exceptional but expected in many cases. Rejecting the promise would mean the caller would have to catch an exception when calling `await xy` and then run follow-up code in the catch block. Instead of rejecting promises the resolved promise contains a `isSuccess` property.
|
|
@@ -44,9 +91,18 @@ npx cap update
|
|
|
44
91
|
|
|
45
92
|
## Usage
|
|
46
93
|
|
|
47
|
-
|
|
94
|
+
```TypeScript
|
|
95
|
+
import { GoogleOneTapAuth, SignInResult } from 'capacitor-native-google-one-tap-signin';
|
|
48
96
|
|
|
49
|
-
|
|
97
|
+
await GoogleOneTapAuth.initialize({ clientId: clientId });
|
|
98
|
+
let signInResult = await GoogleOneTapAuth.tryAutoSignIn();
|
|
99
|
+
if (!signInResult.isSuccess) {
|
|
100
|
+
signInResult = await GoogleOneTapAuth.renderSignInButton('appleid-signin', {}, { locale: 'en-us', theme: 'outline', text: 'continue_with', shape: 'rectangular' });
|
|
101
|
+
}
|
|
102
|
+
console.log(signInResult);
|
|
103
|
+
|
|
104
|
+
// See the demo folder for an example application.
|
|
105
|
+
```
|
|
50
106
|
|
|
51
107
|
## License
|
|
52
108
|
|
package/android/build.gradle
CHANGED
|
@@ -4,7 +4,7 @@ buildscript {
|
|
|
4
4
|
google()
|
|
5
5
|
}
|
|
6
6
|
dependencies {
|
|
7
|
-
classpath 'com.android.tools.build:gradle:7.
|
|
7
|
+
classpath 'com.android.tools.build:gradle:7.4.0'
|
|
8
8
|
classpath 'com.google.gms:google-services:4.3.13'
|
|
9
9
|
}
|
|
10
10
|
}
|
|
@@ -14,7 +14,7 @@ apply plugin: 'com.android.library'
|
|
|
14
14
|
android {
|
|
15
15
|
compileSdkVersion 31
|
|
16
16
|
defaultConfig {
|
|
17
|
-
minSdkVersion
|
|
17
|
+
minSdkVersion 24
|
|
18
18
|
targetSdkVersion 31
|
|
19
19
|
versionCode 1
|
|
20
20
|
versionName "1.0"
|
|
@@ -51,5 +51,6 @@ dependencies {
|
|
|
51
51
|
implementation 'com.google.android.gms:play-services-auth:20.4.1'
|
|
52
52
|
implementation 'com.google.android.gms:play-services-tasks:18.0.2'
|
|
53
53
|
implementation 'com.google.android.gms:play-services-base:18.0.1'
|
|
54
|
+
implementation 'org.json:json:20220924'
|
|
54
55
|
}
|
|
55
56
|
|
package/android/src/main/java/com/proventask/capacitor/GoogleOneTapAuth/GoogleOneTapAuth.java
CHANGED
|
@@ -14,39 +14,65 @@ import com.getcapacitor.JSObject;
|
|
|
14
14
|
import com.getcapacitor.Plugin;
|
|
15
15
|
import com.getcapacitor.PluginCall;
|
|
16
16
|
import com.getcapacitor.PluginMethod;
|
|
17
|
+
import com.getcapacitor.annotation.ActivityCallback;
|
|
17
18
|
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
18
19
|
import com.google.android.gms.auth.api.identity.BeginSignInRequest;
|
|
19
20
|
import com.google.android.gms.auth.api.identity.Identity;
|
|
20
21
|
import com.google.android.gms.auth.api.identity.SignInClient;
|
|
22
|
+
import com.google.android.gms.auth.api.signin.GoogleSignIn;
|
|
23
|
+
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
|
|
24
|
+
import com.google.android.gms.auth.api.signin.GoogleSignInClient;
|
|
25
|
+
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
|
|
26
|
+
import com.google.android.gms.auth.api.signin.GoogleSignInStatusCodes;
|
|
21
27
|
import com.google.android.gms.common.ConnectionResult;
|
|
22
28
|
import com.google.android.gms.common.GoogleApiAvailability;
|
|
23
29
|
import com.google.android.gms.common.api.ApiException;
|
|
30
|
+
import com.google.android.gms.tasks.Task;
|
|
31
|
+
|
|
32
|
+
import org.json.JSONException;
|
|
33
|
+
import org.json.JSONObject;
|
|
24
34
|
|
|
25
|
-
import java.util.Base64;
|
|
26
35
|
import java.util.concurrent.CompletableFuture;
|
|
27
36
|
import java.util.concurrent.ExecutionException;
|
|
28
37
|
import java.util.concurrent.Future;
|
|
29
38
|
|
|
30
39
|
@CapacitorPlugin()
|
|
31
40
|
public class GoogleOneTapAuth extends Plugin {
|
|
41
|
+
private enum SignInMethod {NotSignedIn, OneTap, GoogleSignIn}
|
|
32
42
|
private static final String TAG = "GoogleOneTapAuth Plugin";
|
|
33
43
|
private String clientId;
|
|
44
|
+
private String nonce;
|
|
34
45
|
private SignInClient oneTapClient;
|
|
46
|
+
private GoogleSignInClient googleSignInClient;
|
|
35
47
|
private ActivityResultLauncher<IntentSenderRequest> googleOneTapSignInActivityResultHandlerIntentSenderRequest;
|
|
36
|
-
private CompletableFuture<JSObject>
|
|
48
|
+
private CompletableFuture<JSObject> oneTapSignInFuture;
|
|
49
|
+
private CompletableFuture<JSObject> googleSignInFuture;
|
|
50
|
+
private SignInMethod signInMethod = SignInMethod.NotSignedIn;
|
|
37
51
|
|
|
38
52
|
@Override
|
|
39
53
|
public void load() {
|
|
40
|
-
|
|
54
|
+
registerSignInResultHandler();
|
|
55
|
+
}
|
|
41
56
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
57
|
+
@PluginMethod()
|
|
58
|
+
public void initialize(PluginCall call) {
|
|
59
|
+
initializeOptionsFromCall(call);
|
|
45
60
|
oneTapClient = Identity.getSignInClient(this.getActivity());
|
|
46
|
-
|
|
61
|
+
|
|
62
|
+
var gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
|
|
63
|
+
.requestEmail()
|
|
64
|
+
.requestIdToken(clientId)
|
|
65
|
+
.build();
|
|
66
|
+
googleSignInClient = GoogleSignIn.getClient(this.getActivity(), gso);
|
|
67
|
+
call.resolve();
|
|
47
68
|
}
|
|
48
69
|
|
|
49
|
-
private void
|
|
70
|
+
private void initializeOptionsFromCall(PluginCall call) {
|
|
71
|
+
clientId = call.getString("clientId");
|
|
72
|
+
nonce = call.getString("nonce");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private void registerSignInResultHandler() {
|
|
50
76
|
googleOneTapSignInActivityResultHandlerIntentSenderRequest = bridge.registerForActivityResult(
|
|
51
77
|
new ActivityResultContracts.StartIntentSenderForResult(),
|
|
52
78
|
result -> {
|
|
@@ -56,59 +82,41 @@ public class GoogleOneTapAuth extends Plugin {
|
|
|
56
82
|
try {
|
|
57
83
|
Intent data = result.getData();
|
|
58
84
|
var credential = oneTapClient.getSignInCredentialFromIntent(data);
|
|
59
|
-
|
|
60
|
-
|
|
85
|
+
signInMethod = SignInMethod.OneTap;
|
|
86
|
+
oneTapSignInFuture.complete(createSuccessResponse(credential.getGoogleIdToken()));
|
|
61
87
|
} catch (ApiException e) {
|
|
62
|
-
|
|
88
|
+
oneTapSignInFuture.complete(createErrorResponse(null, e.toString()));
|
|
63
89
|
}
|
|
64
90
|
} else {
|
|
65
91
|
var resultCodeString = ActivityResult.resultCodeToString(resultCode);
|
|
66
|
-
|
|
92
|
+
oneTapSignInFuture.complete(createErrorResponse(null, resultCodeString));
|
|
67
93
|
}
|
|
68
94
|
});
|
|
69
95
|
}
|
|
70
96
|
|
|
71
|
-
@PluginMethod()
|
|
72
|
-
public void initialize(PluginCall call) {
|
|
73
|
-
// Currently no code to run here.
|
|
74
|
-
call.resolve();
|
|
75
|
-
}
|
|
76
|
-
|
|
77
97
|
@PluginMethod()
|
|
78
98
|
public void tryAutoSignIn(PluginCall call) {
|
|
79
99
|
try {
|
|
80
100
|
var signInResult = beginSignIn(true).get();
|
|
101
|
+
if (!signInResult.getBool("isSuccess")) {
|
|
102
|
+
signInResult = beginSignIn(false).get();
|
|
103
|
+
}
|
|
104
|
+
if (!signInResult.getBool("isSuccess")) {
|
|
105
|
+
signInResult = googleSilentSignIn(call).get();
|
|
106
|
+
}
|
|
81
107
|
call.resolve(signInResult);
|
|
82
|
-
} catch (ExecutionException e) {
|
|
83
|
-
call.reject(e.toString());
|
|
84
|
-
} catch (InterruptedException e) {
|
|
85
|
-
call.reject(e.toString());
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
@PluginMethod()
|
|
90
|
-
public void trySignInWithPrompt(PluginCall call) {
|
|
91
|
-
try {
|
|
92
|
-
var signInResult = beginSignIn(false).get();
|
|
93
|
-
call.resolve(signInResult);
|
|
94
|
-
} catch (ExecutionException e) {
|
|
95
|
-
call.reject(e.toString());
|
|
96
|
-
} catch (InterruptedException e) {
|
|
108
|
+
} catch (ExecutionException | InterruptedException e) {
|
|
97
109
|
call.reject(e.toString());
|
|
98
110
|
}
|
|
99
111
|
}
|
|
100
112
|
|
|
113
|
+
// This method is not part of the api but only called from GoogleOneTapAuth.ts.
|
|
101
114
|
@PluginMethod()
|
|
102
|
-
public void
|
|
115
|
+
public void triggerGoogleSignIn(PluginCall call) {
|
|
103
116
|
try {
|
|
104
|
-
var signInResult =
|
|
105
|
-
if (!signInResult.getBool("isSuccess")) {
|
|
106
|
-
signInResult = beginSignIn(false).get();
|
|
107
|
-
}
|
|
117
|
+
var signInResult = googleSignIn(call).get();
|
|
108
118
|
call.resolve(signInResult);
|
|
109
|
-
} catch (ExecutionException e) {
|
|
110
|
-
call.reject(e.toString());
|
|
111
|
-
} catch (InterruptedException e) {
|
|
119
|
+
} catch (ExecutionException | InterruptedException e) {
|
|
112
120
|
call.reject(e.toString());
|
|
113
121
|
}
|
|
114
122
|
}
|
|
@@ -116,7 +124,7 @@ public class GoogleOneTapAuth extends Plugin {
|
|
|
116
124
|
private Future<JSObject> beginSignIn(boolean filterByAuthorizedAccounts) {
|
|
117
125
|
var context = this.getContext();
|
|
118
126
|
var beginSignInRequest = createBeginSignInRequest(filterByAuthorizedAccounts);
|
|
119
|
-
|
|
127
|
+
oneTapSignInFuture = new CompletableFuture<>();
|
|
120
128
|
|
|
121
129
|
oneTapClient.beginSignIn(beginSignInRequest)
|
|
122
130
|
.addOnCompleteListener(task -> {
|
|
@@ -126,11 +134,7 @@ public class GoogleOneTapAuth extends Plugin {
|
|
|
126
134
|
var intentSenderRequest = new IntentSenderRequest.Builder(intentSender).build();
|
|
127
135
|
googleOneTapSignInActivityResultHandlerIntentSenderRequest.launch(intentSenderRequest);
|
|
128
136
|
} else {
|
|
129
|
-
|
|
130
|
-
Log.i(TAG, "beginSignIn with FilterByAuthorizedAccounts=true failed", task.getException());
|
|
131
|
-
} else {
|
|
132
|
-
Log.e(TAG, "beginSignIn with FilterByAuthorizedAccounts=false failed", task.getException());
|
|
133
|
-
}
|
|
137
|
+
Log.i(TAG, "beginSignIn with FilterByAuthorizedAccounts=" + filterByAuthorizedAccounts + " did not succeed.", task.getException());
|
|
134
138
|
var errorMessage = task.getException().toString();
|
|
135
139
|
|
|
136
140
|
if (!isGooglePlayServicesAvailable(context)) {
|
|
@@ -140,22 +144,23 @@ public class GoogleOneTapAuth extends Plugin {
|
|
|
140
144
|
errorMessage += "\nGooglePlay is not installed or must be logged in to setup.";
|
|
141
145
|
}
|
|
142
146
|
if (errorMessage.contains("ApiException: 8")) {
|
|
143
|
-
errorMessage += "\nOne reason for the exception is when the device has no internet.";
|
|
147
|
+
errorMessage += "\nOne reason for the exception is when the device has no internet, or one-tap is not possible.";
|
|
144
148
|
}
|
|
145
149
|
// Other errors:
|
|
146
150
|
// com.google.android.gms.common.api.ApiException: 10: Caller not whitelisted to call this API.
|
|
147
151
|
// --> Try if restarting the phone fixes the problem.
|
|
148
152
|
// --> Test to sign in several times to ensure it works repeatedly.
|
|
149
|
-
|
|
153
|
+
oneTapSignInFuture.complete(createErrorResponse(null, errorMessage));
|
|
150
154
|
}
|
|
151
155
|
});
|
|
152
|
-
return
|
|
156
|
+
return oneTapSignInFuture;
|
|
153
157
|
}
|
|
154
158
|
|
|
155
159
|
private BeginSignInRequest createBeginSignInRequest(boolean filterByAuthorizedAccounts) {
|
|
156
160
|
return BeginSignInRequest.builder()
|
|
157
161
|
.setGoogleIdTokenRequestOptions(BeginSignInRequest.GoogleIdTokenRequestOptions.builder()
|
|
158
162
|
.setSupported(true)
|
|
163
|
+
.setNonce(nonce)
|
|
159
164
|
// A OAuth client ID with application type "Web application".
|
|
160
165
|
.setServerClientId(clientId)
|
|
161
166
|
// If true, only the Google accounts that the user has authorized before will show up in the credential list. This can
|
|
@@ -167,27 +172,38 @@ public class GoogleOneTapAuth extends Plugin {
|
|
|
167
172
|
.build();
|
|
168
173
|
}
|
|
169
174
|
|
|
170
|
-
private JSObject createErrorResponse(String noSuccessAdditionalInfo) {
|
|
175
|
+
private JSObject createErrorResponse(String reasonCode, String noSuccessAdditionalInfo) {
|
|
171
176
|
var result = new JSObject();
|
|
172
177
|
result.put("isSuccess", false);
|
|
178
|
+
result.put("noSuccessReasonCode", reasonCode);
|
|
173
179
|
result.put("noSuccessAdditionalInfo", noSuccessAdditionalInfo);
|
|
174
180
|
return result;
|
|
175
181
|
}
|
|
176
182
|
|
|
177
|
-
private JSObject createSuccessResponse(String idToken
|
|
183
|
+
private JSObject createSuccessResponse(String idToken) {
|
|
184
|
+
var decodedIdToken = decodeJwtBody(idToken);
|
|
185
|
+
JSONObject decodedIdTokenJson = null;
|
|
186
|
+
String userId = null;
|
|
187
|
+
String email = null;
|
|
188
|
+
try {
|
|
189
|
+
decodedIdTokenJson = new JSONObject(decodedIdToken);
|
|
190
|
+
userId = decodedIdTokenJson.getString("sub");
|
|
191
|
+
email = decodedIdTokenJson.getString("email");
|
|
192
|
+
} catch (JSONException e) {
|
|
193
|
+
// Do nothing.
|
|
194
|
+
}
|
|
178
195
|
var result = new JSObject();
|
|
179
196
|
result.put("isSuccess", true);
|
|
180
197
|
result.put("idToken", idToken);
|
|
198
|
+
result.put("userId", userId);
|
|
181
199
|
result.put("email", email);
|
|
182
|
-
result.put("decodedIdToken",
|
|
200
|
+
result.put("decodedIdToken", decodedIdTokenJson);
|
|
183
201
|
return result;
|
|
184
202
|
}
|
|
185
203
|
|
|
186
204
|
private String decodeJwtBody(String jwtToken) {
|
|
187
|
-
var decoder = Base64.getUrlDecoder();
|
|
188
205
|
var parts = jwtToken.split("\\.");
|
|
189
|
-
|
|
190
|
-
return payloadJson;
|
|
206
|
+
return new String(android.util.Base64.decode(parts[1], android.util.Base64.DEFAULT));
|
|
191
207
|
}
|
|
192
208
|
|
|
193
209
|
private boolean isGooglePlayServicesAvailable(Context context) {
|
|
@@ -199,16 +215,70 @@ public class GoogleOneTapAuth extends Plugin {
|
|
|
199
215
|
@PluginMethod()
|
|
200
216
|
public void signOut(final PluginCall call) {
|
|
201
217
|
oneTapClient.signOut()
|
|
202
|
-
.addOnCompleteListener(task ->
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
218
|
+
.addOnCompleteListener(task -> call.resolve(signOutResultTaskToJSObject(task)));
|
|
219
|
+
googleSignInClient.signOut()
|
|
220
|
+
.addOnCompleteListener(task -> call.resolve(signOutResultTaskToJSObject(task)));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private JSObject signOutResultTaskToJSObject(Task<Void> completedTask) {
|
|
224
|
+
var signOutResult = new JSObject();
|
|
225
|
+
if (completedTask.isSuccessful()) {
|
|
226
|
+
signOutResult.put("isSuccess", true);
|
|
227
|
+
} else {
|
|
228
|
+
Log.e(TAG, "signOut failed", completedTask.getException());
|
|
229
|
+
signOutResult.put("isSuccess", false);
|
|
230
|
+
signOutResult.put("error", completedTask.getException().toString());
|
|
231
|
+
}
|
|
232
|
+
return signOutResult;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
private JSObject createSuccessResult() {
|
|
236
|
+
var jsObject = new JSObject();
|
|
237
|
+
jsObject.put("isSuccess", true);
|
|
238
|
+
return jsObject;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
private Future<JSObject> googleSilentSignIn(final PluginCall call) {
|
|
242
|
+
googleSignInFuture = new CompletableFuture<>();
|
|
243
|
+
googleSignInClient.silentSignIn()
|
|
244
|
+
.addOnCompleteListener(task -> handleGoogleSignInResult(call, task, "silentSignIn"));
|
|
245
|
+
return googleSignInFuture;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
private Future<JSObject> googleSignIn(final PluginCall call) {
|
|
249
|
+
googleSignInFuture = new CompletableFuture<>();
|
|
250
|
+
var intent = googleSignInClient.getSignInIntent();
|
|
251
|
+
startActivityForResult(call, intent, "googleSignInResultIntentCallback");
|
|
252
|
+
return googleSignInFuture;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
@ActivityCallback
|
|
256
|
+
private void googleSignInResultIntentCallback(PluginCall call, ActivityResult result) {
|
|
257
|
+
if (call == null) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
Intent data = result.getData();
|
|
261
|
+
var task = GoogleSignIn.getSignedInAccountFromIntent(data);
|
|
262
|
+
handleGoogleSignInResult(call, task, "GoogleSignInClient.getSignInIntent");
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
private void handleGoogleSignInResult(PluginCall call, Task<GoogleSignInAccount> completedTask, String triggerMethodName) {
|
|
266
|
+
try {
|
|
267
|
+
var account = completedTask.getResult(ApiException.class);
|
|
268
|
+
signInMethod = SignInMethod.GoogleSignIn;
|
|
269
|
+
Log.i(TAG, triggerMethodName + " result " + account.toString());
|
|
270
|
+
googleSignInFuture.complete(createSuccessResponse(account.getIdToken()));
|
|
271
|
+
} catch (ApiException e) {
|
|
272
|
+
int statusCode = e.getStatusCode();
|
|
273
|
+
var statusCodeString = GoogleSignInStatusCodes.getStatusCodeString(statusCode);
|
|
274
|
+
Log.i(TAG, triggerMethodName + " not successful, statusCodeString " + statusCodeString, e);
|
|
275
|
+
if (statusCode == GoogleSignInStatusCodes.SIGN_IN_REQUIRED
|
|
276
|
+
|| statusCode == GoogleSignInStatusCodes.SIGN_IN_CANCELLED) {
|
|
277
|
+
googleSignInFuture.complete(createErrorResponse(statusCodeString, null));
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
googleSignInFuture.completeExceptionally(e);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
213
283
|
}
|
|
214
284
|
}
|