use std::sync::Arc;
use affinidi_did_resolver_cache_sdk::{DIDCacheClient, config::DIDCacheConfigBuilder};
use affinidi_tdk::secrets_resolver::secrets::Secret;
use affinidi_tdk::secrets_resolver::{SecretsResolver, ThreadedSecretsResolver};
use tracing::{debug, info, warn};
use super::auth::jwt::JwtKeys;
use super::error::AppError;
use super::secret_store::ServerSecrets;
pub fn decode_multibase_ed25519_key(multibase_key: &str) -> Result<[u8; 32], AppError> {
let secret = Secret::from_multibase(multibase_key, None)
.map_err(|e| AppError::Config(format!("invalid multibase key: {e}")))?;
let bytes = secret.get_private_bytes();
bytes
.try_into()
.map_err(|_| AppError::Config("key must be exactly 32 bytes".into()))
}
pub fn init_jwt_keys(secrets: &ServerSecrets) -> Option<Arc<JwtKeys>> {
match decode_multibase_ed25519_key(&secrets.jwt_signing_key) {
Ok(key_bytes) => match JwtKeys::from_ed25519_bytes(&key_bytes) {
Ok(keys) => {
debug!("JWT signing key loaded");
Some(Arc::new(keys))
}
Err(e) => {
warn!("failed to construct JWT keys: {e} — auth endpoints will not work");
None
}
},
Err(e) => {
warn!("failed to load JWT signing key: {e} — auth endpoints will not work");
None
}
}
}
pub async fn init_didcomm_auth(
server_did: Option<&str>,
secrets: &ServerSecrets,
) -> (Option<DIDCacheClient>, Option<Arc<ThreadedSecretsResolver>>) {
let server_did = match server_did {
Some(did) => did,
None => {
warn!("server_did not configured — DIDComm auth endpoints will not work");
return (None, None);
}
};
let did_resolver = match DIDCacheClient::new(DIDCacheConfigBuilder::default().build()).await {
Ok(r) => r,
Err(e) => {
warn!("failed to create DID resolver: {e} — DIDComm auth endpoints will not work");
return (None, None);
}
};
let (secrets_resolver, _handle) = ThreadedSecretsResolver::new(None).await;
let kid = format!("{server_did}#key-0");
match Secret::from_multibase(&secrets.signing_key, Some(&kid)) {
Ok(secret) => {
secrets_resolver.insert(secret).await;
debug!(kid = %kid, "server signing secret loaded");
}
Err(e) => warn!("failed to decode signing_key: {e}"),
}
let kid = format!("{server_did}#key-1");
match Secret::from_multibase(&secrets.key_agreement_key, Some(&kid)) {
Ok(secret) => {
secrets_resolver.insert(secret).await;
debug!(kid = %kid, "server key-agreement secret loaded");
}
Err(e) => warn!("failed to decode key_agreement_key: {e}"),
}
info!("DIDComm auth initialized for DID {server_did}");
(Some(did_resolver), Some(Arc::new(secrets_resolver)))
}
pub async fn shutdown_signal() {
let ctrl_c = async {
tokio::signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
};
#[cfg(unix)]
let terminate = async {
tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
.expect("failed to install SIGTERM handler")
.recv()
.await;
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
() = ctrl_c => info!("received SIGINT"),
() = terminate => info!("received SIGTERM"),
}
}