@@ -29,129 +29,134 @@ import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException
2929import com.google.firebase.auth.FirebaseAuthInvalidUserException
3030import com.google.firebase.auth.GoogleAuthProvider
3131import 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"
3246
3347class SignInKickstarter (application : Application ? ) : SignInViewModelBase(application) {
48+
3449 private val app: Application = checkNotNull(application)
3550
51+ /* *
52+ * Entry point. If an email link is detected, immediately launch the email catcher.
53+ * Otherwise, launch startAuthMethodChoice.
54+ */
3655 fun start () {
3756 if (! TextUtils .isEmpty(arguments.emailLink)) {
3857 setResult(
3958 Resource .forFailure<IdpResponse >(
4059 IntentRequiredException (
41- EmailLinkCatcherActivity .createIntent(
42- app,
43- arguments
44- ),
60+ EmailLinkCatcherActivity .createIntent(app, arguments),
4561 RequestCodes .EMAIL_FLOW
4662 )
4763 )
4864 )
4965 return
5066 }
51-
5267 startAuthMethodChoice()
5368 }
5469
70+
71+ /* *
72+ * Fallback: if no credential was obtained (or after a failed Credential Manager attempt)
73+ * choose the proper sign‑in flow.
74+ */
5575 private fun startAuthMethodChoice () {
5676 if (! arguments.shouldShowProviderChoice()) {
5777 val firstIdpConfig = arguments.defaultOrFirstProvider
5878 val firstProvider = firstIdpConfig.providerId
5979 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+ )
6587 )
6688 )
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+ )
7796 )
7897 )
79- )
80-
8198 else -> redirectSignIn(firstProvider, null )
8299 }
83100 } else {
84101 setResult(
85102 Resource .forFailure<IdpResponse >(
86103 IntentRequiredException (
87- AuthMethodPickerActivity .createIntent(
88- app,
89- arguments
90- ),
104+ AuthMethodPickerActivity .createIntent(app, arguments),
91105 RequestCodes .AUTH_PICKER_FLOW
92106 )
93107 )
94108 )
95109 }
96110 }
97111
112+ /* *
113+ * Helper to route to the proper sign‑in activity for a given provider.
114+ */
98115 private fun redirectSignIn (provider : String , id : String? ) {
99116 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+ )
105124 )
106125 )
107- )
108-
109126 PhoneAuthProvider .PROVIDER_ID -> {
110- val args = Bundle ()
111- args.putString(ExtraConstants .PHONE , id)
127+ val args = Bundle ().apply { putString(ExtraConstants .PHONE , id) }
112128 setResult(
113129 Resource .forFailure<IdpResponse >(
114130 IntentRequiredException (
115- PhoneActivity .createIntent(
116- app,
117- arguments, args
118- ),
131+ PhoneActivity .createIntent(app, arguments, args),
119132 RequestCodes .PHONE_FLOW
120133 )
121134 )
122135 )
123136 }
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+ )
133146 )
134147 )
135- )
136148 }
137149 }
138150
151+ /* *
152+ * Legacy onActivityResult handler for other flows.
153+ */
139154 fun onActivityResult (requestCode : Int , resultCode : Int , data : Intent ? ) {
140155 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 -> {
155160 if (resultCode == RequestCodes .EMAIL_LINK_WRONG_DEVICE_FLOW ||
156161 resultCode == RequestCodes .EMAIL_LINK_INVALID_LINK_FLOW
157162 ) {
@@ -163,74 +168,79 @@ class SignInKickstarter(application: Application?) : SignInViewModelBase(applica
163168 setResult(Resource .forFailure(UserCancellationException ()))
164169 } else if (response.isSuccessful) {
165170 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 ) {
169172 handleMergeFailure(response)
170173 } else {
171- setResult(
172- Resource .forFailure(
173- response.error!!
174- )
175- )
174+ setResult(Resource .forFailure(response.error!! ))
176175 }
177176 }
177+ else -> startAuthMethodChoice()
178178 }
179179 }
180180
181181 /* *
182- * Minimal change: Adapted to work with the new SignInCredential .
182+ * Handle a successfully returned Credential from the Credential Manager .
183183 */
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
191189 val response = IdpResponse .Builder (
192- User .Builder (GoogleAuthProvider .PROVIDER_ID , id ).build()
190+ User .Builder (EmailAuthProvider .PROVIDER_ID , username ).build()
193191 ).build()
194192 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()
201206 }
202- .addOnFailureListener { e: Exception ? -> startAuthMethodChoice() }
203- } else {
204- startAuthMethodChoice()
205207 }
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 )
225214 )
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()
227234 }
235+ } else {
236+ Log .e(TAG , " Unexpected type of credential" )
228237 startAuthMethodChoice()
229238 }
239+ }
240+ else -> {
241+ Log .e(TAG , " Unexpected type of credential" )
242+ startAuthMethodChoice()
243+ }
230244 }
231245 }
232-
233- companion object {
234- private const val TAG = " SignInKickstarter"
235- }
236- }
246+ }
0 commit comments