diff --git a/src/frontend/src/flows/verifiableCredentials/allowCredentials.json b/src/frontend/src/flows/verifiableCredentials/allowCredentials.json
new file mode 100644
index 0000000000..3abd794094
--- /dev/null
+++ b/src/frontend/src/flows/verifiableCredentials/allowCredentials.json
@@ -0,0 +1,9 @@
+{
+ "en": {
+ "title": "Credential Access Request",
+ "allow_start": "Allow verifying credential",
+ "allow_sep_with": "with",
+ "cancel": "Cancel",
+ "allow": "Allow"
+ }
+}
diff --git a/src/frontend/src/flows/verifiableCredentials/allowCredentials.ts b/src/frontend/src/flows/verifiableCredentials/allowCredentials.ts
new file mode 100644
index 0000000000..4a88d6f890
--- /dev/null
+++ b/src/frontend/src/flows/verifiableCredentials/allowCredentials.ts
@@ -0,0 +1,109 @@
+import { mkAnchorInput } from "$src/components/anchorInput";
+import { mainWindow } from "$src/components/mainWindow";
+import { I18n } from "$src/i18n";
+import { mount, renderPage } from "$src/utils/lit-html";
+import { TemplateResult, html } from "lit-html";
+
+import copyJson from "./allowCredentials.json";
+
+/* A screen prompting the user to allow (or cancel) issuing verified
+ * credentials */
+
+const allowCredentialsTemplate = ({
+ i18n,
+ relyingOrigin,
+ providerOrigin,
+ consentMessage,
+ userNumber,
+ onAllow,
+ onCancel,
+ scrollToTop = false,
+}: {
+ i18n: I18n;
+ relyingOrigin: string;
+ providerOrigin: string;
+ consentMessage: string;
+ userNumber?: bigint;
+ onAllow: (userNumber: bigint) => void;
+ onCancel: () => void;
+ /* put the page into view */
+ scrollToTop?: boolean;
+}): TemplateResult => {
+ const copy = i18n.i18n(copyJson);
+ const anchorInput = mkAnchorInput({
+ userNumber,
+ onSubmit: (userNumber) => onAllow(userNumber),
+ });
+
+ const slot = html`
+ window.scrollTo(0, 0)) : undefined}
+ >
+ ${copy.title}
+
+
+ ${anchorInput.template}
+
+
+ ${copy.allow_start}
+ ${providerOrigin} ${copy.allow_sep_with}
+ ${relyingOrigin}?
+
+
+
+
+
+
+
+
+ `;
+
+ return mainWindow({
+ showFooter: false,
+ showLogo: false,
+ slot,
+ });
+};
+
+export const allowCredentialsPage = renderPage(allowCredentialsTemplate);
+
+// Prompt to allow verifying credentials
+export const allowCredentials = ({
+ relyingOrigin,
+ providerOrigin,
+ consentMessage,
+ userNumber,
+}: {
+ relyingOrigin: string;
+ providerOrigin: string;
+ consentMessage: string;
+ userNumber?: bigint;
+}): Promise<{ tag: "allowed"; userNumber: bigint } | { tag: "canceled" }> => {
+ return new Promise((resolve) =>
+ allowCredentialsPage({
+ i18n: new I18n(),
+ relyingOrigin,
+ providerOrigin,
+ consentMessage,
+ userNumber,
+ onAllow: (userNumber) => resolve({ tag: "allowed", userNumber }),
+ onCancel: () => resolve({ tag: "canceled" }),
+ scrollToTop: true,
+ })
+ );
+};
diff --git a/src/showcase/src/pages/[page].astro b/src/showcase/src/pages/[page].astro
index 6ccd26c815..473612fa13 100644
--- a/src/showcase/src/pages/[page].astro
+++ b/src/showcase/src/pages/[page].astro
@@ -54,6 +54,7 @@ export const iiPageNames = [
"showMessage",
"showSpinner",
"addDeviceSuccess",
+ "allowCredentials",
];
export function getStaticPaths() {
diff --git a/src/showcase/src/showcase.ts b/src/showcase/src/showcase.ts
index 9eafee9e13..7dcaf84dfe 100644
--- a/src/showcase/src/showcase.ts
+++ b/src/showcase/src/showcase.ts
@@ -58,6 +58,8 @@ import { NonEmptyArray } from "$src/utils/utils";
import { TemplateResult, html, render } from "lit-html";
import { asyncReplace } from "lit-html/directives/async-replace.js";
+import { allowCredentialsPage } from "$src/flows/verifiableCredentials/allowCredentials";
+
const identityBackground = loadIdentityBackground();
const userNumber = BigInt(10000);
@@ -630,6 +632,19 @@ export const iiPages: Record void> = {
deviceAlias: chromeDevice.alias,
onContinue: () => console.log("Continue"),
}),
+ allowCredentials: () =>
+ allowCredentialsPage({
+ i18n,
+ relyingOrigin: "https://oc.app",
+ providerOrigin: "https://nns.ic0.app",
+ consentMessage: `
+# ACME Inc Employment Credential
+
+Credential that states that the holder is employed by the ACME Inc at the time of issuance.
+ `,
+ onAllow: () => toast.info(html`Allowed`),
+ onCancel: () => toast.info(html`Canceled`),
+ }),
};
const showcase: TemplateResult = html`