Skip to content

Commit

Permalink
vp request for jwt credential
Browse files Browse the repository at this point in the history
  • Loading branch information
endimion committed Apr 25, 2024
1 parent c807267 commit 1d8aad5
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 0 deletions.
30 changes: 30 additions & 0 deletions data/presentation_definition_jwt.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"id": "d49ee616-0e8d-4698-aff5-2a8a2362652d",
"name": "id-card-proof",
"format": {
"jwt_vc": {
"alg": ["ES256", "ES384"]
}
},
"input_descriptors": [
{
"id": "abd4acb1-1dcb-41ad-8596-ceb1401a69c7",
"format": {
"jwt_vc": {
"alg": ["ES256", "ES384"]
}
},
"constraints": {
"fields": [
{
"path": ["$.credentialSubject.given_name"]
},
{
"path": ["$.credentialSubject.last_name"]
}
]
},
"limit_disclosure": "required"
}
]
}
121 changes: 121 additions & 0 deletions routes/verifierRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { digest } from "@sd-jwt/crypto-nodejs";
import qr from "qr-image";
import imageDataURI from "image-data-uri";
import { streamToBuffer } from "@jorgeferrero/stream-to-buffer";
import jwt from "jsonwebtoken";

const verifierRouter = express.Router();

Expand All @@ -23,6 +24,11 @@ const publicKeyPem = fs.readFileSync("./public-key.pem", "utf-8");
const presentation_definition = JSON.parse(
fs.readFileSync("./data/presentation_definition.json", "utf-8")
);

const presentation_definition_jwt = JSON.parse(
fs.readFileSync("./data/presentation_definition_jwt.json", "utf-8")
);

const jwks = pemToJWK(publicKeyPem, "public");

let verificationSessions = []; //TODO these should be redis or something a proper cache...
Expand Down Expand Up @@ -124,15 +130,115 @@ verifierRouter.post("/direct_post/:id", async (req, res) => {
}
});

// *******************************************************

verifierRouter.get("/generateVPRequest-jwt", async (req, res) => {
const stateParam = req.query.id ? req.query.id : uuidv4();
const nonce = generateNonce(16);

let request_uri = serverURL + "/vpRequestJwt/" + stateParam;
const response_uri = serverURL + "/direct_post_jwt"; //not used

const vpRequest = buildVP(
serverURL,
response_uri,
request_uri,
stateParam,
nonce,
encodeURIComponent(JSON.stringify(presentation_definition_jwt))
);

let code = qr.image(vpRequest, {
type: "png",
ec_level: "H",
size: 10,
margin: 10,
});
let mediaType = "PNG";
let encodedQR = imageDataURI.encode(await streamToBuffer(code), mediaType);
res.json({
qr: encodedQR,
deepLink: vpRequest,
sessionId: stateParam,
});

// res.json({ vpRequest: vpRequest });
});

verifierRouter.get("/vpRequestJwt/:id", async (req, res) => {
const uuid = req.params.id ? req.params.id : uuidv4();
//url.searchParams.get("presentation_definition");
const stateParam = uuidv4();
const nonce = generateNonce(16);

const response_uri = serverURL + "/direct_post_jwt" + "/" + uuid;
let clientId = serverURL + "/direct_post_jwt" + "/" + uuid;
sessions.push(uuid);
verificationSessions.push({
uuid: uuid,
status: "pending",
claims: null,
});

let jwtToken = buildVpRequestJwt(
stateParam,
nonce,
clientId,
response_uri,
presentation_definition_jwt,
jwks,
serverURL,
privateKey
);
res.type("text/plain").send(jwtToken);
});

verifierRouter.post("/direct_post_jwt/:id", async (req, res) => {
const sessionId = req.params.id;
const jwtVp = req.body.vp_token;
// Log received request
console.log("Received direct_post VP for session:", sessionId);
if (!jwtVp) {
console.error("No VP token provided.");
return res.sendStatus(400); // Bad Request
}
let decodedWithHeader;
try {
decodedWithHeader = jwt.decode(jwtVp, { complete: true });
} catch (error) {
console.error("Failed to decode JWT:", error);
return res.sendStatus(400); // Bad Request due to invalid JWT
}
const credentialsJwtArray =
decodedWithHeader?.payload?.vp?.verifiableCredential;
if (!credentialsJwtArray) {
console.error("Invalid JWT structure.");
return res.sendStatus(400); // Bad Request
}
// Convert credentials to claims
let claims;
try {
claims = await flattenCredentialsToClaims(credentialsJwtArray);
if (!claims) {
throw new Error("Claims conversion returned null or undefined.");
}
} catch (error) {
console.error("Error processing claims:", error);
return res.sendStatus(500); // Internal Server Error
}
// Update session status
const index = sessions.indexOf(sessionId);
console.log("Session index:", index);
if (index === -1) {
console.error("Session ID not found.");
return res.sendStatus(404); // Not Found
}
// Log successful verification
verificationSessions[index].status = "success";
verificationSessions[index].claims = claims;
console.log("Verification success:", verificationSessions[index]);
res.sendStatus(200); // OK
});

verifierRouter.get(["/verificationStatus"], (req, res) => {
let sessionId = req.query.sessionId;
Expand Down Expand Up @@ -199,4 +305,19 @@ function buildVP(
return result;
}

async function flattenCredentialsToClaims(credentials) {
let claimsResult = {};
credentials.forEach((credentialJwt) => {
let decodedCredential = jwt.decode(credentialJwt, {
complete: true,
});
if (decodedCredential) {
let claims = decodedCredential.payload.vc.credentialSubject;
console.log(claims);
claimsResult = { ...claimsResult, ...claims };
}
});
return claimsResult;
}

export default verifierRouter;

0 comments on commit 1d8aad5

Please sign in to comment.