Skip to content
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

Non blocking passkeys #867

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@
) -> 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 @@
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 @@
#[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 @@
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 @@

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 @@
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 @@

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 @@
}

#[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 @@
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 @@
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 @@
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 @@
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 @@
}

#[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 @@
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
}
Loading