DecideAI Whitepaper
Decide ID integration
Decide ID integration
  • Decide ID
  • Verified Credentials Integration
Powered by GitBook
On this page
  • Decide ID Integration Guide
  • Overview
  • Prerequisites
  • Integration Steps
  • Verification Requirements
  • Best Practices
  • Security Considerations
  • Additional Resources

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:

npm install @dfinity/verifiable-credentials

Integration Steps

1. Import Required Dependencies

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:

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:

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:

Rust Canister Example

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:

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:

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

PreviousDecide ID

Last updated 1 month ago

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
DFINITY Verifiable Credentials SDK
Internet Identity Documentation