Skip to content

Commit 74be1c3

Browse files
authored
Merge pull request #22 from provenant-dev/data-attestation-cred
feat: add create data attestation credential methods
2 parents 7e6ce50 + 7de3af4 commit 74be1c3

File tree

2 files changed

+158
-11
lines changed

2 files changed

+158
-11
lines changed

Diff for: examples/web-react/src/App.tsx

+99-9
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
import { AuthorizeResult, createClient } from "signify-polaris-web";
1+
import { AuthorizeResult, createClient, CreateCredentialResult, ExtensionClient } from "signify-polaris-web";
22
import { FormEvent, useEffect, useState } from "react";
33

4-
const client = createClient();
5-
64
export function App() {
75
const [extensionId, setExtensionId] = useState<string | false | null>(null);
86
const [error, setError] = useState<string | null>(null);
@@ -11,8 +9,14 @@ export function App() {
119
const [pending, setPending] = useState(false);
1210
const [url, setUrl] = useState(window.location.href);
1311
const [method, setMethod] = useState("GET");
12+
const [dataDigest, setDataDigest] = useState('');
13+
const [digestAlgo, setDigestAlgo] = useState('SHA-256');
14+
const [attestCredResult, setAttestCredResult] = useState<CreateCredentialResult | null>(null);
15+
const [extensionClient, setExtensionClient] = useState<ExtensionClient | null>(null)
1416

1517
useEffect(() => {
18+
const client = createClient();
19+
setExtensionClient(client)
1620
client.isExtensionInstalled().then((result) => setExtensionId(result));
1721
}, []);
1822

@@ -23,7 +27,8 @@ export function App() {
2327
setPending(true);
2428

2529
try {
26-
const result = await client.authorize({ message: `Message ${Date.now()}` });
30+
const result = await extensionClient.authorize({ message: `Message ${Date.now()}` });
31+
console.log('authorize result: ', result)
2732
setAuthorizeResult(result);
2833
} catch (error) {
2934
setError(error.message ?? "Something went wrong");
@@ -39,7 +44,8 @@ export function App() {
3944
setHeaders(null);
4045

4146
try {
42-
const result = await client.signRequest({ url, method });
47+
const result = await extensionClient.signRequest({ url, method });
48+
console.log('signRequest result: ', result)
4349
setHeaders(result.headers);
4450
} catch (error) {
4551
setError(error.message ?? "Something went wrong");
@@ -48,6 +54,60 @@ export function App() {
4854
}
4955
}
5056

57+
async function handleAttestCredential(ev: FormEvent) {
58+
ev.preventDefault();
59+
setError(null);
60+
setPending(true);
61+
62+
try {
63+
//// example of data attestation schema:
64+
//// https://github.com/provenant-dev/public-schema/blob/main/attestation/attestation.schema.json
65+
let schemaSaid = 'ENDcMNUZjag27T_GTxiCmB2kYstg_kqipqz39906E_FD'
66+
let credData = { digest: dataDigest, digestAlgo: 'SHA-256' }
67+
const result = await extensionClient.createDataAttestationCredential({
68+
credData: credData,
69+
schemaSaid: schemaSaid
70+
});
71+
console.log('create data attestation credential result: ', result)
72+
setAttestCredResult(result);
73+
74+
} catch (error) {
75+
setError(error.message ?? "Something went wrong");
76+
} finally {
77+
setPending(false);
78+
}
79+
}
80+
81+
async function handleDownloadCredential(ev: FormEvent) {
82+
ev.preventDefault();
83+
setError(null);
84+
setPending(true);
85+
86+
87+
try {
88+
let credSAID = attestCredResult?.acdc?._ked?.d
89+
const credential = await extensionClient.getCredential(credSAID, true);
90+
console.log('get credential result: ', credential)
91+
if (!credential?.credential) {
92+
setError("Unable to get credential");
93+
return
94+
}
95+
const blob = new Blob([credential.credential], { type: 'text/plain' });
96+
const url = URL.createObjectURL(blob);
97+
const link = document.createElement('a');
98+
link.href = url;
99+
link.download = 'attestation-credential.cesr';
100+
link.click();
101+
URL.revokeObjectURL(url);
102+
103+
} catch (error) {
104+
setError(error.message ?? "Something went wrong");
105+
} finally {
106+
setPending(false);
107+
}
108+
}
109+
110+
51111
return (
52112
<div style={{ maxWidth: 800, margin: "auto" }}>
53113
<section>
@@ -63,13 +123,13 @@ export function App() {
63123
<form id="headers-form" onSubmit={handleSignHeaders}>
64124
<div>
65125
<label htmlFor="url">URL</label>
66-
<input id="url" value={url} onChange={(ev) => setUrl(ev.currentTarget.value)} />
126+
<input id="url" value={url} onChange={(ev) => setUrl(ev.currentTarget.value)} style={{ marginLeft: '10px' }} />
67127
</div>
68-
<div>
128+
<div style={{ marginTop: '10px' }}>
69129
<label htmlFor="method">Method</label>
70-
<input id="method" value={method} onChange={(ev) => setMethod(ev.currentTarget.value)} />
130+
<input id="method" value={method} onChange={(ev) => setMethod(ev.currentTarget.value)} style={{ marginLeft: '10px' }} />
71131
</div>
72-
<div>
132+
<div style={{ marginTop: '10px' }}>
73133
<button type="submit" disabled={!authorizeResult}>
74134
Request Signed Headers
75135
</button>
@@ -104,6 +164,36 @@ export function App() {
104164
</code>
105165
</pre>
106166
</section>
167+
168+
<section>
169+
<h1>Create Data Attestation Credential</h1>
170+
<form id="headers-form" onSubmit={handleAttestCredential}>
171+
<div>
172+
<label htmlFor="dataDigest">Data Digest</label>
173+
<input id="dataDigest" value={dataDigest} onChange={(ev) => setDataDigest(ev.currentTarget.value)} style={{ marginLeft: '10px' }} />
174+
</div>
175+
<div style={{ marginTop: '10px' }}>
176+
<label htmlFor="digestAlgo">Digest Algorithm</label>
177+
<input id="digestAlgo"
178+
value={digestAlgo}
179+
onChange={(ev) => setDigestAlgo(ev.currentTarget.value)}
180+
style={{ marginLeft: '10px' }}
181+
placeholder="e.g. SHA-256"
182+
/>
183+
</div>
184+
<div style={{ marginTop: '10px' }}>
185+
<button type="submit" disabled={!authorizeResult || !dataDigest}>
186+
Attest with Selected AID
187+
</button>
188+
<button type="submit"
189+
disabled={!authorizeResult || !attestCredResult}
190+
style={{ marginLeft: '10px' }}
191+
onClick={handleDownloadCredential}>
192+
Download Attestation Credential
193+
</button>
194+
</div>
195+
</form>
196+
</section>
107197
</div>
108198
);
109199
}

Diff for: src/client.ts

+59-2
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,34 @@ export interface SignRequestResult {
108108
headers: Record<string, string>;
109109
}
110110

111+
export interface CredentialResult {
112+
/**
113+
* If the extension responds with a credential, the data will be contained here.
114+
*/
115+
credential: any;
116+
117+
}
118+
119+
export interface CreateCredentialArgs {
120+
/**
121+
* credential attributes as per schema
122+
*/
123+
credData: any;
124+
125+
/**
126+
* SAID of the schema
127+
*
128+
*/
129+
schemaSaid: string;
130+
}
131+
132+
export interface CreateCredentialResult {
133+
acdc: Record<string, any>;
134+
iss: Record<string, any>;
135+
anc: Record<string, any>;
136+
op: Record<string, any>;
137+
}
138+
111139
export interface ConfigureVendorArgs {
112140
/**
113141
* The vendor url
@@ -126,8 +154,8 @@ type PendingRequest<T = unknown> = { resolve: (value: T) => void; reject: (reaso
126154

127155
class Deferred<T = void> implements PromiseLike<T> {
128156
promise: Promise<T>;
129-
resolve: (value: T) => void = () => {};
130-
reject: (reason?: Error) => void = () => {};
157+
resolve: (value: T) => void = () => { };
158+
reject: (reason?: Error) => void = () => { };
131159

132160
constructor() {
133161
this.promise = new Promise<T>((resolve, reject) => {
@@ -322,6 +350,35 @@ export class ExtensionClient {
322350
return this.sendMessage("/signify/clear-session", { payload });
323351
};
324352

353+
/**
354+
* Sends a /signify/credential/create/data-attestation message to the extension.
355+
*
356+
* The extension decides whether or not it needs to prompt the user to approve the signing
357+
* or automatically sign the request.
358+
*
359+
* Example of data attestation schema: https://github.com/provenant-dev/public-schema/blob/main/attestation/attestation.schema.json
360+
*
361+
* @param payload Information about data attestation credential.
362+
* @returns {CreateCredentialResult}
363+
*/
364+
createDataAttestationCredential = async (payload: CreateCredentialArgs): Promise<CreateCredentialResult> => {
365+
return this.sendMessage("/signify/credential/create/data-attestation", { payload });
366+
};
367+
368+
/**
369+
* Sends a /signify/credential/get message to the extension.
370+
*
371+
* The extension decides whether or not it needs to prompt the user to approve the signing
372+
* or automatically sign the request.
373+
*
374+
* @param said credential SAID.
375+
* @param includeCESR include credential CESR stream in response.
376+
* @returns {CredentialResult}
377+
*/
378+
getCredential = async (said: string, includeCESR: boolean = false): Promise<CredentialResult> => {
379+
return this.sendMessage("/signify/credential/get", { payload: { id: said, includeCESR } });
380+
};
381+
325382
/**
326383
* Configures the extension with the specified vendor.
327384
* @param payload The vendor configuration

0 commit comments

Comments
 (0)