SDK for verifying passport proofs from Self.
You can install with this command:
npm install @selfxyz/core
# or
yarn add @selfxyz/core
Initialize the verifier with your RPC URL and application scope:
import { SelfBackendVerifier } from '@selfxyz/core';
const selfBackendVerifier = new SelfBackendVerifier(
process.env.CELO_RPC_URL as string, // e.g., 'https://forno.celo.org'
process.env.SCOPE as string // Your application's unique scope. Should be the same as when initializing SelfApp
);
You can configure which verification rules to apply:
// Set minimum age verification (must be between 10-100)
selfBackendVerifier.setMinimumAge(20);
// Set nationality verification
selfBackendVerifier.setNationality('France');
// Set excluded countries verification (max 40 countries)
selfBackendVerifier.excludeCountries('Iran', 'North Korea', 'Russia', 'Syria');
// Enable passport number OFAC check (default: false)
selfBackendVerifier.enablePassportNoOfacCheck();
// Enable name and date of birth OFAC check (default: false)
selfBackendVerifier.enableNameAndDobOfacCheck();
// Enable name and year of birth OFAC check (default: false)
selfBackendVerifier.enableNameAndYobOfacCheck();
Verify a proof with the received proof and public signals:
const result = await selfBackendVerifier.verify(proof, publicSignals);
You can extract the user identifier from the public signals:
import { getUserIdentifier } from '@selfxyz/core';
const userId = await getUserIdentifier(publicSignals);
This allows linking proofs with verification requests generated by @selfxyz/qrcode
.
The verify
method returns a detailed verification result:
export interface SelfVerificationResult {
// Overall verification status
isValid: boolean;
// Detailed validation statuses
isValidDetails: {
isValidScope: boolean; // Proof was generated for the expected scope
isValidAttestationId: boolean; // Attestation ID matches expected value
isValidProof: boolean; // Cryptographic validity of the proof
isValidNationality: boolean; // Nationality check (when enabled)
};
// User identifier from the proof
userId: string;
// Application scope
application: string;
// Cryptographic nullifier to prevent reuse
nullifier: string;
// Revealed data from the passport
credentialSubject: {
merkle_root?: string; // Merkle root used for proof generation
attestation_id?: string; // Identity type (1 for passport)
current_date?: string; // Proof generation timestamp
issuing_state?: string; // Passport issuing country
name?: string; // User's name
passport_number?: string; // Passport number
nationality?: string; // User's nationality
date_of_birth?: string; // Date of birth
gender?: string; // Gender
expiry_date?: string; // Passport expiry date
older_than?: string; // Age verification result
passport_no_ofac?: boolean; // Passport OFAC check result.
// Gives true if the user passed the check (is not on the list),
// false if the check was not requested or if the user is in the list
name_and_dob_ofac?: boolean; // Name and DOB OFAC check result
name_and_yob_ofac?: boolean; // Name and birth year OFAC check result
};
// Original proof data
proof: {
value: {
proof: any;
publicSignals: any;
};
};
// Error information if verification failed
error?: any;
}
Here's an example of implementing an API endpoint that uses the SDK:
import { NextApiRequest, NextApiResponse } from 'next';
import { getUserIdentifier, SelfBackendVerifier, countryCodes } from '@selfxyz/core';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method === 'POST') {
try {
const { proof, publicSignals } = req.body;
if (!proof || !publicSignals) {
return res.status(400).json({ message: 'Proof and publicSignals are required' });
}
// Extract user ID from the proof
const userId = await getUserIdentifier(publicSignals);
console.log('Extracted userId:', userId);
// Initialize and configure the verifier
const selfBackendVerifier = new SelfBackendVerifier(
'https://forno.celo.org',
'my-application-scope'
);
// Configure verification options
selfBackendVerifier.setMinimumAge(18);
selfBackendVerifier.excludeCountries(
countryCodes.IRN, // Iran
countryCodes.PRK // North Korea
);
selfBackendVerifier.enableNameAndDobOfacCheck();
// Verify the proof
const result = await selfBackendVerifier.verify(proof, publicSignals);
if (result.isValid) {
// Return successful verification response
return res.status(200).json({
status: 'success',
result: true,
credentialSubject: result.credentialSubject,
});
} else {
// Return failed verification response
return res.status(400).json({
status: 'error',
result: false,
message: 'Verification failed',
details: result.isValidDetails,
});
}
} catch (error) {
console.error('Error verifying proof:', error);
return res.status(500).json({
status: 'error',
result: false,
message: error instanceof Error ? error.message : 'Unknown error',
});
}
} else {
return res.status(405).json({ message: 'Method not allowed' });
}
}
The SDK provides a countryCodes
object for referencing ISO country codes:
import { countryCodes } from '@selfxyz/core';
// Examples of usage
const iranCode = countryCodes.IRN; // "Iran"
const northKoreaCode = countryCodes.PRK; // "North Korea"
// Use in excludeCountries
selfBackendVerifier.excludeCountries(countryCodes.IRN, countryCodes.PRK, countryCodes.SYR);
This backend SDK is designed to work with the @selfxyz/qrcode
package. When configuring your QR code, set the verification endpoint to point to your API that uses this SDK:
import { SelfAppBuilder } from '@selfxyz/qrcode';
const selfApp = new SelfAppBuilder({
appName: 'My Application',
scope: 'my-application-scope',
endpoint: 'https://my-api.com/api/verify', // Your API using SelfBackendVerifier
logoBase64: myLogoBase64,
userId,
disclosures: {
name: true,
nationality: true,
date_of_birth: true,
passport_number: true,
minimumAge: 20,
excludedCountries: ['IRN', 'PRK'],
ofac: true,
},
}).build();
For a more advanced implementation example, see the playground.