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 4, 2022
1 parent f56e856 commit 27e58dc
Showing 1 changed file with 174 additions and 1 deletion.
175 changes: 174 additions & 1 deletion cmd/wallet-js-sdk/src/oidc/presentation/openid4vp.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@ SPDX-License-Identifier: Apache-2.0
*/

import axios from "axios";
import { decode } from "js-base64";
import { decode, encode } from "js-base64";
import { CredentialManager, DIDManager } from "@";
import * as jose from "jose";
import { generateNonce } from "@/util/helper";

const ID_TOKEN_EXPIRY = 10 * 60; // 10 minutes
const VP_TOKEN_EXPIRY = 10 * 60; // 10 minutes

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

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

return response.results;
}

async submitOIDCPresentation(
presentationQuery,
sign = async function (iss, aud, iat, nonce) {
return "";
}
) {
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"
);
}

const header = new Object();
Object.defineProperty(header, "alg", {
value: alg,
});
Object.defineProperty(header, "kid", {
// TODO: clarify where we get this param from
value: kid,
});
Object.defineProperty(header, "typ", {
value: "JWT",
});

const encodedHeader = encode(JSON.stringify(header), true);

const id_token = await generateIdToken(
presentationQuery.presentation_submission,
encodedHeader,
(sign = async function (iss, aud, iat, nonce) {
return "";
})
);

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

const vp_token = await generateVpToken(
encodedHeader,
vp,
(sign = async function (iss, aud, iat, nonce) {
return "";
})
);

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

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

async function generateIdToken(
presentation_submission,
encodedHeader,
sign = async function (iss, aud, iat, nonce) {
return "";
}
) {
if (!presentation_submission) {
throw new TypeError(
"Error generating ID Token: presentation_submission is be empty"
);
}

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

const iat = new Date().getTime() / 1000;

const payload = new Object();
// TODO: dynamically pass sub
Object.defineProperty(payload, "sub", {
value: "sub",
});
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: iss,
});
Object.defineProperty(payload, "iat", {
value: iat,
});
Object.defineProperty(payload, "exp", {
value: iat + ID_TOKEN_EXPIRY,
});

const encodedPayload = encode(JSON.stringify(payload), true);

const signature = await sign(kid, this.client_id, iat, this.nonce);

return `${encodedHeader}.${encodedPayload}.${signature}`;
}

async function generateVpToken(
encodedHeader,
vp,
sign = async function (iss, aud, iat, nonce) {
return "";
}
) {
const iat = new Date().getTime() / 1000;
const payload = new Object();
Object.defineProperty(payload, "nonce", {
value: this.nonce,
});
Object.defineProperty(payload, "vp", {
value: vp,
});
Object.defineProperty(payload, "aud", {
// TODO: clarify where we get this param from
value: "",
});
Object.defineProperty(payload, "iss", {
// TODO: clarify where we get this param from
value: "",
});
Object.defineProperty(payload, "iat", {
value: iat,
});
Object.defineProperty(payload, "nbf", {
value: iat,
});
Object.defineProperty(payload, "exp", {
value: iat + VP_TOKEN_EXPIRY,
});

const encodedPayload = encode(JSON.stringify(payload), true);

const signature = await sign(kid, this.client_id, iat, this.nonce);

return `${encodedHeader}.${encodedPayload}.${signature}`;
}

0 comments on commit 27e58dc

Please sign in to comment.