Skip to content

Commit

Permalink
[WIP] Non blocking passkeys
Browse files Browse the repository at this point in the history
  • Loading branch information
dani-garcia committed Jul 2, 2024
1 parent 569e1fc commit 60c8e20
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/bitwarden-uniffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ log = "0.4.20"
env_logger = "0.11.1"
schemars = { version = ">=0.8, <0.9", optional = true }
thiserror = ">=1.0.40, <2.0"
tokio = { version = "1.36.0" }
uniffi = "=0.27.2"
uuid = ">=1.3.3, <2"

Expand Down
166 changes: 166 additions & 0 deletions crates/bitwarden-uniffi/src/platform/fido2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ impl ClientFido2 {
)))
}

pub fn nonblocking_client(
self: Arc<Self>,
user_interface: Arc<dyn Fido2NonBlockingUserInterface>,
credential_store: Arc<dyn Fido2CredentialStore>,
) -> Arc<ClientFido2NonBlockingClient> {
Arc::new(ClientFido2NonBlockingClient(
self.0.clone(),
user_interface,
credential_store,
))
}

Check warning on line 57 in crates/bitwarden-uniffi/src/platform/fido2.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-uniffi/src/platform/fido2.rs#L47-L57

Added lines #L47 - L57 were not covered by tests

pub fn decrypt_fido2_autofill_credentials(
self: Arc<Self>,
cipher_view: CipherView,
Expand Down Expand Up @@ -170,6 +182,52 @@ impl ClientFido2Client {
}
}

#[derive(uniffi::Object)]

Check warning on line 185 in crates/bitwarden-uniffi/src/platform/fido2.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-uniffi/src/platform/fido2.rs#L185

Added line #L185 was not covered by tests
pub struct ClientFido2NonBlockingClient(
pub(crate) Arc<Client>,
pub(crate) Arc<dyn Fido2NonBlockingUserInterface>,
pub(crate) Arc<dyn Fido2CredentialStore>,
);

#[uniffi::export]

Check warning on line 192 in crates/bitwarden-uniffi/src/platform/fido2.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-uniffi/src/platform/fido2.rs#L192

Added line #L192 was not covered by tests
impl ClientFido2NonBlockingClient {
pub async fn register(
&self,
origin: String,
request: String,
client_data: ClientData,
) -> Result<PublicKeyCredentialAuthenticatorAttestationResponse> {
let fido2 = self.0 .0.fido2();
let ui = UniffiTraitBridge(self.1.as_ref());
let cs = UniffiTraitBridge(self.2.as_ref());
let mut client = fido2.create_client(&ui, &cs);

Check warning on line 203 in crates/bitwarden-uniffi/src/platform/fido2.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-uniffi/src/platform/fido2.rs#L194-L203

Added lines #L194 - L203 were not covered by tests

let result = client
.register(origin, request, client_data)
.await
.map_err(Error::Fido2Client)?;
Ok(result)
}

Check warning on line 210 in crates/bitwarden-uniffi/src/platform/fido2.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-uniffi/src/platform/fido2.rs#L205-L210

Added lines #L205 - L210 were not covered by tests

pub async fn authenticate(
&self,
origin: String,
request: String,
client_data: ClientData,
) -> Result<PublicKeyCredentialAuthenticatorAssertionResponse> {
let fido2 = self.0 .0.fido2();
let ui = UniffiTraitBridge(self.1.as_ref());
let cs = UniffiTraitBridge(self.2.as_ref());
let mut client = fido2.create_client(&ui, &cs);

Check warning on line 221 in crates/bitwarden-uniffi/src/platform/fido2.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-uniffi/src/platform/fido2.rs#L212-L221

Added lines #L212 - L221 were not covered by tests

let result = client
.authenticate(origin, request, client_data)
.await
.map_err(Error::Fido2Client)?;
Ok(result)
}

Check warning on line 228 in crates/bitwarden-uniffi/src/platform/fido2.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-uniffi/src/platform/fido2.rs#L223-L228

Added lines #L223 - L228 were not covered by tests
}

// Note that uniffi doesn't support external traits for now it seems, so we have to duplicate them
// here.

Expand Down Expand Up @@ -231,6 +289,62 @@ pub trait Fido2UserInterface: Send + Sync {
async fn is_verification_enabled(&self) -> bool;
}

