kych

OAuth 2.0 API for Swiyu to enable Taler integration of Swiyu for KYC (experimental)
Log | Files | Refs | Submodules | README | LICENSE

models.rs (8125B)


      1 use serde::{Deserialize, Serialize};
      2 use uuid::Uuid;
      3 use axum::{
      4     response::{IntoResponse, Response},
      5     http::{StatusCode, header},
      6 };
      7 
      8 pub struct PrettyJson<T>(pub T);
      9 
     10 impl<T> IntoResponse for PrettyJson<T>
     11 where
     12     T: Serialize,
     13 {
     14     fn into_response(self) -> Response {
     15         match serde_json::to_string_pretty(&self.0) {
     16             Ok(json) => (
     17                 StatusCode::OK,
     18                 [(header::CONTENT_TYPE, "application/json")],
     19                 json,
     20             ).into_response(),
     21             Err(e) => (
     22                 StatusCode::INTERNAL_SERVER_ERROR,
     23                 format!("Serialization error: {}", e),
     24             ).into_response(),
     25         }
     26     }
     27 }
     28 
     29 #[derive(Debug, Deserialize, Serialize)]
     30 pub struct SetupResponse {
     31     pub nonce: String,
     32 }
     33 
     34 #[derive(Debug, Deserialize)]
     35 pub struct AuthorizeQuery {
     36     pub response_type: String,
     37     pub client_id: String,
     38     pub redirect_uri: String,
     39     pub state: String,
     40     pub scope: String,
     41 }
     42 
     43 #[derive(Debug, Deserialize, Serialize)]
     44 pub struct AuthorizeResponse {
     45     #[serde(rename = "verificationId")]
     46     pub verification_id: Uuid,
     47     pub verification_url: String,
     48     #[serde(skip_serializing_if = "Option::is_none")]
     49     pub verification_deeplink: Option<String>,
     50     pub state: String,
     51 }
     52 
     53 #[derive(Debug, Deserialize, Serialize)]
     54 pub struct TokenRequest {
     55     pub grant_type: String,
     56     pub code: String,
     57     pub client_id: String,
     58     pub client_secret: String,
     59     pub redirect_uri: String,
     60 }
     61 
     62 #[derive(Debug, Deserialize, Serialize)]
     63 pub struct TokenResponse {
     64     pub access_token: String,
     65     pub token_type: String,
     66     pub expires_in: u64,
     67 }
     68 
     69 // Info endpoint
     70 #[derive(Debug, Deserialize, Serialize, Clone)]
     71 pub struct VerifiableCredential {
     72     #[serde(flatten)]
     73     pub data: serde_json::Value,
     74 }
     75 
     76 // Notification webhook from Swiyu Verifier
     77 #[derive(Debug, Deserialize, Serialize)]
     78 pub struct NotificationRequest {
     79     pub verification_id: Uuid,
     80     pub timestamp: String,
     81 }
     82 
     83 // Status endpoint
     84 #[derive(Debug, Deserialize)]
     85 pub struct StatusQuery {
     86     pub state: String,
     87 }
     88 
     89 #[derive(Debug, Serialize)]
     90 pub struct StatusResponse {
     91     pub status: String,
     92 }
     93 
     94 #[derive(Debug, Serialize)]
     95 pub struct ConfigResponse {
     96     pub name: String,
     97     pub version: String,
     98     pub status: String,
     99     pub vc_type: String,
    100     pub vc_format: String,
    101     pub vc_algorithms: Vec<String>,
    102     pub vc_claims: Vec<String>,
    103 }
    104 
    105 // Notification payload sent to Client (Exchange, etc.)
    106 #[derive(Debug, Serialize)]
    107 pub struct ClientNotification {
    108     pub nonce: String,
    109     pub status: String,
    110     pub code: String,
    111     pub verification_id: Uuid,
    112     pub timestamp: String,
    113 }
    114 
    115 // Error response
    116 #[derive(Debug, Serialize, Deserialize)]
    117 pub struct ErrorResponse {
    118     pub error: String,
    119 }
    120 
    121 impl ErrorResponse {
    122     pub fn new(error: impl Into<String>) -> Self {
    123         Self {
    124             error: error.into(),
    125         }
    126     }
    127 }
    128 
    129 // Swiyu Verifier API models
    130 
    131 /// Request body for creating a verification with Swiyu Verifier
    132 /// POST /management/api/verifications
    133 ///
    134 /// The verifier dropped DIF Presentation Exchange with its 3.0.0 release
    135 /// ("Dropped support for SWIYU-API-Version 1"); `dcql_query` is the only way
    136 /// to state what is to be presented and is a required field.  Trust anchors
    137 /// moved out of this request and into the verifier's own configuration
    138 /// (`SWIYU_TRUST_ISSUER_DID`) in 5.0.0, and `response_type` is no longer part
    139 /// of the schema, so neither is sent any more.
    140 #[derive(Debug, Serialize, Deserialize)]
    141 pub struct SwiyuCreateVerificationRequest {
    142     pub accepted_issuer_dids: Vec<String>,
    143 
    144     /// If omitted, Swiyu defaults to true (beta requires true)
    145     #[serde(skip_serializing_if = "Option::is_none")]
    146     pub jwt_secured_authorization_request: Option<bool>,
    147 
    148     /// Response mode: how the wallet sends the response back
    149     /// REQUIRED - will throw NullPointerException if omitted
    150     pub response_mode: ResponseMode,
    151 
    152     /// What the wallet is asked to present.  REQUIRED by the verifier.
    153     pub dcql_query: DcqlQuery,
    154 
    155     pub configuration_override: ConfigurationOverride,
    156 }
    157 
    158 /// Response mode type
    159 #[derive(Debug, Serialize, Deserialize, Clone)]
    160 #[serde(rename_all = "snake_case")]
    161 pub enum ResponseMode {
    162     /// Wallet sends a clear text response
    163     DirectPost,
    164 
    165     /// Wallet sends an encrypted response
    166     #[serde(rename = "direct_post.jwt")]
    167     DirectPostJwt,
    168 }
    169 
    170 /// Configuration override for a specific verification
    171 /// Can be empty object with all fields set to None
    172 #[derive(Debug, Serialize, Deserialize, Clone, Default)]
    173 pub struct ConfigurationOverride {
    174     /// Override for the EXTERNAL_URL - the url the wallet should call
    175     #[serde(skip_serializing_if = "Option::is_none")]
    176     pub external_url: Option<String>,
    177 
    178     #[serde(skip_serializing_if = "Option::is_none")]
    179     pub verifier_did: Option<String>,
    180 
    181     #[serde(skip_serializing_if = "Option::is_none")]
    182     pub verification_method: Option<String>,
    183 
    184     /// ID of the key in the HSM
    185     #[serde(skip_serializing_if = "Option::is_none")]
    186     pub key_id: Option<String>,
    187 
    188     /// The pin which protects the key in the HSM
    189     #[serde(skip_serializing_if = "Option::is_none")]
    190     pub key_pin: Option<String>,
    191 }
    192 
    193 /// Identifier of the one credential query KyCH sends, and therefore the key
    194 /// under which the verifier reports the disclosed claims back in
    195 /// `wallet_response.dcql_response`.  Must match `^[a-zA-Z0-9_-]+$`.
    196 pub const DCQL_CREDENTIAL_ID: &str = "kych_verification";
    197 
    198 /// A DCQL query as defined in OpenID4VP 1.0 section 6.
    199 #[derive(Debug, Serialize, Deserialize, Clone)]
    200 pub struct DcqlQuery {
    201     pub credentials: Vec<DcqlCredential>,
    202 }
    203 
    204 /// One credential query (OpenID4VP 1.0 section 6.1).
    205 #[derive(Debug, Serialize, Deserialize, Clone)]
    206 pub struct DcqlCredential {
    207     /// Identifies this credential in the response; see `DCQL_CREDENTIAL_ID`.
    208     pub id: String,
    209 
    210     /// Credential format identifier; the verifier accepts `dc+sd-jwt` (the
    211     /// canonical SD-JWT VC type) and still `vc+sd-jwt`.
    212     pub format: String,
    213 
    214     pub meta: DcqlCredentialMeta,
    215 
    216     /// The claims to disclose.  Omitted rather than sent empty, because the
    217     /// verifier rejects an empty `claims` array.
    218     #[serde(skip_serializing_if = "Option::is_none")]
    219     pub claims: Option<Vec<DcqlClaim>>,
    220 
    221     /// KyCH always requires holder binding: the credential must be presented
    222     /// by the wallet that holds its key, not merely be known to it.
    223     pub require_cryptographic_holder_binding: bool,
    224 }
    225 
    226 /// Metadata constraints of a credential query; for SD-JWT VCs the accepted
    227 /// values of the `vct` claim.
    228 #[derive(Debug, Serialize, Deserialize, Clone)]
    229 pub struct DcqlCredentialMeta {
    230     pub vct_values: Vec<String>,
    231 }
    232 
    233 /// One claims query (OpenID4VP 1.0 section 6.3).  `path` is a claims path
    234 /// pointer, so a top-level claim is a single-element path.
    235 #[derive(Debug, Serialize, Deserialize, Clone)]
    236 pub struct DcqlClaim {
    237     pub path: Vec<String>,
    238 }
    239 
    240 /// Response from Swiyu Verifier Management API
    241 /// Used for both POST /management/api/verifications and GET /management/api/verifications/{id}
    242 #[derive(Debug, Serialize, Deserialize)]
    243 pub struct SwiyuManagementResponse {
    244     pub id: Uuid,
    245     #[serde(skip_serializing_if = "Option::is_none")]
    246     pub request_nonce: Option<String>,
    247     pub state: SwiyuVerificationStatus,
    248     pub verification_url: String,
    249     #[serde(skip_serializing_if = "Option::is_none")]
    250     pub verification_deeplink: Option<String>,
    251     #[serde(skip_serializing_if = "Option::is_none")]
    252     pub dcql_query: Option<serde_json::Value>,
    253     #[serde(skip_serializing_if = "Option::is_none")]
    254     pub wallet_response: Option<serde_json::Value>,
    255     /// Per-credential trust and status results.  Only present when the
    256     /// verifier runs with `additional-audit-information.credential-evaluation-enabled`.
    257     #[serde(skip_serializing_if = "Option::is_none")]
    258     pub credential_evaluation: Option<serde_json::Value>,
    259 }
    260 
    261 #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
    262 #[serde(rename_all = "UPPERCASE")]
    263 pub enum SwiyuVerificationStatus {
    264     Pending,
    265     Success,
    266     Failed,
    267 }