# Quickstart: Credential Filtering API & Parsed Output

**Branch**: `003-credential-filtering-api` | **Date**: 2026-01-21

## Overview

This feature adds comprehensive credential filtering and typed response parsing to the LastID Rust SDK, enabling:
- Domain-based filtering on employment and persona credentials
- Issuer and subject DID constraints
- Typed accessors for common claim fields
- Parsed credential structs for type-safe access

## Prerequisites

Add to `Cargo.toml`:
```toml
[dependencies]
lastid-sdk = { version = "0.1", features = ["full"] }
```

## Quick Examples

### 1. Enterprise Domain Filtering

Filter employment credentials to a specific company domain:

```rust
use lastid_sdk::{EmploymentPolicy, EmploymentField, PolicyBuilder};

let policy = EmploymentPolicy::new()
    .with_employer_domain("acme.com")           // Only acme.com employees
    .with_issuer_did("did:lastid:acme-corp")    // Must be issued by Acme's DID
    .with_fields(&[EmploymentField::Company, EmploymentField::Title])
    .with_callback("https://example.com/callback");

let request = policy.to_request()?;
```

### 2. Subject Binding for Re-Authentication

Verify a returning user matches their known identity:

```rust
use lastid_sdk::{BaseCredentialPolicy, PolicyBuilder};

let known_user_did = "did:lastid:user-abc123";

let policy = BaseCredentialPolicy::new()
    .with_subject_did(known_user_did)  // Must be this specific user
    .with_callback("https://example.com/callback");

let request = policy.to_request()?;
```

### 3. Email Domain Filtering on Persona

Accept only users with corporate email domains:

```rust
use lastid_sdk::{PersonaPolicy, PersonaField, PolicyBuilder};

let policy = PersonaPolicy::new()
    .with_email_domain("bigcorp.com")  // Only @bigcorp.com emails
    .with_fields(&[PersonaField::DisplayName, PersonaField::Email])
    .with_callback("https://example.com/callback");

let request = policy.to_request()?;
```

### 4. Typed Response Parsing

Access verified claims without JSON parsing:

```rust
use lastid_sdk::{VerifiedCredential, ParsedPersona};

// After verification...
let credential: VerifiedCredential = /* from verification */;

// Use typed accessors
if let Some(email) = credential.email() {
    println!("Email: {}", email);
}

if let Some(display_name) = credential.display_name() {
    println!("Name: {}", display_name);
}

// Or parse into a typed struct
let persona = ParsedPersona::try_from(&credential)?;
println!("Company: {:?}", persona.company);
println!("Job Title: {:?}", persona.job_title);
```

### 5. Device Attestation Access

Check device security for policy decisions:

```rust
use lastid_sdk::VerifiedCredential;

let credential: VerifiedCredential = /* from verification */;

if let Some(attestation) = credential.device_attestation() {
    println!("Has secure element: {}", attestation.has_secure_element);
    println!("Integrity status: {}", attestation.integrity_status);
    println!("Is emulator: {}", attestation.is_emulator);
}

if let Some(security) = credential.device_security() {
    println!("Security level: {}", security.security_level);
    println!("Biometric used: {}", security.biometric_used);
}
```

### 6. Multi-Credential Chain Handling

Access all credentials in a presentation:

```rust
use lastid_sdk::{VerifiedResponse, ParsedPersona, ParsedVerifiedEmail};

let response: VerifiedResponse = /* from verification */;

// Iterate all credentials
for cred in response.credentials() {
    println!("Credential type: {}", cred.credential_type);
}

// Get specific credential by type
if let Some(persona_cred) = response.credential_by_type("LastID.Persona") {
    let persona = ParsedPersona::try_from(persona_cred)?;
    println!("Display name: {:?}", persona.display_name);
}

// Get the primary (leaf) credential
let primary = response.primary_credential().unwrap();
println!("Primary credential: {}", primary.credential_type);

// Check the chain order
println!("Chain: {:?}", response.credential_chain());
// Output: ["LastID.Base", "LastID.Persona", "LastID.VerifiedEmail"]
```

## Common Patterns

### Combining Multiple Constraints

```rust
use lastid_sdk::{EmploymentPolicy, EmploymentField, EmploymentStatus, PolicyBuilder};

let policy = EmploymentPolicy::new()
    .with_employer_domain("acme.com")
    .with_issuer_did("did:lastid:acme-hr")
    .with_status(EmploymentStatus::Current)
    .with_subject_did("did:lastid:known-employee")
    .with_fields(&[EmploymentField::Company, EmploymentField::Title])
    .with_callback("https://example.com/callback");
```

### Generic Claim Constraints

```rust
use lastid_sdk::{PersonaPolicy, PolicyBuilder};

let policy = PersonaPolicy::new()
    .with_claim_constraint("visibility", serde_json::json!("public"))
    .with_callback("https://example.com/callback");
```

### Age Verification

```rust
use lastid_sdk::{AgeProofPolicy, PolicyBuilder};

let policy = AgeProofPolicy::new()
    .with_minimum_age(21)  // Must be 21+
    .with_callback("https://example.com/callback");
```

### Trust Level Requirements

```rust
use lastid_sdk::{TrustPolicy, PolicyBuilder};

let policy = TrustPolicy::new()
    .with_minimum_level(3)  // Require trust level >= 3
    .with_callback("https://example.com/callback");
```

## Error Handling

### Build-Time Validation

Invalid inputs are caught at build time:

```rust
use lastid_sdk::{EmploymentPolicy, PolicyBuilder};

let policy = EmploymentPolicy::new()
    .with_employer_domain("user@acme.com")  // Invalid: contains @
    .with_callback("https://example.com");

let result = policy.to_request();
assert!(result.is_err());
// Error: PolicyValidation: Employer domain must be a valid domain
```

### Parse-Time Validation

Type mismatches are caught when parsing:

```rust
use lastid_sdk::{VerifiedCredential, ParsedEmployment};

let persona_credential: VerifiedCredential = /* LastID.Persona */;

let result = ParsedEmployment::try_from(&persona_credential);
assert!(result.is_err());
// Error: CredentialParse: Expected LastID.Employment, got LastID.Persona
```

## Next Steps

- Run examples: `cargo run --example enterprise_domain_filter`
- See full API docs: `cargo doc --open`
- Integration tests: `cargo test --features full`
