Skip to content

Commit e0ab63f

Browse files
committed
feat: move from SmartLock to CredentialManager
1 parent 5b6c9f0 commit e0ab63f

File tree

11 files changed

+330
-336
lines changed

11 files changed

+330
-336
lines changed

auth/build.gradle.kts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import com.android.build.gradle.internal.dsl.TestOptions
22

33
plugins {
4-
id("com.android.library")
5-
id("com.vanniktech.maven.publish")
4+
id("com.android.library")
5+
id("com.vanniktech.maven.publish")
6+
id("org.jetbrains.kotlin.android")
67
}
78

89
android {
@@ -26,8 +27,8 @@ android {
2627
}
2728

2829
compileOptions {
29-
sourceCompatibility = JavaVersion.VERSION_1_8
30-
targetCompatibility = JavaVersion.VERSION_1_8
30+
sourceCompatibility = JavaVersion.VERSION_17
31+
targetCompatibility = JavaVersion.VERSION_17
3132
}
3233

3334
lint {
@@ -62,6 +63,9 @@ android {
6263
isIncludeAndroidResources = true
6364
}
6465
}
66+
kotlinOptions {
67+
jvmTarget = "1.8"
68+
}
6569
}
6670

6771
dependencies {
@@ -72,8 +76,12 @@ dependencies {
7276
implementation(Config.Libs.Androidx.fragment)
7377
implementation(Config.Libs.Androidx.customTabs)
7478
implementation(Config.Libs.Androidx.constraint)
79+
implementation("androidx.credentials:credentials:1.3.0")
80+
implementation("androidx.core:core-ktx:1.10.1")
7581

7682
implementation(Config.Libs.Androidx.lifecycleExtensions)
83+
implementation("androidx.core:core-ktx:1.13.1")
84+
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0")
7785
annotationProcessor(Config.Libs.Androidx.lifecycleCompiler)
7886

7987
implementation(platform(Config.Libs.Firebase.bom))

auth/src/main/java/com/firebase/ui/auth/ui/credentials/CredentialSaveActivity.java

Lines changed: 0 additions & 79 deletions
This file was deleted.
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package com.firebase.ui.auth.ui.credentials
2+
3+
import android.content.Context
4+
import android.content.Intent
5+
import android.os.Bundle
6+
import android.util.Log
7+
import com.firebase.ui.auth.IdpResponse
8+
import com.firebase.ui.auth.data.model.FlowParameters
9+
import com.firebase.ui.auth.data.model.Resource
10+
import com.firebase.ui.auth.ui.InvisibleActivityBase
11+
import com.firebase.ui.auth.util.ExtraConstants
12+
import com.firebase.ui.auth.viewmodel.ResourceObserver
13+
import com.firebase.ui.auth.viewmodel.credentialmanager.CredentialManagerHandler
14+
import com.google.android.gms.auth.api.credentials.Credential
15+
import androidx.lifecycle.ViewModelProvider
16+
import com.google.firebase.auth.FirebaseAuth
17+
18+
class CredentialSaveActivity : InvisibleActivityBase() {
19+
20+
companion object {
21+
private const val TAG = "CredentialSaveActivity"
22+
23+
@JvmStatic
24+
fun createIntent(
25+
context: Context,
26+
flowParams: FlowParameters,
27+
credential: Credential,
28+
response: IdpResponse
29+
): Intent {
30+
return createBaseIntent(context, CredentialSaveActivity::class.java, flowParams).apply {
31+
putExtra(ExtraConstants.CREDENTIAL, credential)
32+
putExtra(ExtraConstants.IDP_RESPONSE, response)
33+
}
34+
}
35+
}
36+
37+
private lateinit var credentialManagerHandler: CredentialManagerHandler
38+
39+
override fun onCreate(savedInstanceState: Bundle?) {
40+
super.onCreate(savedInstanceState)
41+
42+
val response: IdpResponse? = intent.getParcelableExtra(ExtraConstants.IDP_RESPONSE)
43+
val credential: Credential? = intent.getParcelableExtra(ExtraConstants.CREDENTIAL)
44+
45+
credentialManagerHandler = ViewModelProvider(this)
46+
.get(CredentialManagerHandler::class.java)
47+
.apply {
48+
// Initialize with flow parameters
49+
init(flowParams)
50+
// If we have an IdpResponse, set it so subsequent operations can report results
51+
response?.let { setResponse(it) }
52+
53+
// Observe the operation resource
54+
operation.observe(
55+
this@CredentialSaveActivity,
56+
object : ResourceObserver<IdpResponse>(this@CredentialSaveActivity) {
57+
override fun onSuccess(response: IdpResponse) {
58+
// Done saving – success
59+
finish(RESULT_OK, response.toIntent())
60+
}
61+
62+
override fun onFailure(e: Exception) {
63+
// We don’t want to block the sign-in flow just because saving failed,
64+
// so return RESULT_OK
65+
response?.let {
66+
finish(RESULT_OK, it.toIntent())
67+
} ?: finish(RESULT_OK, null)
68+
}
69+
}
70+
)
71+
}
72+
73+
val currentOp: Resource<IdpResponse>? = credentialManagerHandler.operation.value
74+
75+
if (currentOp == null) {
76+
Log.d(TAG, "Launching save operation.")
77+
// In the old SmartLock flow, you saved a `Credential`;
78+
// with CredentialManager, we typically need email & password for the new request.
79+
// Example usage: pass the current user & the password.
80+
// Adjust as needed for passkeys or other flows.
81+
val firebaseUser = FirebaseAuth.getInstance().currentUser
82+
val password = credential?.password
83+
84+
credentialManagerHandler.saveCredentials(this, firebaseUser, password)
85+
} else {
86+
Log.d(TAG, "Save operation in progress, doing nothing.")
87+
}
88+
}
89+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package com.firebase.ui.auth.viewmodel.credentialmanager
2+
3+
import android.app.Application
4+
import androidx.lifecycle.ViewModel
5+
import androidx.lifecycle.viewModelScope
6+
import androidx.credentials.CreatePasswordRequest
7+
import androidx.credentials.CredentialManager
8+
import androidx.credentials.exceptions.CreateCredentialException
9+
import com.firebase.ui.auth.ErrorCodes
10+
import com.firebase.ui.auth.FirebaseUiException
11+
import com.firebase.ui.auth.IdpResponse
12+
import com.firebase.ui.auth.data.model.Resource
13+
import com.firebase.ui.auth.viewmodel.AuthViewModelBase
14+
import com.google.firebase.auth.FirebaseUser
15+
import kotlinx.coroutines.launch
16+
17+
class CredentialManagerHandler(application: Application) :
18+
AuthViewModelBase<IdpResponse>(application) {
19+
20+
private val credentialManager = CredentialManager.create(application)
21+
private var response: IdpResponse? = null
22+
23+
fun setResponse(newResponse: IdpResponse) {
24+
response = newResponse
25+
}
26+
27+
/**
28+
* Saves credentials via Credential Manager if enabled in [getArguments().enableCredentials].
29+
* Uses a password-based credential for demonstration; adapt to passkeys or other flows as needed.
30+
*/
31+
fun saveCredentials(
32+
activity: androidx.activity.ComponentActivity,
33+
firebaseUser: FirebaseUser?,
34+
password: String?
35+
) {
36+
if (!arguments.enableCredentials) {
37+
setResult(Resource.forSuccess(response!!))
38+
return
39+
}
40+
setResult(Resource.forLoading())
41+
42+
if (firebaseUser == null || firebaseUser.email.isNullOrEmpty() || password.isNullOrEmpty()) {
43+
setResult(
44+
Resource.forFailure(
45+
FirebaseUiException(
46+
ErrorCodes.UNKNOWN_ERROR,
47+
"Invalid FirebaseUser or missing password."
48+
)
49+
)
50+
)
51+
return
52+
}
53+
54+
// Example: Password credential with the user's email as the identifier
55+
val request = CreatePasswordRequest(
56+
id = firebaseUser.email!!,
57+
password = password
58+
)
59+
60+
viewModelScope.launch {
61+
try {
62+
credentialManager.createCredential(activity, request)
63+
setResult(Resource.forSuccess(response!!))
64+
} catch (e: CreateCredentialException) {
65+
setResult(
66+
Resource.forFailure(
67+
FirebaseUiException(
68+
ErrorCodes.UNKNOWN_ERROR,
69+
"Error saving credential with Credential Manager.",
70+
e
71+
)
72+
)
73+
)
74+
} catch (e: Exception) {
75+
setResult(
76+
Resource.forFailure(
77+
FirebaseUiException(
78+
ErrorCodes.UNKNOWN_ERROR,
79+
"Unexpected error saving credential.",
80+
e
81+
)
82+
)
83+
)
84+
}
85+
}
86+
}
87+
}

0 commit comments

Comments
 (0)