Skip to content

Commit

Permalink
combined fido2 and password credential provider into BitwardenCredent…
Browse files Browse the repository at this point in the history
…ialProviderService in order to not show 2 locked entries in the credential api.
  • Loading branch information
Nailik committed Oct 20, 2024
1 parent 94cd7af commit 1ec7bdd
Show file tree
Hide file tree
Showing 10 changed files with 92 additions and 185 deletions.
17 changes: 1 addition & 16 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -242,22 +242,7 @@
</intent-filter>
<meta-data
android:name="android.credentials.provider"
android:resource="@xml/fido_provider" />
</service>

<service
android:name="com.x8bit.bitwarden.data.autofill.password.BitwardenPasswordProviderService"
android:enabled="true"
android:exported="true"
android:label="@string/bitwarden"
android:permission="android.permission.BIND_CREDENTIAL_PROVIDER_SERVICE"
tools:targetApi="upside_down_cake">
<intent-filter>
<action android:name="android.service.credentials.CredentialProviderService" />
</intent-filter>
<meta-data
android:name="android.credentials.provider"
android:resource="@xml/password_provider" />
android:resource="@xml/provider" />
</service>

<!-- This is required to support in-app language picker in Android 12 (API 32) and below -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,16 @@ import androidx.credentials.exceptions.CreateCredentialException
import androidx.credentials.exceptions.GetCredentialException
import androidx.credentials.provider.BeginCreateCredentialRequest
import androidx.credentials.provider.BeginCreateCredentialResponse
import androidx.credentials.provider.BeginCreatePasswordCredentialRequest
import androidx.credentials.provider.BeginCreatePublicKeyCredentialRequest
import androidx.credentials.provider.BeginGetCredentialRequest
import androidx.credentials.provider.BeginGetCredentialResponse
import androidx.credentials.provider.BeginGetPasswordOption
import androidx.credentials.provider.BeginGetPublicKeyCredentialOption
import androidx.credentials.provider.CredentialProviderService
import androidx.credentials.provider.ProviderClearCredentialStateRequest
import com.x8bit.bitwarden.data.autofill.fido2.processor.Fido2ProviderProcessor
import com.x8bit.bitwarden.data.autofill.password.processor.PasswordProviderProcessor
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
Expand All @@ -34,27 +39,50 @@ class BitwardenCredentialProviderService : CredentialProviderService() {
* isn't easily testable.
*/
@Inject
lateinit var processor: Fido2ProviderProcessor
lateinit var fido2Processor: Fido2ProviderProcessor
/**
* A processor to handle the Password credential fulfillment. We keep the service light because it
* isn't easily testable.
*/
@Inject
lateinit var passwordProcessor: PasswordProviderProcessor

override fun onBeginCreateCredentialRequest(
request: BeginCreateCredentialRequest,
cancellationSignal: CancellationSignal,
callback: OutcomeReceiver<BeginCreateCredentialResponse, CreateCredentialException>,
) {
processor.processCreateCredentialRequest(
request,
cancellationSignal,
callback,
)
when(request) {
is BeginCreatePublicKeyCredentialRequest -> {
fido2Processor.processCreateCredentialRequest(
request,
cancellationSignal,
callback,
)
}
is BeginCreatePasswordCredentialRequest -> {
passwordProcessor.processCreateCredentialRequest(
request,
cancellationSignal,
callback,
)
}
}
}

override fun onBeginGetCredentialRequest(
request: BeginGetCredentialRequest,
cancellationSignal: CancellationSignal,
callback: OutcomeReceiver<BeginGetCredentialResponse, GetCredentialException>,
) {
processor.processGetCredentialRequest(
request,
fido2Processor.processGetCredentialRequest(
request.beginGetCredentialOptions.filterIsInstance<BeginGetPublicKeyCredentialOption>(),
cancellationSignal,
callback,
)
passwordProcessor.processGetCredentialRequest(
request.callingAppInfo,
request.beginGetCredentialOptions.filterIsInstance<BeginGetPasswordOption>(),
cancellationSignal,
callback,
)
Expand All @@ -65,7 +93,12 @@ class BitwardenCredentialProviderService : CredentialProviderService() {
cancellationSignal: CancellationSignal,
callback: OutcomeReceiver<Void?, ClearCredentialException>,
) {
processor.processClearCredentialStateRequest(
fido2Processor.processClearCredentialStateRequest(
request,
cancellationSignal,
callback,
)
passwordProcessor.processClearCredentialStateRequest(
request,
cancellationSignal,
callback,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ object Fido2ProviderModule {
@ApplicationContext context: Context,
authRepository: AuthRepository,
vaultRepository: VaultRepository,
fido2CredentialStore: Fido2CredentialStore,
fido2CredentialManager: Fido2CredentialManager,
dispatcherManager: DispatcherManager,
intentManager: IntentManager,
Expand All @@ -48,7 +47,6 @@ object Fido2ProviderModule {
context,
authRepository,
vaultRepository,
fido2CredentialStore,
fido2CredentialManager,
intentManager,
clock,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import androidx.credentials.exceptions.CreateCredentialException
import androidx.credentials.exceptions.GetCredentialException
import androidx.credentials.provider.BeginCreateCredentialRequest
import androidx.credentials.provider.BeginCreateCredentialResponse
import androidx.credentials.provider.BeginCreatePublicKeyCredentialRequest
import androidx.credentials.provider.BeginGetCredentialRequest
import androidx.credentials.provider.BeginGetCredentialResponse
import androidx.credentials.provider.BeginGetPublicKeyCredentialOption
import androidx.credentials.provider.ProviderClearCredentialStateRequest

/**
Expand All @@ -26,22 +28,22 @@ interface Fido2ProviderProcessor {
* @param callback the callback object to be used to notify the response or error
*/
fun processCreateCredentialRequest(
request: BeginCreateCredentialRequest,
request: BeginCreatePublicKeyCredentialRequest,
cancellationSignal: CancellationSignal,
callback: OutcomeReceiver<BeginCreateCredentialResponse, CreateCredentialException>,
)

/**
* Process the [BeginGetCredentialRequest] and invoke the [callback] with the result.
*
* @param request The request data form the OS that contains data about the requesting provider.
* @param beginGetCredentialOptions data from the OS that contains data about the requesting provider.
* @param cancellationSignal signal for observing cancellation requests. The system will use
* this to notify us that the result is no longer needed and we should stop handling it in order
* to save our resources.
* @param callback the callback object to be used to notify the response or error
*/
fun processGetCredentialRequest(
request: BeginGetCredentialRequest,
beginGetCredentialOptions: List<BeginGetPublicKeyCredentialOption>,
cancellationSignal: CancellationSignal,
callback: OutcomeReceiver<BeginGetCredentialResponse, GetCredentialException>,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,15 @@ import androidx.credentials.exceptions.GetCredentialException
import androidx.credentials.exceptions.GetCredentialUnknownException
import androidx.credentials.exceptions.GetCredentialUnsupportedException
import androidx.credentials.provider.AuthenticationAction
import androidx.credentials.provider.BeginCreateCredentialRequest
import androidx.credentials.provider.BeginCreateCredentialResponse
import androidx.credentials.provider.BeginCreatePublicKeyCredentialRequest
import androidx.credentials.provider.BeginGetCredentialRequest
import androidx.credentials.provider.BeginGetCredentialResponse
import androidx.credentials.provider.BeginGetPublicKeyCredentialOption
import androidx.credentials.provider.CreateEntry
import androidx.credentials.provider.CredentialEntry
import androidx.credentials.provider.ProviderClearCredentialStateRequest
import androidx.credentials.provider.PublicKeyCredentialEntry
import com.bitwarden.fido.Fido2CredentialAutofillView
import com.bitwarden.sdk.Fido2CredentialStore
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.auth.repository.model.UserState
Expand Down Expand Up @@ -55,7 +52,6 @@ class Fido2ProviderProcessorImpl(
private val context: Context,
private val authRepository: AuthRepository,
private val vaultRepository: VaultRepository,
private val fido2CredentialStore: Fido2CredentialStore,
private val fido2CredentialManager: Fido2CredentialManager,
private val intentManager: IntentManager,
private val clock: Clock,
Expand All @@ -66,7 +62,7 @@ class Fido2ProviderProcessorImpl(
private val scope = CoroutineScope(dispatcherManager.unconfined)

override fun processCreateCredentialRequest(
request: BeginCreateCredentialRequest,
request: BeginCreatePublicKeyCredentialRequest,
cancellationSignal: CancellationSignal,
callback: OutcomeReceiver<BeginCreateCredentialResponse, CreateCredentialException>,
) {
Expand All @@ -77,7 +73,7 @@ class Fido2ProviderProcessorImpl(
}

val createCredentialJob = scope.launch {
processCreateCredentialRequest(request = request)
handleCreatePasskeyQuery(request = request)
?.let { callback.onResult(it) }
?: callback.onError(CreateCredentialUnknownException())
}
Expand All @@ -89,18 +85,6 @@ class Fido2ProviderProcessorImpl(
}
}

private fun processCreateCredentialRequest(
request: BeginCreateCredentialRequest,
): BeginCreateCredentialResponse? {
return when (request) {
is BeginCreatePublicKeyCredentialRequest -> {
handleCreatePasskeyQuery(request)
}

else -> null
}
}

private fun handleCreatePasskeyQuery(
request: BeginCreatePublicKeyCredentialRequest,
): BeginCreateCredentialResponse? {
Expand Down Expand Up @@ -144,7 +128,7 @@ class Fido2ProviderProcessorImpl(
}

override fun processGetCredentialRequest(
request: BeginGetCredentialRequest,
beginGetCredentialOptions: List<BeginGetPublicKeyCredentialOption>,
cancellationSignal: CancellationSignal,
callback: OutcomeReceiver<BeginGetCredentialResponse, GetCredentialException>,
) {
Expand Down Expand Up @@ -179,7 +163,7 @@ class Fido2ProviderProcessorImpl(
try {
val credentialEntries = getMatchingFido2CredentialEntries(
userId = userState.activeUserId,
request = request,
beginGetCredentialOptions = beginGetCredentialOptions,
)

callback.onResult(
Expand All @@ -200,20 +184,15 @@ class Fido2ProviderProcessorImpl(
@Throws(GetCredentialUnsupportedException::class)
private suspend fun getMatchingFido2CredentialEntries(
userId: String,
request: BeginGetCredentialRequest,
beginGetCredentialOptions: List<BeginGetPublicKeyCredentialOption>,
): List<CredentialEntry> =
request
.beginGetCredentialOptions
beginGetCredentialOptions
.flatMap { option ->
if (option is BeginGetPublicKeyCredentialOption) {
val relyingPartyId = fido2CredentialManager
.getPasskeyAssertionOptionsOrNull(requestJson = option.requestJson)
?.relyingPartyId
?: throw GetCredentialUnknownException("Invalid data.")
buildCredentialEntries(userId, relyingPartyId, option)
} else {
throw GetCredentialUnsupportedException("Unsupported option.")
}
val relyingPartyId = fido2CredentialManager
.getPasskeyAssertionOptionsOrNull(requestJson = option.requestJson)
?.relyingPartyId
?: throw GetCredentialUnknownException("Invalid data.")
buildCredentialEntries(userId, relyingPartyId, option)
}

private suspend fun buildCredentialEntries(
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import androidx.credentials.exceptions.CreateCredentialException
import androidx.credentials.exceptions.GetCredentialException
import androidx.credentials.provider.BeginCreateCredentialRequest
import androidx.credentials.provider.BeginCreateCredentialResponse
import androidx.credentials.provider.BeginCreatePasswordCredentialRequest
import androidx.credentials.provider.BeginGetCredentialRequest
import androidx.credentials.provider.BeginGetCredentialResponse
import androidx.credentials.provider.BeginGetPasswordOption
import androidx.credentials.provider.CallingAppInfo
import androidx.credentials.provider.ProviderClearCredentialStateRequest


Expand All @@ -27,22 +30,23 @@ interface PasswordProviderProcessor {
* @param callback the callback object to be used to notify the response or error
*/
fun processCreateCredentialRequest(
request: BeginCreateCredentialRequest,
request: BeginCreatePasswordCredentialRequest,
cancellationSignal: CancellationSignal,
callback: OutcomeReceiver<BeginCreateCredentialResponse, CreateCredentialException>,
)

/**
* Process the [BeginGetCredentialRequest] and invoke the [callback] with the result.
*
* @param request The request data form the OS that contains data about the requesting provider.
* @param beginGetPasswordOptions The request data from the OS that contains data about the requesting provider.
* @param cancellationSignal signal for observing cancellation requests. The system will use
* this to notify us that the result is no longer needed and we should stop handling it in order
* to save our resources.
* @param callback the callback object to be used to notify the response or error
*/
fun processGetCredentialRequest(
request: BeginGetCredentialRequest,
callingAppInfo: CallingAppInfo?,
beginGetPasswordOptions: List<BeginGetPasswordOption>,
cancellationSignal: CancellationSignal,
callback: OutcomeReceiver<BeginGetCredentialResponse, GetCredentialException>,
)
Expand Down
Loading

0 comments on commit 1ec7bdd

Please sign in to comment.