mirror of
https://github.com/selfxyz/self.git
synced 2026-04-05 03:00:53 -04:00
Self sdk and playground (#210)
Co-authored-by: nicoshark <i.am.nicoshark@gmail.com>
This commit is contained in:
@@ -1,199 +1,245 @@
|
||||
# How to use this SDK
|
||||
## Install
|
||||
You can install with this command
|
||||
```
|
||||
npm i @selfxyz/core
|
||||
# @selfxyz/core
|
||||
|
||||
SDK for verifying passport proofs from Self.
|
||||
|
||||
## Installation
|
||||
|
||||
You can install with this command:
|
||||
|
||||
```bash
|
||||
npm install @selfxyz/core
|
||||
# or
|
||||
yarn add @selfxyz/core
|
||||
```
|
||||
|
||||
## Initialize
|
||||
You should have CELO_RPC_URL and SCOPE in your environment or somewhere in your code.
|
||||
## Initialization
|
||||
|
||||
Initialize the verifier with your RPC URL and application scope:
|
||||
|
||||
```typescript
|
||||
import { SelfBackendVerifier } from "@selfxyz/core";
|
||||
|
||||
const selfBackendVerifier = new SelfBackendVerifier(
|
||||
process.env.CELO_RPC_URL as string,
|
||||
process.env.SCOPE as string,
|
||||
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
|
||||
);
|
||||
```
|
||||
## Setup
|
||||
You can setup which data you want to verify in this sdk
|
||||
|
||||
## Configuration
|
||||
|
||||
You can configure which verification rules to apply:
|
||||
|
||||
```typescript
|
||||
// Set minimum age verification
|
||||
// Set minimum age verification (must be between 10-100)
|
||||
selfBackendVerifier.setMinimumAge(20);
|
||||
|
||||
// Set nationality verification
|
||||
selfBackendVerifier.setNationality('France')
|
||||
// Set exclude countries verification
|
||||
// At most 40
|
||||
selfBackendVerifier.excludeCountries('Country Name1', 'Country Name2', 'Coutry Name3', 'etc...');
|
||||
// Enable if you want to do passport number ofac check
|
||||
// Default false
|
||||
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 if you want to do name and date of birth ofac check
|
||||
// Default false
|
||||
|
||||
// Enable name and date of birth OFAC check (default: false)
|
||||
selfBackendVerifier.enableNameAndDobOfacCheck();
|
||||
// Enable if you want to do name and year of birth ofac check
|
||||
// Default false
|
||||
|
||||
// Enable name and year of birth OFAC check (default: false)
|
||||
selfBackendVerifier.enableNameAndYobOfacCheck();
|
||||
```
|
||||
|
||||
## Verification
|
||||
You can do the verification with this
|
||||
|
||||
Verify a proof with the received proof and public signals:
|
||||
|
||||
```typescript
|
||||
const result = await selfBackendVerifier.verify(
|
||||
request.body.proof,
|
||||
request.body.publicSignals
|
||||
);
|
||||
const result = await selfBackendVerifier.verify(proof, publicSignals);
|
||||
```
|
||||
## Result
|
||||
Result from the verify function is like this
|
||||
|
||||
## Extracting User Identifier
|
||||
|
||||
You can extract the user identifier from the public signals:
|
||||
|
||||
```typescript
|
||||
import { getUserIdentifier } from "@selfxyz/core";
|
||||
|
||||
const userId = await getUserIdentifier(publicSignals);
|
||||
```
|
||||
|
||||
This allows linking proofs with verification requests generated by `@selfxyz/qrcode`.
|
||||
|
||||
## Verification Result
|
||||
|
||||
The `verify` method returns a detailed verification result:
|
||||
|
||||
```typescript
|
||||
export interface SelfVerificationResult {
|
||||
// Check if the whole verification is succeeded
|
||||
isValid: boolean;
|
||||
isValidDetails: {
|
||||
// Verifies that the proof is generated under the expected scope.
|
||||
isValidScope: boolean;
|
||||
// Checks that the attestation identifier in the proof matches the expected value.
|
||||
isValidAttestationId: boolean;
|
||||
// Verifies the cryptographic validity of the proof.
|
||||
isValidProof: boolean;
|
||||
// Ensures that the revealed nationality is correct (when nationality verification is enabled).
|
||||
isValidNationality: boolean;
|
||||
};
|
||||
// User Identifier which is included in the proof
|
||||
userId: string;
|
||||
// Application name which is showed as scope
|
||||
application: string;
|
||||
// A cryptographic value used to prevent double registration or reuse of the same proof.
|
||||
nullifier: string;
|
||||
// Revealed data by users
|
||||
credentialSubject: {
|
||||
// Merkle root which is used to generate proof.
|
||||
merkle_root?: string;
|
||||
// Proved identity type, for passport this value is fixed as 1.
|
||||
attestation_id?: string;
|
||||
// Date when the proof is generated
|
||||
current_date?: string;
|
||||
// Revealed issuing state in the passport
|
||||
issuing_state?: string;
|
||||
// Revealed name in the passport
|
||||
name?: string;
|
||||
// Revealed passport number in the passport
|
||||
passport_number?: string;
|
||||
// Revealed nationality in the passport
|
||||
nationality?: string;
|
||||
// Revealed date of birth in the passport
|
||||
date_of_birth?: string;
|
||||
// Revealed gender in the passport
|
||||
gender?: string;
|
||||
// Revealed expiry date in the passport
|
||||
expiry_date?: string;
|
||||
// Result of older than
|
||||
older_than?: string;
|
||||
// Result of passport number ofac check
|
||||
passport_no_ofac?: string;
|
||||
// Result of name and date of birth ofac check
|
||||
name_and_dob_ofac?: string;
|
||||
// Result of name and year of birth ofac check
|
||||
name_and_yob_ofac?: string;
|
||||
};
|
||||
proof: {
|
||||
// Proof which is used for this verification
|
||||
value: {
|
||||
proof: Groth16Proof;
|
||||
publicSignals: PublicSignals;
|
||||
};
|
||||
// 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;
|
||||
}
|
||||
```
|
||||
|
||||
## How to return the result in your api implementation
|
||||
This backend SDK is designed to be used with APIs managed by third parties, and it communicates with Self's managed relayer to enable smooth usage of applications provided by Self.
|
||||
## API Implementation Example
|
||||
|
||||
When using it:
|
||||
1. Set the endpoint of the API that imports this backend SDK in SelfAppBuilder
|
||||
```typescript
|
||||
const selfApp = new SelfAppBuilder({
|
||||
appName: "Application name",
|
||||
scope: "Application id",
|
||||
endpoint: "API endpoint which imports this backend sdk",
|
||||
logoBase64: logo,
|
||||
userId,
|
||||
disclosures: {
|
||||
name: true,
|
||||
nationality: true,
|
||||
date_of_birth: true,
|
||||
passport_number: true,
|
||||
minimumAge: 20,
|
||||
excludedCountries: [
|
||||
"Exclude countries which you want"
|
||||
],
|
||||
ofac: true,
|
||||
}
|
||||
}).build();
|
||||
```
|
||||
|
||||
2. This API needs to return values in the following format:
|
||||
```typescript
|
||||
response: {
|
||||
200: t.Object({
|
||||
status: t.String(),
|
||||
result: t.Boolean(),
|
||||
}),
|
||||
500: t.Object({
|
||||
status: t.String(),
|
||||
result: t.Boolean(),
|
||||
message: t.String(),
|
||||
}),
|
||||
},
|
||||
```
|
||||
Bit more explanation to the values in the return value.
|
||||
|
||||
status: Indicates that the API processing has completed successfully
|
||||
|
||||
result: Contains the verification result from the SelfBackendVerifier
|
||||
|
||||
message: Represents the error details when an error occurs
|
||||
|
||||
Here is the little example to implement the api.
|
||||
Here's an example of implementing an API endpoint that uses the SDK:
|
||||
|
||||
```typescript
|
||||
try {
|
||||
const result = await selfBackendVerifier.verify(
|
||||
request.body.proof,
|
||||
request.body.publicSignals
|
||||
);
|
||||
return {
|
||||
status: "success",
|
||||
result: result.isValid,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
status: "error",
|
||||
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",
|
||||
};
|
||||
message: error instanceof Error ? error.message : 'Unknown error'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return res.status(405).json({ message: 'Method not allowed' });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# When you run the tests
|
||||
## Working with Country Codes
|
||||
|
||||
First you need to copy the abi files to the sdk/core/src/abi folder.
|
||||
The SDK provides a `countryCodes` object for referencing ISO country codes:
|
||||
|
||||
```
|
||||
cd ../sdk/core
|
||||
yarn run copy-abi
|
||||
```typescript
|
||||
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
|
||||
);
|
||||
```
|
||||
|
||||
Then you need to run the local hardhat node.
|
||||
## Integration with SelfQRcode
|
||||
|
||||
```
|
||||
cd contracts
|
||||
npx hardhat node
|
||||
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:
|
||||
|
||||
```typescript
|
||||
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();
|
||||
```
|
||||
|
||||
Then you need to run the tests in the contract dir.
|
||||
## Example
|
||||
|
||||
```
|
||||
yarn run test:sdkcore:local
|
||||
```
|
||||
For a more advanced implementation example, see the [playground](https://github.com/selfxyz/playground/blob/main/pages/api/verify.ts).
|
||||
@@ -1,3 +1,5 @@
|
||||
import { SelfBackendVerifier } from './src/SelfBackendVerifier';
|
||||
import { getUserIdentifier } from './src/utils/utils';
|
||||
import { countryCodes } from '../../common/src/constants/constants';
|
||||
|
||||
export { SelfBackendVerifier };
|
||||
export { SelfBackendVerifier, getUserIdentifier, countryCodes };
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "@openpassport/core",
|
||||
"version": "0.0.24",
|
||||
"name": "@selfxyz/core",
|
||||
"version": "0.0.3",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/zk-passport/openpassport"
|
||||
"url": "https://github.com/celo-org/self"
|
||||
},
|
||||
"license": "MIT",
|
||||
"author": "turnoffthiscomputer",
|
||||
@@ -21,31 +21,15 @@
|
||||
"install-sdk": "cd ../common && yarn && cd ../sdk && yarn",
|
||||
"lint": "prettier --check .",
|
||||
"prepublishOnly": "npm run build",
|
||||
"publish": "npm publish --access public",
|
||||
"test": "yarn ts-mocha -p ./tsconfig.json tests/openPassportVerifier.test.ts --exit"
|
||||
"publish": "npm publish --access public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@openpassport/zk-kit-imt": "^0.0.5",
|
||||
"@openpassport/zk-kit-lean-imt": "^0.0.6",
|
||||
"@openpassport/zk-kit-smt": "^0.0.1",
|
||||
"@types/react": "^18.3.4",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"elliptic": "^6.5.7",
|
||||
"ethers": "^6.13.5",
|
||||
"fs": "^0.0.1-security",
|
||||
"js-sha1": "^0.7.0",
|
||||
"js-sha256": "^0.11.0",
|
||||
"js-sha512": "^0.9.0",
|
||||
"msgpack-lite": "^0.1.26",
|
||||
"next": "^14.2.8",
|
||||
"node-forge": "https://github.com/remicolin/forge",
|
||||
"pako": "^2.1.0",
|
||||
"pkijs": "^3.2.4",
|
||||
"poseidon-lite": "^0.2.0",
|
||||
"snarkjs": "^0.7.4",
|
||||
"uuid": "^10.0.0",
|
||||
"zlib": "^1.0.5"
|
||||
"snarkjs": "^0.7.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.3.6",
|
||||
@@ -57,15 +41,9 @@
|
||||
"@types/node-forge": "^1.3.5",
|
||||
"@types/pako": "^2.0.3",
|
||||
"@types/snarkjs": "^0.7.8",
|
||||
"asn1js": "^3.0.5",
|
||||
"axios": "^1.7.2",
|
||||
"chai": "^4.3.8",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"dotenv": "^16.4.5",
|
||||
"mocha": "^10.3.0",
|
||||
"prettier": "^3.3.3",
|
||||
"ts-loader": "^9.5.1",
|
||||
"ts-mocha": "^10.0.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.4.5"
|
||||
},
|
||||
|
||||
@@ -5,28 +5,39 @@ import { ethers } from 'ethers';
|
||||
import { PublicSignals } from 'snarkjs';
|
||||
import {
|
||||
countryCodes,
|
||||
countryNames,
|
||||
getCountryCode,
|
||||
} from '../../../common/src/constants/constants';
|
||||
import type { SelfVerificationResult } from '../../../common/src/utils/selfAttestation';
|
||||
import { castToScope } from '../../../common/src/utils/circuits/uuid';
|
||||
import { castToScope, castToUserIdentifier, UserIdType } from '../../../common/src/utils/circuits/uuid';
|
||||
import { CIRCUIT_CONSTANTS, revealedDataTypes } from '../../../common/src/constants/constants';
|
||||
import { packForbiddenCountriesList } from '../../../common/src/utils/contracts/formatCallData';
|
||||
|
||||
type CountryCode = (typeof countryCodes)[keyof typeof countryCodes];
|
||||
|
||||
export class SelfBackendVerifier {
|
||||
protected scope: string;
|
||||
protected attestationId: number = 1;
|
||||
protected user_identifier_type: UserIdType = 'uuid';
|
||||
protected targetRootTimestamp: { enabled: boolean; value: number } = {
|
||||
enabled: false,
|
||||
value: 0,
|
||||
};
|
||||
|
||||
protected nationality: { enabled: boolean; value: (typeof countryNames)[number] } = {
|
||||
protected nationality: {
|
||||
enabled: boolean;
|
||||
value: CountryCode;
|
||||
} = {
|
||||
enabled: false,
|
||||
value: '' as (typeof countryNames)[number],
|
||||
value: '' as CountryCode,
|
||||
};
|
||||
protected minimumAge: { enabled: boolean; value: string } = { enabled: false, value: '18' };
|
||||
protected excludedCountries: { enabled: boolean; value: (typeof countryNames)[number][] } = {
|
||||
protected minimumAge: { enabled: boolean; value: string } = {
|
||||
enabled: false,
|
||||
value: '18',
|
||||
};
|
||||
protected excludedCountries: {
|
||||
enabled: boolean;
|
||||
value: CountryCode[];
|
||||
} = {
|
||||
enabled: false,
|
||||
value: [],
|
||||
};
|
||||
@@ -34,20 +45,22 @@ export class SelfBackendVerifier {
|
||||
protected nameAndDobOfac: boolean = false;
|
||||
protected nameAndYobOfac: boolean = false;
|
||||
|
||||
protected registryContract: any;
|
||||
protected verifyAllContract: any;
|
||||
protected registryContract: ethers.Contract;
|
||||
protected verifyAllContract: ethers.Contract;
|
||||
|
||||
constructor(rpcUrl: string, scope: string) {
|
||||
constructor(
|
||||
rpcUrl: string,
|
||||
scope: string,
|
||||
user_identifier_type: UserIdType = 'uuid'
|
||||
) {
|
||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
||||
this.registryContract = new ethers.Contract(REGISTRY_ADDRESS, registryAbi, provider);
|
||||
this.verifyAllContract = new ethers.Contract(VERIFYALL_ADDRESS, verifyAllAbi, provider);
|
||||
this.scope = scope;
|
||||
this.user_identifier_type = user_identifier_type;
|
||||
}
|
||||
|
||||
public async verify(
|
||||
proof: any,
|
||||
publicSignals: PublicSignals
|
||||
): Promise<SelfVerificationResult> {
|
||||
public async verify(proof: any, publicSignals: PublicSignals): Promise<SelfVerificationResult> {
|
||||
const excludedCountryCodes = this.excludedCountries.value.map((country) =>
|
||||
getCountryCode(country)
|
||||
);
|
||||
@@ -69,7 +82,10 @@ export class SelfBackendVerifier {
|
||||
ofacEnabled: [this.passportNoOfac, this.nameAndDobOfac, this.nameAndYobOfac],
|
||||
vcAndDiscloseProof: {
|
||||
a: proof.a,
|
||||
b: [[proof.b[0][1], proof.b[0][0]],[proof.b[1][1], proof.b[1][0]]],
|
||||
b: [
|
||||
[proof.b[0][1], proof.b[0][0]],
|
||||
[proof.b[1][1], proof.b[1][0]],
|
||||
],
|
||||
c: proof.c,
|
||||
pubSignals: publicSignals,
|
||||
},
|
||||
@@ -92,6 +108,11 @@ export class SelfBackendVerifier {
|
||||
const currentRoot = await this.registryContract.getIdentityCommitmentMerkleRoot();
|
||||
const timestamp = await this.registryContract.rootTimestamps(currentRoot);
|
||||
|
||||
const user_identifier = castToUserIdentifier(
|
||||
BigInt(publicSignals[CIRCUIT_CONSTANTS.VC_AND_DISCLOSE_USER_IDENTIFIER_INDEX]),
|
||||
this.user_identifier_type
|
||||
);
|
||||
|
||||
let result: any;
|
||||
try {
|
||||
result = await this.verifyAllContract.verifyAll(timestamp, vcAndDiscloseHubProof, types);
|
||||
@@ -104,18 +125,18 @@ export class SelfBackendVerifier {
|
||||
isValidProof: false,
|
||||
isValidNationality: false,
|
||||
},
|
||||
userId: publicSignals[CIRCUIT_CONSTANTS.VC_AND_DISCLOSE_USER_IDENTIFIER_INDEX],
|
||||
userId: user_identifier,
|
||||
application: this.scope,
|
||||
nullifier: publicSignals[CIRCUIT_CONSTANTS.VC_AND_DISCLOSE_NULLIFIER_INDEX],
|
||||
credentialSubject: null,
|
||||
credentialSubject: {},
|
||||
proof: {
|
||||
value: {
|
||||
proof: proof,
|
||||
publicSignals: publicSignals,
|
||||
},
|
||||
},
|
||||
error: error
|
||||
}
|
||||
error: error,
|
||||
};
|
||||
}
|
||||
|
||||
let isValidNationality = true;
|
||||
@@ -136,10 +157,10 @@ export class SelfBackendVerifier {
|
||||
date_of_birth: result[0][revealedDataTypes.date_of_birth],
|
||||
gender: result[0][revealedDataTypes.gender],
|
||||
expiry_date: result[0][revealedDataTypes.expiry_date],
|
||||
older_than: result[0][revealedDataTypes.older_than],
|
||||
passport_no_ofac: result[0][revealedDataTypes.passport_no_ofac],
|
||||
name_and_dob_ofac: result[0][revealedDataTypes.name_and_dob_ofac],
|
||||
name_and_yob_ofac: result[0][revealedDataTypes.name_and_yob_ofac],
|
||||
older_than: result[0][revealedDataTypes.older_than].toString(),
|
||||
passport_no_ofac: result[0][revealedDataTypes.passport_no_ofac].toString() === '1',
|
||||
name_and_dob_ofac: result[0][revealedDataTypes.name_and_dob_ofac].toString() === '1',
|
||||
name_and_yob_ofac: result[0][revealedDataTypes.name_and_yob_ofac].toString() === '1',
|
||||
};
|
||||
|
||||
const attestation: SelfVerificationResult = {
|
||||
@@ -150,7 +171,7 @@ export class SelfBackendVerifier {
|
||||
isValidProof: result[1],
|
||||
isValidNationality: isValidNationality,
|
||||
},
|
||||
userId: publicSignals[CIRCUIT_CONSTANTS.VC_AND_DISCLOSE_USER_IDENTIFIER_INDEX],
|
||||
userId: user_identifier,
|
||||
application: this.scope,
|
||||
nullifier: publicSignals[CIRCUIT_CONSTANTS.VC_AND_DISCLOSE_NULLIFIER_INDEX],
|
||||
credentialSubject: credentialSubject,
|
||||
@@ -160,7 +181,7 @@ export class SelfBackendVerifier {
|
||||
publicSignals: publicSignals,
|
||||
},
|
||||
},
|
||||
error: result[2]
|
||||
error: result[2],
|
||||
};
|
||||
|
||||
return attestation;
|
||||
@@ -177,12 +198,12 @@ export class SelfBackendVerifier {
|
||||
return this;
|
||||
}
|
||||
|
||||
setNationality(country: (typeof countryNames)[number]): this {
|
||||
setNationality(country: CountryCode): this {
|
||||
this.nationality = { enabled: true, value: country };
|
||||
return this;
|
||||
}
|
||||
|
||||
excludeCountries(...countries: (typeof countryNames)[number][]): this {
|
||||
excludeCountries(...countries: CountryCode[]): this {
|
||||
if (countries.length > 40) {
|
||||
throw new Error('Number of excluded countries cannot exceed 40');
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,294 +1,294 @@
|
||||
export const verifyAllAbi = [
|
||||
{
|
||||
"inputs": [
|
||||
inputs: [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "hubAddress",
|
||||
"type": "address"
|
||||
internalType: 'address',
|
||||
name: 'hubAddress',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "registryAddress",
|
||||
"type": "address"
|
||||
}
|
||||
internalType: 'address',
|
||||
name: 'registryAddress',
|
||||
type: 'address',
|
||||
},
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "constructor"
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'constructor',
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
inputs: [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
}
|
||||
internalType: 'address',
|
||||
name: 'owner',
|
||||
type: 'address',
|
||||
},
|
||||
],
|
||||
"name": "OwnableInvalidOwner",
|
||||
"type": "error"
|
||||
name: 'OwnableInvalidOwner',
|
||||
type: 'error',
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
inputs: [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "account",
|
||||
"type": "address"
|
||||
}
|
||||
internalType: 'address',
|
||||
name: 'account',
|
||||
type: 'address',
|
||||
},
|
||||
],
|
||||
"name": "OwnableUnauthorizedAccount",
|
||||
"type": "error"
|
||||
name: 'OwnableUnauthorizedAccount',
|
||||
type: 'error',
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "previousOwner",
|
||||
"type": "address"
|
||||
indexed: true,
|
||||
internalType: 'address',
|
||||
name: 'previousOwner',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "newOwner",
|
||||
"type": "address"
|
||||
}
|
||||
indexed: true,
|
||||
internalType: 'address',
|
||||
name: 'newOwner',
|
||||
type: 'address',
|
||||
},
|
||||
],
|
||||
"name": "OwnershipTransferred",
|
||||
"type": "event"
|
||||
name: 'OwnershipTransferred',
|
||||
type: 'event',
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "hub",
|
||||
"outputs": [
|
||||
inputs: [],
|
||||
name: 'hub',
|
||||
outputs: [
|
||||
{
|
||||
"internalType": "contract IIdentityVerificationHubV1",
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
internalType: 'contract IIdentityVerificationHubV1',
|
||||
name: '',
|
||||
type: 'address',
|
||||
},
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "owner",
|
||||
"outputs": [
|
||||
inputs: [],
|
||||
name: 'owner',
|
||||
outputs: [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
internalType: 'address',
|
||||
name: '',
|
||||
type: 'address',
|
||||
},
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "registry",
|
||||
"outputs": [
|
||||
inputs: [],
|
||||
name: 'registry',
|
||||
outputs: [
|
||||
{
|
||||
"internalType": "contract IIdentityRegistryV1",
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
internalType: 'contract IIdentityRegistryV1',
|
||||
name: '',
|
||||
type: 'address',
|
||||
},
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "renounceOwnership",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
inputs: [],
|
||||
name: 'renounceOwnership',
|
||||
outputs: [],
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
inputs: [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "hubAddress",
|
||||
"type": "address"
|
||||
}
|
||||
internalType: 'address',
|
||||
name: 'hubAddress',
|
||||
type: 'address',
|
||||
},
|
||||
],
|
||||
"name": "setHub",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
name: 'setHub',
|
||||
outputs: [],
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
inputs: [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "registryAddress",
|
||||
"type": "address"
|
||||
}
|
||||
internalType: 'address',
|
||||
name: 'registryAddress',
|
||||
type: 'address',
|
||||
},
|
||||
],
|
||||
"name": "setRegistry",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
name: 'setRegistry',
|
||||
outputs: [],
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
inputs: [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "newOwner",
|
||||
"type": "address"
|
||||
}
|
||||
internalType: 'address',
|
||||
name: 'newOwner',
|
||||
type: 'address',
|
||||
},
|
||||
],
|
||||
"name": "transferOwnership",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
name: 'transferOwnership',
|
||||
outputs: [],
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
inputs: [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "targetRootTimestamp",
|
||||
"type": "uint256"
|
||||
internalType: 'uint256',
|
||||
name: 'targetRootTimestamp',
|
||||
type: 'uint256',
|
||||
},
|
||||
{
|
||||
"components": [
|
||||
components: [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "olderThanEnabled",
|
||||
"type": "bool"
|
||||
internalType: 'bool',
|
||||
name: 'olderThanEnabled',
|
||||
type: 'bool',
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "olderThan",
|
||||
"type": "uint256"
|
||||
internalType: 'uint256',
|
||||
name: 'olderThan',
|
||||
type: 'uint256',
|
||||
},
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "forbiddenCountriesEnabled",
|
||||
"type": "bool"
|
||||
internalType: 'bool',
|
||||
name: 'forbiddenCountriesEnabled',
|
||||
type: 'bool',
|
||||
},
|
||||
{
|
||||
"internalType": "uint256[4]",
|
||||
"name": "forbiddenCountriesListPacked",
|
||||
"type": "uint256[4]"
|
||||
internalType: 'uint256[4]',
|
||||
name: 'forbiddenCountriesListPacked',
|
||||
type: 'uint256[4]',
|
||||
},
|
||||
{
|
||||
"internalType": "bool[3]",
|
||||
"name": "ofacEnabled",
|
||||
"type": "bool[3]"
|
||||
internalType: 'bool[3]',
|
||||
name: 'ofacEnabled',
|
||||
type: 'bool[3]',
|
||||
},
|
||||
{
|
||||
"components": [
|
||||
components: [
|
||||
{
|
||||
"internalType": "uint256[2]",
|
||||
"name": "a",
|
||||
"type": "uint256[2]"
|
||||
internalType: 'uint256[2]',
|
||||
name: 'a',
|
||||
type: 'uint256[2]',
|
||||
},
|
||||
{
|
||||
"internalType": "uint256[2][2]",
|
||||
"name": "b",
|
||||
"type": "uint256[2][2]"
|
||||
internalType: 'uint256[2][2]',
|
||||
name: 'b',
|
||||
type: 'uint256[2][2]',
|
||||
},
|
||||
{
|
||||
"internalType": "uint256[2]",
|
||||
"name": "c",
|
||||
"type": "uint256[2]"
|
||||
internalType: 'uint256[2]',
|
||||
name: 'c',
|
||||
type: 'uint256[2]',
|
||||
},
|
||||
{
|
||||
"internalType": "uint256[21]",
|
||||
"name": "pubSignals",
|
||||
"type": "uint256[21]"
|
||||
}
|
||||
internalType: 'uint256[21]',
|
||||
name: 'pubSignals',
|
||||
type: 'uint256[21]',
|
||||
},
|
||||
],
|
||||
"internalType": "struct IVcAndDiscloseCircuitVerifier.VcAndDiscloseProof",
|
||||
"name": "vcAndDiscloseProof",
|
||||
"type": "tuple"
|
||||
}
|
||||
internalType: 'struct IVcAndDiscloseCircuitVerifier.VcAndDiscloseProof',
|
||||
name: 'vcAndDiscloseProof',
|
||||
type: 'tuple',
|
||||
},
|
||||
],
|
||||
"internalType": "struct IIdentityVerificationHubV1.VcAndDiscloseHubProof",
|
||||
"name": "proof",
|
||||
"type": "tuple"
|
||||
internalType: 'struct IIdentityVerificationHubV1.VcAndDiscloseHubProof',
|
||||
name: 'proof',
|
||||
type: 'tuple',
|
||||
},
|
||||
{
|
||||
"internalType": "enum IIdentityVerificationHubV1.RevealedDataType[]",
|
||||
"name": "types",
|
||||
"type": "uint8[]"
|
||||
}
|
||||
internalType: 'enum IIdentityVerificationHubV1.RevealedDataType[]',
|
||||
name: 'types',
|
||||
type: 'uint8[]',
|
||||
},
|
||||
],
|
||||
"name": "verifyAll",
|
||||
"outputs": [
|
||||
name: 'verifyAll',
|
||||
outputs: [
|
||||
{
|
||||
"components": [
|
||||
components: [
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "issuingState",
|
||||
"type": "string"
|
||||
internalType: 'string',
|
||||
name: 'issuingState',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
"internalType": "string[]",
|
||||
"name": "name",
|
||||
"type": "string[]"
|
||||
internalType: 'string[]',
|
||||
name: 'name',
|
||||
type: 'string[]',
|
||||
},
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "passportNumber",
|
||||
"type": "string"
|
||||
internalType: 'string',
|
||||
name: 'passportNumber',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "nationality",
|
||||
"type": "string"
|
||||
internalType: 'string',
|
||||
name: 'nationality',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "dateOfBirth",
|
||||
"type": "string"
|
||||
internalType: 'string',
|
||||
name: 'dateOfBirth',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "gender",
|
||||
"type": "string"
|
||||
internalType: 'string',
|
||||
name: 'gender',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "expiryDate",
|
||||
"type": "string"
|
||||
internalType: 'string',
|
||||
name: 'expiryDate',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "olderThan",
|
||||
"type": "uint256"
|
||||
internalType: 'uint256',
|
||||
name: 'olderThan',
|
||||
type: 'uint256',
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "passportNoOfac",
|
||||
"type": "uint256"
|
||||
internalType: 'uint256',
|
||||
name: 'passportNoOfac',
|
||||
type: 'uint256',
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "nameAndDobOfac",
|
||||
"type": "uint256"
|
||||
internalType: 'uint256',
|
||||
name: 'nameAndDobOfac',
|
||||
type: 'uint256',
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "nameAndYobOfac",
|
||||
"type": "uint256"
|
||||
}
|
||||
internalType: 'uint256',
|
||||
name: 'nameAndYobOfac',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
"internalType": "struct IIdentityVerificationHubV1.ReadableRevealedData",
|
||||
"name": "",
|
||||
"type": "tuple"
|
||||
internalType: 'struct IIdentityVerificationHubV1.ReadableRevealedData',
|
||||
name: '',
|
||||
type: 'tuple',
|
||||
},
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
internalType: 'bool',
|
||||
name: '',
|
||||
type: 'bool',
|
||||
},
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "",
|
||||
"type": "string"
|
||||
}
|
||||
internalType: 'string',
|
||||
name: '',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
}
|
||||
];
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { CIRCUIT_CONSTANTS } from '../../../../common/src/constants/constants';
|
||||
import { castToUserIdentifier, UserIdType } from '../../../../common/src/utils/circuits/uuid';
|
||||
import { BigNumberish } from 'ethers';
|
||||
import { PublicSignals } from 'snarkjs';
|
||||
|
||||
export function parseSolidityCalldata<T>(rawCallData: string, _type: T): T {
|
||||
const parsed = JSON.parse('[' + rawCallData + ']');
|
||||
@@ -13,3 +16,10 @@ export function parseSolidityCalldata<T>(rawCallData: string, _type: T): T {
|
||||
pubSignals: parsed[3].map((x: string) => x.replace(/"/g, '')) as BigNumberish[],
|
||||
} as T;
|
||||
}
|
||||
|
||||
export async function getUserIdentifier(publicSignals: PublicSignals, user_identifier_type: UserIdType = 'uuid'): Promise<string> {
|
||||
return castToUserIdentifier(
|
||||
BigInt(publicSignals[CIRCUIT_CONSTANTS.VC_AND_DISCLOSE_USER_IDENTIFIER_INDEX]),
|
||||
user_identifier_type
|
||||
);
|
||||
}
|
||||
|
||||
Binary file not shown.
17
sdk/qrcode/OpenPassportQRcode.d.ts
vendored
17
sdk/qrcode/OpenPassportQRcode.d.ts
vendored
@@ -1,17 +0,0 @@
|
||||
import React from 'react';
|
||||
import { SelfAttestation, SelfVerifier } from '@openpassport/core';
|
||||
import { UserIdType } from '../../common/src/utils/circuits/uuid';
|
||||
|
||||
interface OpenPassportQRcodeProps {
|
||||
appName: string;
|
||||
userId: string;
|
||||
userIdType: UserIdType;
|
||||
selfVerifier: SelfVerifier;
|
||||
onSuccess: (attestation: SelfAttestation) => void;
|
||||
websocketUrl?: string;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
declare const OpenPassportQRcode: React.FC<OpenPassportQRcodeProps>;
|
||||
|
||||
export { OpenPassportQRcode, OpenPassportQRcodeProps };
|
||||
@@ -1,127 +1,193 @@
|
||||
# Installation
|
||||
# @selfxyz/qrcode
|
||||
|
||||
A React component for generating QR codes for Self passport verification.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
yarn add @selfxyz/sdk
|
||||
npm install @selfxyz/qrcode
|
||||
# or
|
||||
yarn add @selfxyz/qrcode
|
||||
```
|
||||
|
||||
# Generate a QR code
|
||||
## Basic Usage
|
||||
|
||||
### Create an AppType type object:
|
||||
### 1. Import the SelfQRcodeWrapper component
|
||||
|
||||
```typescript
|
||||
import { AppType } from '@selfxyz/sdk';
|
||||
const appName = '🤠 Cowboy App';
|
||||
const scope = 'cowboyApp';
|
||||
const userID = 'user1234';
|
||||
const sessionID = uuidv4();
|
||||
```tsx
|
||||
import SelfQRcodeWrapper, { SelfApp, SelfAppBuilder } from '@selfxyz/qrcode';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
```
|
||||
|
||||
const cowboyApp: AppType = {
|
||||
name: appName,
|
||||
scope,
|
||||
userId: userID,
|
||||
sessionId: sessionID,
|
||||
circuit: 'prove',
|
||||
arguments: {
|
||||
disclosureOptions: { older_than: '18', nationality: 'France' },
|
||||
### 2. Create a SelfApp instance using SelfAppBuilder
|
||||
|
||||
```tsx
|
||||
// Generate a unique user ID
|
||||
const userId = uuidv4();
|
||||
|
||||
// Create a SelfApp instance using the builder pattern
|
||||
const selfApp = new SelfAppBuilder({
|
||||
appName: "My App",
|
||||
scope: "my-app-scope",
|
||||
endpoint: "https://myapp.com/api/verify",
|
||||
logoBase64: "base64EncodedLogo", // Optional
|
||||
userId,
|
||||
// Optional disclosure requirements
|
||||
disclosures: {
|
||||
// DG1 disclosures
|
||||
issuing_state: true,
|
||||
name: true,
|
||||
nationality: true,
|
||||
date_of_birth: true,
|
||||
passport_number: true,
|
||||
gender: true,
|
||||
expiry_date: true,
|
||||
// Custom verification rules
|
||||
minimumAge: 18,
|
||||
excludedCountries: ["IRN", "PRK"],
|
||||
ofac: true,
|
||||
},
|
||||
};
|
||||
}).build();
|
||||
```
|
||||
|
||||
| Parameter | Optional | Description |
|
||||
| ----------- | -------- | ------------------------------------------------------------- |
|
||||
| `scope` | M | The scope of your application, is unique for each application |
|
||||
| `name` | M | Name of the application |
|
||||
| `userId` | M | User ID |
|
||||
| `sessionId` | M | Session ID |
|
||||
| `circuit` | M | Circuit to use, only `prove` is available for now |
|
||||
| `arguments` | O | Optional disclosure options, based on passport attributes |
|
||||
### 3. Render the QR code component
|
||||
|
||||
### Display the QR code
|
||||
|
||||
Use the appType object defined above to generate a QR code.
|
||||
The generated QR code is an `HTML element` that you can display in your app.
|
||||
|
||||
```typescript
|
||||
import { QRCodeGenerator } from '@selfxyz/sdk';
|
||||
|
||||
// [...] define cowboyApp as described above
|
||||
|
||||
const qrCode: HTMLElement = await QRCodeGenerator.generateQRCode(cowboyApp);
|
||||
```tsx
|
||||
function MyComponent() {
|
||||
return (
|
||||
<SelfQRcodeWrapper
|
||||
selfApp={selfApp}
|
||||
onSuccess={() => {
|
||||
console.log('Verification successful');
|
||||
// Perform actions after successful verification
|
||||
}}
|
||||
darkMode={false} // Optional: set to true for dark mode
|
||||
size={300} // Optional: customize QR code size (default: 300)
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
# Verify the proof
|
||||
`SelfQRcodeWrapper` wraps `SelfQRcode` to prevent server-side rendering when using nextjs. When not using nextjs, `SelfQRcode` can be used instead.
|
||||
|
||||
## 1 Step flow
|
||||
## SelfApp Configuration
|
||||
|
||||
To use the `OpenPassportVerifier`, import and initialize it as follows:
|
||||
The `SelfAppBuilder` allows you to configure your application's verification requirements:
|
||||
|
||||
```typescript
|
||||
import { OpenPassportVerifier } from '@selfxyz/sdk';
|
||||
const verifier = new OpenPassportVerifier({
|
||||
scope: 'cowboyApp',
|
||||
requirements: [
|
||||
['older_than', '18'],
|
||||
['nationality', 'France'],
|
||||
],
|
||||
});
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `appName` | string | Yes | The name of your application |
|
||||
| `scope` | string | Yes | A unique identifier for your application |
|
||||
| `endpoint` | string | Yes | The endpoint that will verify the proof |
|
||||
| `logoBase64` | string | No | Base64-encoded logo to display in the Self app |
|
||||
| `userId` | string | Yes | Unique identifier for the user |
|
||||
| `disclosures` | object | No | Disclosure and verification requirements |
|
||||
|
||||
### Disclosure Options
|
||||
|
||||
The `disclosures` object can include the following options:
|
||||
|
||||
| Option | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| `issuing_state` | boolean | Request disclosure of passport issuing state |
|
||||
| `name` | boolean | Request disclosure of the user's name |
|
||||
| `nationality` | boolean | Request disclosure of nationality |
|
||||
| `date_of_birth` | boolean | Request disclosure of birth date |
|
||||
| `passport_number` | boolean | Request disclosure of passport number |
|
||||
| `gender` | boolean | Request disclosure of gender |
|
||||
| `expiry_date` | boolean | Request disclosure of passport expiry date |
|
||||
| `minimumAge` | number | Verify the user is at least this age |
|
||||
| `excludedCountries` | string[] | Array of country codes to exclude |
|
||||
| `ofac` | boolean | Enable OFAC compliance check |
|
||||
|
||||
## Component Props
|
||||
|
||||
The `SelfQRcodeWrapper` component accepts the following props:
|
||||
|
||||
| Prop | Type | Required | Default | Description |
|
||||
|------|------|----------|---------|-------------|
|
||||
| `selfApp` | SelfApp | Yes | - | The SelfApp configuration object |
|
||||
| `onSuccess` | () => void | Yes | - | Callback function executed on successful verification |
|
||||
| `websocketUrl` | string | No | WS_DB_RELAYER | Custom WebSocket URL for verification |
|
||||
| `size` | number | No | 300 | QR code size in pixels |
|
||||
| `darkMode` | boolean | No | false | Enable dark mode styling |
|
||||
| `children` | React.ReactNode | No | - | Custom children to render |
|
||||
|
||||
## Complete Example
|
||||
|
||||
Here's a complete example of how to implement the Self QR code in a React application:
|
||||
|
||||
```tsx
|
||||
'use client';
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import SelfQRcodeWrapper, { SelfApp, SelfAppBuilder } from '@selfxyz/qrcode';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
function VerificationPage() {
|
||||
const [userId, setUserId] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
// Generate a user ID when the component mounts
|
||||
setUserId(uuidv4());
|
||||
}, []);
|
||||
|
||||
if (!userId) return null;
|
||||
|
||||
// Create the SelfApp configuration
|
||||
const selfApp = new SelfAppBuilder({
|
||||
appName: "My Application",
|
||||
scope: "my-application-scope",
|
||||
endpoint: "https://myapp.com/api/verify",
|
||||
userId,
|
||||
disclosures: {
|
||||
// Request passport information
|
||||
name: true,
|
||||
nationality: true,
|
||||
date_of_birth: true,
|
||||
|
||||
// Set verification rules
|
||||
minimumAge: 18,
|
||||
excludedCountries: ["IRN", "PRK", "RUS"],
|
||||
ofac: true,
|
||||
},
|
||||
}).build();
|
||||
|
||||
return (
|
||||
<div className="verification-container">
|
||||
<h1>Verify Your Identity</h1>
|
||||
<p>Scan this QR code with the Self app to verify your identity</p>
|
||||
|
||||
<SelfQRcodeWrapper
|
||||
selfApp={selfApp}
|
||||
onSuccess={() => {
|
||||
// Handle successful verification
|
||||
console.log("Verification successful!");
|
||||
// Redirect or update UI
|
||||
}}
|
||||
size={350}
|
||||
/>
|
||||
|
||||
<p className="text-sm text-gray-500">
|
||||
User ID: {userId.substring(0, 8)}...
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default VerificationPage;
|
||||
```
|
||||
|
||||
### Parameters for `OpenPassportVerifier`
|
||||
## Example
|
||||
|
||||
| Parameter | Optional | Description |
|
||||
| --------------- | -------- | --------------------------------------------------------------------------------- |
|
||||
| `scope` | M | The scope of your application, is unique for each application. |
|
||||
| `attestationId` | O | The ID of the attestation, defaults to `PASSPORT_ATTESTATION_ID`. |
|
||||
| `requirements` | O | An array of requirements, each an array with an attribute and its expected value. |
|
||||
| `rpcUrl` | O | The RPC URL to connect to the blockchain, defaults to `DEFAULT_RPC_URL`. |
|
||||
| `dev_mode` | O | Allow users with generated passport to pass the verification. |
|
||||
For a more comprehensive and interactive example, please refer to the [playground](https://github.com/selfxyz/playground/blob/main/app/page.tsx).
|
||||
|
||||
### Verify the proof
|
||||
## Verification Flow
|
||||
|
||||
The function fired from the OpenPassport app will send an `OpenPassportVerifierInputs` object.
|
||||
1. Your application displays the QR code to the user
|
||||
2. The user scans the QR code with the Self app
|
||||
3. The Self app guides the user through the passport verification process
|
||||
4. The proof is generated and sent to your verification endpoint
|
||||
5. Upon successful verification, the `onSuccess` callback is triggered
|
||||
|
||||
```typescript
|
||||
const result: OpenPassportVerifierReport = await verifier.verify(openPassportVerifierInputs);
|
||||
```
|
||||
|
||||
From the `result` object, you can inspect the validity of any submitted attribute.
|
||||
To check the overall validity of the proof, you can inspect the `valid` attribute.
|
||||
|
||||
```typescript
|
||||
require(result.valid);
|
||||
```
|
||||
|
||||
Nullifier and user identifier are accessible from the `result` object.
|
||||
|
||||
```typescript
|
||||
const nullifier: number = result.nullifier;
|
||||
const user_identifier: number = result.user_identifier;
|
||||
```
|
||||
|
||||
## 2 Steps flow
|
||||
|
||||
### 🚧 Work in progress 🚧
|
||||
|
||||
# Development
|
||||
|
||||
Install the dependencies
|
||||
|
||||
```bash
|
||||
yarn install-sdk
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
To run the tests, you need to download the circuits and the zkey files from the AWS s3 bucket.
|
||||
This script will also compile the circuits to generate the wasm files.
|
||||
Make sure that the circuits in the circuits folder are up to date with the AWS zkey files.
|
||||
|
||||
```bash
|
||||
yarn download-circuits
|
||||
```
|
||||
|
||||
Then run the tests with the following command:
|
||||
|
||||
```bash
|
||||
yarn test
|
||||
```
|
||||
The QR code component displays the current verification status with an LED indicator and changes its appearance based on the verification state.
|
||||
@@ -16,15 +16,16 @@ const QRCodeSVG = dynamic(() => import('qrcode.react').then((mod) => mod.QRCodeS
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
interface OpenPassportQRcodeProps {
|
||||
interface SelfQRcodeProps {
|
||||
selfApp: SelfApp;
|
||||
onSuccess: () => void;
|
||||
websocketUrl?: string;
|
||||
size?: number;
|
||||
darkMode?: boolean;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
// Create a wrapper component that handles client-side rendering
|
||||
const OpenPassportQRcodeWrapper: React.FC<OpenPassportQRcodeProps> = (props) => {
|
||||
const SelfQRcodeWrapper = (props: SelfQRcodeProps) => {
|
||||
const [isClient, setIsClient] = useState(false);
|
||||
useEffect(() => {
|
||||
setIsClient(true);
|
||||
@@ -33,16 +34,16 @@ const OpenPassportQRcodeWrapper: React.FC<OpenPassportQRcodeProps> = (props) =>
|
||||
if (!isClient) {
|
||||
return null;
|
||||
}
|
||||
return <OpenPassportQRcode {...props} />;
|
||||
return <SelfQRcode {...props} />;
|
||||
};
|
||||
|
||||
// Your existing OpenPassportQRcode component
|
||||
const OpenPassportQRcode: React.FC<OpenPassportQRcodeProps> = ({
|
||||
const SelfQRcode = ({
|
||||
selfApp,
|
||||
onSuccess,
|
||||
websocketUrl = WS_DB_RELAYER,
|
||||
size = 300,
|
||||
}) => {
|
||||
darkMode = false,
|
||||
}: SelfQRcodeProps) => {
|
||||
const [proofStep, setProofStep] = useState(QRcodeSteps.WAITING_FOR_MOBILE);
|
||||
const [proofVerified, setProofVerified] = useState(false);
|
||||
const [sessionId] = useState(uuidv4());
|
||||
@@ -116,6 +117,8 @@ const OpenPassportQRcode: React.FC<OpenPassportQRcodeProps> = ({
|
||||
<QRCodeSVG
|
||||
value={generateUniversalLink()}
|
||||
size={size}
|
||||
bgColor={darkMode ? '#000000' : '#ffffff'}
|
||||
fgColor={darkMode ? '#ffffff' : '#000000'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -128,7 +131,7 @@ const OpenPassportQRcode: React.FC<OpenPassportQRcodeProps> = ({
|
||||
};
|
||||
|
||||
// Export the wrapper component as the default export
|
||||
export default OpenPassportQRcodeWrapper;
|
||||
export default SelfQRcodeWrapper;
|
||||
|
||||
// Also export other components/types that might be needed
|
||||
export { OpenPassportQRcode, SelfApp, SelfAppBuilder };
|
||||
export { SelfQRcode, SelfApp, SelfAppBuilder };
|
||||
@@ -1,7 +1,7 @@
|
||||
import OpenPassportQRcodeWrapper, { OpenPassportQRcode, SelfApp, SelfAppBuilder } from './OpenPassportQRcode';
|
||||
import SelfQRcodeWrapper, { SelfQRcode, SelfApp, SelfAppBuilder } from './SelfQRcode';
|
||||
import { WebAppInfo } from './utils/websocket';
|
||||
|
||||
export default OpenPassportQRcodeWrapper;
|
||||
export { OpenPassportQRcode, SelfApp, SelfAppBuilder };
|
||||
export default SelfQRcodeWrapper;
|
||||
export { SelfQRcode, SelfApp, SelfAppBuilder };
|
||||
|
||||
export type { WebAppInfo };
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "@openpassport/qrcode2",
|
||||
"version": "0.0.1",
|
||||
"name": "@selfxyz/qrcode",
|
||||
"version": "0.0.9",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/zk-passport/openpassport"
|
||||
"url": "https://github.com/celo-org/self"
|
||||
},
|
||||
"license": "MIT",
|
||||
"author": "turnoffthiscomputer",
|
||||
@@ -17,58 +17,28 @@
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"download-circuits": "cd ../circuits && ./scripts/download_circuits_from_aws.sh && cd ../sdk",
|
||||
"format": "prettier --write .",
|
||||
"install-sdk": "cd ../common && yarn && cd ../sdk && yarn",
|
||||
"lint": "prettier --check .",
|
||||
"prepublishOnly": "npm run build",
|
||||
"test": "yarn ts-mocha -p ./tsconfig.json tests/openPassportVerifier.test.ts --exit"
|
||||
"test": "yarn ts-mocha -p ./tsconfig.json tests/openPassportVerifier.test.ts --exit",
|
||||
"publish": "npm publish --access public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@openpassport/core": "0.0.12",
|
||||
"@types/react": "^18.3.4",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"elliptic": "^6.5.7",
|
||||
"fs": "^0.0.1-security",
|
||||
"js-sha1": "^0.7.0",
|
||||
"js-sha256": "^0.11.0",
|
||||
"js-sha512": "^0.9.0",
|
||||
"lottie-react": "^2.4.0",
|
||||
"msgpack-lite": "^0.1.26",
|
||||
"next": "^14.2.8",
|
||||
"node-forge": "https://github.com/remicolin/forge",
|
||||
"pako": "^2.1.0",
|
||||
"pkijs": "^3.2.4",
|
||||
"poseidon-lite": "^0.2.0",
|
||||
"qrcode.react": "^4.1.0",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"react-spinners": "^0.14.1",
|
||||
"snarkjs": "^0.7.4",
|
||||
"socket.io-client": "^4.8.1",
|
||||
"uuid": "^10.0.0",
|
||||
"zlib": "^1.0.5"
|
||||
"uuid": "^10.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@openpassport/zk-kit-imt": "^0.0.5",
|
||||
"@openpassport/zk-kit-lean-imt": "^0.0.6",
|
||||
"@openpassport/zk-kit-smt": "^0.0.1",
|
||||
"@types/chai": "^4.3.6",
|
||||
"@types/chai-as-promised": "^7.1.8",
|
||||
"@types/circomlibjs": "^0.1.6",
|
||||
"@types/expect": "^24.3.0",
|
||||
"@types/mocha": "^10.0.6",
|
||||
"@types/node": "^20.11.19",
|
||||
"@types/node-forge": "^1.3.5",
|
||||
"@types/pako": "^2.0.3",
|
||||
"@types/snarkjs": "^0.7.8",
|
||||
"asn1js": "^3.0.5",
|
||||
"axios": "^1.7.2",
|
||||
"chai": "^4.3.8",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"dotenv": "^16.4.5",
|
||||
"ethers": "^6.13.0",
|
||||
"mocha": "^10.3.0",
|
||||
"prettier": "^3.3.3",
|
||||
"ts-loader": "^9.5.1",
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"include": [
|
||||
"index.ts",
|
||||
"src/**/*",
|
||||
"OpenPassportQRcode.tsx",
|
||||
"SelfQRcode.tsx",
|
||||
"common/**/*",
|
||||
"circuits/**/*",
|
||||
"circuits/**/*.json",
|
||||
|
||||
1496
sdk/qrcode/yarn.lock
1496
sdk/qrcode/yarn.lock
File diff suppressed because it is too large
Load Diff
@@ -12,12 +12,11 @@
|
||||
"@emotion/react": "^11.13.3",
|
||||
"@emotion/styled": "^11.13.0",
|
||||
"@mui/material": "^6.0.2",
|
||||
"@openpassport/core": "^0.0.11",
|
||||
"@openpassport/qrcode": "^0.0.14",
|
||||
"axios": "^1.7.7",
|
||||
"next": "14.2.8",
|
||||
"react": "^18",
|
||||
"react-dom": "^18"
|
||||
"react-dom": "^18",
|
||||
"uuid": "^11.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { SelfAppBuilder } from '../../../../../qrcode/OpenPassportQRcode';
|
||||
import OpenPassportQRcodeWrapper from '../../../../../qrcode/OpenPassportQRcode';
|
||||
import { SelfAppBuilder } from '../../../../../qrcode/SelfQRcode';
|
||||
import SelfQRcodeWrapper from '../../../../../qrcode/SelfQRcode';
|
||||
import { v4 } from 'uuid';
|
||||
import {logo} from './logo';
|
||||
|
||||
@@ -31,7 +31,7 @@ export default function Prove() {
|
||||
|
||||
return (
|
||||
<div className="h-screen w-full bg-white flex flex-col items-center justify-center gap-4">
|
||||
<OpenPassportQRcodeWrapper
|
||||
<SelfQRcodeWrapper
|
||||
selfApp={selfApp}
|
||||
onSuccess={() => {
|
||||
window.location.href = '/success';
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user