dfns_sdk_rust/signer.rs
1//! User-action signing.
2//!
3//! Write operations on the Dfns API require a *user action signature*: the client
4//! requests a challenge, signs it with the caller's credential, and replays the
5//! assertion on the real request. The signing keys never leave the caller.
6
7use async_trait::async_trait;
8use serde::{Deserialize, Serialize};
9
10use crate::error::Error;
11
12/// The challenge returned by `POST /auth/action/init`, to be signed by the credential.
13#[derive(Debug, Clone, Deserialize)]
14pub struct UserActionChallenge {
15 #[serde(rename = "challenge")]
16 pub challenge: String,
17 #[serde(rename = "challengeIdentifier")]
18 pub challenge_identifier: String,
19}
20
21/// The credential-specific payload of a signed assertion.
22#[derive(Debug, Clone, Serialize)]
23pub struct CredentialAssertionData {
24 #[serde(rename = "credId")]
25 pub cred_id: String,
26 #[serde(rename = "clientData")]
27 pub client_data: String,
28 #[serde(rename = "signature")]
29 pub signature: String,
30}
31
32/// A signed challenge assertion, sent as the `firstFactor` of `POST /auth/action`.
33#[derive(Debug, Clone, Serialize)]
34pub struct CredentialAssertion {
35 #[serde(rename = "kind")]
36 pub kind: String,
37 #[serde(rename = "credentialAssertion")]
38 pub credential_assertion: CredentialAssertionData,
39}
40
41/// Implemented by credential backends (WebAuthn, raw key, KMS, ...).
42///
43/// The signer only signs the challenge; the transport owns the init/complete dance and
44/// turns the assertion into the `X-DFNS-USERACTION` token. This trait is intentionally
45/// minimal and hand-maintained: real implementations handle the credential-specific crypto
46/// and live outside the generated code.
47#[async_trait]
48pub trait UserActionSigner: Send + Sync {
49 async fn sign(&self, challenge: &UserActionChallenge) -> Result<CredentialAssertion, Error>;
50}