Skip to main content

playwright_rs/protocol/
credentials.rs

1//! WebAuthn virtual-authenticator credentials.
2//!
3//! Obtained via [`BrowserContext::credentials`](crate::protocol::BrowserContext::credentials).
4//! Install a virtual authenticator, then register / list / delete passkeys
5//! programmatically to drive `navigator.credentials.create()/get()` ceremonies
6//! in tests without real hardware.
7//!
8//! ```no_run
9//! # use playwright_rs::Playwright;
10//! # async fn ex() -> playwright_rs::Result<()> {
11//! # let pw = Playwright::launch().await?;
12//! # let browser = pw.chromium().launch().await?;
13//! # let context = browser.new_context().await?;
14//! let creds = context.credentials();
15//! creds.install().await?;
16//! let cred = creds.create("example.com", None).await?;
17//! assert_eq!(creds.get(None).await?.len(), 1);
18//! creds.delete(&cred.id).await?;
19//! # Ok(())
20//! # }
21//! ```
22//!
23//! See: <https://playwright.dev/docs/api/class-credentials>
24
25use crate::error::Result;
26use crate::server::channel::Channel;
27use serde_json::json;
28
29/// A virtual WebAuthn credential (passkey) held by the virtual authenticator.
30#[derive(Debug, Clone, serde::Deserialize)]
31#[serde(rename_all = "camelCase")]
32#[non_exhaustive]
33pub struct VirtualCredential {
34    /// Base64url credential ID.
35    pub id: String,
36    /// Relying-party (origin) ID the credential is scoped to.
37    pub rp_id: String,
38    /// Base64url user handle, if the credential has one.
39    #[serde(default)]
40    pub user_handle: String,
41    /// Base64url-encoded PKCS#8 private key.
42    #[serde(default)]
43    pub private_key: String,
44    /// Base64url-encoded public key.
45    #[serde(default)]
46    pub public_key: String,
47}
48
49/// Optional fields for [`Credentials::create`]. When omitted, the authenticator
50/// generates them.
51#[derive(Debug, Default, Clone)]
52#[non_exhaustive]
53pub struct CredentialsCreateOptions {
54    /// Explicit base64url credential ID.
55    pub id: Option<String>,
56    /// Base64url user handle to associate.
57    pub user_handle: Option<String>,
58    /// Base64url PKCS#8 private key to import.
59    pub private_key: Option<String>,
60    /// Base64url public key to import.
61    pub public_key: Option<String>,
62}
63
64impl CredentialsCreateOptions {
65    /// Set an explicit credential ID.
66    pub fn id(mut self, id: impl Into<String>) -> Self {
67        self.id = Some(id.into());
68        self
69    }
70    /// Set the user handle.
71    pub fn user_handle(mut self, user_handle: impl Into<String>) -> Self {
72        self.user_handle = Some(user_handle.into());
73        self
74    }
75    /// Import a specific private key (base64url PKCS#8).
76    pub fn private_key(mut self, private_key: impl Into<String>) -> Self {
77        self.private_key = Some(private_key.into());
78        self
79    }
80    /// Import a specific public key (base64url).
81    pub fn public_key(mut self, public_key: impl Into<String>) -> Self {
82        self.public_key = Some(public_key.into());
83        self
84    }
85}
86
87/// Filters for [`Credentials::get`]. With no filter set, all credentials are
88/// returned.
89#[derive(Debug, Default, Clone)]
90#[non_exhaustive]
91pub struct CredentialsGetOptions {
92    /// Only return credentials scoped to this relying-party ID.
93    pub rp_id: Option<String>,
94    /// Only return the credential with this ID.
95    pub id: Option<String>,
96}
97
98impl CredentialsGetOptions {
99    /// Filter by relying-party ID.
100    pub fn rp_id(mut self, rp_id: impl Into<String>) -> Self {
101        self.rp_id = Some(rp_id.into());
102        self
103    }
104    /// Filter by credential ID.
105    pub fn id(mut self, id: impl Into<String>) -> Self {
106        self.id = Some(id.into());
107        self
108    }
109}
110
111/// Manages the browser context's virtual WebAuthn authenticator.
112///
113/// See: <https://playwright.dev/docs/api/class-credentials>
114#[derive(Clone)]
115pub struct Credentials {
116    channel: Channel,
117}
118
119impl std::fmt::Debug for Credentials {
120    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121        f.debug_struct("Credentials").finish_non_exhaustive()
122    }
123}
124
125impl Credentials {
126    pub(crate) fn new(channel: Channel) -> Self {
127        Self { channel }
128    }
129
130    /// Installs a virtual WebAuthn authenticator on the context. Call before
131    /// registering credentials or driving `navigator.credentials` ceremonies.
132    ///
133    /// # Errors
134    ///
135    /// Returns error if:
136    /// - The browser context has been closed
137    /// - Communication with the browser process fails
138    ///
139    /// See: <https://playwright.dev/docs/api/class-credentials#credentials-install>
140    pub async fn install(&self) -> Result<()> {
141        self.channel
142            .send_no_result("credentialsInstall", json!({}))
143            .await
144    }
145
146    /// Registers a virtual credential scoped to `rp_id`, returning the created
147    /// credential (with any authenticator-generated fields filled in).
148    ///
149    /// # Errors
150    ///
151    /// Returns error if:
152    /// - The browser context has been closed
153    /// - Communication with the browser process fails
154    ///
155    /// See: <https://playwright.dev/docs/api/class-credentials#credentials-create>
156    pub async fn create(
157        &self,
158        rp_id: &str,
159        options: impl Into<Option<CredentialsCreateOptions>>,
160    ) -> Result<VirtualCredential> {
161        let options = options.into();
162        let mut params = json!({ "rpId": rp_id });
163        if let Some(o) = options {
164            if let Some(id) = o.id {
165                params["id"] = json!(id);
166            }
167            if let Some(uh) = o.user_handle {
168                params["userHandle"] = json!(uh);
169            }
170            if let Some(pk) = o.private_key {
171                params["privateKey"] = json!(pk);
172            }
173            if let Some(pk) = o.public_key {
174                params["publicKey"] = json!(pk);
175            }
176        }
177        #[derive(serde::Deserialize)]
178        struct R {
179            credential: VirtualCredential,
180        }
181        let r: R = self.channel.send("credentialsCreate", params).await?;
182        Ok(r.credential)
183    }
184
185    /// Lists virtual credentials, optionally filtered by relying-party or ID.
186    ///
187    /// # Errors
188    ///
189    /// Returns error if:
190    /// - The browser context has been closed
191    /// - Communication with the browser process fails
192    ///
193    /// See: <https://playwright.dev/docs/api/class-credentials#credentials-get>
194    pub async fn get(
195        &self,
196        options: impl Into<Option<CredentialsGetOptions>>,
197    ) -> Result<Vec<VirtualCredential>> {
198        let options = options.into();
199        let mut params = json!({});
200        if let Some(o) = options {
201            if let Some(rp_id) = o.rp_id {
202                params["rpId"] = json!(rp_id);
203            }
204            if let Some(id) = o.id {
205                params["id"] = json!(id);
206            }
207        }
208        #[derive(serde::Deserialize)]
209        struct R {
210            credentials: Vec<VirtualCredential>,
211        }
212        let r: R = self.channel.send("credentialsGet", params).await?;
213        Ok(r.credentials)
214    }
215
216    /// Deletes the credential with the given ID.
217    ///
218    /// # Errors
219    ///
220    /// Returns error if:
221    /// - The browser context has been closed
222    /// - Communication with the browser process fails
223    ///
224    /// See: <https://playwright.dev/docs/api/class-credentials#credentials-delete>
225    pub async fn delete(&self, id: &str) -> Result<()> {
226        self.channel
227            .send_no_result("credentialsDelete", json!({ "id": id }))
228            .await
229    }
230}