Skip to main content

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}