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