Prerequisites
Make sure you have key pair consisting of :
Read Generate Key Pair section for detail info.
Overview
We need to sign User Data and Data Verification Request (DVR) before sending a request to the zkPass service. This ensures that the payload is not tampered during transport.
Example Implementation
JWKS
The JSON Web Key Set (JWKS) is a set of keys containing the public keys used to verify any JSON Web Token (JWT) issued by the Authorization Server and signed, You can find a complete definition of JWKS here .
Upload your publicKeyJWKS
so that it's accessible from the internet. This will be used by zkPass service to verify the validity of the user data.
Example of the uploaded publicKeyJWKS
Copy {
"keys" : [
{
"x" : "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAELOmrNI4A9ML4iGJXpYlaZiYGVCxB" ,
"y" : "k+evjhOZEbCLj17o/ZdfEv7dUZIRKRoZ1bud5Gq8OCItDlXkTyMrtWrhdA==" ,
"kid" : "q6ZFSOJcTiZWJWkvUshpFw5v20xstZN/T4lt4zpKsUg="
}
]
}
JWS
A JSON Web Signature (abbreviated JWS) is an IETF-proposed standard (RFC 7515) for signing arbitrary data. This is used as the basis for a variety of web-based technologies including JSON Web Token. You can find a complete definition of JWS here .
This is an example code of how you can sign a JSON object as JWS format in Typescript. Let's say you uploaded your publicKeyJWKS
to https://mywebsite/my-keys.json
Copy import { SignJWT , importPKCS8 } from "jose" ;
async signDataToJwsToken (
privateKey: string ,
userDataOrDVR: any
): Promise < string > {
const verifyingKeyJwks = {
jku : "https://mywebsite/my-keys.json" ,
kid : "q6ZFSOJcTiZWJWkvUshpFw5v20xstZN/T4lt4zpKsUg="
}
const alg = "ES256" ;
const importedPrivateKey = await importPKCS8 (privateKey , alg);
return await new SignJWT ({ data: userDataOrDVR })
. setProtectedHeader ({
alg: alg ,
jku: verifyingKeyJwks.jku ,
kid: verifyingKeyJwks.kid ,
})
. sign (importedPrivateKey);
}
Example for User Data
Copy {
"testID" : "SCREEN-7083-12345" ,
"testName" : "QualityHealth Comprehensive Screen" ,
"testDate" : "2023-08-27T14:00:00Z" ,
"lab" : {
"name" : "QualityHealth Labs" ,
"ID" : "QH801874" ,
"address" : "1234 Elm St, Oakland, USA"
} ,
"subject" : {
"firstName" : "Jane" ,
"lastName" : "Doe" ,
"dateOfBirth" : "1985-12-12" ,
"bloodType" : "A+" ,
"DNAInfo" : {
"markers" : {
"APOE" : [ "E3" , "E3" ] ,
"BRCA1" : "Normal" ,
"MTHFR" : [ "C677T" , "A1298C" ]
} ,
"haplogroups" : {
"paternal" : "R1b1" ,
"maternal" : "H1a1"
}
} ,
"contact" : {
"email" : "jane.doe@gmail.com" ,
"phone" : "650-555-1234"
} ,
"address" : {
"street" : "789 Oak Street" ,
"city" : "San Jose" ,
"state" : "CA" ,
"zip" : "95134"
}
} ,
"measuredPanelsNgML" : {
"amphetamines" : 0 ,
"cocaine" : 8 ,
"opiates" : 102 ,
"benzodiazepines" : 0
}
}
Below is the example of Query in DVR, this query will be included in full DVR
Copy [
{
"assign" : {
"blood_test_status" : {
"and" : [
{
"==" : [{ "dvar" : "lab.ID" } , "QH801874" ]
} ,
{
"==" : [{ "dvar" : "testID" } , "SCREEN-7083-12345" ]
} ,
{
"~==" : [{ "dvar" : "subject.firstName" } , "jane" ]
} ,
{
"~==" : [{ "dvar" : "subject.lastName" } , "doe" ]
} ,
{
"==" : [{ "dvar" : "subject.dateOfBirth" } , "1985-12-12" ]
} ,
{
"==" : [{ "dvar" : "measuredPanelsNgML.amphetamines" } , 0 ]
} ,
{
"<=" : [{ "dvar" : "measuredPanelsNgML.cocaine" } , 10 ]
}
]
}
}
} ,
{ "output" : { "result" : { "lvar" : "blood_test_status" } } }
]
Below is the example of full DVR that will be generated into DVR Token
Copy {
"zkvm" : "r0" ,
"dvr_title" : "My DVR" ,
"dvr_id" : "47343cd7-a2be-404b-811a-ab1c6307c9f9" ,
"query_engine_ver" : "1.0.0" ,
"query_method_ver" : "9b7ae23b4cdbc3272a83a8b0928f1f9d778659753cb530583e55949cdec0c7cf" ,
"query": "[{\"assign\":{\"blood_test_status\":{\"and\":[{\"==\":[{\"dvar\":\"lab.ID\"},\"QH801874\"]},{\"==\":[{\"dvar\":\"testID\"},\"SCREEN-7083-12345\"]},{\"~==\":[{\"dvar\":\"subject.firstName\"},\"jane\"]},{\"~==\":[{\"dvar\":\"subject.lastName\"},\"doe\"]},{\"==\":[{\"dvar\":\"subject.dateOfBirth\"},\"1985-12-12\"]},{\"==\":[{\"dvar\":\"subject.contact.email\"},\"jane.doe@gmail.com\"]},{\"==\":[{\"dvar\":\"measuredPanelsNgML.amphetamines\"},0]},{\"<=\":[{\"dvar\":\"measuredPanelsNgML.cocaine\"},10]}]}}},{\"output\":{\"name\":{\"dvar\":\"subject.firstName\"}}},{\"output\":{\"email\":{\"dvar\":\"subject.contact.email\"}}},{\"output\":{\"result\":{\"lvar\":\"blood_test_status\"}}}]",
"user_data_requests" : {
"" : {
"user_data_url" : "https://hostname/api/user_data/" ,
"user_data_verifying_key" : {
"KeysetEndpoint" : {
"jku" : "https://raw.githubusercontent.com/gl-zkPass/zkpass-sdk/main/docs/zkpass/sample-jwks/issuer-key.json" ,
"kid" : "k-1" ,
}
}
}
} ,
"dvr_verifying_key" : {
"KeysetEndpoint" : {
"jku" : "https://raw.githubusercontent.com/gl-zkPass/zkpass-sdk/main/docs/zkpass/sample-jwks/verifier-key.json" ,
"kid" : "k-1"
}
}
}
Output Example
After this section you should have :
User Data Token : User Data in JSON Web Signature (JWS) format.
DVR Token : DVR in JSON Web Signature (JWS) format.
Here's the example of User Data Token & DVR Token in JWS format.
Copy {
"userDataToken": "eyJhbGciOiJFUzI1NiIsImprdSI6Imh0dHBzOi8vZ2RwLWFkbWluLmdpdGh1Yi5pby96a3Bhc3Mtc2RrL3prcGFzcy9zYW1wbGUtandrcy9pc3N1ZXIta2V5Lmpzb24iLCJraWQiOiJrLTEifQ.eyJkYXRhIjp7InRlc3RJRCI6IlNDUkVFTi03MDgzLTEyMzQ1IiwidGVzdE5hbWUiOiJRdWFsaXR5SGVhbHRoIENvbXByZWhlbnNpdmUgU2NyZWVuIiwidGVzdERhdGUiOiIyMDIzLTA4LTI3VDE0OjAwOjAwWiIsImxhYiI6eyJuYW1lIjoiUXVhbGl0eUhlYWx0aCBMYWJzIiwiSUQiOiJRSDgwMTg3NCIsImFkZHJlc3MiOiIxMjM0IEVsbSBTdCwgT2FrbGFuZCwgVVNBIn0sInN1YmplY3QiOnsiZmlyc3ROYW1lIjoiSmFuZSIsImxhc3ROYW1lIjoiRG9lIiwiZGF0ZU9mQmlydGgiOiIxOTg1LTEyLTEyIiwiYmxvb2RUeXBlIjoiQSsiLCJETkFJbmZvIjp7Im1hcmtlcnMiOnsiQVBPRSI6WyJFMyIsIkUzIl0sIkJSQ0ExIjoiTm9ybWFsIiwiTVRIRlIiOlsiQzY3N1QiLCJBMTI5OEMiXX0sImhhcGxvZ3JvdXBzIjp7InBhdGVybmFsIjoiUjFiMSIsIm1hdGVybmFsIjoiSDFhMSJ9fSwiY29udGFjdCI6eyJlbWFpbCI6ImphbmUuZG9lQGdtYWlsLmNvbSIsInBob25lIjoiNjUwLTU1NS0xMjM0In0sImFkZHJlc3MiOnsic3RyZWV0IjoiNzg5IE9hayBTdHJlZXQiLCJjaXR5IjoiU2FuIEpvc2UiLCJzdGF0ZSI6IkNBIiwiemlwIjoiOTUxMzQifX0sIm1lYXN1cmVkUGFuZWxzTmdNTCI6eyJhbXBoZXRhbWluZXMiOjAsImNvY2FpbmUiOjgsIm9waWF0ZXMiOjEwMiwiYmVuem9kaWF6ZXBpbmVzIjowfX19.XPEpOCwUXJKWGP0gUpCc7Efdw1j8FSO9nhGbWO45jMwtOUsG_s4SRdg85TtocGf8R5wMXrFDwlppqRkOjeCjpg",
"dvrToken": "eyJhbGciOiJFUzI1NiIsImprdSI6Imh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9nbC16a1Bhc3MvemtwYXNzLXNkay9tYWluL2RvY3MvemtwYXNzL3NhbXBsZS1qd2tzL3ZlcmlmaWVyLWtleS5qc29uIiwia2lkIjoiay0xIn0.eyJkYXRhIjp7Inprdm0iOiJyMCIsImR2cl90aXRsZSI6Ik15IERWUiIsImR2cl9pZCI6IjEwNjdjNjA2LTkwYWYtNDI2OS05MjE2LWZkMTFmNzBlOTgzZiIsInF1ZXJ5X2VuZ2luZV92ZXIiOiIwLjMuMC1yYy4xIiwicXVlcnlfbWV0aG9kX3ZlciI6IjFmYjMyZWYzNzljZDc2NmYzYzkzYTk4Y2Y1MjIxMmMzYTA1YmQzYjNhM2Y0MTBmNTg4NzZmNmYyMjhmNGU5MyIsInF1ZXJ5IjoiW3tcImFzc2lnblwiOntcImJsb29kX3Rlc3Rfc3RhdHVzXCI6e1wiYW5kXCI6W3tcIj09XCI6W3tcImR2YXJcIjpcImxhYi5JRFwifSxcIlFIODAxODc0XCJdfSx7XCI9PVwiOlt7XCJkdmFyXCI6XCJ0ZXN0SURcIn0sXCJTQ1JFRU4tNzA4My0xMjM0NVwiXX0se1wifj09XCI6W3tcImR2YXJcIjpcInN1YmplY3QuZmlyc3ROYW1lXCJ9LFwiamFuZVwiXX0se1wifj09XCI6W3tcImR2YXJcIjpcInN1YmplY3QubGFzdE5hbWVcIn0sXCJkb2VcIl19LHtcIj09XCI6W3tcImR2YXJcIjpcInN1YmplY3QuZGF0ZU9mQmlydGhcIn0sXCIxOTg1LTEyLTEyXCJdfSx7XCI9PVwiOlt7XCJkdmFyXCI6XCJzdWJqZWN0LmNvbnRhY3QuZW1haWxcIn0sXCJqYW5lLmRvZUBnbWFpbC5jb21cIl19LHtcIj09XCI6W3tcImR2YXJcIjpcIm1lYXN1cmVkUGFuZWxzTmdNTC5hbXBoZXRhbWluZXNcIn0sMF19LHtcIjw9XCI6W3tcImR2YXJcIjpcIm1lYXN1cmVkUGFuZWxzTmdNTC5jb2NhaW5lXCJ9LDEwXX1dfX19LHtcIm91dHB1dFwiOntcIm5hbWVcIjp7XCJkdmFyXCI6XCJzdWJqZWN0LmZpcnN0TmFtZVwifX19LHtcIm91dHB1dFwiOntcImVtYWlsXCI6e1wiZHZhclwiOlwic3ViamVjdC5jb250YWN0LmVtYWlsXCJ9fX0se1wib3V0cHV0XCI6e1wicmVzdWx0XCI6e1wibHZhclwiOlwiYmxvb2RfdGVzdF9zdGF0dXNcIn19fV0iLCJ1c2VyX2RhdGFfdXJsIjoiaHR0cHM6Ly9ob3N0bmFtZS9hcGkvdXNlcl9kYXRhLyIsInVzZXJfZGF0YV92ZXJpZnlpbmdfa2V5Ijp7IktleXNldEVuZHBvaW50Ijp7ImprdSI6Imh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9nbC16a1Bhc3MvemtwYXNzLXNkay9tYWluL2RvY3MvemtwYXNzL3NhbXBsZS1qd2tzL2lzc3Vlci1rZXkuanNvbiIsImtpZCI6ImstMSJ9fSwiZHZyX3ZlcmlmeWluZ19rZXkiOnsiS2V5c2V0RW5kcG9pbnQiOnsiamt1IjoiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2dsLXprUGFzcy96a3Bhc3Mtc2RrL21haW4vZG9jcy96a3Bhc3Mvc2FtcGxlLWp3a3MvdmVyaWZpZXIta2V5Lmpzb24iLCJraWQiOiJrLTEifX19fQ.G5moHbBbNbgen5I8-UZDK5cbzQzUkwnp1GTvUxXnPk-q6EpuaJq2Zud_GBesVGjspZTuaZk9J1GJp8ri65M8aw"
}
Last updated 2 months ago