Skip to content

Commit

Permalink
feat: sdk to sdk sdjwt holder verification (#292)
Browse files Browse the repository at this point in the history
Signed-off-by: Francisco Javier Ribo Labrador <elribonazo@gmail.com>
  • Loading branch information
elribonazo authored Oct 31, 2024
1 parent 5713cf9 commit 1d841f4
Show file tree
Hide file tree
Showing 53 changed files with 2,555 additions and 936 deletions.
7 changes: 1 addition & 6 deletions demos/next/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

105 changes: 89 additions & 16 deletions demos/next/src/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,28 +197,89 @@ export const initiatePresentationRequest = createAsyncThunk<
type
} = options;

if (type === SDK.Domain.CredentialType.JWT) {
await agent.initiatePresentationRequest<SDK.Domain.CredentialType>(
SDK.Domain.CredentialType.JWT,
toDID,
presentationClaims
);
}

if (type === SDK.Domain.CredentialType.AnonCreds) {
await agent.initiatePresentationRequest<SDK.Domain.CredentialType>(
SDK.Domain.CredentialType.AnonCreds,
toDID,
presentationClaims
);
}
await agent.initiatePresentationRequest<typeof type>(
type,
toDID,
presentationClaims
);

return api.fulfillWithValue(null)
} catch (err) {
return api.rejectWithValue(err as Error);
}
})

//This is for demonstration purposes and assumes that
//The Cloud agent is running on port ::::::
//Resolver at some point will be configurable to run on specific universal resolver endpoints
//for testnet, mainnet switching, etc
class ShortFormDIDResolverSample implements SDK.Domain.DIDResolver {
method: string = "prism"

private async parseResponse(response: Response) {
const data = await response.text();
try {
return JSON.parse(data);
}
catch {
return data;
}
}

async resolve(didString: string): Promise<SDK.Domain.DIDDocument> {
const url = "http://localhost:8000/cloud-agent/dids/" + didString;
const response = await fetch(url, {
"headers": {
"accept": "*/*",
"accept-language": "en",
"cache-control": "no-cache",
"pragma": "no-cache",
"sec-gpc": "1"
},
"method": "GET",
"mode": "cors",
"credentials": "omit"
})
if (!response.ok) {
throw new Error('Failed to fetch data');
}
const data = await response.json();
const didDocument = data.didDocument;

const servicesProperty = new SDK.Domain.Services(
didDocument.service
)
const verificationMethodsProperty = new SDK.Domain.VerificationMethods(
didDocument.verificationMethod
)
const coreProperties: SDK.Domain.DIDDocumentCoreProperty[] = [];
const authenticate: SDK.Domain.Authentication[] = [];
const assertion: SDK.Domain.AssertionMethod[] = [];

for (const verificationMethod of didDocument.verificationMethod) {
const isAssertion = didDocument.assertionMethod.find((method) => method === verificationMethod.id)
if (isAssertion) {
assertion.push(new SDK.Domain.AssertionMethod([isAssertion], [verificationMethod]))
}
const isAuthentication = didDocument.authentication.find((method) => method === verificationMethod.id)
if (isAuthentication) {
authenticate.push(new SDK.Domain.Authentication([isAuthentication], [verificationMethod]));
}
}

coreProperties.push(...authenticate);
coreProperties.push(servicesProperty);
coreProperties.push(verificationMethodsProperty);

const resolved = new SDK.Domain.DIDDocument(
SDK.Domain.DID.fromString(didString),
coreProperties
);

return resolved;
}
}

