kych

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

models.rs (8341B)


      1 use serde::{Deserialize, Serialize};
      2 use uuid::Uuid;
      3 use std::collections::HashMap;
      4 use axum::{
      5     response::{IntoResponse, Response},
      6     http::{StatusCode, header},
      7 };
      8 
      9 pub struct PrettyJson<T>(pub T);
     10 
     11 impl<T> IntoResponse for PrettyJson<T>
     12 where
     13     T: Serialize,
     14 {
     15     fn into_response(self) -> Response {
     16         match serde_json::to_string_pretty(&self.0) {
     17             Ok(json) => (
     18                 StatusCode::OK,
     19                 [(header::CONTENT_TYPE, "application/json")],
     20                 json,
     21             ).into_response(),
     22             Err(e) => (
     23                 StatusCode::INTERNAL_SERVER_ERROR,
     24                 format!("Serialization error: {}", e),
     25             ).into_response(),
     26         }
     27     }
     28 }
     29 
     30 #[derive(Debug, Deserialize, Serialize)]
     31 pub struct SetupResponse {
     32     pub nonce: String,
     33 }
     34 
     35 #[derive(Debug, Deserialize)]
     36 pub struct AuthorizeQuery {
     37     pub response_type: String,
     38     pub client_id: String,
     39     pub redirect_uri: String,
     40     pub state: String,
     41     pub scope: String,
     42 }
     43 
     44 #[derive(Debug, Deserialize, Serialize)]
     45 pub struct AuthorizeResponse {
     46     #[serde(rename = "verificationId")]
     47     pub verification_id: Uuid,
     48     pub verification_url: String,
     49     #[serde(skip_serializing_if = "Option::is_none")]
     50     pub verification_deeplink: Option<String>,
     51     pub state: String,
     52 }
     53 
     54 // Token endpoint
     55 // WARING: RFC 6749 also requires:
     56 // - redirect_uri
     57 #[derive(Debug, Deserialize, Serialize)]
     58 pub struct TokenRequest {
     59     pub grant_type: String,
     60     pub code: String,
     61     pub client_id: String,
     62     pub client_secret: String,
     63 }
     64 
     65 #[derive(Debug, Deserialize, Serialize)]
     66 pub struct TokenResponse {
     67     pub access_token: String,
     68     pub token_type: String,
     69     pub expires_in: u64,
     70 }
     71 
     72 // Info endpoint
     73 #[derive(Debug, Deserialize, Serialize, Clone)]
     74 pub struct VerifiableCredential {
     75     #[serde(flatten)]
     76     pub data: serde_json::Value,
     77 }
     78 
     79 // Notification webhook from Swiyu Verifier
     80 #[derive(Debug, Deserialize, Serialize)]
     81 pub struct NotificationRequest {
     82     pub verification_id: Uuid,
     83     pub timestamp: String,
     84 }
     85 
     86 // Notification payload sent to Client (Exchange, etc.)
     87 #[derive(Debug, Serialize)]
     88 pub struct ClientNotification {
     89     pub nonce: String,
     90     pub status: String,
     91     pub code: String,
     92     pub verification_id: Uuid,
     93     pub timestamp: String,
     94 }
     95 
     96 // Error response
     97 #[derive(Debug, Serialize, Deserialize)]
     98 pub struct ErrorResponse {
     99     pub error: String,
    100 }
    101 
    102 impl ErrorResponse {
    103     pub fn new(error: impl Into<String>) -> Self {
    104         Self {
    105             error: error.into(),
    106         }
    107     }
    108 }
    109 
    110 // Swiyu Verifier API models
    111 
    112 /// Default issuer DID for Swiyu verification
    113 pub fn default_accepted_issuer_dids() -> Vec<String> {
    114     vec!["did:tdw:QmPEZPhDFR4nEYSFK5bMnvECqdpf1tPTPJuWs9QrMjCumw:identifier-reg.trust-infra.swiyu-int.admin.ch:api:v1:did:9a5559f0-b81c-4368-a170-e7b4ae424527".to_string()]
    115 }
    116 
    117 /// Request body for creating a verification with Swiyu Verifier
    118 /// POST /management/api/verifications
    119 #[derive(Debug, Serialize, Deserialize)]
    120 pub struct SwiyuCreateVerificationRequest {
    121     #[serde(default = "default_accepted_issuer_dids")]
    122     pub accepted_issuer_dids: Vec<String>,
    123     #[serde(skip_serializing_if = "Option::is_none")]
    124     pub trust_anchors: Option<Vec<TrustAnchor>>,
    125 
    126     /// If omitted, Swiyu defaults to true (beta requires true)
    127     #[serde(skip_serializing_if = "Option::is_none")]
    128     pub jwt_secured_authorization_request: Option<bool>,
    129 
    130     /// Response mode: how the wallet sends the response back
    131     /// REQUIRED - will throw NullPointerException if omitted
    132     pub response_mode: ResponseMode,
    133 
    134     /// Response type - must be "vp_token" for VP requests
    135     pub response_type: String,
    136 
    137     pub presentation_definition: PresentationDefinition,
    138 
    139     pub configuration_override: ConfigurationOverride,
    140 
    141     /// Optional - (wallet migration in progress)
    142     #[serde(skip_serializing_if = "Option::is_none")]
    143     pub dcql_query: Option<serde_json::Value>,
    144 }
    145 
    146 /// Trust anchor for credential verification
    147 #[derive(Debug, Serialize, Deserialize, Clone)]
    148 pub struct TrustAnchor {
    149     pub did: String,
    150     pub trust_registry_uri: String,
    151 }
    152 
    153 /// Response mode type
    154 #[derive(Debug, Serialize, Deserialize, Clone)]
    155 #[serde(rename_all = "snake_case")]
    156 pub enum ResponseMode {
    157     /// Wallet sends a clear text response
    158     DirectPost,
    159 
    160     /// Wallet sends an encrypted response
    161     #[serde(rename = "direct_post.jwt")]
    162     DirectPostJwt,
    163 }
    164 
    165 /// Configuration override for a specific verification
    166 /// Can be empty object with all fields set to None
    167 #[derive(Debug, Serialize, Deserialize, Clone, Default)]
    168 pub struct ConfigurationOverride {
    169     /// Override for the EXTERNAL_URL - the url the wallet should call
    170     #[serde(skip_serializing_if = "Option::is_none")]
    171     pub external_url: Option<String>,
    172 
    173     #[serde(skip_serializing_if = "Option::is_none")]
    174     pub verifier_did: Option<String>,
    175 
    176     #[serde(skip_serializing_if = "Option::is_none")]
    177     pub verification_method: Option<String>,
    178 
    179     /// ID of the key in the HSM
    180     #[serde(skip_serializing_if = "Option::is_none")]
    181     pub key_id: Option<String>,
    182 
    183     /// The pin which protects the key in the HSM
    184     #[serde(skip_serializing_if = "Option::is_none")]
    185     pub key_pin: Option<String>,
    186 }
    187 
    188 /// Presentation Definition according to DIF Presentation Exchange
    189 #[derive(Debug, Serialize, Deserialize, Clone)]
    190 pub struct PresentationDefinition {
    191     pub id: String,
    192     #[serde(skip_serializing_if = "Option::is_none")]
    193     pub name: Option<String>,
    194     #[serde(skip_serializing_if = "Option::is_none")]
    195     pub purpose: Option<String>,
    196     #[serde(skip_serializing_if = "Option::is_none")]
    197     pub format: Option<HashMap<String, FormatAlgorithm>>,
    198     pub input_descriptors: Vec<InputDescriptor>,
    199 }
    200 
    201 /// Input descriptor describing required credential attributes
    202 #[derive(Debug, Serialize, Deserialize, Clone)]
    203 pub struct InputDescriptor {
    204     pub id: String,
    205     #[serde(skip_serializing_if = "Option::is_none")]
    206     pub name: Option<String>,
    207     #[serde(skip_serializing_if = "Option::is_none")]
    208     pub purpose: Option<String>,
    209     #[serde(skip_serializing_if = "Option::is_none")]
    210     pub format: Option<HashMap<String, FormatAlgorithm>>,
    211     pub constraints: Constraint,
    212 }
    213 
    214 /// Constraints on credential fields
    215 #[derive(Debug, Serialize, Deserialize, Clone)]
    216 pub struct Constraint {
    217     pub fields: Vec<Field>,
    218 }
    219 
    220 /// Field specification with JSONPath
    221 #[derive(Debug, Serialize, Deserialize, Clone)]
    222 pub struct Field {
    223     pub path: Vec<String>,
    224     #[serde(skip_serializing_if = "Option::is_none")]
    225     pub id: Option<String>,
    226     #[serde(skip_serializing_if = "Option::is_none")]
    227     pub name: Option<String>,
    228     #[serde(skip_serializing_if = "Option::is_none")]
    229     pub purpose: Option<String>,
    230     #[serde(skip_serializing_if = "Option::is_none")]
    231     pub filter: Option<Filter>,
    232 }
    233 
    234 /// Filter for field constraints
    235 #[derive(Debug, Serialize, Deserialize, Clone)]
    236 pub struct Filter {
    237     #[serde(rename = "type")]
    238     pub filter_type: String,
    239     #[serde(rename = "const", skip_serializing_if = "Option::is_none")]
    240     pub const_value: Option<String>,
    241 }
    242 
    243 /// Format algorithm specification for SD-JWT
    244 #[derive(Debug, Serialize, Deserialize, Clone)]
    245 pub struct FormatAlgorithm {
    246     #[serde(rename = "sd-jwt_alg_values")]
    247     pub sd_jwt_alg_values: Vec<String>,
    248     #[serde(rename = "kb-jwt_alg_values")]
    249     pub kb_jwt_alg_values: Vec<String>,
    250 }
    251 
    252 /// Response from Swiyu Verifier Management API
    253 /// Used for both POST /management/api/verifications and GET /management/api/verifications/{id}
    254 #[derive(Debug, Serialize, Deserialize)]
    255 pub struct SwiyuManagementResponse {
    256     pub id: Uuid,
    257     #[serde(skip_serializing_if = "Option::is_none")]
    258     pub request_nonce: Option<String>,
    259     pub state: SwiyuVerificationStatus,
    260     pub verification_url: String,
    261     #[serde(skip_serializing_if = "Option::is_none")]
    262     pub verification_deeplink: Option<String>,
    263     pub presentation_definition: PresentationDefinition,
    264     #[serde(skip_serializing_if = "Option::is_none")]
    265     pub dcql_query: Option<serde_json::Value>,
    266     #[serde(skip_serializing_if = "Option::is_none")]
    267     pub wallet_response: Option<serde_json::Value>,
    268 }
    269 
    270 #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
    271 #[serde(rename_all = "UPPERCASE")]
    272 pub enum SwiyuVerificationStatus {
    273     Pending,
    274     Success,
    275     Failed,
    276 }