credential_exchange_format/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use serde::{Deserialize, Serialize};
4
5mod b64url;
6mod credential_scope;
7mod document;
8mod editable_field;
9mod extensions;
10mod identity;
11mod login;
12mod passkey;
13
14pub use self::{
15    b64url::*, credential_scope::*, document::*, editable_field::*, extensions::*, identity::*,
16    login::*, passkey::*,
17};
18
19type Uri = String;
20
21#[derive(Clone, Debug, Serialize, Deserialize)]
22#[serde(rename_all = "camelCase", bound(deserialize = "E: Deserialize<'de>"))]
23pub struct Header<E = ()> {
24    /// The version of the format definition contained within this exchange payload. The version
25    /// MUST correspond to a published level of the CXF standard.
26    pub version: Version,
27    /// The name of the exporting app as a [relying party identifier](https://www.w3.org/TR/webauthn-3/#relying-party-identifier).
28    pub exporter_rp_id: String,
29    /// The display name of the exporting app to be presented to the user.
30    pub exporter_display_name: String,
31    /// The UNIX timestamp during at which the export document was completed.
32    pub timestamp: u64,
33    /// The list of [Account]s being exported.
34    pub accounts: Vec<Account<E>>,
35}
36
37#[derive(Clone, Debug, Serialize, Deserialize)]
38pub struct Version {
39    /// The major version of the payload's format. Changes to this version indicates an
40    /// incompatible breaking change with previous versions.
41    pub major: u8,
42    /// The minor version of the payload's format. Changes to this version indicates new
43    /// functionality which is purely additive and that is compatible with previous versions under
44    /// the same [Version::major].
45    pub minor: u8,
46}
47
48#[derive(Clone, Debug, Serialize, Deserialize)]
49#[serde(rename_all = "camelCase", bound(deserialize = "E: Deserialize<'de>"))]
50pub struct Account<E = ()> {
51    /// A unique identifier for the [Account] which is machine generated and an opaque byte
52    /// sequence with a maximum size of 64 bytes. It SHOULD NOT to be displayed to the user.
53    pub id: B64Url,
54    /// A pseudonym defined by the user to name their account. If none is set, this should be an
55    /// empty string.
56    pub username: String,
57    /// The email used to register the account in the previous provider.
58    pub email: String,
59    /// This field holds the user’s full name.
60    #[serde(default, skip_serializing_if = "Option::is_none")]
61    pub full_name: Option<String>,
62    /// All the collections this account owns. If the user has collections that were shared with
63    /// them by another account, it MUST NOT be present in this list.
64    pub collections: Vec<Collection<E>>,
65    /// All items that this account owns. If the user has access to items that were shared with
66    /// them by another account, it MUST NOT be present in this list.
67    pub items: Vec<Item<E>>,
68    /// This field contains all the extensions to the [Account]’s attributes.
69    #[serde(default, skip_serializing_if = "Option::is_none")]
70    pub extensions: Option<Vec<Extension<E>>>, // default []
71}
72
73#[derive(Clone, Debug, Serialize, Deserialize)]
74#[serde(rename_all = "camelCase", bound(deserialize = "E: Deserialize<'de>"))]
75pub struct Collection<E = ()> {
76    /// A unique identifier for the [Collection] which is machine generated and an opaque byte
77    /// sequence with a maximum size of 64 bytes. It SHOULD NOT be displayed to the user.
78    pub id: B64Url,
79    /// This member contains the UNIX timestamp in seconds at which this [Collection] was
80    /// originally created. If this member is not set, but the importing provider requires this
81    /// member in their proprietary data model, the importer SHOULD use the current timestamp at
82    /// the time the provider encounters this 8Collection].
83    #[serde(default, skip_serializing_if = "Option::is_none")]
84    pub creation_at: Option<u64>,
85    /// This member contains the UNIX timestamp in seconds of the last modification brought to this
86    /// [Collection]. If this member is not set, but the importing provider requires this member in
87    /// their proprietary data model, the importer SHOULD use the current timestamp at the time the
88    /// provider encounters this [Collection].
89    #[serde(default, skip_serializing_if = "Option::is_none")]
90    pub modified_at: Option<u64>,
91    /// The display name of the [Collection].
92    pub title: String,
93    /// This field is a subtitle or a description of the [Collection].
94    #[serde(default, skip_serializing_if = "Option::is_none")]
95    pub subtitle: Option<String>,
96    /// Enumerates all the [LinkedItem] in this [Collection]. A [LinkedItem] contains the necessary
97    /// data to indicate which [Items][Item] are part of this [Collection].
98    pub items: Vec<LinkedItem>,
99    #[serde(default, skip_serializing_if = "Option::is_none")]
100    /// Enumerates any sub-collections if the provider supports recursive organization.
101    pub sub_collections: Option<Vec<Collection<E>>>,
102    /// This enumeration contains all the extensions to the [Collection]’s attributes.
103    #[serde(default, skip_serializing_if = "Option::is_none")]
104    pub extensions: Option<Vec<Extension<E>>>,
105}
106
107#[derive(Clone, Debug, Serialize, Deserialize)]
108pub struct LinkedItem {
109    /// The [Item’s id][Item::id] that this [LinkedItem] refers to. Note that this [Item] might not
110    /// be sent as part of the current exchange.
111    pub item: B64Url,
112    /// This member indicates the [Account’s id][Account::id] the referenced [Item] belongs to. If
113    /// not present, the [Item] belongs to the current [Account] being exchanged.
114    #[serde(default, skip_serializing_if = "Option::is_none")]
115    pub account: Option<B64Url>,
116}
117
118#[derive(Clone, Debug, Serialize, Deserialize)]
119#[serde(rename_all = "camelCase", bound(deserialize = "E: Deserialize<'de>"))]
120pub struct Item<E = ()> {
121    /// A unique identifier for the [Item] which is machine generated and an opaque byte sequence
122    /// with a maximum size of 64 bytes. It SHOULD NOT be displayed to the user.
123    pub id: B64Url,
124    /// The member contains the UNIX timestamp in seconds at which this item was originally
125    /// created. If this member is not set, but the importing provider requires this
126    /// member in their proprietary data model, the importer SHOULD use the current timestamp
127    /// at the time the provider encounters this [Item].
128    #[serde(default, skip_serializing_if = "Option::is_none")]
129    pub creation_at: Option<u64>,
130    /// This member contains the UNIX timestamp in seconds of the last modification brought to this
131    /// [Item]. If this member is not set, but the importing provider requires this member in
132    /// their proprietary data model, the importer SHOULD use the current timestamp at the time
133    /// the provider encounters this [Item].
134    #[serde(default, skip_serializing_if = "Option::is_none")]
135    pub modified_at: Option<u64>,
136    /// This member’s value is the user-defined name or title of the item.
137    pub title: String,
138    /// This member is a subtitle or description for the [Item].
139    #[serde(default, skip_serializing_if = "Option::is_none")]
140    pub subtitle: Option<String>,
141    /// This member denotes whether the user has marked the [Item] as a favorite to easily present
142    /// in the UI.
143    #[serde(default, skip_serializing_if = "Option::is_none")]
144    pub favorite: Option<bool>,
145    /// This member defines the scope where the [Item::credentials] SHOULD be presented. The
146    /// credentials SHOULD only be presented within this scope unless otherwise specified by a
147    /// specific [Credential] type.
148    #[serde(default, skip_serializing_if = "Option::is_none")]
149    pub scope: Option<CredentialScope>,
150    /// This member contains a set of [Credentials][Item::credentials] that SHOULD be associated to
151    /// the type.
152    pub credentials: Vec<Credential<E>>,
153    /// This member contains user-defined tags that they may use to organize the item.
154    #[serde(default, skip_serializing_if = "Option::is_none")]
155    pub tags: Option<Vec<String>>,
156    /// This member contains all the extensions the exporter MAY have to define the [Item] type
157    /// that is being exported to be as complete of an export as possible.
158    #[serde(default, skip_serializing_if = "Option::is_none")]
159    pub extensions: Option<Vec<Extension<E>>>,
160}
161
162#[derive(Clone, Debug, Serialize, Deserialize)]
163#[serde(
164    tag = "type",
165    rename_all = "kebab-case",
166    bound(deserialize = "E: Deserialize<'de>")
167)]
168#[non_exhaustive]
169pub enum Credential<E = ()> {
170    Address(Box<AddressCredential<E>>),
171    ApiKey(Box<ApiKeyCredential<E>>),
172    BasicAuth(Box<BasicAuthCredential<E>>),
173    CreditCard(Box<CreditCardCredential<E>>),
174    CustomFields(Box<CustomFieldsCredential<E>>),
175    DriversLicense(Box<DriversLicenseCredential<E>>),
176    File(Box<FileCredential>),
177    GeneratedPassword(Box<GeneratedPasswordCredential>),
178    IdentityDocument(Box<IdentityDocumentCredential<E>>),
179    ItemReference(Box<ItemReferenceCredential>),
180    Note(Box<NoteCredential<E>>),
181    Passkey(Box<PasskeyCredential>),
182    Passport(Box<PassportCredential<E>>),
183    PersonName(Box<PersonNameCredential<E>>),
184    SshKey(Box<SshKeyCredential<E>>),
185    Totp(Box<TotpCredential>),
186    Wifi(Box<WifiCredential<E>>),
187    #[serde(untagged)]
188    Unknown {
189        ty: String,
190        #[serde(flatten)]
191        content: serde_json::Map<String, serde_json::Value>,
192    },
193}
194
195/// An [ItemReferenceCredential] is a pointer to another [Item], denoting that the two items MAY be
196/// logically linked together.
197#[derive(Clone, Debug, Serialize, Deserialize)]
198#[serde(rename_all = "camelCase")]
199pub struct ItemReferenceCredential {
200    /// A [LinkedItem] which references another [Item].
201    ///
202    /// **Note**: The other [item][Item] SHOULD be in the exchange if it is owned by the same
203    /// [Account]. However, the other item MAY NOT be in the exchange if it is owned by a different
204    /// account and shared with the currenly exchanged account.
205    pub reference: LinkedItem,
206}