capacitor-native-google-one-tap-signin 5.2.0 → 6.0.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/README.md +45 -38
- package/android/build.gradle +16 -3
- package/android/src/main/java/com/proventask/capacitor/googleOneTapAuth/GoogleOneTapAuth.kt +247 -0
- package/android/src/main/java/com/proventask/capacitor/googleOneTapAuth/SignInResult.kt +15 -0
- package/dist/docs.json +45 -51
- package/dist/esm/GoogleOneTapAuth.d.ts +5 -5
- package/dist/esm/GoogleOneTapAuth.js +7 -16
- package/dist/esm/GoogleOneTapAuth.js.map +1 -1
- package/dist/esm/definitions.d.ts +8 -23
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.js +4 -3
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +11 -19
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +11 -19
- package/dist/plugin.js.map +1 -1
- package/package.json +7 -7
- package/android/src/main/java/com/proventask/capacitor/GoogleOneTapAuth/GoogleOneTapAuth.java +0 -240
- package/android/src/main/java/com/proventask/capacitor/GoogleOneTapAuth/SignInResult.java +0 -16
package/README.md
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
|
-
# Capacitor Native
|
|
1
|
+
# Capacitor Native Sign in with Google plugin
|
|
2
2
|
|
|
3
|
-
Wraps the native android
|
|
3
|
+
Wraps the native android and iOS Sign in with Google api for ionic capacitor apps.
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
- Auto sign-in returning users (that you did not sign out). No user action needed but shows a status UI for a few seconds.*
|
|
7
|
+
- New users can sign-up with one tap.
|
|
8
|
+
- After sign-out users can sign-in with one tap.
|
|
9
|
+
- You can attach a handler to your own custom sign-in button.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
* To avoid any UI, you could create your own authentication cookie. Send the google idToken to your server, verify it, create a JWT and return it as cookie. When the user returns, just verify your JWT cookie.
|
|
4
13
|
|
|
5
14
|
This library intends to provide the best google authentication experience for each platform.
|
|
6
15
|
|
|
@@ -11,9 +20,7 @@ This library intends to provide the best google authentication experience for ea
|
|
|
11
20
|
|
|
12
21
|
After a successful authentication, the idToken is returned as base64 and as object.
|
|
13
22
|
|
|
14
|
-
The
|
|
15
|
-
|
|
16
|
-
The android minSdkVersion is 24, however the one-tap library seems to require API version 29. For lower API versions GoogleSignIn is used.
|
|
23
|
+
The android minSdkVersion is 24. Tested with android 10 and above.
|
|
17
24
|
|
|
18
25
|
# Install and Configure
|
|
19
26
|
|
|
@@ -25,6 +32,8 @@ The major version of this plugin indicates the capacitor version that it is comp
|
|
|
25
32
|
npm i --save capacitor-native-google-one-tap-signin
|
|
26
33
|
```
|
|
27
34
|
|
|
35
|
+
See [capacitor-native-google-one-tap-signin](https://www.npmjs.com/package/capacitor-native-google-one-tap-signin) on NPM
|
|
36
|
+
|
|
28
37
|
#### 2. Update capacitor dependencies
|
|
29
38
|
|
|
30
39
|
```sh
|
|
@@ -44,14 +53,13 @@ If you get the error `[GSI_LOGGER]: The given origin is not allowed for the give
|
|
|
44
53
|
|
|
45
54
|
# Usage
|
|
46
55
|
|
|
56
|
+
### Example 1: Showing the button only if auto-sign-in or one-tap sign in fail.
|
|
47
57
|
```TypeScript
|
|
48
58
|
import { GoogleOneTapAuth, SignInResult } from 'capacitor-native-google-one-tap-signin';
|
|
49
59
|
|
|
50
60
|
await GoogleOneTapAuth.initialize({ clientId: clientId });
|
|
51
61
|
|
|
52
|
-
|
|
53
|
-
const signInResult = await GoogleOneTapAuth.tryAutoOrOneTapSignIn()
|
|
54
|
-
.then(res => res.signInResultOptionPromise);
|
|
62
|
+
const signInResult = await GoogleOneTapAuth.tryAutoOrOneTapSignIn();
|
|
55
63
|
if (signInResult.isSuccess) {
|
|
56
64
|
console.log(signInResult);
|
|
57
65
|
} else {
|
|
@@ -62,20 +70,34 @@ if (signInResult.isSuccess) {
|
|
|
62
70
|
|
|
63
71
|
// See the demo folder for an example application.
|
|
64
72
|
```
|
|
65
|
-
|
|
73
|
+
|
|
74
|
+
### Example 2: Trigger auto-sign-in and if not successful one-tap sign and show the button in parallel.
|
|
66
75
|
```TypeScript
|
|
67
76
|
import { GoogleOneTapAuth, SignInResult } from 'capacitor-native-google-one-tap-signin';
|
|
68
77
|
|
|
69
78
|
await GoogleOneTapAuth.initialize({ clientId: clientId });
|
|
70
79
|
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
80
|
+
const autoOrOneTapSuccessPromise = new Promise<SuccessSignInResult>(async (resolve) => {
|
|
81
|
+
const autoOrOneTapResult = await GoogleOneTapAuth.tryAutoOrOneTapSignIn();
|
|
82
|
+
// Ignore if the result is not success because the user can use the button.
|
|
83
|
+
if (autoOrOneTapResult.isSuccess) {
|
|
84
|
+
resolve(autoOrOneTapResult.success!);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
const successResult = await GoogleOneTapAuth.addSignInActionToExistingButton(
|
|
88
|
+
'google-signin-existing-btn-parent', 'google-signin-existing-btn');
|
|
89
|
+
|
|
76
90
|
const signInResultSuccess = await Promise.race([autoOrOneTapSuccessPromise, renderButtonPromise]);
|
|
77
91
|
console.log(signInResultSuccess);
|
|
92
|
+
```
|
|
78
93
|
|
|
94
|
+
```HTML
|
|
95
|
+
<div id='google-signin-existing-btn-parent'>
|
|
96
|
+
<button id='google-signin-existing-btn'>Custom Sign-in Button</button>
|
|
97
|
+
</div>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
```TypeScript
|
|
79
101
|
// See the demo folder for an example application.
|
|
80
102
|
```
|
|
81
103
|
|
|
@@ -108,7 +130,7 @@ See `src/definitions.ts` for a complete definition.
|
|
|
108
130
|
initialize(options: InitializeOptions): Promise<void>;
|
|
109
131
|
|
|
110
132
|
/**
|
|
111
|
-
* Tries to
|
|
133
|
+
* Tries to first auto-sign-in the user and if not successful sign-in the user with just one tap/click.
|
|
112
134
|
* If there is a single google account and that account has previously signed into the app,
|
|
113
135
|
* then that user is auto signed in. A short popover is displayed during sign-in.
|
|
114
136
|
* If there are multiple google accounts and more than one have previously signed into the
|
|
@@ -117,41 +139,26 @@ initialize(options: InitializeOptions): Promise<void>;
|
|
|
117
139
|
* the app or if the user has opt out of One Tap, then the response will indicate that
|
|
118
140
|
* the auto sign-in did not succeed.
|
|
119
141
|
* See https://developers.google.com/identity/gsi/web/guides/features
|
|
120
|
-
* @returns A Promise object that
|
|
121
|
-
* when authentication succeeds, the second on error and the third on success or error.
|
|
142
|
+
* @returns A Promise object that resolves to an object with isSuccess, success and noSuccess properties.
|
|
122
143
|
*/
|
|
123
144
|
tryAutoOrOneTapSignIn()
|
|
124
|
-
: Promise<
|
|
125
|
-
successPromise: Promise<SuccessSignInResult>;
|
|
126
|
-
noSuccess: Promise<NoSuccessSignInResult>;
|
|
127
|
-
signInResultOptionPromise: Promise<SignInResultOption>;
|
|
128
|
-
}>;
|
|
145
|
+
: Promise<SignInResultOption>;
|
|
129
146
|
|
|
130
147
|
/**
|
|
131
148
|
* Tries to show the sign-in UI without trying to auto sign-in the user.
|
|
132
|
-
* @returns A Promise object that
|
|
133
|
-
* when authentication succeeds, the second on error and the third on success or error.
|
|
149
|
+
* @returns A Promise object that resolves to an object with isSuccess, success and noSuccess properties.
|
|
134
150
|
*/
|
|
135
151
|
tryOneTapSignIn()
|
|
136
|
-
: Promise<
|
|
137
|
-
successPromise: Promise<SuccessSignInResult>;
|
|
138
|
-
noSuccess: Promise<NoSuccessSignInResult>;
|
|
139
|
-
signInResultOptionPromise: Promise<SignInResultOption>;
|
|
140
|
-
}>;
|
|
152
|
+
: Promise<SignInResultOption>;
|
|
141
153
|
|
|
142
154
|
/**
|
|
143
155
|
* Tries to auto-sign-in the user without any user interaction needed.
|
|
144
156
|
* If there is a single google account and that account has previously signed into the app,
|
|
145
157
|
* then that user is auto signed in. A short popover is displayed during sign-in.
|
|
146
|
-
* @returns A Promise object that
|
|
147
|
-
* when authentication succeeds, the second on error and the third on success or error.
|
|
158
|
+
* @returns A Promise object that resolves to an object with isSuccess, success and noSuccess properties.
|
|
148
159
|
*/
|
|
149
160
|
tryAutoSignIn()
|
|
150
|
-
: Promise<
|
|
151
|
-
successPromise: Promise<SuccessSignInResult>;
|
|
152
|
-
noSuccess: Promise<NoSuccessSignInResult>;
|
|
153
|
-
signInResultOptionPromise: Promise<SignInResultOption>;
|
|
154
|
-
}>;
|
|
161
|
+
: Promise<SignInResultOption>;
|
|
155
162
|
|
|
156
163
|
/**
|
|
157
164
|
* Allows using a custom sign-in button.
|
|
@@ -184,7 +191,7 @@ renderSignInButton(
|
|
|
184
191
|
: Promise<SuccessSignInResult>;
|
|
185
192
|
|
|
186
193
|
/**
|
|
187
|
-
* Closes the One Tap prompt
|
|
194
|
+
* Closes the One Tap prompt.
|
|
188
195
|
*/
|
|
189
196
|
cancelOneTapDialog(): void;
|
|
190
197
|
|
|
@@ -202,7 +209,7 @@ getNonce(): string;
|
|
|
202
209
|
```
|
|
203
210
|
|
|
204
211
|
# Design decisions
|
|
205
|
-
Promises will not be rejected for anticipated unsuccessful control flow. For example, if the one tap auto-login does not succeed because there is 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.
|
|
212
|
+
Promises will not be rejected for anticipated unsuccessful control flow. For example, if the one tap auto-login does not succeed because there is 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.
|
|
206
213
|
|
|
207
214
|
Instead of creating one signIn method with many parameters that are only used in some cases (=unclear dependencies), there are two methods with different parameters and different return types.
|
|
208
215
|
|
package/android/build.gradle
CHANGED
|
@@ -10,16 +10,17 @@ buildscript {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
apply plugin: 'com.android.library'
|
|
13
|
+
apply plugin: 'org.jetbrains.kotlin.android'
|
|
13
14
|
|
|
14
15
|
android {
|
|
15
|
-
namespace "com.proventask.capacitor.
|
|
16
|
-
|
|
16
|
+
namespace "com.proventask.capacitor.googleOneTapAuth.capacitorgoogleauth"
|
|
17
|
+
compileSdk 34
|
|
17
18
|
defaultConfig {
|
|
18
19
|
minSdkVersion 24
|
|
19
20
|
targetSdkVersion 31
|
|
20
21
|
versionCode 1
|
|
21
22
|
versionName "1.0"
|
|
22
|
-
testInstrumentationRunner "
|
|
23
|
+
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
|
23
24
|
}
|
|
24
25
|
buildTypes {
|
|
25
26
|
release {
|
|
@@ -34,6 +35,9 @@ android {
|
|
|
34
35
|
targetCompatibility JavaVersion.VERSION_17
|
|
35
36
|
sourceCompatibility JavaVersion.VERSION_17
|
|
36
37
|
}
|
|
38
|
+
kotlinOptions {
|
|
39
|
+
jvmTarget = '17'
|
|
40
|
+
}
|
|
37
41
|
}
|
|
38
42
|
|
|
39
43
|
repositories {
|
|
@@ -46,12 +50,21 @@ dependencies {
|
|
|
46
50
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
|
47
51
|
implementation project(':capacitor-android')
|
|
48
52
|
implementation 'androidx.appcompat:appcompat:1.4.2'
|
|
53
|
+
implementation 'androidx.test:monitor:1.6.1'
|
|
54
|
+
implementation 'androidx.test.ext:junit:1.1.5'
|
|
55
|
+
implementation 'androidx.core:core-ktx:1.13.1'
|
|
49
56
|
testImplementation 'junit:junit:4.13'
|
|
50
57
|
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
|
51
58
|
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
|
52
59
|
implementation 'com.google.android.gms:play-services-auth:20.4.1'
|
|
53
60
|
implementation 'com.google.android.gms:play-services-tasks:18.0.2'
|
|
54
61
|
implementation 'com.google.android.gms:play-services-base:18.0.1'
|
|
62
|
+
implementation 'androidx.credentials:credentials:1.2.2'
|
|
63
|
+
implementation 'com.google.android.libraries.identity.googleid:googleid:1.1.0'
|
|
64
|
+
|
|
65
|
+
// optional - needed for credentials support from play services, for devices running
|
|
66
|
+
// Android 13 and below.
|
|
67
|
+
implementation 'androidx.credentials:credentials-play-services-auth:1.2.2'
|
|
55
68
|
implementation 'org.json:json:20220924'
|
|
56
69
|
}
|
|
57
70
|
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
package com.proventask.capacitor.googleOneTapAuth
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.util.Log
|
|
5
|
+
import androidx.credentials.ClearCredentialStateRequest
|
|
6
|
+
import androidx.credentials.CredentialManager
|
|
7
|
+
import androidx.credentials.CredentialOption
|
|
8
|
+
import androidx.credentials.CustomCredential
|
|
9
|
+
import androidx.credentials.GetCredentialRequest
|
|
10
|
+
import androidx.credentials.GetCredentialResponse
|
|
11
|
+
import androidx.credentials.exceptions.ClearCredentialException
|
|
12
|
+
import androidx.credentials.exceptions.GetCredentialCancellationException
|
|
13
|
+
import androidx.credentials.exceptions.GetCredentialInterruptedException
|
|
14
|
+
import com.getcapacitor.JSObject
|
|
15
|
+
import com.getcapacitor.Plugin
|
|
16
|
+
import com.getcapacitor.PluginCall
|
|
17
|
+
import com.getcapacitor.PluginMethod
|
|
18
|
+
import com.getcapacitor.annotation.CapacitorPlugin
|
|
19
|
+
import com.google.android.gms.common.ConnectionResult
|
|
20
|
+
import com.google.android.gms.common.GoogleApiAvailability
|
|
21
|
+
import com.google.android.libraries.identity.googleid.GetGoogleIdOption
|
|
22
|
+
import com.google.android.libraries.identity.googleid.GetSignInWithGoogleOption
|
|
23
|
+
import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential
|
|
24
|
+
import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException
|
|
25
|
+
import kotlinx.coroutines.CompletableDeferred
|
|
26
|
+
import kotlinx.coroutines.Deferred
|
|
27
|
+
import kotlinx.coroutines.GlobalScope
|
|
28
|
+
import kotlinx.coroutines.launch
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@CapacitorPlugin
|
|
32
|
+
class GoogleOneTapAuth : Plugin() {
|
|
33
|
+
private val TAG = "GoogleOneTapAuth Plugin"
|
|
34
|
+
private val ReasonCodeSignInCancelled = "SIGN_IN_CANCELLED"
|
|
35
|
+
private val ReasonCodeSignInInterrupted = "SIGN_IN_INTERRUPTED"
|
|
36
|
+
private var webClientId: String? = null
|
|
37
|
+
private var nonce: String? = null
|
|
38
|
+
private lateinit var credentialManager: CredentialManager
|
|
39
|
+
|
|
40
|
+
override fun load() {
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@PluginMethod
|
|
44
|
+
fun initialize(call: PluginCall) {
|
|
45
|
+
initializeOptionsFromCall(call)
|
|
46
|
+
credentialManager = CredentialManager.create(context)
|
|
47
|
+
call.resolve()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private fun initializeOptionsFromCall(call: PluginCall) {
|
|
51
|
+
// This must be the Client ID of type Web application NOT the Android Client ID.
|
|
52
|
+
webClientId = call.getString("clientId")
|
|
53
|
+
nonce = call.getString("nonce")
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@PluginMethod
|
|
57
|
+
fun tryAutoOrOneTapSignIn(call: PluginCall) {
|
|
58
|
+
GlobalScope.launch {
|
|
59
|
+
var signInResult = signIn(true).await()
|
|
60
|
+
|
|
61
|
+
if (signInResult.idToken == null && signInResult.noSuccessReasonCode != ReasonCodeSignInCancelled) {
|
|
62
|
+
signInResult = signIn(false).await()
|
|
63
|
+
}
|
|
64
|
+
call.resolve(convertToJsonResult(signInResult))
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@PluginMethod
|
|
69
|
+
fun tryAutoSignIn(call: PluginCall) {
|
|
70
|
+
GlobalScope.launch {
|
|
71
|
+
signIn(call, true)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@PluginMethod
|
|
76
|
+
fun tryOneTapSignIn(call: PluginCall) {
|
|
77
|
+
GlobalScope.launch {
|
|
78
|
+
signIn(call, false)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// For sign-up, set filterByAuthorizedAccounts to false.
|
|
83
|
+
private suspend fun signIn(call: PluginCall, filterByAuthorizedAccounts: Boolean) {
|
|
84
|
+
var signInResult = signIn(filterByAuthorizedAccounts).await()
|
|
85
|
+
if (signInResult.idToken == null && signInResult.noSuccessReasonCode !== ReasonCodeSignInInterrupted) {
|
|
86
|
+
signInResult = signIn(filterByAuthorizedAccounts).await()
|
|
87
|
+
}
|
|
88
|
+
call.resolve(convertToJsonResult(signInResult))
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private suspend fun signIn(filterByAuthorizedAccounts: Boolean): Deferred<SignInResult> {
|
|
92
|
+
val googleIdOption = createGoogleIdOption(filterByAuthorizedAccounts)
|
|
93
|
+
return launchGetCredentialRequest(googleIdOption)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
private fun createGoogleIdOption(filterByAuthorizedAccounts: Boolean): GetGoogleIdOption {
|
|
97
|
+
val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
|
|
98
|
+
.setFilterByAuthorizedAccounts(filterByAuthorizedAccounts)
|
|
99
|
+
.setServerClientId(this.webClientId!!)
|
|
100
|
+
// Enable automatic sign-in for returning users that have not signed out.
|
|
101
|
+
.setAutoSelectEnabled(true)
|
|
102
|
+
.setNonce(this.nonce)
|
|
103
|
+
.build();
|
|
104
|
+
return googleIdOption;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private fun createSignInWithGoogleButtonOption(): GetSignInWithGoogleOption {
|
|
108
|
+
// Trigger the Sign in with Google button flow.
|
|
109
|
+
val signInWithGoogleOption = GetSignInWithGoogleOption.Builder(webClientId!!)
|
|
110
|
+
// .setHostedDomainFilter()
|
|
111
|
+
.setNonce(nonce)
|
|
112
|
+
.build()
|
|
113
|
+
return signInWithGoogleOption;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private suspend fun launchGetCredentialRequest(credentialOption: CredentialOption): Deferred<SignInResult> {
|
|
117
|
+
val googleSignInDeferred = CompletableDeferred<SignInResult>()
|
|
118
|
+
val request: GetCredentialRequest = GetCredentialRequest.Builder()
|
|
119
|
+
.addCredentialOption(credentialOption)
|
|
120
|
+
.build()
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
val result = credentialManager.getCredential(request = request, context = this.context)
|
|
124
|
+
handleSignIn(googleSignInDeferred, result)
|
|
125
|
+
} catch (e: GetCredentialCancellationException) {
|
|
126
|
+
googleSignInDeferred.complete(SignInResult(ReasonCodeSignInCancelled, e.message))
|
|
127
|
+
} catch (e: GetCredentialInterruptedException) { // should retry
|
|
128
|
+
googleSignInDeferred.complete(SignInResult(ReasonCodeSignInInterrupted, e.message))
|
|
129
|
+
} catch (t: Throwable) { // GetCredentialException
|
|
130
|
+
val exceptionType = t::class.java.simpleName
|
|
131
|
+
val errorMsg = "$exceptionType error in getCredential."
|
|
132
|
+
Log.i(TAG, errorMsg, t)
|
|
133
|
+
var errorMessage = t.toString()
|
|
134
|
+
|
|
135
|
+
if (!isGooglePlayServicesAvailable(context)) {
|
|
136
|
+
errorMessage += "\nGooglePlayService is not installed or must be updated."
|
|
137
|
+
}
|
|
138
|
+
if (errorMessage.contains("Missing Feature{name=auth_api_credentials_begin_sign_in")) {
|
|
139
|
+
errorMessage += "\nGooglePlay is not installed or must be logged in to setup."
|
|
140
|
+
}
|
|
141
|
+
if (errorMessage.contains("ApiException: 8")) {
|
|
142
|
+
errorMessage += "\nOne reason for the exception is when the device has no internet, or one-tap is not possible."
|
|
143
|
+
}
|
|
144
|
+
// Other errors:
|
|
145
|
+
// com.google.android.gms.common.api.ApiException: 10: Caller not whitelisted to call this API.
|
|
146
|
+
// --> Try if restarting the phone fixes the problem.
|
|
147
|
+
// --> Test to sign in several times to ensure it works repeatedly.
|
|
148
|
+
googleSignInDeferred.complete(SignInResult(null, errorMessage))
|
|
149
|
+
}
|
|
150
|
+
return googleSignInDeferred;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private fun handleSignIn(
|
|
154
|
+
googleSignInDeferred: CompletableDeferred<SignInResult>,
|
|
155
|
+
response: GetCredentialResponse
|
|
156
|
+
) {
|
|
157
|
+
when (val credential = response.credential) {
|
|
158
|
+
// GoogleIdToken credential
|
|
159
|
+
is CustomCredential -> {
|
|
160
|
+
if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
|
|
161
|
+
try {
|
|
162
|
+
val googleIdTokenCredential =
|
|
163
|
+
GoogleIdTokenCredential.createFrom(credential.data)
|
|
164
|
+
googleSignInDeferred.complete(SignInResult(googleIdTokenCredential.idToken))
|
|
165
|
+
} catch (e: GoogleIdTokenParsingException) {
|
|
166
|
+
Log.e(TAG, "Received an invalid google id token response", e)
|
|
167
|
+
googleSignInDeferred.completeExceptionally(e)
|
|
168
|
+
}
|
|
169
|
+
} else {
|
|
170
|
+
// Catch any unrecognized custom credential type here.
|
|
171
|
+
Log.e(TAG, "Unexpected CustomCredential type of credential")
|
|
172
|
+
googleSignInDeferred.completeExceptionally(Exception("Unexpected CustomCredential type: " + credential.type))
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
else -> {
|
|
177
|
+
// Catch any unrecognized credential type here.
|
|
178
|
+
Log.e(TAG, "Unexpected type of credential")
|
|
179
|
+
googleSignInDeferred.completeExceptionally(Exception("Unexpected type of credential: " + credential.type))
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// This method is not part of the api but only called from GoogleOneTapAuth.ts in method renderSignInButton.
|
|
185
|
+
@PluginMethod
|
|
186
|
+
fun triggerGoogleSignIn(call: PluginCall) {
|
|
187
|
+
GlobalScope.launch {
|
|
188
|
+
// Trigger the Sign in with Google button flow.
|
|
189
|
+
val signInWithGoogleOption = createSignInWithGoogleButtonOption()
|
|
190
|
+
val signInResult = launchGetCredentialRequest(signInWithGoogleOption).await()
|
|
191
|
+
call.resolve(convertToJsonResult(signInResult))
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private fun convertToJsonResult(signInResult: SignInResult): JSObject {
|
|
196
|
+
val result = JSObject()
|
|
197
|
+
val isSuccess = signInResult.idToken != null
|
|
198
|
+
result.put("isSuccess", isSuccess)
|
|
199
|
+
|
|
200
|
+
if (isSuccess) {
|
|
201
|
+
val successObj = JSObject()
|
|
202
|
+
successObj.put("idToken", signInResult.idToken)
|
|
203
|
+
result.put("success", successObj)
|
|
204
|
+
} else {
|
|
205
|
+
val errorObj = JSObject()
|
|
206
|
+
errorObj.put("noSuccessReasonCode", signInResult.noSuccessReasonCode)
|
|
207
|
+
errorObj.put("noSuccessAdditionalInfo", signInResult.noSuccessAdditionalInfo)
|
|
208
|
+
result.put("noSuccess", errorObj)
|
|
209
|
+
}
|
|
210
|
+
return result
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
private fun isGooglePlayServicesAvailable(context: Context): Boolean {
|
|
214
|
+
val googleApiAvailability = GoogleApiAvailability.getInstance()
|
|
215
|
+
val resultCode = googleApiAvailability.isGooglePlayServicesAvailable(context)
|
|
216
|
+
return resultCode == ConnectionResult.SUCCESS
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
@PluginMethod
|
|
220
|
+
fun cancelOneTapDialog(call: PluginCall) {
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
@PluginMethod
|
|
224
|
+
fun signOut(call: PluginCall) {
|
|
225
|
+
GlobalScope.launch {
|
|
226
|
+
try {
|
|
227
|
+
credentialManager.clearCredentialState(ClearCredentialStateRequest())
|
|
228
|
+
call.resolve(createSuccessSignOutResult())
|
|
229
|
+
} catch (e: ClearCredentialException) {
|
|
230
|
+
call.resolve(createErrorSignOutResult(e.toString()))
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
private fun createSuccessSignOutResult(): JSObject {
|
|
236
|
+
val signOutResult = JSObject()
|
|
237
|
+
signOutResult.put("isSuccess", true)
|
|
238
|
+
return signOutResult
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
private fun createErrorSignOutResult(errorMessage: String): JSObject {
|
|
242
|
+
val signOutResult = JSObject()
|
|
243
|
+
signOutResult.put("isSuccess", false)
|
|
244
|
+
signOutResult.put("error", errorMessage)
|
|
245
|
+
return signOutResult
|
|
246
|
+
}
|
|
247
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
package com.proventask.capacitor.googleOneTapAuth
|
|
2
|
+
|
|
3
|
+
data class SignInResult(
|
|
4
|
+
var idToken: String? = null,
|
|
5
|
+
var noSuccessReasonCode: String? = null,
|
|
6
|
+
var noSuccessAdditionalInfo: String? = null
|
|
7
|
+
) {
|
|
8
|
+
constructor(noSuccessReasonCode: String?, noSuccessAdditionalInfo: String?) : this(
|
|
9
|
+
null,
|
|
10
|
+
noSuccessReasonCode,
|
|
11
|
+
noSuccessAdditionalInfo
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
constructor(idToken: String?) : this(idToken, null, null)
|
|
15
|
+
}
|
package/dist/docs.json
CHANGED
|
@@ -30,57 +30,51 @@
|
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
32
|
"name": "tryAutoOrOneTapSignIn",
|
|
33
|
-
"signature": "() => Promise<
|
|
33
|
+
"signature": "() => Promise<SignInResultOption>",
|
|
34
34
|
"parameters": [],
|
|
35
|
-
"returns": "Promise<
|
|
35
|
+
"returns": "Promise<SignInResultOption>",
|
|
36
36
|
"tags": [
|
|
37
37
|
{
|
|
38
38
|
"name": "returns",
|
|
39
|
-
"text": "A Promise object that
|
|
39
|
+
"text": "A Promise object that resolves to an object with isSuccess, success and noSuccess properties."
|
|
40
40
|
}
|
|
41
41
|
],
|
|
42
|
-
"docs": "Tries to
|
|
42
|
+
"docs": "Tries to first auto-sign-in the user and if not successful sign-in the user with just one tap/click.\nIf there is a single google account and that account has previously signed into the app, \nthen that user is auto signed in. A short popover is displayed during sign-in.\nIf there are multiple google accounts and more than one have previously signed into the \napp then a user selection screen is shown.\nIf there is no active google session or if no user session has logged in previously in \nthe app or if the user has opt out of One Tap, then the response will indicate that \nthe auto sign-in did not succeed.\nSee https://developers.google.com/identity/gsi/web/guides/features",
|
|
43
43
|
"complexTypes": [
|
|
44
|
-
"SuccessSignInResult",
|
|
45
|
-
"NoSuccessSignInResult",
|
|
46
44
|
"SignInResultOption"
|
|
47
45
|
],
|
|
48
46
|
"slug": "tryautooronetapsignin"
|
|
49
47
|
},
|
|
50
48
|
{
|
|
51
49
|
"name": "tryOneTapSignIn",
|
|
52
|
-
"signature": "() => Promise<
|
|
50
|
+
"signature": "() => Promise<SignInResultOption>",
|
|
53
51
|
"parameters": [],
|
|
54
|
-
"returns": "Promise<
|
|
52
|
+
"returns": "Promise<SignInResultOption>",
|
|
55
53
|
"tags": [
|
|
56
54
|
{
|
|
57
55
|
"name": "returns",
|
|
58
|
-
"text": "A Promise object that
|
|
56
|
+
"text": "A Promise object that resolves to an object with isSuccess, success and noSuccess properties."
|
|
59
57
|
}
|
|
60
58
|
],
|
|
61
59
|
"docs": "Tries to show the sign-in UI without trying to auto sign-in the user.",
|
|
62
60
|
"complexTypes": [
|
|
63
|
-
"SuccessSignInResult",
|
|
64
|
-
"NoSuccessSignInResult",
|
|
65
61
|
"SignInResultOption"
|
|
66
62
|
],
|
|
67
63
|
"slug": "tryonetapsignin"
|
|
68
64
|
},
|
|
69
65
|
{
|
|
70
66
|
"name": "tryAutoSignIn",
|
|
71
|
-
"signature": "() => Promise<
|
|
67
|
+
"signature": "() => Promise<SignInResultOption>",
|
|
72
68
|
"parameters": [],
|
|
73
|
-
"returns": "Promise<
|
|
69
|
+
"returns": "Promise<SignInResultOption>",
|
|
74
70
|
"tags": [
|
|
75
71
|
{
|
|
76
72
|
"name": "returns",
|
|
77
|
-
"text": "A Promise object that
|
|
73
|
+
"text": "A Promise object that resolves to an object with isSuccess, success and noSuccess properties."
|
|
78
74
|
}
|
|
79
75
|
],
|
|
80
76
|
"docs": "Tries to auto-sign-in the user without any user interaction needed.\nIf there is a single google account and that account has previously signed into the app, \nthen that user is auto signed in. A short popover is displayed during sign-in.",
|
|
81
77
|
"complexTypes": [
|
|
82
|
-
"SuccessSignInResult",
|
|
83
|
-
"NoSuccessSignInResult",
|
|
84
78
|
"SignInResultOption"
|
|
85
79
|
],
|
|
86
80
|
"slug": "tryautosignin"
|
|
@@ -166,7 +160,7 @@
|
|
|
166
160
|
"parameters": [],
|
|
167
161
|
"returns": "void",
|
|
168
162
|
"tags": [],
|
|
169
|
-
"docs": "Closes the One Tap prompt
|
|
163
|
+
"docs": "Closes the One Tap prompt.",
|
|
170
164
|
"complexTypes": [],
|
|
171
165
|
"slug": "cancelonetapdialog"
|
|
172
166
|
},
|
|
@@ -244,6 +238,40 @@
|
|
|
244
238
|
}
|
|
245
239
|
]
|
|
246
240
|
},
|
|
241
|
+
{
|
|
242
|
+
"name": "SignInResultOption",
|
|
243
|
+
"slug": "signinresultoption",
|
|
244
|
+
"docs": "",
|
|
245
|
+
"tags": [],
|
|
246
|
+
"methods": [],
|
|
247
|
+
"properties": [
|
|
248
|
+
{
|
|
249
|
+
"name": "isSuccess",
|
|
250
|
+
"tags": [],
|
|
251
|
+
"docs": "Indicates if the success or noSuccess property is set.",
|
|
252
|
+
"complexTypes": [],
|
|
253
|
+
"type": "boolean"
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
"name": "success",
|
|
257
|
+
"tags": [],
|
|
258
|
+
"docs": "Success result.",
|
|
259
|
+
"complexTypes": [
|
|
260
|
+
"SuccessSignInResult"
|
|
261
|
+
],
|
|
262
|
+
"type": "SuccessSignInResult"
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
"name": "noSuccess",
|
|
266
|
+
"tags": [],
|
|
267
|
+
"docs": "NoSuccess result.",
|
|
268
|
+
"complexTypes": [
|
|
269
|
+
"NoSuccessSignInResult"
|
|
270
|
+
],
|
|
271
|
+
"type": "NoSuccessSignInResult"
|
|
272
|
+
}
|
|
273
|
+
]
|
|
274
|
+
},
|
|
247
275
|
{
|
|
248
276
|
"name": "SuccessSignInResult",
|
|
249
277
|
"slug": "successsigninresult",
|
|
@@ -311,40 +339,6 @@
|
|
|
311
339
|
}
|
|
312
340
|
]
|
|
313
341
|
},
|
|
314
|
-
{
|
|
315
|
-
"name": "SignInResultOption",
|
|
316
|
-
"slug": "signinresultoption",
|
|
317
|
-
"docs": "",
|
|
318
|
-
"tags": [],
|
|
319
|
-
"methods": [],
|
|
320
|
-
"properties": [
|
|
321
|
-
{
|
|
322
|
-
"name": "isSuccess",
|
|
323
|
-
"tags": [],
|
|
324
|
-
"docs": "Indicates if the success or noSuccess property is set.",
|
|
325
|
-
"complexTypes": [],
|
|
326
|
-
"type": "boolean"
|
|
327
|
-
},
|
|
328
|
-
{
|
|
329
|
-
"name": "success",
|
|
330
|
-
"tags": [],
|
|
331
|
-
"docs": "Success result.",
|
|
332
|
-
"complexTypes": [
|
|
333
|
-
"SuccessSignInResult"
|
|
334
|
-
],
|
|
335
|
-
"type": "SuccessSignInResult"
|
|
336
|
-
},
|
|
337
|
-
{
|
|
338
|
-
"name": "noSuccess",
|
|
339
|
-
"tags": [],
|
|
340
|
-
"docs": "NoSuccess result.",
|
|
341
|
-
"complexTypes": [
|
|
342
|
-
"NoSuccessSignInResult"
|
|
343
|
-
],
|
|
344
|
-
"type": "NoSuccessSignInResult"
|
|
345
|
-
}
|
|
346
|
-
]
|
|
347
|
-
},
|
|
348
342
|
{
|
|
349
343
|
"name": "RenderSignInButtonOptions",
|
|
350
344
|
"slug": "rendersigninbuttonoptions",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { InitializeOptions, GoogleOneTapAuthPlugin,
|
|
1
|
+
import type { InitializeOptions, GoogleOneTapAuthPlugin, SuccessSignInResult, SignOutResult, RenderSignInButtonOptions, SignInResultOption } from './definitions';
|
|
2
2
|
declare class GoogleOneTapAuth implements GoogleOneTapAuthPlugin {
|
|
3
3
|
private nonce?;
|
|
4
4
|
private initializeStatus;
|
|
@@ -8,10 +8,10 @@ declare class GoogleOneTapAuth implements GoogleOneTapAuthPlugin {
|
|
|
8
8
|
constructor();
|
|
9
9
|
initialize(options: InitializeOptions): Promise<void>;
|
|
10
10
|
private ensureInitialized;
|
|
11
|
-
tryAutoOrOneTapSignIn(): Promise<
|
|
12
|
-
tryOneTapSignIn(): Promise<
|
|
13
|
-
tryAutoSignIn(): Promise<
|
|
14
|
-
private
|
|
11
|
+
tryAutoOrOneTapSignIn(): Promise<SignInResultOption>;
|
|
12
|
+
tryOneTapSignIn(): Promise<SignInResultOption>;
|
|
13
|
+
tryAutoSignIn(): Promise<SignInResultOption>;
|
|
14
|
+
private onResultReceived;
|
|
15
15
|
cancelOneTapDialog(): void;
|
|
16
16
|
signOut(): Promise<SignOutResult>;
|
|
17
17
|
addSignInActionToExistingButton(buttonParentId: string, buttonId: string): Promise<SuccessSignInResult>;
|