Your First Credential
This tutorial walks through the complete credential lifecycle using BaseID. You will create an issuer, issue a credential, store it in a wallet, and verify it.
Step 1: Set Up the Issuer
Section titled “Step 1: Set Up the Issuer”use baseid_core::types::KeyType;use baseid_crypto::KeyPair;use baseid_did::DidKeyResolver;
// Generate issuer identitylet issuer_kp = KeyPair::generate(KeyType::Ed25519)?;let issuer_doc = DidKeyResolver::create(&issuer_kp.public)?;let issuer_did = issuer_doc.id.clone();let kid = issuer_doc.verification_method[0].id.clone();The issuer needs a key pair and a DID. The kid (key ID) references the specific verification method used for signing.
Step 2: Issue a Credential
Section titled “Step 2: Issue a Credential”use baseid_vc::{sign_credential_jwt, VerifiableCredential};use baseid_vc::credential::Issuer;
let vc = VerifiableCredential { context: vec!["https://www.w3.org/ns/credentials/v2".into()], id: None, r#type: vec!["VerifiableCredential".into(), "UniversityDegree".into()], issuer: Issuer::Uri(issuer_did.clone()), valid_from: Some("2024-01-01T00:00:00Z".into()), valid_until: Some("2029-01-01T00:00:00Z".into()), credential_subject: serde_json::json!({ "id": "did:key:z6MkHolder...", "degree": { "type": "BachelorDegree", "name": "Computer Science" } }), credential_status: None, proof: None,};
let jwt = sign_credential_jwt(&vc, &issuer_kp, &kid)?;println!("Issued JWT: {jwt}");Step 3: Store in Wallet
Section titled “Step 3: Store in Wallet”use baseid_wallet_core::store::InMemoryStore;use baseid_wallet_core::wallet::Wallet;
let store = InMemoryStore::new();let wallet = Wallet::new(store, "did:key:z6MkHolder...".into());// Receive and store the credential JWTThe wallet stores credentials and manages the holder’s key material. InMemoryStore is useful for testing; use a persistent store in production.
Step 4: Present to Verifier
Section titled “Step 4: Present to Verifier”The wallet matches credentials against a presentation request and creates a Verifiable Presentation:
// The verifier sends a presentation request specifying which// credential types and claims are needed.// The wallet selects matching credentials and creates a VP.In a real deployment, this exchange happens over OID4VP or DIDComm v2.
Step 5: Verify
Section titled “Step 5: Verify”use baseid_vc::verify_credential_jwt;
let verified = verify_credential_jwt(&jwt, &issuer_kp.public)?;println!("Issuer: {}", verified.issuer);println!("Valid from: {:?}", verified.valid_from);println!("Subject: {:?}", verified.credential_subject);The verifier checks the cryptographic signature, ensures the credential has not expired, and extracts the claims.
What’s Next
Section titled “What’s Next”- Architecture to understand how the crates fit together
- Credential Formats to explore SD-JWT and mDL
- OID4VCI to issue credentials over the wire