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 80db14f
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 32 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
173 changes: 141 additions & 32 deletions crates/bitwarden-uniffi/src/platform/fido2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ impl ClientFido2 {
) -> Arc<ClientFido2Authenticator> {
Arc::new(ClientFido2Authenticator(
self.0.clone(),
user_interface,
credential_store,
Box::new(UniffiTraitBridge(user_interface)),
Box::new(UniffiTraitBridge(credential_store)),

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

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-uniffi/src/platform/fido2.rs#L30-L31

Added lines #L30 - L31 were not covered by tests
))
}

Expand All @@ -37,11 +37,23 @@ impl ClientFido2 {
user_interface: Arc<dyn Fido2UserInterface>,
credential_store: Arc<dyn Fido2CredentialStore>,
) -> Arc<ClientFido2Client> {
Arc::new(ClientFido2Client(ClientFido2Authenticator(
Arc::new(ClientFido2Client(

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

View check run for this annotation

Codecov / codecov/patch

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

Added line #L40 was not covered by tests
self.0.clone(),
user_interface,
credential_store,
)))
Box::new(UniffiTraitBridge(user_interface)),
Box::new(UniffiTraitBridge(credential_store)),
))
}

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

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-uniffi/src/platform/fido2.rs#L42-L45

Added lines #L42 - L45 were not covered by tests

pub fn nonblocking_client(
self: Arc<Self>,
user_interface: Arc<dyn Fido2NonBlockingUserInterface + 'static>,
credential_store: Arc<dyn Fido2CredentialStore>,
) -> Arc<ClientFido2Client> {
Arc::new(ClientFido2Client(
self.0.clone(),
Box::new(UniffiTraitBridge(user_interface)),
Box::new(UniffiTraitBridge(credential_store)),
))

Check warning on line 56 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-L56

Added lines #L47 - L56 were not covered by tests
}