export const initAgent = createAsyncThunk<
{ agent: SDK.Agent },
{
Expand All @@ -229,7 +290,19 @@ export const initAgent = createAsyncThunk<
>("initAgent", async (options, api) => {
try {
const { mediatorDID, pluto, defaultSeed } = options;
const agent = await Agent.initialize({ mediatorDID, pluto, seed: defaultSeed });

const apollo = new SDK.Apollo();
const extraResolvers = [
ShortFormDIDResolverSample
];
const castor = new SDK.Castor(apollo, extraResolvers)
const agent = await Agent.initialize({
apollo,
castor,
mediatorDID,
pluto,
seed: defaultSeed
});
return api.fulfillWithValue({
agent,
})
Expand Down
14 changes: 14 additions & 0 deletions demos/next/src/components/Agent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

import React from "react"
import { Provider } from "react-redux";
import { store } from "../reducers/store";



//Used to mount the SDK Store
//Called once by the UI
//TODO: Check if the component has already mounted
export const MountSDK = (props: any) => {
const { children } = props;
return <Provider store={store}>{children}</Provider>
}
6 changes: 5 additions & 1 deletion demos/next/src/components/AgentRequire.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useMountedApp } from "@/reducers/store";
import React from "react";

export function AgentRequire({ children, text }: { children: any, text?: string }) {
export function AgentRequire({ children, text, hide }: { hide?: boolean, children: any, text?: string }) {

const { agent } = useMountedApp();
// Function to recursively clone children with disabled prop set to true
Expand All @@ -11,6 +11,10 @@ export function AgentRequire({ children, text }: { children: any, text?: string
React.isValidElement(child) &&
(child.type === 'input' || child.type === 'button')
) {
if (hide) {
return null;

}
return React.cloneElement(child, { disabled: true } as any);
}
// If the child has children, recursively iterate over its children
Expand Down
87 changes: 87 additions & 0 deletions demos/next/src/components/Credential.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@

import React, { useState } from "react";

import SDK from "@hyperledger/identus-edge-agent-sdk";
import { useMountedApp } from "@/reducers/store";
import { AgentRequire } from "@/components/AgentRequire";


function protect(credential: SDK.Domain.Credential) {
const newClaims: any[] = []
credential.claims.forEach((claim) => {
const newClaim = {}
Object.keys(claim).forEach((key) => {
newClaim[key] = "******"
})
newClaims.push(newClaim)
})
return newClaims
}

export function Credential(props) {
const { credential } = props;
const app = useMountedApp();
const [claims, setClaims] = useState(protect(credential));

function revealAttributes(credential: SDK.Domain.Credential, claimIndex: number, field: string) {
app.agent.instance?.pluto.getLinkSecret()
.then((linkSecret) => {
app.agent.instance?.revealCredentialFields(
credential,
[field],
linkSecret!.secret
).then((revealedFields) => {
const revealed = claims.map((claim, index) => {
if (claimIndex === index) {
return {
...claim,
[field]: revealedFields[field]
}
}
return claim
})
setClaims(revealed)
})
})
}

return <div className="w-full mt-5 p-6 bg-white rounded-lg shadow dark:bg-gray-800">
<p className="text-md font-normal text-gray-500 whitespace-normal max-w-full dark:text-gray-400"
style={{
textOverflow: 'ellipsis',
overflow: "hidden"
}}>
Issuer {credential.issuer}
</p>
<p className="mt-5 text-md font-normal text-gray-500 whitespace-normal max-w-full dark:text-gray-400">
Claims:
</p>
{claims.map((claim, claimIndex) =>
Object.keys(claim)
.filter((field) => field !== "id")
.map((field, i) => (
<div
key={`field${i}`}
className="text-md font-normal text-gray-500 dark:text-gray-400"
>
{field}
<AgentRequire hide text="Revealing attributes requires agent running">
{claim[field] === "******" ? (
<button
onClick={() => {
revealAttributes(credential, claimIndex, field);
}}
className="m-3 px-3 py-2 text-md font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
>
Reveal
</button>
) : (
<>: {claim[field]}</>
)}
</AgentRequire>
</div>
))
)}
</div>

}
9 changes: 0 additions & 9 deletions demos/next/src/components/FooterNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,6 @@ import Link from "next/link";
export function FooterNavigation() {
return (
<footer className="fixed bottom-0 left-0 z-20 w-full p-4 bg-white border-t border-gray-200 shadow md:flex md:items-center md:justify-between md:p-6 dark:bg-gray-800 dark:border-gray-600">
<div className="sm:hidden">
<label htmlFor="tabs" className="sr-only">Select your country</label>
<select id="tabs" className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<option>Profile</option>
<option>Canada</option>
<option>France</option>
<option>Germany</option>
</select>
</div>
<ul className="flex justify-evenly items-center mt-3 text-sm font-medium text-center text-gray-500 rounded-lg shadow sm:flex-grow dark:divide-gray-700 dark:text-gray-400">
<li className="w-full">
<Link href="/" className="inline-block w-full p-4 bg-white border-0 border-gray-200 dark:border-gray-700 rounded-tr-lg rounded-br-lg hover:text-gray-700 hover:bg-gray-50 dark:hover:text-white dark:bg-gray-800 dark:hover:bg-gray-700">
Expand Down
Loading

0 comments on commit 1d841f4

Please sign in to comment.