diff --git a/guest_list/src/auth/mod.rs b/guest_list/src/auth/mod.rs index e493234..86926ad 100644 --- a/guest_list/src/auth/mod.rs +++ b/guest_list/src/auth/mod.rs @@ -38,6 +38,7 @@ async fn create_account( connection, configuration, client, + secrets, }): State, Form(request): Form, ) -> impl IntoResponse { @@ -76,6 +77,7 @@ async fn create_account( &client, attempt_id, configuration.server_url, + secrets.resend_auth_token, ) .await .unwrap(); @@ -177,6 +179,7 @@ async fn sign_in( connection, configuration, client, + secrets }): State, Form(request): Form, ) -> Redirect { @@ -221,6 +224,7 @@ async fn sign_in( &client, attempt_id, configuration.server_url, + secrets.resend_auth_token, ) .await .unwrap(); diff --git a/guest_list/src/email.rs b/guest_list/src/email.rs index 68278ef..aa3cb82 100644 --- a/guest_list/src/email.rs +++ b/guest_list/src/email.rs @@ -1,3 +1,4 @@ +use std::sync::Arc; use axum::http::{uri, Uri}; use reqwest::Client; use serde::Serialize; @@ -17,6 +18,7 @@ pub(crate) async fn send_sign_in_email( client: &Client, sign_in_attempt_id: String, server_url: Uri, + resend_auth_token: Arc, ) -> Result<(), reqwest::Error> { // Using resend.com let url = uri::Builder::from(server_url) @@ -31,10 +33,9 @@ pub(crate) async fn send_sign_in_email( html: format!("

Sign in to your account

", url).to_owned(), }; - let token = todo!("Set up Bitwarden secrets"); client .post("https://api.resend.com/emails") - .bearer_auth(token) + .bearer_auth(resend_auth_token) .json(&request) .send() .await?; diff --git a/guest_list/src/main.rs b/guest_list/src/main.rs index 62c50c1..ed2c5aa 100644 --- a/guest_list/src/main.rs +++ b/guest_list/src/main.rs @@ -13,6 +13,7 @@ use tower_http::services::ServeDir; use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::util::SubscriberInitExt; use crate::auth::USER_HOME_PAGE; +use crate::secrets::Secrets; mod auth; mod email; @@ -44,7 +45,7 @@ async fn main() -> Result<(), Error> { .with(tracing_subscriber::fmt::layer()) .init(); - secrets::get_secrets().await?; + let secrets = secrets::get_secrets().await?; // Set up database let connection = database::initialize_database().await; @@ -66,6 +67,7 @@ async fn main() -> Result<(), Error> { connection, client, configuration, + secrets, }; let auth_routes = auth::create_router(); @@ -98,18 +100,19 @@ pub(crate) struct Configuration { server_url: Uri, } + #[derive(Clone)] pub(crate) struct AppState { connection: Connection, client: reqwest::Client, configuration: Configuration, + secrets: Secrets, } #[derive(Template)] #[template(path = "apps.html")] -struct AppsTemplate { -} +struct AppsTemplate {} async fn get_apps_page( user: AuthenticatedUser, diff --git a/guest_list/src/secrets.rs b/guest_list/src/secrets.rs index e5e192d..8b2f4f6 100644 --- a/guest_list/src/secrets.rs +++ b/guest_list/src/secrets.rs @@ -1,49 +1,64 @@ // 🤫 +use std::sync::Arc; use bitwarden::auth::login::AccessTokenLoginRequest; use bitwarden::Client; -use bitwarden::secrets_manager::secrets::{SecretIdentifiersRequest, SecretIdentifiersResponse}; +use bitwarden::secrets_manager::secrets::{SecretGetRequest, SecretIdentifiersRequest, SecretIdentifiersResponse}; use dotenv::dotenv; use thiserror::Error; use uuid::{Uuid, uuid}; #[derive(Debug, Error)] pub(super)enum GetSecretsError { + #[cfg(debug_assertions)] #[error("Error loading dotenv file")] DotEnvError(#[from] dotenv::Error), - #[error("No BWS token in environment")] + #[error("No Bitwarden Secrets Manager token in environment")] NoBwsToken(std::env::VarError), - #[error("No BWS organization id in environment")] - NoBwsOrganizationId(std::env::VarError), - #[error("Error parsing BWS organization id. Is it a valid UUID?")] - InvalidBwsOrganizationId(#[from] uuid::Error), - #[error("Error getting secrets from BWS")] + #[error("Error getting secrets from Bitwarden Secrets Manager")] BwsError(#[from] bitwarden::error::Error), + #[error("No id for Resend API key found")] + NoResendSecretId(std::env::VarError), + #[error("Error parsing secret id. Is it a valid UUID?")] + InvalidSecretId(#[from] uuid::Error), + +} + + +#[derive(Clone)] +pub(crate) struct Secrets { + pub(crate) resend_auth_token: Arc, } -pub(super) async fn get_secrets() -> Result { +pub(super) async fn get_secrets() -> Result { // Use default settings let mut client = Client::new(None); // Set up machine account token #[cfg(debug_assertions)] { + // Use .env files only for debug convenience let result = dotenv(); - if result.is_err() { - tracing::warn!("No dotenv loaded in debug mode"); + if let Err(error) = result { + tracing::warn!("No dotenv loaded in debug mode: {}", error); } - } let token = std::env::var("BWS_TOKEN").map_err(GetSecretsError::NoBwsToken)?; let token = AccessTokenLoginRequest { access_token: token, state_file: None }; client.auth().login_access_token(&token).await?; - let organization_id = std::env::var("BWS_ORGANIZATION_ID").map_err(GetSecretsError::NoBwsOrganizationId)?.parse::()?; - let identifier = SecretIdentifiersRequest { - organization_id, + // Ids are not a secret but should still be avoided to be shared where possible (obfuscation) + let resend_secret_id = std::env::var("BWS_RESEND_SECRET_ID").map_err(GetSecretsError::NoResendSecretId)?.parse::()?; + let request = SecretGetRequest{ + id: resend_secret_id, + }; + + let secret = client.secrets().get(&request).await.unwrap(); + + let secrets = Secrets { + resend_auth_token: secret.value.into(), }; - let secrets = client.secrets().list(&identifier).await?; Ok(secrets) }