Skip to content

Commit

Permalink
feat: implement revoke and activate vc
Browse files Browse the repository at this point in the history
Signed-off-by: Nam Hoang <[email protected]>
  • Loading branch information
namhoang1604 committed Aug 1, 2023
1 parent 3cb270f commit 015ab9c
Show file tree
Hide file tree
Showing 13 changed files with 545 additions and 103 deletions.
3 changes: 2 additions & 1 deletion packages/core-types/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"ICredentialStatusVerifier": "./src/types/ICredentialStatusVerifier.ts",
"ICredentialStatusManager": "./src/types/ICredentialStatusManager.ts",
"IRenderer": "./src/types/IRender.ts",
"IEncryptedStorage": "./src/types/IEncryptedStorage.ts"
"IEncryptedStorage": "./src/types/IEncryptedStorage.ts",
"IRevocationList2020": "./src/types/IRevocationList2020.ts"
}
},
"dependencies": {
Expand Down
93 changes: 93 additions & 0 deletions packages/core-types/src/plugin.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -6111,5 +6111,98 @@
}
}
}
},
"IRevocationList2020": {
"components": {
"schemas": {
"IHashCredentialArgs": {
"type": "object",
"properties": {
"hash": {
"type": "string"
}
},
"required": [
"hash"
]
},
"CredentialStatus": {
"type": "object",
"properties": {
"revoked": {
"type": "boolean"
}
},
"required": [
"revoked"
],
"description": "Represents the result of a status check.\n\nImplementations MUST populate the `revoked` boolean property, but they can return additional metadata that is method specific."
},
"IRevocationListDataArgs": {
"type": "object",
"properties": {
"revocationListPath": {
"type": "string"
},
"bitStringLength": {
"type": "string"
},
"revocationVCIssuer": {
"type": "string"
}
},
"required": [
"revocationVCIssuer"
]
}
},
"methods": {
"activateCredential": {
"description": "",
"arguments": {
"$ref": "#/components/schemas/IHashCredentialArgs"
},
"returnType": {
"$ref": "#/components/schemas/CredentialStatus"
}
},
"checkStatus": {
"description": "",
"arguments": {
"$ref": "#/components/schemas/IHashCredentialArgs"
},
"returnType": {
"$ref": "#/components/schemas/CredentialStatus"
}
},
"getRevocationData": {
"description": "",
"arguments": {
"$ref": "#/components/schemas/IRevocationListDataArgs"
},
"returnType": {
"type": "object"
}
},
"getRevocationListVC": {
"description": "",
"arguments": {
"type": "string"
},
"returnType": {
"type": "object"
}
},
"revokeCredential": {
"description": "",
"arguments": {
"$ref": "#/components/schemas/IHashCredentialArgs"
},
"returnType": {
"$ref": "#/components/schemas/CredentialStatus"
}
}
}
}
}
}
40 changes: 29 additions & 11 deletions packages/core-types/src/types/IRevocationList2020.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,48 @@
import { IPluginMethodMap, IAgent } from './IAgent';
import { IPluginMethodMap, IAgent } from './IAgent.js';
import { Request } from 'express';
import { CredentialStatus, VerifiableCredential } from './vc-data-model.js';

/**
* @public
*/
export interface RequestWithAgent extends Request {
agent?: IAgent;
}

/**
* @public
*/
export interface IRevocationListDataArgs {
revocationListPath: string;
bitStringLength: string;
req: RequestWithAgent;
revocationListPath?: string;
bitStringLength?: string;
revocationVCIssuer: string;

[x: string]: any;
}

