Only provide AccountCredentials at account creation time
This commit is contained in:
parent
266ccc2415
commit
11c231bace
|
@ -19,7 +19,7 @@ async fn main() -> anyhow::Result<()> {
|
|||
// Alternatively, restore an account from serialized credentials by
|
||||
// using `Account::from_credentials()`.
|
||||
|
||||
let account = Account::create(
|
||||
let (account, credentials) = Account::create(
|
||||
&NewAccount {
|
||||
contact: &[],
|
||||
terms_of_service_agreed: true,
|
||||
|
@ -29,6 +29,10 @@ async fn main() -> anyhow::Result<()> {
|
|||
None,
|
||||
)
|
||||
.await?;
|
||||
info!(
|
||||
"account credentials:\n\n{}",
|
||||
serde_json::to_string_pretty(&credentials).unwrap()
|
||||
);
|
||||
|
||||
// Create the ACME order based on the given domain names.
|
||||
// Note that this only needs an `&Account`, so the library will let you
|
||||
|
@ -141,11 +145,6 @@ async fn main() -> anyhow::Result<()> {
|
|||
|
||||
info!("certficate chain:\n\n{}", cert_chain_pem);
|
||||
info!("private key:\n\n{}", cert.serialize_private_key_pem());
|
||||
info!(
|
||||
"account credentials:\n\n{}",
|
||||
serde_json::to_string_pretty(&account.credentials()).unwrap()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
85
src/lib.rs
85
src/lib.rs
|
@ -3,7 +3,6 @@
|
|||
#![warn(unreachable_pub)]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
|
@ -203,53 +202,61 @@ impl Account {
|
|||
///
|
||||
/// The [`AccountCredentials`] type is opaque, but supports deserialization.
|
||||
#[cfg(feature = "hyper-rustls")]
|
||||
pub fn from_credentials(credentials: AccountCredentials<'_>) -> Result<Self, Error> {
|
||||
pub async fn from_credentials(credentials: AccountCredentials) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
inner: Arc::new(AccountInner::from_credentials(
|
||||
credentials,
|
||||
Box::<DefaultClient>::default(),
|
||||
)?),
|
||||
inner: Arc::new(
|
||||
AccountInner::from_credentials(credentials, Box::<DefaultClient>::default())
|
||||
.await?,
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
/// Restore an existing account from the given credentials and HTTP client
|
||||
///
|
||||
/// The [`AccountCredentials`] type is opaque, but supports deserialization.
|
||||
pub fn from_credentials_and_http(
|
||||
credentials: AccountCredentials<'_>,
|
||||
pub async fn from_credentials_and_http(
|
||||
credentials: AccountCredentials,
|
||||
http: Box<dyn HttpClient>,
|
||||
) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
inner: Arc::new(AccountInner::from_credentials(credentials, http)?),
|
||||
inner: Arc::new(AccountInner::from_credentials(credentials, http).await?),
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a new account on the `server_url` with the information in [`NewAccount`]
|
||||
///
|
||||
/// The returned [`AccountCredentials`] can be serialized and stored for later use.
|
||||
/// Use [`Account::from_credentials()`] to restore the account from the credentials.
|
||||
#[cfg(feature = "hyper-rustls")]
|
||||
pub async fn create(
|
||||
account: &NewAccount<'_>,
|
||||
server_url: &str,
|
||||
external_account: Option<&ExternalAccountKey>,
|
||||
) -> Result<Account, Error> {
|
||||
) -> Result<(Account, AccountCredentials), Error> {
|
||||
Self::create_inner(
|
||||
account,
|
||||
external_account,
|
||||
Client::new(server_url, Box::<DefaultClient>::default()).await?,
|
||||
server_url,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Create a new account with a custom HTTP client
|
||||
///
|
||||
/// The returned [`AccountCredentials`] can be serialized and stored for later use.
|
||||
/// Use [`Account::from_credentials()`] to restore the account from the credentials.
|
||||
pub async fn create_with_http(
|
||||
account: &NewAccount<'_>,
|
||||
server_url: &str,
|
||||
external_account: Option<&ExternalAccountKey>,
|
||||
http: Box<dyn HttpClient>,
|
||||
) -> Result<Account, Error> {
|
||||
) -> Result<(Account, AccountCredentials), Error> {
|
||||
Self::create_inner(
|
||||
account,
|
||||
external_account,
|
||||
Client::new(server_url, http).await?,
|
||||
server_url,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
@ -258,7 +265,8 @@ impl Account {
|
|||
account: &NewAccount<'_>,
|
||||
external_account: Option<&ExternalAccountKey>,
|
||||
client: Client,
|
||||
) -> Result<Account, Error> {
|
||||
server_url: &str,
|
||||
) -> Result<(Account, AccountCredentials), Error> {
|
||||
let key = Key::generate()?;
|
||||
let payload = NewAccountPayload {
|
||||
new_account: account,
|
||||
|
@ -285,13 +293,28 @@ impl Account {
|
|||
|
||||
// The response redirects, we don't need the body
|
||||
let _ = Problem::from_response(rsp).await?;
|
||||
Ok(Self {
|
||||
inner: Arc::new(AccountInner {
|
||||
let id = account_url.ok_or("failed to get account URL")?;
|
||||
let credentials = AccountCredentials {
|
||||
id: id.clone(),
|
||||
key_pkcs8: BASE64_URL_SAFE_NO_PAD.encode(&key.pkcs8_der),
|
||||
directory: Some(server_url.to_owned()),
|
||||
// We support deserializing URLs for compatibility with versions pre 0.4,
|
||||
// but we prefer to get fresh URLs from the `server_url` for newer credentials.
|
||||
urls: None,
|
||||
};
|
||||
|
||||
let account = AccountInner {
|
||||
client,
|
||||
key,
|
||||
id: account_url.ok_or("failed to get account URL")?,
|
||||
}),
|
||||
})
|
||||
id: id.clone(),
|
||||
};
|
||||
|
||||
Ok((
|
||||
Self {
|
||||
inner: Arc::new(account),
|
||||
},
|
||||
credentials,
|
||||
))
|
||||
}
|
||||
|
||||
/// Create a new order based on the given [`NewOrder`]
|
||||
|
@ -320,13 +343,6 @@ impl Account {
|
|||
url: order_url.ok_or("no order URL found")?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the account's credentials, which can be serialized
|
||||
///
|
||||
/// Pass the credentials to [`Account::from_credentials`] to regain access to the `Account`.
|
||||
pub fn credentials(&self) -> AccountCredentials<'_> {
|
||||
self.inner.credentials()
|
||||
}
|
||||
}
|
||||
|
||||
struct AccountInner {
|
||||
|
@ -336,17 +352,18 @@ struct AccountInner {
|
|||
}
|
||||
|
||||
impl AccountInner {
|
||||
fn from_credentials(
|
||||
credentials: AccountCredentials<'_>,
|
||||
async fn from_credentials(
|
||||
credentials: AccountCredentials,
|
||||
http: Box<dyn HttpClient>,
|
||||
) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
id: credentials.id,
|
||||
key: Key::from_pkcs8_der(BASE64_URL_SAFE_NO_PAD.decode(&credentials.key_pkcs8)?)?,
|
||||
client: Client {
|
||||
http,
|
||||
urls: credentials.urls.into_owned(),
|
||||
client: match (credentials.directory, credentials.urls) {
|
||||
(Some(server_url), _) => Client::new(&server_url, http).await?,
|
||||
(None, Some(urls)) => Client { http, urls },
|
||||
(None, None) => return Err("no server URLs found".into()),
|
||||
},
|
||||
id: credentials.id.into_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -368,14 +385,6 @@ impl AccountInner {
|
|||
) -> Result<Response<Body>, Error> {
|
||||
self.client.post(payload, nonce, self, url).await
|
||||
}
|
||||
|
||||
fn credentials(&self) -> AccountCredentials<'_> {
|
||||
AccountCredentials {
|
||||
id: Cow::Borrowed(&self.id),
|
||||
key_pkcs8: BASE64_URL_SAFE_NO_PAD.encode(&self.key.pkcs8_der),
|
||||
urls: Cow::Borrowed(&self.client.urls),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Signer for AccountInner {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
|
||||
use base64::prelude::{Engine, BASE64_URL_SAFE_NO_PAD};
|
||||
|
@ -53,10 +52,11 @@ impl From<&'static str> for Error {
|
|||
/// the account credentials to a file or secret manager and restore the
|
||||
/// account from persistent storage.
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct AccountCredentials<'a> {
|
||||
pub(crate) id: Cow<'a, str>,
|
||||
pub struct AccountCredentials {
|
||||
pub(crate) id: String,
|
||||
pub(crate) key_pkcs8: String,
|
||||
pub(crate) urls: Cow<'a, DirectoryUrls>,
|
||||
pub(crate) directory: Option<String>,
|
||||
pub(crate) urls: Option<DirectoryUrls>,
|
||||
}
|
||||
|
||||
/// An RFC 7807 problem document as returned by the ACME server
|
||||
|
|
Loading…
Reference in New Issue