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}