/**
* @public
*/
export interface RequestWithAgent extends Request {
agent?: IAgent;
export interface IHashCredentialArgs {
hash: string;
}

/**
* @public
*/
export interface IRevocationStore extends IPluginMethodMap {
getRevocationData(
args: IRevocationListDataArgs,
req: RequestWithAgent
): Promise<{ revocationListFullUrl: string; indexCounter: number }>;
export interface IRevocationList2020 extends IPluginMethodMap {
getRevocationData(args: IRevocationListDataArgs): Promise<any>;
getRevocationListVC(revocationListFullUrlPath: string): Promise<any>;
checkStatus(
args: IHashCredentialArgs,
context: { agent?: IAgent }
): Promise<CredentialStatus>;
revokeCredential(
args: IHashCredentialArgs,
context: { agent?: IAgent }
): Promise<CredentialStatus>;
activateCredential(
args: IHashCredentialArgs,
context: { agent?: IAgent }
): Promise<CredentialStatus>;
}
4 changes: 2 additions & 2 deletions packages/demo-explorer/src/components/AgentDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const AgentDropdown: React.FC<{ children: React.ReactNode }> = ({
(_agent: any) => _agent.context?.id === defaultAgentId,
)
if (!existingAgent) {
if (schema && agentUrl && apiKey && schemaUrl) {
if (schema && agentUrl && schemaUrl) {
addAgentConfig({
context: { id: defaultAgentId, name: 'Agent', schema: schemaUrl },
remoteAgents: [
Expand All @@ -53,7 +53,7 @@ const AgentDropdown: React.FC<{ children: React.ReactNode }> = ({
},
],
})
setActiveAgentId('agentApi')
setActiveAgentId(defaultAgentId)
}
}
}
Expand Down
172 changes: 130 additions & 42 deletions packages/demo-explorer/src/components/CredentialInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,65 +1,153 @@
import React from 'react'
import { Descriptions } from 'antd'
import React, { useCallback, useEffect, useState } from 'react'
import { Alert, Button, Descriptions, Spin } from 'antd'
import { VerifiableCredential } from '@veramo/core'
import { format } from 'date-fns'
import { useVeramo } from '@veramo-community/veramo-react'

interface CredentialInfoProps {
credential: VerifiableCredential
hash: string
}

interface TableRow {
key: string
value: string
}

const CredentialInfo: React.FC<CredentialInfoProps> = ({ credential }) => {
if (!credential) return null
const CredentialInfo: React.FC<CredentialInfoProps> = ({
credential,
hash,
}) => {
const { agent } = useVeramo()
const [credentialData] = useState<any>(credential)
const [data, setData] = useState<Array<TableRow>>([])
const [loading, setLoading] = useState(false)
const [revoked, setRevoked] = useState(false)
const [errorMessage, setErrorMessage] = useState<null | string>()

const data: Array<TableRow> = []
const fetchVCStatus = useCallback(async () => {
setLoading(true)
await checkVCStatus({ hash })
setLoading(false)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [hash])

for (const key in credential.credentialSubject) {
let value = credential.credentialSubject[key]
value = typeof value === 'string' ? value : JSON.stringify(value)
data.push({ key, value })
useEffect(() => {
setData([])
for (const key in credentialData.credentialSubject) {
let value = credentialData.credentialSubject[key]
value = typeof value === 'string' ? value : JSON.stringify(value)
setData((d) => [...d, { key, value }])
}
fetchVCStatus()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [credentialData])

if (!credential || !hash) return null

const revoke = async () => {
setLoading(true)
try {
const { revoked: vcRevoked } = await agent?.revokeCredential({ hash })
setRevoked(vcRevoked || false)
} catch (e: any) {
console.log(e)
setErrorMessage(e.message + ' Please refresh the page and try again.')
}
setLoading(false)
}

const activate = async () => {
setLoading(true)
try {
const { revoked: vcRevoked } = await agent?.activateCredential({ hash })
setRevoked(vcRevoked || false)
} catch (e: any) {
console.log(e)
setErrorMessage(e.message + ' Please refresh the page and try again.')
}
setLoading(false)
}

const checkVCStatus = async (args: { hash: string }) => {
const { revoked: vcRevoked } = await agent?.checkStatus(args)

setRevoked(vcRevoked || false)
}

return (
<>
<Descriptions
bordered
column={{ xxl: 2, xl: 2, lg: 2, md: 1, sm: 1, xs: 1 }}
>
<Descriptions.Item label="Type">
{(credential.type as string[]).join(',')}
</Descriptions.Item>
<Descriptions.Item label="Context">
{(credential['@context'] as string[]).join(',')}
</Descriptions.Item>
<Descriptions.Item label="Issuer">
{(credential.issuer as { id: string }).id as string}
</Descriptions.Item>
<Descriptions.Item label="Issuance date">
{format(new Date(credential.issuanceDate), 'PPP')}
</Descriptions.Item>
<Descriptions.Item label="Proof type">
{credential.proof.type}
</Descriptions.Item>
<Descriptions.Item label="Id">{credential.id}</Descriptions.Item>
</Descriptions>
<Spin spinning={loading} tip="Loading...">
<>
<Descriptions
bordered
column={{ xxl: 2, xl: 2, lg: 2, md: 1, sm: 1, xs: 1 }}
>
<Descriptions.Item label="Type">
{(credentialData.type as string[]).join(',')}
</Descriptions.Item>
<Descriptions.Item label="Context">
{(credentialData['@context'] as string[]).join(',')}
</Descriptions.Item>
<Descriptions.Item label="Issuer">
{(credentialData.issuer as { id: string }).id as string}
</Descriptions.Item>
<Descriptions.Item label="Issuance date">
{format(new Date(credentialData.issuanceDate), 'PPP')}
</Descriptions.Item>
<Descriptions.Item label="Proof type">
{credentialData.proof.type}
</Descriptions.Item>
<Descriptions.Item label="Id">{credentialData.id}</Descriptions.Item>
</Descriptions>

<br />
<br />

<Descriptions
bordered
column={{ xxl: 1, xl: 1, lg: 1, md: 1, sm: 1, xs: 1 }}
>
{data.map((i) => (
<Descriptions.Item label={i.key} key={i.key}>
{i.value}
<Descriptions
bordered
column={{ xxl: 1, xl: 1, lg: 1, md: 1, sm: 1, xs: 1 }}
>
{data.map((i) => (
<Descriptions.Item label={i.key} key={i.key}>
{i.value}
</Descriptions.Item>
))}
<Descriptions.Item label="Status">
{revoked ? 'Revoked' : 'Active'}
</Descriptions.Item>
))}
</Descriptions>
</>
</Descriptions>
<br />

{revoked ? (
<Button
type="primary"
onClick={() => {
activate()
}}
disabled={loading}
>
Active
</Button>
) : (
<Button
danger
type="primary"
onClick={() => {
revoke()
}}
disabled={loading}
>
Revoke
</Button>
)}
{errorMessage && (
<>
<br />
<br />
<Alert message={errorMessage} type="error" />
</>
)}
</>
</Spin>
)
}

Expand Down
2 changes: 1 addition & 1 deletion packages/demo-explorer/src/components/CredentialTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const CredentialTabs: React.FC<CredentialTabsProps> = ({
{
key: '1',
label: 'Info',
children: <CredentialInfo credential={credential} />,
children: <CredentialInfo credential={credential} hash={hash} />,
},
{
key: '2',
Expand Down
Loading

0 comments on commit 015ab9c

Please sign in to comment.