Implementation
@discoxyz/selective-disclosure (early alpha)
Early Alpha Release of Disco Selective Disclosure SDK, provide us feedback ask@disco.xyz!
Prerequisite
Some knowledge of React.
Some knowledge of working with wallets such as Metamask or Rainbow.
A Disco developer API key.
Installation
To get started, add @discoxyz/selective-disclosure
to your project:
yarn add @discoxyz/selective-disclosure
npm i @discoxyz/selective-disclosure
Key exports
DisclosureProvider
This provider handles API keys and authentication, to trigger the modal and access storage resources. For example, use this approach in the index.tsx file of the React project.
import { DisclosureProvider } from '@discoxyz/selective-disclosure'
// Currently we support disco & ceramic endpoints
const config = {
projectKey: 'ABCD...' // Your Disco API key
}
const App = ({children}) => {
return (
<DisclosureProvider config={config}>
{children}
</DisclosureProvider>
)
}
requestAsync(request, provider)
request
An object representing the credential, or collection of credentials, that you are looking for.
const requestShape = {
'AND': [ // This means that each criteria must be met
{
schema: [Membership], // Will find a match from an array
issuer: [Disco]
},{
schema: [Participation],
issuer: [Guild]
}
]
}
Response
{
status: 'success',
message: 'Presented 2 credentials successfully',
presentation: ...
}
{
status: 'rejected',
message: 'The presentation request was rejected'
}
{
status: 'invalid',
message: 'The presentation was invalid'
}
{
status: 'notConnected',
message: 'No wallet is connected'
}
{
status: 'invalidApi',
message: 'An API endpoint could not be reached...'
}
{
status: 'error',
message: ...
}
Requesting a Presentation Example
import { useDiscoDisclosure } from "@discoxyz/selective-disclosure";
import ( React } from "react";
import { useAccount } from "wagmi";
const Disclose: React.FC = () => {
// ✅ Use Request Async
const { requestAsync } = useDiscoDisclosure();
// Store the result
const [result, setResult] = useState<RequestResult | undefined>()
const requestShape = {
sdRequest: {
'AND': [ // This means that each criteria must be met
{
schema: ["Membership"], // required
issuer: [<DiscoDID>], // optional
count: 1 // optional and defaults to 1
},{
schema: ["Participation"], // required
issuer: [<GuildDID>], // optional
count: 2 // optional and defaults to 1
}
]
}
}
async function sendRequest() {
// 📨 Send the request. This triggers a modal for the user
const result = await requestAsync(requestShape)
// If no presentation, errors are present
if (!result.presentation) return
// Now set the result
setResult(result)
}
return (
<div>
<button onClick={sendRequest}>Reveal Credential</button>
{result ? <p>{status.message}</p>
: <p>Request not started</p>}
</div>
)
}
Example of a Verifiable Presentation from const result = await requestAsync(provider, requestShape)
from above code snippet.
{
"presentation": {
"@context": [
"https://www.w3.org/2018/credentials/v1"
],
"type": [
"VerifiablePresentation"
],
"issuanceDate": "2023-11-17T18:10:51.945Z",
"verifiableCredential": [
"{\"@context\":[\"https://www.w3.org/2018/credentials/v1\"],\"type\":[\"VerifiableCredential\",\"GmCredential\"],\"issuer\":{\"id\":\"did:web:staging-be.disco.xyz/v1/mick\"},\"issuanceDate\":\"2023-09-22T14:53:58.452Z\",\"id\":\"https://api.disco.xyz/credential/7170505e-37b1-49bb-8cd0-bd4709b4e9db\",\"credentialSubject\":{\"id\":\"did:3:kjzl6cwe1jw149lyliyfh4mbjgy59rhoktkhsoc7suu2trqje2wigrgea21bqy6\"},\"credentialSchema\":{\"id\":\"https://raw.githubusercontent.com/discoxyz/disco-schemas/main/json/GMCredential/1-0-0.json\",\"type\":\"JsonSchemaValidator2018\"}}",
"{\"@context\":[\"https://www.w3.org/2018/credentials/v1\"],\"type\":[\"VerifiableCredential\",\"GmCredential\"],\"issuer\":{\"id\":\"did:3:kjzl6cwe1jw14a7u9sx3thx9gg9uh7u5tqjkzcnr5pi5zzkap7kiztgsfhzayzt\"},\"issuanceDate\":\"2023-09-22T12:00:17.369Z\",\"id\":\"https://api.disco.xyz/credential/098514a6-1e3c-4209-8507-212dbed37169\",\"credentialSubject\":{\"id\":\"did:3:kjzl6cwe1jw149lyliyfh4mbjgy59rhoktkhsoc7suu2trqje2wigrgea21bqy6\"},\"credentialSchema\":{\"id\":\"https://raw.githubusercontent.com/discoxyz/disco-schemas/main/json/GMCredential/1-0-0.json\",\"type\":\"JsonSchemaValidator2018\"},\"proof\":{\"verificationMethod\":\"did:3:kjzl6cwe1jw14a7u9sx3thx9gg9uh7u5tqjkzcnr5pi5zzkap7kiztgsfhzayzt#controller\",\"created\":\"2023-09-22T12:00:17.390Z\",\"proofPurpose\":\"assertionMethod\",\"type\":\"EthereumEip712Signature2021\",\"proofValue\":\"0x100353098f280cd351f3b9ee22642854f4b38ac99ef6ab13d874b26afdff44886101130b1270c2031a657774446e2cbbaf25ab32178959a3fe167365810d00801c\",\"eip712Domain\":{\"domain\":{\"chainId\":1,\"name\":\"Disco Verifiable Credential\",\"version\":\"1\"},\"messageSchema\":{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\"}],\"Proof\":[{\"name\":\"created\",\"type\":\"string\"},{\"name\":\"proofPurpose\",\"type\":\"string\"},{\"name\":\"type\",\"type\":\"string\"},{\"name\":\"verificationMethod\",\"type\":\"string\"}],\"Issuer\":[{\"name\":\"id\",\"type\":\"string\"}],\"CredentialSubject\":[{\"name\":\"id\",\"type\":\"string\"}],\"VerifiableCredential\":[{\"name\":\"@context\",\"type\":\"string[]\"},{\"name\":\"credentialSubject\",\"type\":\"CredentialSubject\"},{\"name\":\"id\",\"type\":\"string\"},{\"name\":\"issuanceDate\",\"type\":\"string\"},{\"name\":\"issuer\",\"type\":\"Issuer\"},{\"name\":\"proof\",\"type\":\"Proof\"},{\"name\":\"type\",\"type\":\"string[]\"}]},\"primaryType\":\"VerifiableCredential\"}}}",
"{\"@context\":[\"https://www.w3.org/2018/credentials/v1\"],\"type\":[\"VerifiableCredential\",\"GmCredential\"],\"issuer\":{\"id\":\"did:3:kjzl6cwe1jw14a7u9sx3thx9gg9uh7u5tqjkzcnr5pi5zzkap7kiztgsfhzayzt\"},\"issuanceDate\":\"2023-09-22T11:59:24.784Z\",\"id\":\"https://api.disco.xyz/credential/21df13f1-14d6-40a5-a27a-18a96e0980b2\",\"credentialSubject\":{\"id\":\"did:3:kjzl6cwe1jw149lyliyfh4mbjgy59rhoktkhsoc7suu2trqje2wigrgea21bqy6\"},\"credentialSchema\":{\"id\":\"https://raw.githubusercontent.com/discoxyz/disco-schemas/main/json/GMCredential/1-0-0.json\",\"type\":\"JsonSchemaValidator2018\"},\"proof\":{\"verificationMethod\":\"did:3:kjzl6cwe1jw14a7u9sx3thx9gg9uh7u5tqjkzcnr5pi5zzkap7kiztgsfhzayzt#controller\",\"created\":\"2023-09-22T11:59:24.880Z\",\"proofPurpose\":\"assertionMethod\",\"type\":\"EthereumEip712Signature2021\",\"proofValue\":\"0x9123cda1825121738eeb14c04407fd7f160deadcbac52f517f9364c92ff7dfe84fd8d26596abb781926985e499e120c0342f96c47671a80a5e2d10c189e3cd121c\",\"eip712Domain\":{\"domain\":{\"chainId\":1,\"name\":\"Disco Verifiable Credential\",\"version\":\"1\"},\"messageSchema\":{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\"}],\"Proof\":[{\"name\":\"created\",\"type\":\"string\"},{\"name\":\"proofPurpose\",\"type\":\"string\"},{\"name\":\"type\",\"type\":\"string\"},{\"name\":\"verificationMethod\",\"type\":\"string\"}],\"Issuer\":[{\"name\":\"id\",\"type\":\"string\"}],\"CredentialSubject\":[{\"name\":\"id\",\"type\":\"string\"}],\"VerifiableCredential\":[{\"name\":\"@context\",\"type\":\"string[]\"},{\"name\":\"credentialSubject\",\"type\":\"CredentialSubject\"},{\"name\":\"id\",\"type\":\"string\"},{\"name\":\"issuanceDate\",\"type\":\"string\"},{\"name\":\"issuer\",\"type\":\"Issuer\"},{\"name\":\"proof\",\"type\":\"Proof\"},{\"name\":\"type\",\"type\":\"string[]\"}]},\"primaryType\":\"VerifiableCredential\"}}}"
],
"proof": {
"verificationMethod": "did:3:kjzl6cwe1jw149lyliyfh4mbjgy59rhoktkhsoc7suu2trqje2wigrgea21bqy6#controller",
"created": "2023-11-17T18:10:51.945Z",
"proofPurpose": "assertionMethod",
"type": "EthereumEip712Signature2021",
"proofValue": "0x1bf34c9227e7948ca8ab19589a813cb9cfc3f276aeaa14565b663f11e87719d8040c2c3cf915625412032e17b7c2441e838056e8ef5a385dc1c59702612533981c",
"eip712": {
"domain": {
"chainId": 1,
"name": "VerifiablePresentation",
"version": "1"
},
"types": {
"EIP712Domain": [
{
"name": "name",
"type": "string"
},
{
"name": "version",
"type": "string"
},
{
"name": "chainId",
"type": "uint256"
}
],
"Proof": [
{
"name": "created",
"type": "string"
},
{
"name": "proofPurpose",
"type": "string"
},
{
"name": "type",
"type": "string"
},
{
"name": "verificationMethod",
"type": "string"
}
],
"VerifiablePresentation": [
{
"name": "@context",
"type": "string[]"
},
{
"name": "issuanceDate",
"type": "string"
},
{
"name": "proof",
"type": "Proof"
},
{
"name": "type",
"type": "string[]"
},
{
"name": "verifiableCredential",
"type": "string[]"
}
]
},
"primaryType": "VerifiablePresentation"
}
}
},
"status": "success",
"message": ""
}
Extracting Issuers Example
// Received Presentaion such as in sendRequest from above example
const result = {
"presentation": {
..., //other properties
"verifiableCredential": [
]
},
"status": "success",
"message":
}
// After receiving the presentation,
// you can extract the credentials array from the following properties.
const credentials = result.presentation.verifiableCredential
// These Credentials can either be JWT's or JSON-LD.
// You can use a regex like below to determine
// And a library like did-jwt or jsonwebtoken to decode the jwts.
import { decodeJWT } from "did-jwt";
const jwtRegex = /^([a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/;
const res = credentials.map((vc: any) => {
return vc.match(jwtRegex) ? decodeJWT(vc).payload : JSON.parse(vc)
})
const issuers = res.map((vc:any) => {
return vc.issuer.id;
})
//Now with the issuers we can see who signed the credential for this user
console.log(issuers)