use super::RpcHandler; use crate::credentials; use crate::identity_manager::IdentityManager; use anyhow::Result; impl RpcHandler { /// Issue a Verifiable Credential from one of the user's identities. pub(super) async fn handle_identity_issue_credential( &self, params: Option, ) -> Result { let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?; let issuer_id = params .get("issuer_id") .and_then(|v| v.as_str()) .ok_or_else(|| anyhow::anyhow!("Missing issuer_id"))?; let subject_did = params .get("subject_did") .and_then(|v| v.as_str()) .ok_or_else(|| anyhow::anyhow!("Missing subject_did"))?; let credential_type = params .get("type") .and_then(|v| v.as_str()) .unwrap_or("VerifiableCredential"); let claims = params .get("claims") .cloned() .unwrap_or(serde_json::json!({})); let expires_at = params.get("expires_at").and_then(|v| v.as_str()); let manager = IdentityManager::new(&self.config.data_dir).await?; let issuer_record = manager.get(issuer_id).await?; let issuer_did = issuer_record.did.clone(); // Capture identity_id for the signing closure let data_dir = self.config.data_dir.clone(); let sign_id = issuer_id.to_string(); let vc = credentials::issue_credential( &self.config.data_dir, &issuer_did, subject_did, credential_type, claims, expires_at, |bytes| { // Use block_in_place to avoid deadlocking the tokio runtime let hex_msg = hex::encode(bytes); tokio::task::block_in_place(|| { let rt = tokio::runtime::Handle::current(); rt.block_on(async { let mgr = IdentityManager::new(&data_dir).await?; mgr.sign(&sign_id, hex_msg.as_bytes()).await }) }) }, ) .await?; Ok(serde_json::json!({ "id": vc.id, "issuer": vc.issuer, "subject": vc.subject, "type": vc.credential_type, "issued_at": vc.issued_at, "status": vc.status, })) } /// Verify a credential by its ID. pub(super) async fn handle_identity_verify_credential( &self, params: Option, ) -> Result { let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?; let credential_id = params .get("id") .and_then(|v| v.as_str()) .ok_or_else(|| anyhow::anyhow!("Missing id"))?; let store = credentials::load_credentials(&self.config.data_dir).await?; let vc = store .credentials .iter() .find(|c| c.id == credential_id) .ok_or_else(|| anyhow::anyhow!("Credential not found"))?; let data_dir = self.config.data_dir.clone(); let valid = credentials::verify_credential(vc, |did, bytes, signature| { let hex_msg = hex::encode(bytes); tokio::task::block_in_place(|| { let rt = tokio::runtime::Handle::current(); rt.block_on(async { let mgr = IdentityManager::new(&data_dir).await?; mgr.verify(did, hex_msg.as_bytes(), signature).await }) }) })?; Ok(serde_json::json!({ "id": vc.id, "valid": valid, "status": vc.status, })) } /// List all credentials, optionally filtered by DID. pub(super) async fn handle_identity_list_credentials( &self, params: Option, ) -> Result { let filter_did = params .as_ref() .and_then(|p| p.get("did")) .and_then(|v| v.as_str()); let creds = credentials::list_credentials(&self.config.data_dir, filter_did).await?; let items: Vec = creds .into_iter() .map(|c| { serde_json::json!({ "id": c.id, "issuer": c.issuer, "subject": c.subject, "type": c.credential_type, "claims": c.claims, "issued_at": c.issued_at, "expires_at": c.expires_at, "status": c.status, }) }) .collect(); Ok(serde_json::json!({ "credentials": items })) } /// Revoke a credential. pub(super) async fn handle_identity_revoke_credential( &self, params: Option, ) -> Result { let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?; let id = params .get("id") .and_then(|v| v.as_str()) .ok_or_else(|| anyhow::anyhow!("Missing id"))?; credentials::revoke_credential(&self.config.data_dir, id).await?; Ok(serde_json::json!({ "ok": true })) } }