@@ -29,129 +29,134 @@ import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException
29
29
import com.google.firebase.auth.FirebaseAuthInvalidUserException
30
30
import com.google.firebase.auth.GoogleAuthProvider
31
31
import com.google.firebase.auth.PhoneAuthProvider
32
+ import kotlinx.coroutines.launch
33
+ import androidx.lifecycle.viewModelScope
34
+
35
+ import androidx.credentials.Credential
36
+ import androidx.credentials.CustomCredential
37
+ import androidx.credentials.GetCredentialRequest
38
+ import androidx.credentials.GetPasswordOption
39
+ import androidx.credentials.PasswordCredential
40
+ import androidx.credentials.PublicKeyCredential
41
+ import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential
42
+ import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException
43
+
44
+
45
+ private const val TAG = " SignInKickstarter"
32
46
33
47
class SignInKickstarter (application : Application ? ) : SignInViewModelBase(application) {
48
+
34
49
private val app: Application = checkNotNull(application)
35
50
51
+ /* *
52
+ * Entry point. If an email link is detected, immediately launch the email catcher.
53
+ * Otherwise, launch startAuthMethodChoice.
54
+ */
36
55
fun start () {
37
56
if (! TextUtils .isEmpty(arguments.emailLink)) {
38
57
setResult(
39
58
Resource .forFailure<IdpResponse >(
40
59
IntentRequiredException (
41
- EmailLinkCatcherActivity .createIntent(
42
- app,
43
- arguments
44
- ),
60
+ EmailLinkCatcherActivity .createIntent(app, arguments),
45
61
RequestCodes .EMAIL_FLOW
46
62
)
47
63
)
48
64
)
49
65
return
50
66
}
51
-
52
67
startAuthMethodChoice()
53
68
}
54
69
70
+
71
+ /* *
72
+ * Fallback: if no credential was obtained (or after a failed Credential Manager attempt)
73
+ * choose the proper sign‑in flow.
74
+ */
55
75
private fun startAuthMethodChoice () {
56
76
if (! arguments.shouldShowProviderChoice()) {
57
77
val firstIdpConfig = arguments.defaultOrFirstProvider
58
78
val firstProvider = firstIdpConfig.providerId
59
79
when (firstProvider) {
60
- AuthUI .EMAIL_LINK_PROVIDER , EmailAuthProvider .PROVIDER_ID -> setResult(
61
- Resource .forFailure<IdpResponse >(
62
- IntentRequiredException (
63
- EmailActivity .createIntent(app, arguments),
64
- RequestCodes .EMAIL_FLOW
80
+ AuthUI .EMAIL_LINK_PROVIDER , EmailAuthProvider .PROVIDER_ID ->
81
+ setResult(
82
+ Resource .forFailure<IdpResponse >(
83
+ IntentRequiredException (
84
+ EmailActivity .createIntent(app, arguments),
85
+ RequestCodes .EMAIL_FLOW
86
+ )
65
87
)
66
88
)
67
- )
68
-
69
- PhoneAuthProvider .PROVIDER_ID -> setResult(
70
- Resource .forFailure<IdpResponse >(
71
- IntentRequiredException (
72
- PhoneActivity .createIntent(
73
- app,
74
- arguments, firstIdpConfig.params
75
- ),
76
- RequestCodes .PHONE_FLOW
89
+ PhoneAuthProvider .PROVIDER_ID ->
90
+ setResult(
91
+ Resource .forFailure<IdpResponse >(
92
+ IntentRequiredException (
93
+ PhoneActivity .createIntent(app, arguments, firstIdpConfig.params),
94
+ RequestCodes .PHONE_FLOW
95
+ )
77
96
)
78
97
)
79
- )
80
-
81
98
else -> redirectSignIn(firstProvider, null )
82
99
}
83
100
} else {
84
101
setResult(
85
102
Resource .forFailure<IdpResponse >(
86
103
IntentRequiredException (
87
- AuthMethodPickerActivity .createIntent(
88
- app,
89
- arguments
90
- ),
104
+ AuthMethodPickerActivity .createIntent(app, arguments),
91
105
RequestCodes .AUTH_PICKER_FLOW
92
106
)
93
107
)
94
108
)
95
109
}
96
110
}
97
111
112
+ /* *
113
+ * Helper to route to the proper sign‑in activity for a given provider.
114
+ */
98
115
private fun redirectSignIn (provider : String , id : String? ) {
99
116
when (provider) {
100
- EmailAuthProvider .PROVIDER_ID -> setResult(
101
- Resource .forFailure<IdpResponse >(
102
- IntentRequiredException (
103
- EmailActivity .createIntent(app, arguments, id),
104
- RequestCodes .EMAIL_FLOW
117
+ EmailAuthProvider .PROVIDER_ID ->
118
+ setResult(
119
+ Resource .forFailure<IdpResponse >(
120
+ IntentRequiredException (
121
+ EmailActivity .createIntent(app, arguments, id),
122
+ RequestCodes .EMAIL_FLOW
123
+ )
105
124
)
106
125
)
107
- )
108
-
109
126
PhoneAuthProvider .PROVIDER_ID -> {
110
- val args = Bundle ()
111
- args.putString(ExtraConstants .PHONE , id)
127
+ val args = Bundle ().apply { putString(ExtraConstants .PHONE , id) }
112
128
setResult(
113
129
Resource .forFailure<IdpResponse >(
114
130
IntentRequiredException (
115
- PhoneActivity .createIntent(
116
- app,
117
- arguments, args
118
- ),
131
+ PhoneActivity .createIntent(app, arguments, args),
119
132
RequestCodes .PHONE_FLOW
120
133
)
121
134
)
122
135
)
123
136
}
124
-
125
- else -> setResult(
126
- Resource .forFailure<IdpResponse >(
127
- IntentRequiredException (
128
- SingleSignInActivity .createIntent(
129
- app, arguments,
130
- User . Builder (provider, id).build()
131
- ),
132
- RequestCodes . PROVIDER_FLOW
137
+ else ->
138
+ setResult(
139
+ Resource .forFailure<IdpResponse >(
140
+ IntentRequiredException (
141
+ SingleSignInActivity .createIntent(
142
+ app, arguments, User . Builder (provider, id).build()
143
+ ),
144
+ RequestCodes . PROVIDER_FLOW
145
+ )
133
146
)
134
147
)
135
- )
136
148
}
137
149
}
138
150
151
+ /* *
152
+ * Legacy onActivityResult handler for other flows.
153
+ */
139
154
fun onActivityResult (requestCode : Int , resultCode : Int , data : Intent ? ) {
140
155
when (requestCode) {
141
- RequestCodes .CRED_HINT -> if (resultCode == Activity .RESULT_OK && data != null ) {
142
- try {
143
- val signInClient = Identity .getSignInClient(app)
144
- val credential = signInClient.getSignInCredentialFromIntent(data)
145
- handleCredential(credential)
146
- } catch (e: ApiException ) {
147
- // Optionally log the error
148
- startAuthMethodChoice()
149
- }
150
- } else {
151
- startAuthMethodChoice()
152
- }
153
-
154
- RequestCodes .EMAIL_FLOW , RequestCodes .AUTH_PICKER_FLOW , RequestCodes .PHONE_FLOW , RequestCodes .PROVIDER_FLOW -> {
156
+ RequestCodes .EMAIL_FLOW ,
157
+ RequestCodes .AUTH_PICKER_FLOW ,
158
+ RequestCodes .PHONE_FLOW ,
159
+ RequestCodes .PROVIDER_FLOW -> {
155
160
if (resultCode == RequestCodes .EMAIL_LINK_WRONG_DEVICE_FLOW ||
156
161
resultCode == RequestCodes .EMAIL_LINK_INVALID_LINK_FLOW
157
162
) {
@@ -163,74 +168,79 @@ class SignInKickstarter(application: Application?) : SignInViewModelBase(applica
163
168
setResult(Resource .forFailure(UserCancellationException ()))
164
169
} else if (response.isSuccessful) {
165
170
setResult(Resource .forSuccess(response))
166
- } else if (response.error!! .errorCode ==
167
- ErrorCodes .ANONYMOUS_UPGRADE_MERGE_CONFLICT
168
- ) {
171
+ } else if (response.error!! .errorCode == ErrorCodes .ANONYMOUS_UPGRADE_MERGE_CONFLICT ) {
169
172
handleMergeFailure(response)
170
173
} else {
171
- setResult(
172
- Resource .forFailure(
173
- response.error!!
174
- )
175
- )
174
+ setResult(Resource .forFailure(response.error!! ))
176
175
}
177
176
}
177
+ else -> startAuthMethodChoice()
178
178
}
179
179
}
180
180
181
181
/* *
182
- * Minimal change: Adapted to work with the new SignInCredential .
182
+ * Handle a successfully returned Credential from the Credential Manager .
183
183
*/
184
- private fun handleCredential (credential : SignInCredential ) {
185
- val id = credential.id
186
- val password = credential.password
187
- if (TextUtils .isEmpty(password)) {
188
- // Instead of checking accountType, check for a Google ID token.
189
- val googleIdToken = credential.googleIdToken
190
- if (! TextUtils .isEmpty(googleIdToken)) {
184
+ private fun handleCredentialManagerResult (credential : Credential ) {
185
+ when (credential) {
186
+ is PasswordCredential -> {
187
+ val username = credential.id
188
+ val password = credential.password
191
189
val response = IdpResponse .Builder (
192
- User .Builder (GoogleAuthProvider .PROVIDER_ID , id ).build()
190
+ User .Builder (EmailAuthProvider .PROVIDER_ID , username ).build()
193
191
).build()
194
192
setResult(Resource .forLoading())
195
- auth.signInWithCredential(GoogleAuthProvider .getCredential(googleIdToken, null ))
196
- .addOnSuccessListener { authResult: AuthResult ? ->
197
- handleSuccess(
198
- response,
199
- authResult!!
200
- )
193
+ auth.signInWithEmailAndPassword(username, password)
194
+ .addOnSuccessListener { authResult: AuthResult ->
195
+ handleSuccess(response, authResult)
196
+ // (Optionally finish the hosting activity here.)
197
+ }
198
+ .addOnFailureListener { e ->
199
+ if (e is FirebaseAuthInvalidUserException ||
200
+ e is FirebaseAuthInvalidCredentialsException
201
+ ) {
202
+ // Sign out using the new API.
203
+ Identity .getSignInClient(app).signOut()
204
+ }
205
+ startAuthMethodChoice()
201
206
}
202
- .addOnFailureListener { e: Exception ? -> startAuthMethodChoice() }
203
- } else {
204
- startAuthMethodChoice()
205
207
}
206
- } else {
207
- val response = IdpResponse .Builder (
208
- User .Builder (EmailAuthProvider .PROVIDER_ID , id).build()
209
- ).build()
210
- setResult(Resource .forLoading())
211
- auth.signInWithEmailAndPassword(id, password!! )
212
- .addOnSuccessListener { authResult: AuthResult ? ->
213
- handleSuccess(
214
- response,
215
- authResult!!
216
- )
217
- }
218
- .addOnFailureListener { e: Exception ? ->
219
- if (e is FirebaseAuthInvalidUserException ||
220
- e is FirebaseAuthInvalidCredentialsException
221
- ) {
222
- // Minimal change: sign out using the new API (delete isn’t available).
223
- Identity .getSignInClient(
224
- app
208
+ is CustomCredential -> {
209
+ if (credential.type == GoogleIdTokenCredential .TYPE_GOOGLE_ID_TOKEN_CREDENTIAL ) {
210
+ try {
211
+ val googleIdTokenCredential = GoogleIdTokenCredential .createFrom(credential.data)
212
+ auth.signInWithCredential(
213
+ GoogleAuthProvider .getCredential(googleIdTokenCredential.idToken, null )
225
214
)
226
- .signOut()
215
+ .addOnSuccessListener { authResult: AuthResult ->
216
+ val response = IdpResponse .Builder (
217
+ User .Builder (
218
+ GoogleAuthProvider .PROVIDER_ID ,
219
+ // Assume the credential data contains the email.
220
+ googleIdTokenCredential.data.getString(" email" )
221
+ ).build()
222
+ )
223
+ .setToken(googleIdTokenCredential.idToken)
224
+ .build()
225
+ handleSuccess(response, authResult)
226
+ }
227
+ .addOnFailureListener { e ->
228
+ Log .e(TAG , " Failed to sign in with Google ID token" , e)
229
+ startAuthMethodChoice()
230
+ }
231
+ } catch (e: GoogleIdTokenParsingException ) {
232
+ Log .e(TAG , " Received an invalid google id token response" , e)
233
+ startAuthMethodChoice()
227
234
}
235
+ } else {
236
+ Log .e(TAG , " Unexpected type of credential" )
228
237
startAuthMethodChoice()
229
238
}
239
+ }
240
+ else -> {
241
+ Log .e(TAG , " Unexpected type of credential" )
242
+ startAuthMethodChoice()
243
+ }
230
244
}
231
245
}
232
-
233
- companion object {
234
- private const val TAG = " SignInKickstarter"
235
- }
236
- }
246
+ }
0 commit comments