Skip to content

Commit

Permalink
Merge pull request #22 from provenant-dev/data-attestation-cred
Browse files Browse the repository at this point in the history
feat: add create data attestation credential methods
  • Loading branch information
HunnySajid authored Nov 8, 2024
2 parents 7e6ce50 + 7de3af4 commit 74be1c3
Show file tree
Hide file tree
Showing 2 changed files with 158 additions and 11 deletions.
108 changes: 99 additions & 9 deletions examples/web-react/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { AuthorizeResult, createClient } from "signify-polaris-web";
import { AuthorizeResult, createClient, CreateCredentialResult, ExtensionClient } from "signify-polaris-web";
import { FormEvent, useEffect, useState } from "react";

const client = createClient();

export function App() {
const [extensionId, setExtensionId] = useState<string | false | null>(null);
const [error, setError] = useState<string | null>(null);
Expand All @@ -11,8 +9,14 @@ export function App() {
const [pending, setPending] = useState(false);
const [url, setUrl] = useState(window.location.href);
const [method, setMethod] = useState("GET");
const [dataDigest, setDataDigest] = useState('');
const [digestAlgo, setDigestAlgo] = useState('SHA-256');
const [attestCredResult, setAttestCredResult] = useState<CreateCredentialResult | null>(null);
const [extensionClient, setExtensionClient] = useState<ExtensionClient | null>(null)

useEffect(() => {
const client = createClient();
setExtensionClient(client)
client.isExtensionInstalled().then((result) => setExtensionId(result));
}, []);

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

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

try {
const result = await client.signRequest({ url, method });
const result = await extensionClient.signRequest({ url, method });
console.log('signRequest result: ', result)
setHeaders(result.headers);
} catch (error) {
setError(error.message ?? "Something went wrong");
Expand All @@ -48,6 +54,60 @@ export function App() {
}
}

async function handleAttestCredential(ev: FormEvent) {
ev.preventDefault();
setError(null);
setPending(true);

try {
//// example of data attestation schema:
//// https://github.com/provenant-dev/public-schema/blob/main/attestation/attestation.schema.json
let schemaSaid = 'ENDcMNUZjag27T_GTxiCmB2kYstg_kqipqz39906E_FD'
let credData = { digest: dataDigest, digestAlgo: 'SHA-256' }
const result = await extensionClient.createDataAttestationCredential({
credData: credData,
schemaSaid: schemaSaid
});
console.log('create data attestation credential result: ', result)
setAttestCredResult(result);

} catch (error) {
setError(error.message ?? "Something went wrong");
} finally {
setPending(false);
}
}

async function handleDownloadCredential(ev: FormEvent) {
ev.preventDefault();
setError(null);
setPending(true);


try {
let credSAID = attestCredResult?.acdc?._ked?.d
const credential = await extensionClient.getCredential(credSAID, true);
console.log('get credential result: ', credential)
if (!credential?.credential) {
setError("Unable to get credential");
return
}
const blob = new Blob([credential.credential], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'attestation-credential.cesr';
link.click();
URL.revokeObjectURL(url);

} catch (error) {
setError(error.message ?? "Something went wrong");
} finally {
setPending(false);
}
}


return (
<div style={{ maxWidth: 800, margin: "auto" }}>
<section>
Expand All @@ -63,13 +123,13 @@ export function App() {
<form id="headers-form" onSubmit={handleSignHeaders}>
<div>
<label htmlFor="url">URL</label>
<input id="url" value={url} onChange={(ev) => setUrl(ev.currentTarget.value)} />
<input id="url" value={url} onChange={(ev) => setUrl(ev.currentTarget.value)} style={{ marginLeft: '10px' }} />
</div>
<div>
<div style={{ marginTop: '10px' }}>
<label htmlFor="method">Method</label>
<input id="method" value={method} onChange={(ev) => setMethod(ev.currentTarget.value)} />
<input id="method" value={method} onChange={(ev) => setMethod(ev.currentTarget.value)} style={{ marginLeft: '10px' }} />
</div>
<div>
<div style={{ marginTop: '10px' }}>
<button type="submit" disabled={!authorizeResult}>
Request Signed Headers
</button>
Expand Down Expand Up @@ -104,6 +164,36 @@ export function App() {
</code>
</pre>
</section>

<section>
<h1>Create Data Attestation Credential</h1>
<form id="headers-form" onSubmit={handleAttestCredential}>
<div>
<label htmlFor="dataDigest">Data Digest</label>
<input id="dataDigest" value={dataDigest} onChange={(ev) => setDataDigest(ev.currentTarget.value)} style={{ marginLeft: '10px' }} />
</div>
<div style={{ marginTop: '10px' }}>
<label htmlFor="digestAlgo">Digest Algorithm</label>
<input id="digestAlgo"
value={digestAlgo}
onChange={(ev) => setDigestAlgo(ev.currentTarget.value)}
style={{ marginLeft: '10px' }}
placeholder="e.g. SHA-256"
/>
</div>
<div style={{ marginTop: '10px' }}>
<button type="submit" disabled={!authorizeResult || !dataDigest}>
Attest with Selected AID
</button>
<button type="submit"
disabled={!authorizeResult || !attestCredResult}
style={{ marginLeft: '10px' }}
onClick={handleDownloadCredential}>
Download Attestation Credential
</button>
</div>
</form>
</section>
</div>
);
}
61 changes: 59 additions & 2 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,34 @@ export interface SignRequestResult {
headers: Record<string, string>;
}

export interface CredentialResult {
/**
* If the extension responds with a credential, the data will be contained here.
*/
credential: any;

}

export interface CreateCredentialArgs {
/**
* credential attributes as per schema
*/
credData: any;

/**
* SAID of the schema
*
*/
schemaSaid: string;
}

export interface CreateCredentialResult {
acdc: Record<string, any>;
iss: Record<string, any>;
anc: Record<string, any>;
op: Record<string, any>;
}

export interface ConfigureVendorArgs {
/**
* The vendor url
Expand All @@ -126,8 +154,8 @@ type PendingRequest<T = unknown> = { resolve: (value: T) => void; reject: (reaso

class Deferred<T = void> implements PromiseLike<T> {
promise: Promise<T>;
resolve: (value: T) => void = () => {};
reject: (reason?: Error) => void = () => {};
resolve: (value: T) => void = () => { };
reject: (reason?: Error) => void = () => { };

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

/**
* Sends a /signify/credential/create/data-attestation message to the extension.
*
* The extension decides whether or not it needs to prompt the user to approve the signing
* or automatically sign the request.
*
* Example of data attestation schema: https://github.com/provenant-dev/public-schema/blob/main/attestation/attestation.schema.json
*
* @param payload Information about data attestation credential.
* @returns {CreateCredentialResult}
*/
createDataAttestationCredential = async (payload: CreateCredentialArgs): Promise<CreateCredentialResult> => {
return this.sendMessage("/signify/credential/create/data-attestation", { payload });
};

/**
* Sends a /signify/credential/get message to the extension.
*
* The extension decides whether or not it needs to prompt the user to approve the signing
* or automatically sign the request.
*
* @param said credential SAID.
* @param includeCESR include credential CESR stream in response.
* @returns {CredentialResult}
*/
getCredential = async (said: string, includeCESR: boolean = false): Promise<CredentialResult> => {
return this.sendMessage("/signify/credential/get", { payload: { id: said, includeCESR } });
};

/**
* Configures the extension with the specified vendor.
* @param payload The vendor configuration
Expand Down

0 comments on commit 74be1c3

Please sign in to comment.