Thumbnail image

An Unofficial Rust SDK for 1Password

1Password is a popular choice for password management. Even though it might not be the best or even the industry standard for IT projects.

Yes I do use Azure Key Vault where it makes sense. But I think dealing with the network security around your Key Vault or copying secrets across different secret management solutions, just to make things work isn’t appealing to most users.

1Password’s SDKs for Python, Go, and JavaScript does make it easier to fetch secrets programmatically during development, and even in production environments, without relying on .env files, infrastructure overhead and/or network complexity. But for my recent Rust projects, there was no such solution available. There are a few -unmaintained- crates available for 1Password integration, but they all rely on the op-cli tool, which needs to be installed on your servers to make them work.

That triggered me to think about a solution more ’native’ for developers. Building a Rust SDK for 1Password, following the ‘wrapper’ approach used in their official Python, Javascript and Go SDK’s.

From now on you do not have to use:

  • Shell wrappers around the op CLI
  • Environment variables everywhere
  • 1Password Connect Server
  • Copy-pasting of credentials

Introducing corteq-onepassword

 1use corteq_onepassword::{OnePassword, ExposeSecret};
 2
 3#[tokio::main]
 4async fn main() -> Result<(), Box<dyn std::error::Error>> {
 5    // Create client from OP_SERVICE_ACCOUNT_TOKEN environment variable
 6    let client = OnePassword::from_env()?
 7        .integration("my-app", "1.0.0") // Name of your app for audit purposes on 1password side
 8        .connect()
 9        .await?;
10
11    // Resolve a secret
12    let api_key = client.secret("op://vault/item/api-key").await?;
13
14    // Use the secret (expose only when needed)
15    println!("API key: {}", api_key.expose_secret());
16
17    Ok(())
18}

That’s it. Just a few lines to go from zero to secure secret retrieval.


Building on Secure Foundations

The 1Password official SDK’s are thin wrappers around the SDK Core library.

By creating FFI bindings to libop_uniffi_core, we build on top of the secure core that provides:

  • Secure cryptographic implementation used with official SDKs
  • Zero-knowledge architecture secrets are decrypted client-side
  • Battle-tested security not reinventing any cryptography
  • Easy updates when 1Password updates their SDK, we can update it fairly easily

For a in-depth explanation on the architecture see the Architecture reference on Github


Security by Default

The corteq-onepassword crate uses the secrecy crate to ensure secrets are protected from the moment they leave 1Password. We need to explicitly expose the secret:

 1let secret = op.secret("op://vault/item/field").await?;
 2
 3// This won't compile
 4// println!("{}", secret);
 5
 6// Debug shows [REDACTED]
 7// dbg!(&secret);
 8
 9// Explicit action required to access the value
10let value: &str = secret.expose_secret();

When a SecretString is dropped, its memory will be zeroed, ensuring no secrets lingering around in memory.


Possible Next Steps

For now I only needed secret resolution. The other SDK’s do provide support for Vault and Item management. Which gives you the opportunity to automate secret rotation. This may be added to future versions of this Rust SDK. Let me know if this would fill a gap within your Rust projects by creating an issue on Github

On a side-note; I have reached out to the 1Password Developer Community and 1Password themselves requesting their feedback, thoughts and improvements. If any of them reaches out, their input and findings will be shared on GitHub.