-
Notifications
You must be signed in to change notification settings - Fork 1.9k
feat: move from SmartLock to CredentialManager #2180
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
e0ab63f
1b775d8
9d69b9d
3ff24f1
9f7a46f
5400d9d
a380306
1249235
c45845c
1518086
5636089
9c6c5cf
d54c6f4
e0f640c
8284b48
e0c3d47
09251b5
32e3034
345720e
57234a2
5755256
9dac3f5
742e9ab
e4c651d
429741f
94466aa
02a3e29
de3a3bc
25eb18a
c54a24b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,10 @@ | ||
package com.firebase.uidemo; | ||
|
||
import com.squareup.leakcanary.LeakCanary; | ||
|
||
import androidx.appcompat.app.AppCompatDelegate; | ||
import androidx.multidex.MultiDexApplication; | ||
|
||
public class FirebaseUIDemo extends MultiDexApplication { | ||
static { | ||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY); | ||
} | ||
|
||
@Override | ||
public void onCreate() { | ||
super.onCreate(); | ||
if (LeakCanary.isInAnalyzerProcess(this)) { | ||
// This process is dedicated to LeakCanary for heap analysis. | ||
// You should not init your app in this process. | ||
return; | ||
} | ||
LeakCanary.install(this); | ||
} | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package com.firebase.ui.auth.ui.credentials | ||
|
||
import android.content.Context | ||
import android.content.Intent | ||
import android.os.Bundle | ||
import android.util.Log | ||
import com.firebase.ui.auth.IdpResponse | ||
import com.firebase.ui.auth.data.model.FlowParameters | ||
import com.firebase.ui.auth.data.model.Resource | ||
import com.firebase.ui.auth.ui.InvisibleActivityBase | ||
import com.firebase.ui.auth.util.ExtraConstants | ||
import com.firebase.ui.auth.viewmodel.ResourceObserver | ||
import com.firebase.ui.auth.viewmodel.credentialmanager.CredentialManagerHandler | ||
import com.google.android.gms.auth.api.credentials.Credential | ||
import androidx.lifecycle.ViewModelProvider | ||
import com.google.firebase.auth.FirebaseAuth | ||
|
||
class CredentialSaveActivity : InvisibleActivityBase() { | ||
|
||
companion object { | ||
thatfiredev marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
private const val TAG = "CredentialSaveActivity" | ||
|
||
@JvmStatic | ||
fun createIntent( | ||
context: Context, | ||
flowParams: FlowParameters, | ||
credential: Credential, | ||
response: IdpResponse | ||
): Intent { | ||
return createBaseIntent(context, CredentialSaveActivity::class.java, flowParams).apply { | ||
putExtra(ExtraConstants.CREDENTIAL, credential) | ||
putExtra(ExtraConstants.IDP_RESPONSE, response) | ||
} | ||
} | ||
} | ||
|
||
private lateinit var credentialManagerHandler: CredentialManagerHandler | ||
|
||
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
|
||
val response: IdpResponse? = intent.getParcelableExtra(ExtraConstants.IDP_RESPONSE) | ||
val credential: Credential? = intent.getParcelableExtra(ExtraConstants.CREDENTIAL) | ||
|
||
credentialManagerHandler = ViewModelProvider(this) | ||
.get(CredentialManagerHandler::class.java) | ||
.apply { | ||
// Initialize with flow parameters | ||
init(flowParams) | ||
// If we have an IdpResponse, set it so subsequent operations can report results | ||
response?.let { setResponse(it) } | ||
|
||
// Observe the operation resource | ||
operation.observe( | ||
this@CredentialSaveActivity, | ||
object : ResourceObserver<IdpResponse>(this@CredentialSaveActivity) { | ||
override fun onSuccess(response: IdpResponse) { | ||
// Done saving – success | ||
finish(RESULT_OK, response.toIntent()) | ||
} | ||
|
||
override fun onFailure(e: Exception) { | ||
// We don’t want to block the sign-in flow just because saving failed, | ||
// so return RESULT_OK | ||
response?.let { | ||
finish(RESULT_OK, it.toIntent()) | ||
} ?: finish(RESULT_OK, null) | ||
} | ||
} | ||
) | ||
} | ||
|
||
val currentOp: Resource<IdpResponse>? = credentialManagerHandler.operation.value | ||
|
||
if (currentOp == null) { | ||
Log.d(TAG, "Launching save operation.") | ||
// In the old SmartLock flow, you saved a `Credential`; | ||
// with CredentialManager, we typically need email & password for the new request. | ||
// Example usage: pass the current user & the password. | ||
// Adjust as needed for passkeys or other flows. | ||
val firebaseUser = FirebaseAuth.getInstance().currentUser | ||
val password = credential?.password | ||
|
||
credentialManagerHandler.saveCredentials(this, firebaseUser, password) | ||
} else { | ||
Log.d(TAG, "Save operation in progress, doing nothing.") | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package com.firebase.ui.auth.viewmodel.credentialmanager | ||
|
||
import android.app.Application | ||
import androidx.lifecycle.ViewModel | ||
import androidx.lifecycle.viewModelScope | ||
import androidx.credentials.CreatePasswordRequest | ||
import androidx.credentials.CredentialManager | ||
import androidx.credentials.exceptions.CreateCredentialException | ||
import com.firebase.ui.auth.ErrorCodes | ||
import com.firebase.ui.auth.FirebaseUiException | ||
import com.firebase.ui.auth.IdpResponse | ||
import com.firebase.ui.auth.data.model.Resource | ||
import com.firebase.ui.auth.viewmodel.AuthViewModelBase | ||
import com.google.firebase.auth.FirebaseUser | ||
import kotlinx.coroutines.launch | ||
|
||
class CredentialManagerHandler(application: Application) : | ||
AuthViewModelBase<IdpResponse>(application) { | ||
|
||
private val credentialManager = CredentialManager.create(application) | ||
private var response: IdpResponse? = null | ||
|
||
fun setResponse(newResponse: IdpResponse) { | ||
response = newResponse | ||
} | ||
|
||
/** | ||
* Saves credentials via Credential Manager if enabled in [getArguments().enableCredentials]. | ||
* Uses a password-based credential for demonstration; adapt to passkeys or other flows as needed. | ||
*/ | ||
fun saveCredentials( | ||
activity: androidx.activity.ComponentActivity, | ||
firebaseUser: FirebaseUser?, | ||
password: String? | ||
) { | ||
if (!arguments.enableCredentials) { | ||
setResult(Resource.forSuccess(response!!)) | ||
return | ||
} | ||
setResult(Resource.forLoading()) | ||
|
||
if (firebaseUser == null || firebaseUser.email.isNullOrEmpty() || password.isNullOrEmpty()) { | ||
setResult( | ||
Resource.forFailure( | ||
FirebaseUiException( | ||
ErrorCodes.UNKNOWN_ERROR, | ||
"Invalid FirebaseUser or missing password." | ||
) | ||
) | ||
) | ||
return | ||
} | ||
|
||
// Example: Password credential with the user's email as the identifier | ||
val request = CreatePasswordRequest( | ||
id = firebaseUser.email!!, | ||
password = password | ||
) | ||
|
||
viewModelScope.launch { | ||
try { | ||
credentialManager.createCredential(activity, request) | ||
|
||
setResult(Resource.forSuccess(response!!)) | ||
} catch (e: CreateCredentialException) { | ||
setResult( | ||
Resource.forFailure( | ||
FirebaseUiException( | ||
ErrorCodes.UNKNOWN_ERROR, | ||
"Error saving credential with Credential Manager.", | ||
e | ||
) | ||
) | ||
) | ||
} catch (e: Exception) { | ||
setResult( | ||
Resource.forFailure( | ||
FirebaseUiException( | ||
ErrorCodes.UNKNOWN_ERROR, | ||
"Unexpected error saving credential.", | ||
e | ||
) | ||
) | ||
) | ||
} | ||
} | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.