pub fn decrypt_fido2_autofill_credentials(
Expand All @@ -62,8 +74,8 @@ impl ClientFido2 {
#[derive(uniffi::Object)]
pub struct ClientFido2Authenticator(
pub(crate) Arc<Client>,
pub(crate) Arc<dyn Fido2UserInterface>,
pub(crate) Arc<dyn Fido2CredentialStore>,
pub(crate) Box<dyn bitwarden::fido::Fido2UserInterface>,
pub(crate) Box<dyn bitwarden::fido::Fido2CredentialStore>,
);

#[uniffi::export]
Expand All @@ -73,9 +85,7 @@ impl ClientFido2Authenticator {
request: MakeCredentialRequest,
) -> Result<MakeCredentialResult> {
let fido2 = self.0 .0.fido2();
let ui = UniffiTraitBridge(self.1.as_ref());
let cs = UniffiTraitBridge(self.2.as_ref());
let mut auth = fido2.create_authenticator(&ui, &cs);
let mut auth = fido2.create_authenticator(self.1.as_ref(), self.2.as_ref());

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

View check run for this annotation

Codecov / codecov/patch

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

Added line #L88 was not covered by tests

let result = auth
.make_credential(request)
Expand All @@ -86,9 +96,7 @@ impl ClientFido2Authenticator {

pub async fn get_assertion(&self, request: GetAssertionRequest) -> Result<GetAssertionResult> {
let fido2 = self.0 .0.fido2();
let ui = UniffiTraitBridge(self.1.as_ref());
let cs = UniffiTraitBridge(self.2.as_ref());
let mut auth = fido2.create_authenticator(&ui, &cs);
let mut auth = fido2.create_authenticator(self.1.as_ref(), self.2.as_ref());

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

View check run for this annotation

Codecov / codecov/patch

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

Added line #L99 was not covered by tests

let result = auth
.get_assertion(request)
Expand All @@ -102,10 +110,7 @@ impl ClientFido2Authenticator {
rp_id: String,
) -> Result<Vec<Fido2CredentialAutofillView>> {
let fido2 = self.0 .0.fido2();

let ui = UniffiTraitBridge(self.1.as_ref());
let cs = UniffiTraitBridge(self.2.as_ref());
let mut auth = fido2.create_authenticator(&ui, &cs);
let mut auth = fido2.create_authenticator(self.1.as_ref(), self.2.as_ref());

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

View check run for this annotation

Codecov / codecov/patch

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

Added line #L113 was not covered by tests

let result = auth
.silently_discover_credentials(rp_id)
Expand All @@ -116,9 +121,7 @@ impl ClientFido2Authenticator {

pub async fn credentials_for_autofill(&self) -> Result<Vec<Fido2CredentialAutofillView>> {
let fido2 = self.0 .0.fido2();
let ui = UniffiTraitBridge(self.1.as_ref());
let cs = UniffiTraitBridge(self.2.as_ref());
let mut auth = fido2.create_authenticator(&ui, &cs);
let mut auth = fido2.create_authenticator(self.1.as_ref(), self.2.as_ref());

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

View check run for this annotation

Codecov / codecov/patch

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

Added line #L124 was not covered by tests

let result = auth
.credentials_for_autofill()
Expand All @@ -129,7 +132,11 @@ impl ClientFido2Authenticator {
}

#[derive(uniffi::Object)]
pub struct ClientFido2Client(pub(crate) ClientFido2Authenticator);
pub struct ClientFido2Client(
pub(crate) Arc<Client>,
pub(crate) Box<dyn bitwarden::fido::Fido2UserInterface>,
pub(crate) Box<dyn bitwarden::fido::Fido2CredentialStore>,
);

#[uniffi::export]
impl ClientFido2Client {
Expand All @@ -139,10 +146,8 @@ impl ClientFido2Client {
request: String,
client_data: ClientData,
) -> Result<PublicKeyCredentialAuthenticatorAttestationResponse> {
let fido2 = self.0 .0 .0.fido2();
let ui = UniffiTraitBridge(self.0 .1.as_ref());
let cs = UniffiTraitBridge(self.0 .2.as_ref());
let mut client = fido2.create_client(&ui, &cs);
let fido2 = self.0 .0.fido2();
let mut client = fido2.create_client(self.1.as_ref(), self.2.as_ref());

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

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-uniffi/src/platform/fido2.rs#L149-L150

Added lines #L149 - L150 were not covered by tests

let result = client
.register(origin, request, client_data)
Expand All @@ -157,10 +162,8 @@ impl ClientFido2Client {
request: String,
client_data: ClientData,
) -> Result<PublicKeyCredentialAuthenticatorAssertionResponse> {
let fido2 = self.0 .0 .0.fido2();
let ui = UniffiTraitBridge(self.0 .1.as_ref());
let cs = UniffiTraitBridge(self.0 .2.as_ref());
let mut client = fido2.create_client(&ui, &cs);
let fido2 = self.0 .0.fido2();
let mut client = fido2.create_client(self.1.as_ref(), self.2.as_ref());

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

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-uniffi/src/platform/fido2.rs#L165-L166

Added lines #L165 - L166 were not covered by tests

let result = client
.authenticate(origin, request, client_data)
Expand Down Expand Up @@ -231,6 +234,59 @@ 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 239 in crates/bitwarden-uniffi/src/platform/fido2.rs

View check run for this annotation

Codecov / codecov/patch

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

Added line #L239 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 250 in crates/bitwarden-uniffi/src/platform/fido2.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-uniffi/src/platform/fido2.rs#L244-L250

Added lines #L244 - L250 were not covered by tests
}
#[uniffi::export(async_runtime = "tokio")]

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

View check run for this annotation

Codecov / codecov/patch

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

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

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

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-uniffi/src/platform/fido2.rs#L254-L259

Added lines #L254 - L259 were not covered by tests
}
};
}

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

#[uniffi::export(with_foreign)]

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

View check run for this annotation

Codecov / codecov/patch

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

Added line #L267 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 All @@ -252,7 +308,7 @@ pub trait Fido2CredentialStore: Send + Sync {
struct UniffiTraitBridge<T>(T);

#[async_trait::async_trait]
impl bitwarden::fido::Fido2CredentialStore for UniffiTraitBridge<&dyn Fido2CredentialStore> {
impl bitwarden::fido::Fido2CredentialStore for UniffiTraitBridge<Arc<dyn Fido2CredentialStore>> {
async fn find_credentials(
&self,
ids: Option<Vec<Vec<u8>>>,
Expand Down Expand Up @@ -317,7 +373,7 @@ impl From<bitwarden::fido::UIHint<'_, CipherView>> for UIHint {
}

#[async_trait::async_trait]
impl bitwarden::fido::Fido2UserInterface for UniffiTraitBridge<&dyn Fido2UserInterface> {
impl bitwarden::fido::Fido2UserInterface for UniffiTraitBridge<Arc<dyn Fido2UserInterface>> {
async fn check_user<'a>(
&self,
options: CheckUserOptions,
Expand Down Expand Up @@ -357,3 +413,56 @@ impl bitwarden::fido::Fido2UserInterface for UniffiTraitBridge<&dyn Fido2UserInt
self.0.is_verification_enabled().await
}
}

#[async_trait::async_trait]
impl bitwarden::fido::Fido2UserInterface
for UniffiTraitBridge<Arc<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 437 in crates/bitwarden-uniffi/src/platform/fido2.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-uniffi/src/platform/fido2.rs#L425-L437

Added lines #L425 - L437 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 450 in crates/bitwarden-uniffi/src/platform/fido2.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-uniffi/src/platform/fido2.rs#L441-L450

Added lines #L441 - L450 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 467 in crates/bitwarden-uniffi/src/platform/fido2.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-uniffi/src/platform/fido2.rs#L455-L467

Added lines #L455 - L467 were not covered by tests
}

0 comments on commit 80db14f

Please sign in to comment.