baseid_oid4vci/
credential_offer.rs

1//! Credential offer types.
2
3use serde::{Deserialize, Serialize};
4
5/// A credential offer from an issuer to a holder.
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct CredentialOffer {
8    /// The credential issuer identifier.
9    pub credential_issuer: String,
10    /// Credential configuration IDs being offered.
11    pub credential_configuration_ids: Vec<String>,
12    /// Optional grants (authorization code or pre-authorized code).
13    #[serde(skip_serializing_if = "Option::is_none")]
14    pub grants: Option<Grants>,
15}
16
17/// Grant types for credential issuance.
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct Grants {
20    /// Authorization code grant.
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub authorization_code: Option<AuthorizationCodeGrant>,
23    /// Pre-authorized code grant.
24    #[serde(
25        rename = "urn:ietf:params:oauth:grant-type:pre-authorized_code",
26        skip_serializing_if = "Option::is_none"
27    )]
28    pub pre_authorized_code: Option<PreAuthorizedCodeGrant>,
29}
30
31/// Authorization code grant parameters.
32#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct AuthorizationCodeGrant {
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub issuer_state: Option<String>,
36}
37
38/// Pre-authorized code grant parameters.
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct PreAuthorizedCodeGrant {
41    #[serde(rename = "pre-authorized_code")]
42    pub pre_authorized_code: String,
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub tx_code: Option<serde_json::Value>,
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50
51    #[test]
52    fn spec_vector_offer_pre_auth() {
53        let offer: CredentialOffer =
54            serde_json::from_str(baseid_test_vectors::oid4vci::CREDENTIAL_OFFER_PRE_AUTH).unwrap();
55        assert_eq!(
56            offer.credential_issuer,
57            "https://credential-issuer.example.com"
58        );
59        assert_eq!(offer.credential_configuration_ids.len(), 2);
60        assert_eq!(
61            offer.credential_configuration_ids[1],
62            "org.iso.18013.5.1.mDL"
63        );
64
65        let grants = offer.grants.unwrap();
66        let pre_auth = grants.pre_authorized_code.unwrap();
67        assert_eq!(pre_auth.pre_authorized_code, "oaKazRN8I0IbtZ0C7JuMn5");
68        let tx = pre_auth.tx_code.unwrap();
69        assert_eq!(tx["length"], 4);
70        assert_eq!(tx["input_mode"], "numeric");
71    }
72
73    #[test]
74    fn spec_vector_offer_auth_code() {
75        let offer: CredentialOffer =
76            serde_json::from_str(baseid_test_vectors::oid4vci::CREDENTIAL_OFFER_AUTH_CODE).unwrap();
77        let grants = offer.grants.unwrap();
78        let auth = grants.authorization_code.unwrap();
79        assert!(auth.issuer_state.is_some());
80        assert!(grants.pre_authorized_code.is_none());
81    }
82
83    #[test]
84    fn spec_vector_offer_empty_tx_code() {
85        let offer: CredentialOffer =
86            serde_json::from_str(baseid_test_vectors::oid4vci::CREDENTIAL_OFFER_EMPTY_TX_CODE)
87                .unwrap();
88        let pre_auth = offer.grants.unwrap().pre_authorized_code.unwrap();
89        assert_eq!(pre_auth.pre_authorized_code, "adhjhdjajkdkhjhdj");
90        // Empty object tx_code should parse
91        assert!(pre_auth.tx_code.is_some());
92    }
93}