macro_rules! continuation {
($name:ident, $type:ty) => {
#[derive(uniffi::Object)]

Check warning on line 294 in crates/bitwarden-uniffi/src/platform/fido2.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-uniffi/src/platform/fido2.rs#L294

Added line #L294 was not covered by tests
pub struct $name {
tx: tokio::sync::mpsc::Sender<Result<$type, Fido2CallbackError>>,
}

impl $name {
pub fn new() -> (
std::sync::Arc<Self>,
tokio::sync::mpsc::Receiver<Result<$type, Fido2CallbackError>>,
) {
let (tx, rx) = tokio::sync::mpsc::channel(1);
(std::sync::Arc::new(Self { tx }), rx)
}

Check warning on line 306 in crates/bitwarden-uniffi/src/platform/fido2.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-uniffi/src/platform/fido2.rs#L300-L306

Added lines #L300 - L306 were not covered by tests
}

#[uniffi::export(async_runtime = "tokio")]

Check warning on line 309 in crates/bitwarden-uniffi/src/platform/fido2.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-uniffi/src/platform/fido2.rs#L309

Added line #L309 was not covered by tests
impl $name {
pub async fn complete(&self, result: $type) {
let _ = self.tx.send(Ok(result)).await;
}

Check warning on line 313 in crates/bitwarden-uniffi/src/platform/fido2.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-uniffi/src/platform/fido2.rs#L311-L313

Added lines #L311 - L313 were not covered by tests

pub async fn fail(&self, error: Fido2CallbackError) {
let _ = self.tx.send(Err(error)).await;
}

Check warning on line 317 in crates/bitwarden-uniffi/src/platform/fido2.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-uniffi/src/platform/fido2.rs#L315-L317

Added lines #L315 - L317 were not covered by tests
}
};
}

continuation!(CompletionObjectCheckUserResult, CheckUserResult);
continuation!(CompletionObjectCipherViewWrapper, CipherViewWrapper);

#[uniffi::export(with_foreign)]

Check warning on line 325 in crates/bitwarden-uniffi/src/platform/fido2.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-uniffi/src/platform/fido2.rs#L325

Added line #L325 was not covered by tests
#[async_trait::async_trait]
pub trait Fido2NonBlockingUserInterface: Send + Sync {
async fn check_user(
&self,
options: CheckUserOptions,
hint: UIHint,
completion: Arc<CompletionObjectCheckUserResult>,
) -> Result<(), Fido2CallbackError>;
async fn pick_credential_for_authentication(
&self,
available_credentials: Vec<CipherView>,
completion: Arc<CompletionObjectCipherViewWrapper>,
) -> Result<(), Fido2CallbackError>;
async fn check_user_and_pick_credential_for_creation(
&self,
options: CheckUserOptions,
new_credential: Fido2CredentialNewView,
completion: Arc<CompletionObjectCipherViewWrapper>,
) -> Result<(), Fido2CallbackError>;
async fn is_verification_enabled(&self) -> bool;
}

#[uniffi::export(with_foreign)]
#[async_trait::async_trait]
pub trait Fido2CredentialStore: Send + Sync {
Expand Down Expand Up @@ -357,3 +471,55 @@ impl bitwarden::fido::Fido2UserInterface for UniffiTraitBridge<&dyn Fido2UserInt
self.0.is_verification_enabled().await
}
}

#[async_trait::async_trait]
impl bitwarden::fido::Fido2UserInterface for UniffiTraitBridge<&dyn Fido2NonBlockingUserInterface> {
async fn check_user<'a>(
&self,
options: CheckUserOptions,
hint: bitwarden::fido::UIHint<'a, CipherView>,
) -> Result<bitwarden::fido::CheckUserResult, BitFido2CallbackError> {
let (completion, mut rx) = CompletionObjectCheckUserResult::new();

self.0
.check_user(options.clone(), hint.into(), completion)
.await?;

let result = rx.recv().await.expect("Channel should be open")?;

Ok(bitwarden::fido::CheckUserResult {
user_present: result.user_present,
user_verified: result.user_verified,
})
}

Check warning on line 494 in crates/bitwarden-uniffi/src/platform/fido2.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-uniffi/src/platform/fido2.rs#L481-L494

Added lines #L481 - L494 were not covered by tests
async fn pick_credential_for_authentication(
&self,
available_credentials: Vec<CipherView>,
) -> Result<CipherView, BitFido2CallbackError> {
let (completion, mut rx) = CompletionObjectCipherViewWrapper::new();

self.0
.pick_credential_for_authentication(available_credentials, completion)
.await?;

let result = rx.recv().await.expect("Channel should be open")?;
Ok(result.cipher)
}

Check warning on line 507 in crates/bitwarden-uniffi/src/platform/fido2.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-uniffi/src/platform/fido2.rs#L498-L507

Added lines #L498 - L507 were not covered by tests
async fn check_user_and_pick_credential_for_creation(
&self,
options: CheckUserOptions,
new_credential: Fido2CredentialNewView,
) -> Result<CipherView, BitFido2CallbackError> {
let (completion, mut rx) = CompletionObjectCipherViewWrapper::new();

self.0
.check_user_and_pick_credential_for_creation(options, new_credential, completion)
.await?;

let result = rx.recv().await.expect("Channel should be open")?;
Ok(result.cipher)
}
async fn is_verification_enabled(&self) -> bool {
self.0.is_verification_enabled().await
}

Check warning on line 524 in crates/bitwarden-uniffi/src/platform/fido2.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-uniffi/src/platform/fido2.rs#L512-L524

Added lines #L512 - L524 were not covered by tests
}

0 comments on commit 60c8e20

Please sign in to comment.