Skip to main content

Guide: Use ZK Passport to verify citizenship off-chain

Before getting started with implementing the citizenship check, let’s quickly review all the steps of the process on the diagram below.

Basically, it goes likes this:

  1. User installs the RariMe App and scans the passport
  2. Scan a QR-code in the DApp
  3. Gets prompted to generate a proof of eligibility in RariMe App
  4. The app submits the proof to the callback URL(specified in the QR code) for verification
  5. DApp backend fetches the verification status and citizenship data from a REST API

QR code generation and all the interactions with the backend are facilitated by Rarimo's ZK Passport library, which is available as an NPM package: @rarimo/zk-passport. The library provides a convenient API for generating QR codes and verifying the proof status.

You can check out this flow in the sandbox: ZK Passport Demo

Pre-requisites

tip

You can use this public instance of verificator-svc for testing: https://api.app.rarime.com/

To get started, you SHOULD deploy your own instance of https://github.com/rarimo/verificator-svc. This service encapsulates proof verification and provides a convenient REST API for requesting parameters for the QR code, checking the user verification status, etc. You need to trust the instance of verificator-svc so the best option is to host your own instance.

API docs: verificator-svc | ReDoc

Step #1: Ask the user to install RariMe App and scan the passport

Links for installation:

Step #2: Install @rarimo/zk-passport-react package

To install the package, run the following command in your project directory:

npm install @rarimo/zk-passport-react

If you don't use React, you can use the @rarimo/zk-passport package instead. It provides a low-level API for for requesting and passing for verification of ZK Passport proofs.

npm install @rarimo/zk-passport

Step #3: Import the library and render the QR code

The following JS snippet uses the library to render a QR code with a proof of citizenship request and handles the results:

import ZkPassportQrCode from '@rarimo/zk-passport-react'

// this is a public instance of verificator-svc(https://github.com/rarimo/verificator-svc). Replace it with the URL of your instance
const apiUrl = 'https://api.app.rarime.com'

// `requestid` is a public unique ID associated with a user within your system. It will be used in the Step #3 to check the user status
const requestId = 'account-1'

// `event_id` is an arbitrary number that denotes the application scope of the proof
const eventId = 1337

const verificationOpts: RequestVerificationLinkOpts = {
uniqueness: true, // check if the user has already passed the verification
nationalityCheck: true, // include user's citizenship in the output(proof public signals)
eventId: eventId, // scope of the proof
}

return (
<ZkPassportQrCode
// Proof request params
apiUrl={apiUrl}
requestId={requestId}
verificationOptions={verificationOpts}

// QR code component props
qrProps={{ size: 256 }}

// place your callbacks here
onStatusChange={status => console.log(status)}
onSuccess={proof => console.log(proof)}
onError={error => console.error(error)}
/>
)

Step #3: Fetch the verification status and the citizenship code on the backend

You can fetch the verification status for a particular id from this verificator endpoint: https://rarimo.github.io/verificator-svc/#tag/User-verification/operation/getUserStatus

Here is a JS snippet for doing this

const backendUrl = 'https://api.app.rarime.com/'
const userId = 'some-user-id' // the `id` from step #2

const response = await fetch(`${backendUrl}/integrations/verificator-svc/private/verification-status/${userId}`)
const { data } = await response.json()

// responseUserId === userId
const responseUserId = data.id

// Verification status enum:
// - "not_verified" - user is not verified
// - "verified" - user is verified
// - "failed_verification" - user verification failed
// - "uniqueness_check_failed" - user uniqueness check failed
const verificationStatus = data.attributes.status

If the user has status verified, you can fetch the verified citizenship code like this:

const response = await fetch(`${backendUrl}/integrations/verificator-svc/private/user/${userId}`)
const { data } = await response.json()

// ISO 3166 alpha-3 country code: https://www.iban.com/country-codes
// Note: German passports may have a single-letter "D" code
const citizenshipISOCode = data.attributes.nationality // e.g. "UKR"

Conclusion

In this guide, we set up off-chain citizenship verification using ZK Passport and the RariMe App, enabling secure proof of citizenship without exposing private user data.