diff --git a/data/verifier-config.json b/data/verifier-config.json new file mode 100644 index 0000000..a4f57df --- /dev/null +++ b/data/verifier-config.json @@ -0,0 +1,39 @@ +{ + "client_id": "https://dss.aegean.gr", + "client_name": "UAegean EWC Verifier", + "redirect_uris": [ + "https://example-verifier.com/callback" + ], + "vp_formats_supported": [ + "jwt_vc_json", + "ldp_vc" + ], + "vp_token_endpoint_auth_methods_supported": [ + "client_secret_basic", + "client_secret_post" + ], + "presentation_definition_uri": "https://example-verifier.com/presentation-definitions", + "scopes_supported": [ + "openid", + "profile", + "vc_present" + ], + "response_types_supported": [ + "vp_token" + ], + "grant_types_supported": [ + "authorization_code" + ], + "id_token_signing_alg_values_supported": [ + "RS256", + "ES256" + ], + "vp_token_signing_alg_values_supported": [ + "RS256", + "ES256" + ], + "subject_types_supported": [ + "pairwise" + ] + } + \ No newline at end of file diff --git a/package.json b/package.json index 0a40b6f..34650d2 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "type": "module", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "dev": "SERVER_URL=https://fb13-2a02-587-870a-9d00-812c-35df-3c2a-93f4.ngrok-free.app node server.js" + "dev": "SERVER_URL=https://cf55-2a02-587-870d-e900-a1b1-2ac9-eea6-a3b6.ngrok-free.app node server.js" }, "author": "", "license": "ISC", diff --git a/routes/codeFlowJwtRoutes.js b/routes/codeFlowJwtRoutes.js index 0ffaa0f..83b8c59 100644 --- a/routes/codeFlowJwtRoutes.js +++ b/routes/codeFlowJwtRoutes.js @@ -90,16 +90,29 @@ codeFlowRouter.get("/authorize", async (req, res) => { //validations let errors = []; + /* + [{"format":"jwt_vc", + "locations":["https://issuer.example.com"], + "type":"openid_credential", + "types":["VerifiableCredential","VerifiableAttestation","VerifiablePortableDocumentA1"]}] + */ if (!authorizationDetails) { - //errors.push("no credentials requested"); + errors.push("no credentials requested"); console.log(`no credentials requested`); - } else if (authorizationDetails.credential_definition) { - console.log( - `credential ${authorizationDetails.credential_definition.type} was requested` - ); - } else if (authorizationDetails.types) { - //EBSI style - console.log(`credential ${authorizationDetails.types} was requested`); + } else { + try{ + if (authorizationDetails.credential_definition) { + console.log( + `credential ${authorizationDetails.credential_definition.type} was requested` + ); + } else if (authorizationDetails.types) { + //EBSI style + console.log(`credential ${authorizationDetails.types} was requested`); + } + }catch(error){ + errors.push("no credentials requested"); + } + } if (responseType !== "code") { diff --git a/routes/routes.js b/routes/routes.js index b014680..1cb50a6 100644 --- a/routes/routes.js +++ b/routes/routes.js @@ -770,7 +770,7 @@ router.post("/credential", async (req, res) => { const sdjwt = new SDJwtVcInstance({ signer, verifier, - signAlg: "ES384", + signAlg: "ES256", hasher: digest, hashAlg: "SHA-256", saltGenerator: generateSalt, diff --git a/routes/verifierRoutes.js b/routes/verifierRoutes.js index 2744e96..30e6166 100644 --- a/routes/verifierRoutes.js +++ b/routes/verifierRoutes.js @@ -57,13 +57,16 @@ const presentation_definition_alliance_and_education_Id = JSON.parse( ) ); // +const clientMetadata = JSON.parse( + fs.readFileSync("./data/verifier-config.json", "utf-8") +); const jwks = pemToJWK(publicKeyPem, "public"); let verificationSessions = []; //TODO these should be redis or something a proper cache... let sessions = []; -let sessionHistory = new TimedArray(30000) //cache data for 30sec -let verificationResultsHistory = new TimedArray(30000) //cache data for 30sec +let sessionHistory = new TimedArray(30000); //cache data for 30sec +let verificationResultsHistory = new TimedArray(30000); //cache data for 30sec verifierRouter.get("/generateVPRequest", async (req, res) => { const stateParam = req.query.id ? req.query.id : uuidv4(); @@ -211,17 +214,35 @@ verifierRouter.get("/vpRequestJwt/:id", async (req, res) => { claims: null, }); - let jwtToken = buildVpRequestJwt( - stateParam, - nonce, - clientId, - response_uri, - presentation_definition_jwt, - jwks, - serverURL, - privateKey - ); - res.type("text/plain").send(jwtToken); + clientMetadata.presentation_definition_uri = + serverURL + "/presentation-definition/1"; + clientMetadata.redirect_uris = [response_uri]; + clientMetadata.client_id = clientId; + + let vpRequest = { + client_id: clientId, + client_id_scheme: "redirect_uri", + response_uri: response_uri, + response_type: "vp_token", + response_mode: "direct_post", + presentation_definition: presentation_definition_jwt, + nonce: nonce, + state: uuid, + }; + + // console.log("will send vpRequest"); + // console.log(vpRequest); + + res.json(vpRequest); +}); + +verifierRouter.get("/presentation-definition/:type", async (req, res) => { + const { type } = req.params; + if (type == 1) { + res.type("application/json").send(presentation_definition_jwt); + } + console.log("ERROR getting presentatiton-definition type"); + res.status(500); }); // *******************PILOT USE CASES ****************************** @@ -289,17 +310,27 @@ verifierRouter.get("/vpRequest/:type/:id", async (req, res) => { return res.status(400).type("text/plain").send("Invalid type parameter"); } - let jwtToken = buildVpRequestJwt( - stateParam, - nonce, - clientId, - response_uri, - presentationDefinition, - jwks, - serverURL, - privateKey - ); - res.type("text/plain").send(jwtToken); + // let jwtToken = buildVpRequestJwt( + // stateParam, + // nonce, + // clientId, + // response_uri, + // presentationDefinition, + // jwks, + // serverURL, + // privateKey + // ); + let vpRequest = { + client_id: clientId, + client_id_scheme: "redirect_uri", + response_uri: response_uri, + response_type: "vp_token", + response_mode: "direct_post", + presentation_definition: presentation_definition_jwt, + nonce: nonce, + state: uuid, + }; + res.json(vpRequest); }); verifierRouter.post("/direct_post_jwt/:id", async (req, res) => { @@ -327,9 +358,9 @@ verifierRouter.post("/direct_post_jwt/:id", async (req, res) => { // Convert credentials to claims let claims; try { - console.log(credentialsJwtArray) + console.log(credentialsJwtArray); claims = await flattenCredentialsToClaims(credentialsJwtArray); - console.log(claims) + console.log(claims); if (!claims) { throw new Error("Claims conversion returned null or undefined."); } @@ -364,8 +395,8 @@ verifierRouter.get(["/verificationStatus"], (req, res) => { result = verificationSessions[index].claims; sessions.splice(index, 1); verificationSessions.splice(index, 1); - sessionHistory.addElement(sessionId) - verificationResultsHistory.addElement(result) + sessionHistory.addElement(sessionId); + verificationResultsHistory.addElement(result); } // console.log(`new sessions`); // console.log(sessions); @@ -388,7 +419,7 @@ verifierRouter.get(["/verificationStatus"], (req, res) => { verifierRouter.get(["/verificationStatusHistory"], (req, res) => { let sessionId = req.query.sessionId; - let index = sessionHistory.getCurrentArray().indexOf(sessionId); + let index = sessionHistory.getCurrentArray().indexOf(sessionId); if (index >= 0) { res.json({ status: "success", @@ -405,7 +436,6 @@ verifierRouter.get(["/verificationStatusHistory"], (req, res) => { } }); - function buildVP( client_id, redirect_uri, diff --git a/utils/cryptoUtils.js b/utils/cryptoUtils.js index f4fa526..b740802 100644 --- a/utils/cryptoUtils.js +++ b/utils/cryptoUtils.js @@ -49,49 +49,52 @@ export function buildVpRequestJwt( */ /** - * response_uri - * client_id_scheme: "redirect_uri", - * iss": "https://some.io", - * presentation_definition: {} - * "response_type": "vp_token", - "state": "344306f6-0323-4ef5-9348-70fc328efd85", - "exp": 1712309984, - "nonce": "1fRxngVjYq0oETeZxNYfId", - "iat": 1712306384, - "client_id": "https://some.io/openid4vp/authorization-response", - "response_mode": "direct_post" + Location: https://client.example.org/universal-link? + response_type=vp_token + &client_id=https%3A%2F%2Fclient.example.org%2Fcb + &client_id_scheme=redirect_uri + &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb + &presentation_definition=... + &nonce=n-0S6_WzA2Mj + &client_metadata=%7B%22vp_formats%22:%7B%22jwt_vp%22:% + 7B%22alg%22:%5B%22EdDSA%22,%22ES256K%22%5D%7D,%22ldp + _vp%22:%7B%22proof_type%22:%5B%22Ed25519Signature201 + 8%22%5D%7D%7D%7D * */ let jwtPayload = { - client_id_scheme: "redirect_uri", - response_uri: response_uri, //TODO Note: If the Client Identifier scheme redirect_uri is used in conjunction with the Response Mode direct_post, and the response_uri parameter is present, the client_id value MUST be equal to the response_uri value - iss: serverURL, - presentation_definition: presentation_definition, response_type: "vp_token", - state: state, - exp: Math.floor(Date.now() / 1000) + 60, - nonce: nonce, - iat: Math.floor(Date.now() / 1000), client_id: client_id, - response_mode: "direct_post", + client_id_scheme: "redirect_uri", + presentation_definition: presentation_definition, + redirect_uri: response_uri, + // response_mode: "direct_post", + client_metadata : "", + + // response_uri: response_uri, //TODO Note: If the Client Identifier scheme redirect_uri is used in conjunction with the Response Mode direct_post, and the response_uri parameter is present, the client_id value MUST be equal to the response_uri value + // iss: serverURL, + // state: state, + // exp: Math.floor(Date.now() / 1000) + 60, + // nonce: nonce, + // iat: Math.floor(Date.now() / 1000), // nbf: Math.floor(Date.now() / 1000), // redirect_uri: redirect_uri, // scope: "openid", }; - const header = { - alg: "ES256", - kid: `aegean#authentication-key`, //this kid needs to be resolvable from the did.json endpoint - }; - - const token = jwt.sign(jwtPayload, privateKey, { - algorithm: "ES256", - noTimestamp: true, - header, - }); - return token; + // const header = { + // alg: "ES256", + // kid: `aegean#authentication-key`, //this kid needs to be resolvable from the did.json endpoint + // }; + + // const token = jwt.sign(jwtPayload, privateKey, { + // algorithm: "ES256", + // noTimestamp: true, + // header, + // }); + return jwtPayload; } export async function decryptJWE(jweToken, privateKeyPEM) {