Skip to main content

identity_kit/vc/
mod.rs

1//! Verifiable credentials and presentations secured as JWTs.
2//!
3//! The module implements two profiles over the same JOSE layer:
4//!
5//! - **VCDM 1.1 JWT profile** (§6.3.1 of the Verifiable Credentials Data Model 1.1):
6//!   [`Credential`] and [`Presentation`] map to and from a [`JwtClaims`] set whose `vc` or `vp`
7//!   claim holds the data model object and whose registered claims (`iss`, `sub`, `jti`, `nbf`,
8//!   `exp`, `aud`) carry the issuer, subject, id, validity period and audience.
9//! - **VCDM 2.0 with VC-JOSE-COSE**: [`CredentialV2`] and [`PresentationV2`] are themselves the
10//!   JWT payload (`typ: vc+jwt` or `vp+jwt`), and a secured credential travels inside a
11//!   presentation as an [`EnvelopedVc`] holding a `data:application/vc+jwt,…` URL.
12//!
13//! Both are plain data with open extension properties; `@context` is carried, never processed.
14//! [`Jwt`] is a compact JWS as a string, signed with any [`Signer`](crate::jose::Signer) through
15//! [`Jwt::sign`]. The [`validation`] module verifies and validates the JWTs: signature through
16//! a [`KeyResolver`](validation::KeyResolver) and a [`JwsVerifier`](crate::jose::JwsVerifier),
17//! binding of the signing key to the issuer or holder, validity period, audience, nonce and
18//! status.
19//!
20//! Timestamps are [`chrono::DateTime<Utc>`](chrono::DateTime) values, serialized as RFC 3339
21//! in UTC (`2010-01-01T19:23:24Z`), the `dateTimeStamp` form the data model requires.
22//!
23//! # Examples
24//!
25//! The JWT claims of VCDM 1.1 Example 31, decoded to a credential and back.
26//!
27//! ```
28//! use identity_kit::vc::{Credential, JwtClaims};
29//! use serde_json::json;
30//!
31//! let claims: JwtClaims = serde_json::from_value(json!({
32//!   "sub": "did:example:ebfeb1f712ebc6f1c276e12ec21",
33//!   "jti": "http://example.edu/credentials/3732",
34//!   "iss": "https://example.com/keys/foo.jwk",
35//!   "nbf": 1541493724,
36//!   "exp": 1573029723,
37//!   "nonce": "660!6345FSer",
38//!   "vc": {
39//!     "@context": [
40//!       "https://www.w3.org/2018/credentials/v1",
41//!       "https://www.w3.org/2018/credentials/examples/v1"
42//!     ],
43//!     "type": ["VerifiableCredential", "UniversityDegreeCredential"],
44//!     "credentialSubject": {
45//!       "degree": { "type": "BachelorDegree", "name": "Bachelor of Science and Arts" }
46//!     }
47//!   }
48//! }))?;
49//!
50//! let credential = Credential::from_jwt_claims(&claims)?;
51//! assert_eq!(credential.id.as_ref().map(|id| id.as_str()), Some("http://example.edu/credentials/3732"));
52//! assert_eq!(credential.issuer.id().as_str(), "https://example.com/keys/foo.jwk");
53//! assert_eq!(credential.issuance_date.to_rfc3339(), "2018-11-06T08:42:04+00:00");
54//! assert_eq!(
55//!     credential.subject().and_then(|s| s.id.as_ref()).map(|id| id.as_str()),
56//!     Some("did:example:ebfeb1f712ebc6f1c276e12ec21")
57//! );
58//!
59//! // The mapping is reversible; the custom `nonce` claim is the caller's to add back.
60//! let encoded = credential.to_jwt_claims()?.with_claim("nonce", "660!6345FSer");
61//! assert_eq!(encoded, claims);
62//! # Ok::<(), Box<dyn std::error::Error>>(())
63//! ```
64
65mod claims;
66mod credential;
67mod error;
68mod jwt;
69mod model;
70mod presentation;
71mod v2;
72pub mod validation;
73
74pub use credential::Credential;
75pub use error::CredentialError;
76pub use jwt::{Jwt, JwtClaims};
77#[cfg(feature = "sd-jwt")]
78pub(crate) use jwt::{numeric_date, required_numeric_date};
79pub use model::{CONTEXT_V1, CONTEXT_V2, Party, Subject, TypedObject};
80pub use presentation::Presentation;
81pub use v2::{CredentialV2, EnvelopedVc, EnvelopedVp, PresentationV2};