Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 26 additions & 44 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ Read our [documentation](https://dev.proof.com/docs/digital-credentials-overview

- [Installation](#installation)
- [Getting Started](#getting-started)
- [Transaction Templates](#transaction-templates)
- [Custom authorization URL](#custom-authorization-url)
- [Styles](#styles)
- [TypeScript](#typescript)
Expand All @@ -26,67 +25,50 @@ npm install @proof.com/proof-vc-web

## Getting Started

To request a Verifiable Presentation, `init` the client once at the start of your application:

```javascript
import { init } from "@proof.com/proof-vc-web";

init({
environment: "sandbox",
client_id: "<CLIENT_ID>",
callback_uri: "<CALLBACK_URI>",
});
```

then use the `<proof-verify-id />` HTML tag anywhere:
To request a Verifiable Presentation, drop the `<proof-verify-id />` HTML tag anywhere and give it your verifier config:

```html
<proof-verify-id nonce="3e8e4918-e9fb-453a-a538-81152be15c1b" />
<proof-verify-id
environment="sandbox"
client-id="<CLIENT_ID>"
callback-uri="<CALLBACK_URI>"
nonce="3e8e4918-e9fb-453a-a538-81152be15c1b"
/>
```

You can also provide a `login-hint` or `state`:
You can also provide a `login-hint`, `state`, or `response-mode` (`fragment` (default) | `direct_post`):

```html
<proof-verify-id
environment="sandbox"
client-id="<CLIENT_ID>"
callback-uri="<CALLBACK_URI>"
nonce="3e8e4918-e9fb-453a-a538-81152be15c1b"
state="6A2B4CD830"
login-hint="frodo.baggins@theshire"
response-mode="direct_post"
/>
```

### Transaction Templates

You can use _Transaction Templates_ provided by [@proof.com/proof-vc-common](https://github.com/proof/proof-vc-common) via
the `transactionData` prop:

```javascript
import { transactionData } from "@proof.com/proof-vc-web";

const data = transactionData.paymentItemized({
title: "Drive Shaft",
description: "The Roadhouse (18+), May 6 2026",
currency: "USD",
items: [
{ quantity: 2, unit_cost: 40.0, label: "General Admission" },
{ quantity: 2, unit_cost: 11.4, label: "Fees" },
],
});

<proof-verify-id
nonce="3e8e4918-e9fb-453a-a538-81152be15c1b"
transactionData={data}
/>;
```

### Custom authorization URL

You can pass a `resolveAuthorizationUrl` property to create your own authorization request URL (e.g. a Pushed Authorization Request server-side).
When set, the element ignores the `nonce` / `state` / `login-hint` / `transactionData` attributes.
When set, the element ignores the `environment` / `client-id` / `callback-uri` / `response-mode` / `nonce` / `state` / `login-hint` attributes.

```javascript
import { buildAuthorizationUrl } from "@proof.com/proof-vc-web";

<proof-verify-id
resolveAuthorizationUrl={async () => await getAuthorizationRequestURL()}
/>
resolveAuthorizationUrl={() =>
buildAuthorizationUrl({
environment: "sandbox",
clientId: "<CLIENT_ID>",
callbackUri: "<CALLBACK_URI>",
nonce: "3e8e4918-e9fb-453a-a538-81152be15c1b",
scope: "urn:proof:params:scope:verifiable-credentials:basic",
})
}
/>;
```

Return `null` (or `undefined`) to cancel the redirect.
Expand Down Expand Up @@ -122,7 +104,7 @@ Or, drop a triple-slash reference in any `.d.ts` file in your project:
/// <reference types="@proof.com/proof-vc-web/react" />
```

Both forms activate the `React.JSX.IntrinsicElements` augmentation that types `nonce`, `theme`, `size`, `transactionData`, and the other attributes.
Both forms activate the `React.JSX.IntrinsicElements` augmentation that types `environment`, `client-id`, `callback-uri`, `nonce`, `theme`, `size`, and the other attributes.

## Documentation

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@proof.com/proof-vc-common": "^0.2.0"
"@proof.com/proof-vc-common": "^0.3.0"
},
"devDependencies": {
"@types/react": "^19.2.15",
Expand Down
4 changes: 2 additions & 2 deletions site/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
"build": "yarn run webpack",
"check-all": "yarn format && yarn lint && yarn typecheck",
"dev": "yarn run webpack serve",
"format": "prettier --write .",
"format:check": "prettier --check .",
"format": "prettier --write . --ignore-path ../.gitignore",
"format:check": "prettier --check . --ignore-path ../.gitignore",
"lint": "eslint . --fix",
"lint:check": "eslint .",
"typecheck": "tsc --noEmit"
Expand Down
76 changes: 27 additions & 49 deletions site/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,64 +32,42 @@
<body>
<div class="row">
<span class="row__label">dark</span>
<proof-verify-id nonce="123" theme="dark" size="icon"></proof-verify-id>
<proof-verify-id nonce="123" theme="dark" size="small"></proof-verify-id>
<proof-verify-id
nonce="123"
theme="dark"
size="medium"
data-tx
></proof-verify-id>
<proof-verify-id nonce="123" theme="dark" size="large"></proof-verify-id>
<proof-verify-id theme="dark" size="icon"></proof-verify-id>
<proof-verify-id theme="dark" size="small"></proof-verify-id>
<proof-verify-id theme="dark" size="medium"></proof-verify-id>
<proof-verify-id theme="dark" size="large"></proof-verify-id>
</div>
<div class="row">
<span class="row__label">gray</span>
<proof-verify-id nonce="123" theme="gray" size="icon"></proof-verify-id>
<proof-verify-id nonce="123" theme="gray" size="small"></proof-verify-id>
<proof-verify-id nonce="123" theme="gray" size="medium"></proof-verify-id>
<proof-verify-id nonce="123" theme="gray" size="large"></proof-verify-id>
<proof-verify-id theme="gray" size="icon"></proof-verify-id>
<proof-verify-id theme="gray" size="small"></proof-verify-id>
<proof-verify-id theme="gray" size="medium"></proof-verify-id>
<proof-verify-id theme="gray" size="large"></proof-verify-id>
</div>
<div class="row">
<span class="row__label">outline</span>
<proof-verify-id
nonce="123"
theme="outline"
size="icon"
></proof-verify-id>
<proof-verify-id
nonce="123"
theme="outline"
size="small"
></proof-verify-id>
<proof-verify-id
nonce="123"
theme="outline"
size="medium"
></proof-verify-id>
<proof-verify-id
nonce="123"
theme="outline"
size="large"
></proof-verify-id>
<proof-verify-id theme="outline" size="icon"></proof-verify-id>
<proof-verify-id theme="outline" size="small"></proof-verify-id>
<proof-verify-id theme="outline" size="medium"></proof-verify-id>
<proof-verify-id theme="outline" size="large"></proof-verify-id>
</div>
<div class="row">
<span class="row__label">primary</span>
<proof-verify-id
nonce="123"
theme="primary"
size="icon"
></proof-verify-id>
<proof-verify-id
nonce="123"
theme="primary"
size="small"
></proof-verify-id>
<proof-verify-id nonce="123"></proof-verify-id>
<proof-verify-id
nonce="123"
theme="primary"
size="large"
></proof-verify-id>
<proof-verify-id theme="primary" size="icon"></proof-verify-id>
<proof-verify-id theme="primary" size="small"></proof-verify-id>
<proof-verify-id></proof-verify-id>
<proof-verify-id theme="primary" size="large"></proof-verify-id>
</div>

<script>
// Apply the shared verifier config to every demo button. In a real
// app these would be set per element (or rendered server-side).
for (const el of document.querySelectorAll("proof-verify-id")) {
el.setAttribute("environment", "next");
el.setAttribute("client-id", "caxdw5a7d");
el.setAttribute("callback-uri", "http://localhost:4000");
el.setAttribute("nonce", "123");
}
</script>
</body>
</html>
21 changes: 1 addition & 20 deletions site/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1 @@
import { init, transactionData, ProofVerifyId } from "../../src/index.ts";

init({
environment: "next",
clientId: "caxdw5a7d",
callbackUri: "http://localhost:4000",
});

const txDemo = document.querySelector<ProofVerifyId>(
"proof-verify-id[data-tx]",
);
if (txDemo) {
txDemo.transactionData = transactionData.paymentMandate({
payment_instrument: { type: "card", id: "pm_demo" },
payee: { name: "Acme" },
prompt_summary: "Authorize $42.00 to Acme",
amount: 42,
currency: "USD",
});
}
import "../../src/index.ts";
9 changes: 2 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,8 @@ if (

export { ProofVerifyId };
export {
init,
transactionData,
buildAuthorizationUrl,
parseAuthorizationResponse,
type Environment,
type ResponseMode,
type TransactionData,
type WireInstructionsPayload,
type PaymentMandatePayload,
type PaymentItemizedPayload,
type SessionDataPayload,
} from "@proof.com/proof-vc-common";
49 changes: 30 additions & 19 deletions src/proof_verify_id.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
getAuthorizationRequestURL,
type TransactionData,
buildAuthorizationUrl,
type Environment,
type ResponseMode,
} from "@proof.com/proof-vc-common";
import { css } from "./styles.ts";

Expand Down Expand Up @@ -34,16 +35,8 @@ export class ProofVerifyId extends Base {
readonly #button: HTMLButtonElement;
readonly #label: HTMLSpanElement;
#pending = false;
#transactionData: TransactionData | undefined;
#resolveAuthorizationUrl: AuthorizationUrlResolver | undefined;

get transactionData(): TransactionData | undefined {
return this.#transactionData;
}
set transactionData(value: TransactionData | undefined) {
this.#transactionData = value;
}

get resolveAuthorizationUrl(): AuthorizationUrlResolver | undefined {
return this.#resolveAuthorizationUrl;
}
Expand Down Expand Up @@ -109,7 +102,7 @@ export class ProofVerifyId extends Base {
try {
const url = this.#resolveAuthorizationUrl
? await this.#resolveAuthorizationUrl()
: await this.#buildAuthorizationUrl();
: this.#buildAuthorizationUrl();

// A nullish result aborts the redirect (e.g. the resolver cancelled).
if (url) window.location.href = url;
Expand All @@ -119,23 +112,41 @@ export class ProofVerifyId extends Base {
}
}

async #buildAuthorizationUrl(): Promise<string> {
#buildAuthorizationUrl(): string {
const environment = this.getAttribute("environment");
const clientId = this.getAttribute("client-id");
const callbackUri = this.getAttribute("callback-uri");
const nonce = this.nonce || this.getAttribute("nonce");
if (!nonce) {
throw new Error("<proof-verify-id>: 'nonce' is required");

const missing = [
["environment", environment],
["client-id", clientId],
["callback-uri", callbackUri],
["nonce", nonce],
]
.filter(([, value]) => !value)
.map(([name]) => name);
if (missing.length) {
throw new Error(
`<proof-verify-id>: missing required attribute(s): ${missing.join(", ")}`,
);
}

const state = this.getAttribute("state");
const login_hint = this.getAttribute("login-hint");
const transaction_data =
this.#transactionData ?? this.getAttribute("transaction-data");
const response_mode = this.getAttribute("response-mode");

return getAuthorizationRequestURL({
nonce,
return buildAuthorizationUrl({
environment: environment as Environment,
clientId: clientId!,
callbackUri: callbackUri!,
nonce: nonce!,
scope: "urn:proof:params:scope:verifiable-credentials:basic",
...(response_mode !== null && {
responseMode: response_mode as ResponseMode,
}),
...(state !== null && { state }),
...(login_hint !== null && { loginHint: login_hint }),
...(transaction_data !== null && { transactionData: transaction_data }),
});
}

Expand Down
8 changes: 5 additions & 3 deletions src/react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@ import type {
AuthorizationUrlResolver,
ProofVerifyId,
} from "./proof_verify_id.ts";
import type { TransactionData } from "@proof.com/proof-vc-common";
import type { Environment, ResponseMode } from "@proof.com/proof-vc-common";

export interface ProofVerifyIdJSXAttributes extends HTMLAttributes<ProofVerifyId> {
environment?: Environment;
"client-id"?: string;
"callback-uri"?: string;
"response-mode"?: ResponseMode;
nonce?: string;
state?: string;
theme?: "dark" | "gray" | "outline" | "primary";
size?: "icon" | "small" | "medium" | "large";
"login-hint"?: string;
"transaction-data"?: string;
transactionData?: TransactionData;
// Set as a property (React 19 assigns matching props on custom elements);
// on React < 19 use a ref instead, as function props are stringified.
resolveAuthorizationUrl?: AuthorizationUrlResolver;
Expand Down
Loading