Skip to content
This repository has been archived by the owner on Apr 11, 2023. It is now read-only.

Commit

Permalink
feat: implemented OIDC presentation submission API
Browse files Browse the repository at this point in the history
Signed-off-by: Anton Biriukov <anton.biriukov@avast.com>
  • Loading branch information
birtony committed Nov 7, 2022
1 parent f56e856 commit 579a837
Showing 1 changed file with 186 additions and 0 deletions.
186 changes: 186 additions & 0 deletions cmd/wallet-js-sdk/src/oidc/presentation/openid4vp.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ import { decode } from "js-base64";
import { CredentialManager, DIDManager } from "@";
import * as jose from "jose";

// TODO: replace mock function with an actual JWT signer once implemented
async function signJWT({ header, payload }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("mock.signed.jwt");
}, 300);
});
}

/**
* OpenID4VP module is the oidc client that provides APIs for OIDC4VP flows.
*
Expand Down Expand Up @@ -120,6 +129,183 @@ export class OpenID4VP {
}
});

this.client_id = payload.client_id;
this.nonce = payload.nonce;
this.redirect_uri = payload.redirect_uri;

return response.results;
}

/**
* submitOIDCPresentation performs an OIDC presentation submission
* @param {string} kid - consumer's verification method's kid.
* @param {Object} presentationQuery - presentation query object retrieved from user's wallet.
* @param {string} issuer - the issuer's key id.
* @param {number} expiry - time in seconds representing the expiry of the presentation.
*
*
* @returns {Promise<Object>} - empty promise or error if operation fails.
*/
async submitOIDCPresentation(kid, presentationQuery, issuer, expiry) {
if (!kid) {
throw new TypeError(
"Error submitting OpenID4VP presentation: kid cannot be empty"
);
} else if (!presentationQuery) {
throw new TypeError(
"Error submitting OpenID4VP presentation: presentationQuery cannot be empty"
);
} else if (!presentationQuery.presentation_submission) {
throw new TypeError(
"Error submitting OpenID4VP presentation: presentation_submission is missing"
);
} else if (!presentationQuery.type) {
throw new TypeError(
"Error submitting OpenID4VP presentation: type is missing"
);
} else if (!presentationQuery.verifiableCredential) {
throw new TypeError(
"Error submitting OpenID4VP presentation: verifiableCredential is missing"
);
} else if (!issuer) {
throw new TypeError(
"Error submitting OpenID4VP presentation: issuer is missing"
);
} else if (!expiry) {
throw new TypeError(
"Error submitting OpenID4VP presentation: expiry is missing"
);
}

const header = new Object();
Object.defineProperty(header, "alg", {
value: alg,
});
Object.defineProperty(header, "kid", {
value: kid,
});
Object.defineProperty(header, "typ", {
value: "JWT",
});

const idToken = await generateIdToken(
kid,
presentationQuery.presentation_submission,
header,
expiry
);

const vp = new Object();
Object.defineProperty(vp, "@context", {
value: presentationQuery["@context"],
});
Object.defineProperty(vp, "type", {
value: presentationQuery.type,
});
Object.defineProperty(vp, "verifiableCredential", {
value: presentationQuery.verifiableCredential,
});

const vpToken = await generateVpToken(kid, header, vp, expiry);

const authRequest = new URLSearchParams();
authRequest.append("id_token", idToken);
authRequest.append("vp_token", vpToken);

return await axios.post(this.redirect_uri, authRequest).catch((e) => {
throw new Error("Error submitting OIDC presentation:", e);
});
}
}

/**
* generateIdToken generates an ID Token for the presentation submission request
* @param {string} kid - consumer's verification method's kid.
* @param {Object} presentationSubmission - presentation submission.
* @param {Object} header - header for the ID Token.
* @param {number} expiry - time in seconds representing the expiry of the token.
*
*
* @returns {Promise<string>} - a promise resolving with a string containing ID Token or an error.
*/
async function generateIdToken(kid, presentationSubmission, header, expiry) {
if (!kid) {
throw new TypeError("Error generating ID Token: kid cannot be empty");
} else if (!presentationSubmission) {
throw new TypeError(
"Error generating ID Token: presentationSubmission cannot be empty"
);
} else if (!header) {
throw new TypeError("Error generating ID Token: header cannot be empty");
} else if (!expiry) {
throw new TypeError("Error generating ID Token: expiry cannot be empty");
}

const vpToken = new Object();
Object.defineProperty(vpToken, "presentation_submission", {
value: presentationSubmission,
});

const payload = new Object();
Object.defineProperty(payload, "sub", {
value: kid.split("#")[0],
});
Object.defineProperty(payload, "nonce", {
value: this.nonce,
});
Object.defineProperty(payload, "_vp_token", {
value: vpToken,
});
Object.defineProperty(payload, "aud", {
value: this.client_id,
});
Object.defineProperty(payload, "iss", {
value: "https://self-issued.me/v2/openid-vc",
});
Object.defineProperty(payload, "exp", {
value: expiry,
});

return await signJWT({ header, payload });
}

/**
* generateVpToken generates a VP Token for the presentation submission request
* @param {string} kid - consumer's verification method's kid.
* @param {Object} vp - object containing details for the presentation.
* @param {Object} header - header for the VP Token.
* @param {number} expiry - time in seconds representing the expiry of the token.
*
*
* @returns {Promise<string>} - a promise resolving with a string containing ID Token or an error.
*/
async function generateVpToken(kid, vp, header, expiry) {
if (!kid) {
throw new TypeError("Error generating VP Token: kid cannot be empty");
} else if (!vp) {
throw new TypeError("Error generating VP Token: vp cannot be empty");
} else if (!header) {
throw new TypeError("Error generating VP Token: header cannot be empty");
} else if (!expiry) {
throw new TypeError("Error generating VP Token: expiry cannot be empty");
}

const payload = new Object();
Object.defineProperty(payload, "nonce", {
value: this.nonce,
});
Object.defineProperty(payload, "vp", {
value: vp,
});
Object.defineProperty(payload, "aud", {
value: this.client_id,
});
Object.defineProperty(payload, "iss", {
value: kid.split("#")[0],
});
Object.defineProperty(payload, "exp", {
value: expiry,
});

return await signJWT({ header, payload });
}

0 comments on commit 579a837

Please sign in to comment.