> For the complete documentation index, see [llms.txt](https://decideai.gitbook.io/decideai-whitepaper/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://decideai.gitbook.io/decideai-whitepaper/decide-id-integration/verified-credentials-integration.md).

# Verified Credentials Integration

## Decide ID Integration Guide

### Overview

Decide ID provides proof of personhood verification for applications built on the Internet Computer Protocol (ICP). By leveraging ICP's Internet Identity verifiable credentials system, applications can verify that their users are unique individuals through Decide ID's verification service.

### Prerequisites

* Your application must be built on the Internet Computer Protocol
* Install the DFINITY Verifiable Credentials SDK:

```bash
npm install @dfinity/verifiable-credentials
```

### Integration Steps

#### 1. Import Required Dependencies

```typescript
import { requestVerifiablePresentation, VerifiablePresentationResponse } from "@dfinity/verifiable-credentials/request-verifiable-presentation";
import { Principal } from "@dfinity/principal";
```

#### 2. Request Verification

To initiate the verification process, call `requestVerifiablePresentation` with the following configuration:

```typescript
const requestVerification = async (verifyPrincipal: Principal): Promise<void> => {
  try {
    const jwt: string = await new Promise((resolve, reject) => {
      requestVerifiablePresentation({
        onSuccess: async (verifiablePresentation: VerifiablePresentationResponse) => {
          if ('Ok' in verifiablePresentation) {
            resolve(verifiablePresentation.Ok);
          } else {
            reject(new Error(verifiablePresentation.Err));
          }
        },
        onError(err) {
          reject(new Error(err));
        },
        issuerData: {
          origin: 'https://id.decideai.xyz',
          canisterId: Principal.fromText('qgxyr-pyaaa-aaaah-qdcwq-cai'),
        },
        credentialData: {
          credentialSpec: {
            credentialType: 'ProofOfUniqueness',
            arguments: {
              // Specify the minimum date when the user's Decide ID verification must have occurred
              // Format: ISO 8601 timestamp
              minimumVerificationDate: "2024-12-01T00:00:00Z",
              // Specify the minimum reputation level required for verification
              // Possible values: "gold", "silver"
              minimumReputationLevel: "gold"
            },
          },
          credentialSubject: verifyPrincipal,
        },
        identityProvider: new URL('https://identity.ic0.app/'),
        derivationOrigin: window.location.origin,
      });
    });

    // Verify the JWT credentials received
    await verifyCredentials(jwt);

  } catch (error) {
    console.error('Verification failed:', error);
    throw error;
  }
};
```

#### 2. Frontend: Request Verification

The frontend code initiates the verification process and sends the received JWT to your backend canister:

```typescript
const requestVerification = async (verifyPrincipal: Principal): Promise<void> => {
  try {
    const jwt: string = await new Promise((resolve, reject) => {
      requestVerifiablePresentation({
        onSuccess: async (verifiablePresentation: VerifiablePresentationResponse) => {
          if ('Ok' in verifiablePresentation) {
            resolve(verifiablePresentation.Ok);
          } else {
            reject(new Error(verifiablePresentation.Err));
          }
        },
        onError(err) {
          reject(new Error(err));
        },
        issuerData: {
          origin: 'https://id.decideai.xyz',
          canisterId: Principal.fromText('qgxyr-pyaaa-aaaah-qdcwq-cai'),
        },
        credentialData: {
          credentialSpec: {
            credentialType: 'ProofOfUniqueness',
            arguments: {
              minimumVerificationDate: "2024-12-10T00:00:00Z",
            },
          },
          credentialSubject: verifyPrincipal,
        },
        identityProvider: new URL('https://identity.ic0.app/'),
        derivationOrigin: window.location.origin,
      });
    });

    // Send JWT to your backend canister for verification
    await backendCanister.verifyCredential(jwt);

  } catch (error) {
    console.error('Verification failed:', error);
    throw error;
  }
};
```

#### 3. Backend: Verify the Credentials

Verification should be performed in your backend canister. Here are examples in both Rust and Motoko:

You can follow the examples from OpenChat or Burn:\
<https://github.com/open-chat-labs/open-chat/blob/faf0d15ed126697581949a515f8ec8bb3795af21/backend/libraries/proof_of_unique_personhood/src/lib.rs#L20>\
\
<https://github.com/fort-major/burn/blob/6f245abc27202976d3766afe65685a9b47a24631/backend/src/shared/src/decideid.rs#L4>

**Rust Canister Example**

```rust
use candid::Principal;
use ic_canister_sig_creation::IC_ROOT_PK_DER;
use identity_credential::validator::JwtValidationError;

#[derive(Debug)]
pub struct VerificationError {
    message: String,
}

#[update]
async fn verify_credential(vp_jwt: String) -> Result<(), String> {
    let current_time_ns = ic_cdk::api::time(); // Get current IC time in nanoseconds
    
    // The subject we expect in the credential (typically the caller's principal)
    let effective_subject = ic_cdk::api::caller();
    
    // Your application's origin that was used in the credential request
    let derivation_origin = "https://yourdapp.ic0.app";
    
    // Configure the verification parameters
    let vc_flow_signers = VcFlowSigners {
        // Internet Identity canister ID
        ii_canister_id: Principal::from_text("rdmx6-jaaaa-aaaaa-aaadq-cai").unwrap(),
        ii_origin: "https://identity.ic0.app/".to_string(),
        
        // Decide ID canister ID
        issuer_canister_id: Principal::from_text("qgxyr-pyaaa-aaaah-qdcwq-cai").unwrap(),
        issuer_origin: "https://id.decideai.xyz".to_string(),
    };

    // The credential specification that matches what was requested
    let vc_spec = CredentialSpec {
        credential_type: "ProofOfUniqueness".to_string(),
        arguments: {
            let mut args = HashMap::new();
            args.insert(
                "minimumVerificationDate".to_string(),
                ArgumentValue::String("2024-12-10T00:00:00Z".to_string())
            );
            Some(args)
        },
    };

    // Verify the presentation and all included credentials
    validate_ii_presentation_and_claims(
        &vp_jwt,
        effective_subject,
        derivation_origin.to_string(),
        &vc_flow_signers,
        &vc_spec,
        IC_ROOT_PK_DER, // IC root public key for verifying canister signatures
        current_time_ns,
    ).map_err(|e| format!("Verification failed: {:?}", e))?;

    // If verification succeeds, store the verified state
    // This is application-specific - implement based on your needs
    Ok(())
}
```

**Motoko Canister Example ( not available )**

Currently Motoko backend support is not available

These backend verification steps are crucial for security as they:

1. Cannot be tampered with by end users
2. Use secure IC cryptographic primitives
3. Can maintain authoritative state about verified users
4. Can integrate with your canister's other security measures

> ⚠️ **Important**: Never rely on client-side verification alone. Always verify credentials in your backend canister.

````

## Error Handling
The verification process can return several types of errors:

```typescript
type VerificationError = {
  version: string;
  code: 'UNKNOWN' | 'TIMEOUT' | 'USER_ABORT' | 'INVALID_CREDENTIALS';
};

// Handle errors appropriately in your application
const handleVerificationError = (error: VerificationError) => {
  switch (error.code) {
    case 'TIMEOUT':
      // Handle timeout
      break;
    case 'USER_ABORT':
      // Handle user cancellation
      break;
    case 'INVALID_CREDENTIALS':
      // Handle invalid credentials
      break;
    default:
      // Handle unknown errors
      break;
  }
};
````

### Verification Requirements

#### Date Requirements

The **minimumVerificationDate** parameter is a critical security feature that helps ensure the authenticity of user verifications. When specified, Decide ID will only accept verifications that were performed on or after the given date.

Usage:

```typescript
typescriptCopyminimumVerificationDate: "2024-01-01T00:00:00Z" // ISO 8601 format
```

#### Reputation Level Requirements

The **minimumReputationLevel** parameter allows you to specify the required reputation level for verification. DecideAI implements a two-tier reputation system:

* Gold: **The highest reputation level**. Requires a reverification of the account at least once. Note, we will assign "gold" status for 2 weeks for new accounts and then they will transition to "silver" this is so that we don't penalize new accounts. In the future these rules may change.
* Silver: Users are automatically downgraded to silver after two weeks, requiring reverification to regain gold status. Note if a user is gold and you provide silver as the minimum then the credential request will succeed because they have a higher level than requested.

Usage:

```typescript
typescriptCopyminimumReputationLevel: "gold" // Possible values: "gold", "silver"
```

Security Note: Both the minimumVerificationDate and minimumReputationLevel parameters in the credential request must be verified exactly in your backend canister. This ensures that:

* The parameters haven't been modified from what your application requested
* Prevents tampering with the verification requirements
* Maintains consistent security standards across your application

When verifying, don't just check if the date is recent enough or if the reputation level is high enough - verify they match exactly what you requested.

### Best Practices

1. **Error Handling**: Implement comprehensive error handling for various failure scenarios.
2. **User Experience**: Provide clear feedback to users during the verification process.
3. **Security**: Always verify both the cryptographic signatures and semantic content of received credentials.
4. **Caching**: Consider caching verification results (with appropriate expiration) to improve user experience.

### Security Considerations

* Always verify the issuer's canister ID matches Decide ID's official canister
* Implement proper session management for verified users
* Never skip credential verification steps
* Use secure storage for verification results
* Implement rate limiting for verification requests

### Additional Resources

* [DFINITY Verifiable Credentials SDK](https://github.com/dfinity/verifiable-credentials-sdk)
* [Internet Identity Documentation](https://internetcomputer.org/docs/current/developer-docs/identity/verifiable-credentials/)
