How to Passport-Gate Your Project (by Integrating the Passport SDK)

This page walks you through how to integrate the Passport SDK into your dApp, so you can keep out bots and bad actors
In the Getting Started section, we went over how to install each of the Passport libraries. Now, we'll go over the basic usages of each of the libraries so you can get the identity solution you need for your dApp.

I need to…

Create and manage DIDs

The Writer library allows an integrated dApp to write to an authenticated DID Passport stream in Ceramic.

Getting Started with the Writer library

The below tutorial outlines how to:
  • connect a wallet
  • create and authenticate a DID construct a new instance of Writer
  • create a new Passport belonging to a user or retrieve their existing decentralized identity data from Ceramic, and
  • add a stamp to a Passport belonging to a specific user.
At the end of this step, you’ll understand how to interact with Passport DIDs in your project’s verification process.
import {DID} from "dids";
import {EthereumAuthProvider} from "@3id/connect";
// connect to wallet somehow
const provider = wallet.provider;
const address = wallet.accounts[0];
// Create and authenticate a DID
const testDID = new DID({
provider: new EthereumAuthProvider(provider, address),
await testDID.authenticate();
// Construct a writer
const passportWriter = new PassportWriter(testDID);
// Create a Passport belonging to testDID
const passportStreamID = await passportWriter.createPassport();
// Get the Passport by Stream ID
const passport = (await passportWriter.loader.load(passportStreamID)).content;
// Get the Passport belonging to testDID
const testDIDPassport = await passportWriter.getPassport();
// Add a stamp to the Passport belonging to testDID
const newStamp = {
provider: "An Identity Provider",
credential: {...a Verifiable Credential...}
await passportWriter.addStamp(newStamp);

Setting up Ceramic framework

To use the Passport properly, you’ll have to have a basic understanding of the Ceramic framework. Unfamiliar with Ceramic? You can check out their documentation here.
  1. 1.
    Configure the Ceramic Provider
The Provider component has to be added at the root of the application tree in order to use the hooks provided below. It can be used to provide a custom configuration for the Self.ID clients, authentication, state and UI options.
import {Provider} from ''
function App({children}) {
return <Provider client={{ceramic: 'testnet-clay'}} session={true}>{children}</Provider>
2. Retrieve a user’s authenticated DID
The useViewerConnection hook needs to be set up before a user’s authentic DID can be located.
This code snippet explains how to import the hook and use it to connect to a Ceramic stream to retrieve a DID.
import {useViewerConnection} from "";
const [viewerConnection, connectCeramic, disconnectCeramic] = useViewerConnection();
// connect to wallet somehow
const provider = wallet.provider;
const address = wallet.accounts[0];
connectCeramic(new EthereumAuthProvider(provider, address));
useEffect(() => {
switch (viewerConnection.status) {
case "idle": {
// not connected yet
case "connected": {
// user connected - construct a writer authenticated by user's DID
const passportWriter = new PassportWriter(viewerConnection.selfID.did);
// ... do stuff with passport writer ...
case "failed": {
// user refused to connect or authenticate to ceramic
}, [viewerConnection.status]);

Requesting Verifiable Credentials

A user’s verifiable credentials can be retrieved from Gitcoin’s IAM server. There are two servers: a production server and a staging server.
The following code snippet explains the end to end process of retrieving verifiable credentials.
// Fetch a verifiable challenge credential to prove user owns their address
export const fetchChallengeCredential = async (
iamUrl: string = "<>",
payload: RequestPayload
): Promise<IssuedChallenge> => {
// fetch challenge as a credential from API that fits the version, address and type (this credential has a short ttl)
const response: { data: CredentialResponseBody } = await
payload: {
address: payload.address,
type: payload.type,
return {
} as IssuedChallenge;
// Fetch a verifiableCredential
export const fetchVerifiableCredential = async (
iamUrl: string = "<>",
payload: RequestPayload,
signer: { signMessage: (message: string) => Promise<string> } | undefined
): Promise<VerifiableCredentialRecord> => {
// must provide signature for message
if (!signer) {
throw new Error("Unable to sign message without a signer");
// first pull a challenge that can be signed by the user
const {challenge} = await fetchChallengeCredential(iamUrl, payload);
// sign the challenge provided by the IAM
const signature = challenge.credentialSubject.challenge
? (await signer.signMessage(challenge.credentialSubject.challenge)).toString()
: "";
// must provide signature for message
if (!signature) {
throw new Error("Unable to sign message");
// pass the signature as part of the proofs obj
payload.proofs = {...payload.proofs, ...{signature: signature}};
// fetch a credential from the API that fits the version, payload and passes the signature message challenge
const response: { data: CredentialResponseBody } = await
// return everything that was used to create the credential (along with the credential)
return {
} as VerifiableCredentialRecord;

Issuing Verifiable Credentials

With Passport, projects also have the ability to issue their own verifiable credentials, which can be used as Passport stamps and carried with a user across their engagements in the open web.
Here’s an example of how to create a verifiable credential from SpruceID’s DIDKit.
import * as DIDKit from "@spruceid/didkit-wasm-node";
const key = process.env.ISSUER_KEY || DIDKit.generateEd25519Key();
// Keeping track of the hashing mechanism (algo + content)
const VERSION = "v0.0.0";
// utility to create an ordered array of the given input (of the form [[key:string, value:string], ...])
const objToSortedArray = (obj: { [k: string]: string }): string[][] => {
const keys: string[] = Object.keys(obj).sort();
return keys.reduce((out: string[][], key: string) => {
out.push([key, obj[key]]);
return out;
}, [] as string[][]);
// construct and issue a VerifiableCredential via DIDKit
const issueCredential = async (
subjectAddress: string,
provider: string,
record: object,
expiresInSeconds: number,
): Promise<VerifiableCredential> => {
// get DID from key
const issuer = DIDKit.keyToDID("key", key);
// read method from key
const verificationMethod = await DIDKit.keyToVerificationMethod("key", key);
// stringify assertionMethod we feed to didkit-wasm-node
const verifyWithMethod = JSON.stringify({
proofPurpose: "assertionMethod",
const issuanceDate = new Date();
const expirationDate = new Date();
expirationDate.setSeconds(issuanceDate.getSeconds() + expiresInSeconds);
// Generate a hash like SHA256(IAM_PRIVATE_KEY+PII), where PII is the (deterministic) JSON representation
// of the PII object after transforming it to an array of the form [[key:string, value:string], ...]
// with the elements sorted by key
// This hash can be used to de-duplicate provider verifications without revealing PII
const hash = base64.encode(
.update(key, "utf-8")
// generate a verifiableCredential
const credential = await DIDKit.issueCredential(
"@context": ["<>"],
type: ["VerifiableCredential"],
issuanceDate: issuanceDate.toISOString(),
expirationDate: expirationDate.toISOString(),
credentialSubject: {
"@context": [
hash: "<>",
provider: "<>",
// construct a pkh DID on mainnet (:1) for the given wallet address
id: `did:pkh:eip155:1:${subjectAddress}`,
hash: `${VERSION}:${hash}`,
// parse the response of the DIDKit wasm
return JSON.parse(credential) as VerifiableCredential;
const exampleVerifiableCredential = issueCredential(
{myRecord: "my value"},

Read identity data from a Passport

Reader allows an integrated dApp to read from any Passport stream on Ceramic.

Getting Started

To set up the Reader library, import the library and construct a passport reader instance.
Then, pass in a ceramic node URL and network ID that points toty Gitcoin’s main Ceramic node.
// add to your project as a module
import PassportReader from "@gitcoinco/passport-sdk-reader"
// or import the bundle
<script src="./dist/reader.bundle.js" type="script/javascript"/>
// create a new instance pointing at Gitcoins mainnet Ceramic node
const reader = new PassportReader("<>", "1");
// read a Passport for any Ethereum Address
const passport = await reader.getPassport("0x0...");

Reader Methods

Setting up the passport reader instance enables several read-only methods that allow you to get to the content of a Gitcoin Passport.
Passes in an Ethereum address and returns the did:pkh and genesis IDX streams.
reader.getGenesis(address: string): Promise<CeramicGenesis | false>
Passes in an Ethereum address and returns a fully hydrated Passport record, with all verifiable credentials.
reader.getPassport(address: string): Promise<CeramicPassport | Passport | false>
Passes in a Ceramic DID and returns a raw Passport stream record. This is a shallow copy of Passport that needs to have its stamps hydrated.
reader.getPassportStream(address: string): Promise<CeramicPassport | false>

Verify the contents of a Passport

Verifier allows an integrated dApp to verify the contents of a Passport. This is a necessary step to later evaluate the contents of a Passport and produce a cost of forgery score.

Getting Started

  1. 1.
    To set up verifier, import the library as a module or import the bundle, then construct a passportVerifier instance.
// import as a module
import PassportVerifier from "@gitcoinco/passport-sdk-verifier";
// or import the bundle
<script src="./dist/verifier.bundle.js" type="script/javascript"/>
2. Create a new instance pointing at the community clay node on the Ceramic mainnet, along with the criteria you wish to score against.
const verifier = new PassportVerifier();
3. Verify all stamps held in a Passport
const passport = await verifier.verifyPassport("0x0...");
Getting started on a browser may require additional steps.
You may need to asynchronously load @gitcoinco/passport-sdk-verifier before loading the package.
Next Js Example
const [verifier, setVerifier] = useState();
useEffect(() => {
const initVerifier = async () => {
// Dynamically load @gitcoinco/passport-sdk-verifier
const PassportVerifier = (await import("@gitcoinco/passport-sdk-verifier")).PassportVerifier;
setVerifier(new PassportVerifier("<>"));
initVerifier().then(() => {
console.log("Verifier inited :)");
}, []);

Verifier Methods

After the PassportVerifier instance is created, read-only methods for verifying the content of a Gitcoin Passport are exposed.
Pass in an Ethereum address and get back a Passport where each of the stamps includes a verified: boolean field.
PassportVerifier.verifyPassport(address: string, passport?: Passport, additionalStampCheck?: (stamp: Stamp) => boolean): Promise<Passport>
Pass in a stamp and get back a stamp with a verified: boolean field completed.
PassportVerifier.verifyStamp(address: string, stamp: Stamp, additionalStampCheck?: (stamp: Stamp) => boolean): Promise<Stamp>
Pass in a Verifiable Credential and get back a boolean.
PassportVerifier.verifyCredential(credential: VerifiableCredential): Promise<boolean>

Evaluate the contents of a Passport

The Scorer library allows an integrated dApp to score the verifiable credentials held by a Passport based on customized criteria.

Getting Started with Scorer

After importing the library, construct a passportScorer instance to specify the scoring criteria and the Ceramic node URL or network ID, if desired.
  1. 1.
    Import the PassportScorer as a module or bundle. import PassportScorer from "@gitcoinco/passport-sdk-scorer" or ****<script src="./dist/scorer.bundle.js" type="script/javascript"/>
  2. 2.
    Create a new instance that defines the criteria you wish to score against.
const scorer = new PassportScorer([
provider: "BrightID",
issuer: "did:key:z6MkghvGHLobLEdj1bgRLhS4LPGJAvbMA1tn2zcRyqmYU5LC",
score: 0.5
3. Get the score for a specific wallet address.
const score = await scorer.getScore("0x0...");
This instance exposes read-only methods to score the content of a Gitcoin Passport.
To get a score for a wallet address based on the scoring criteria of this instance, as well as any additional checks, use the getScore method.
PassportScorer.getScore(address: string, passport?: Passport, additionalStampCheck?: (stamp: Stamp) => boolean): Promise<number>