Skip to content

Commit

Permalink
Refactor registry client, remove issuerAuth.
Browse files Browse the repository at this point in the history
  • Loading branch information
dmitrizagidulin committed Jan 4, 2024
1 parent ca73636 commit 6ec33fd
Show file tree
Hide file tree
Showing 14 changed files with 15,543 additions and 478 deletions.
12 changes: 7 additions & 5 deletions app/components/VerificationStatusCard/VerificationStatusCard.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import React, { useMemo } from 'react';
import React, { useContext } from 'react';
import { View, Text } from 'react-native';
import { MaterialIcons } from '@expo/vector-icons';
import moment from 'moment';
import { registryCollections } from '@digitalcredentials/issuer-registry-client';

import { VerificationStatusCardProps, StatusItemProps } from './VerificationStatusCard.d';
import dynamicStyleSheet from './VerificationStatusCard.styles';
import { useDynamicStyles } from '../../hooks';
import { BulletList } from '../../components';
import { DidRegistryContext } from '../../init/registries';
import { issuerInRegistries } from '../../lib/verifiableObject';

const DATE_FORMAT = 'MMM D, YYYY';

Expand All @@ -20,10 +21,11 @@ enum LogId {

export default function VerificationStatusCard({ credential, verifyPayload }: VerificationStatusCardProps): JSX.Element {
const { styles } = useDynamicStyles(dynamicStyleSheet);
const registries = useContext(DidRegistryContext);

const { expirationDate, issuer } = credential;

const issuerId = typeof issuer === 'string' ? null : issuer?.id;
const registryList = useMemo(() => issuerId ? registryCollections.issuerDid.registriesFor(issuerId).map(({ name }) => name) : null, [issuerId]);
const registryNames = issuerInRegistries({ issuer, registries });

const details = verifyPayload.result.log?.reduce<Record<string, boolean>>((acc, log) => {
acc[log.id] = log.valid;
Expand Down Expand Up @@ -60,7 +62,7 @@ export default function VerificationStatusCard({ credential, verifyPayload }: Ve
negativeText="Is not verified in a registered institution"
verified={details[LogId.IssuerDIDResolves]}
>
{registryList && <BulletList items={registryList} />}
{registryNames && <BulletList items={registryNames} />}
</StatusItem>
</View>
<View style={styles.container}>
Expand Down
18 changes: 18 additions & 0 deletions app/config/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export const KnownDidRegistries = [
{
'name': 'DCC Pilot Registry',
'url': 'https://digitalcredentials.github.io/issuer-registry/registry.json'
},
{
'name': 'DCC Sandbox Registry',
'url': 'https://digitalcredentials.github.io/sandbox-registry/registry.json'
},
{
'name': 'DCC Community Registry',
'url': 'https://digitalcredentials.github.io/community-registry/registry.json'
},
{
'name': 'DCC Registry',
'url': 'https://digitalcredentials.github.io/dcc-registry/registry.json'
}
];
10 changes: 5 additions & 5 deletions app/hooks/useAppLoading.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useMemo, useState } from 'react';
import { useEffect, useMemo, useState, useContext } from 'react';
import { useSelector } from 'react-redux';

Check failure on line 2 in app/hooks/useAppLoading.ts

View workflow job for this annotation

GitHub Actions / lint (18.x)

Expected indentation of 0 spaces but found 1
import {
Rubik_400Regular,
Expand All @@ -7,10 +7,8 @@ import {
useFonts
} from '@expo-google-fonts/rubik';
import { RobotoMono_400Regular } from '@expo-google-fonts/roboto-mono';
import {
loadRegistryCollections
} from '@digitalcredentials/issuer-registry-client';

import { DidRegistryContext, loadKnownDidRegistries } from '../init/registries';
import {
lock,
pollWalletState,
Expand All @@ -23,6 +21,8 @@ import { initializeLogger } from '../init/logger';
export function useAppLoading(): boolean {
const [loading, setLoading] = useState(true);

const didRegistries = useContext(DidRegistryContext);

const primaryTasks = [
useFontsLoaded(),
useWalletStateInitialized(),
Expand All @@ -37,7 +37,7 @@ export function useAppLoading(): boolean {
async function runSecondaryTasks() {
await Promise.all([
initializeLogger(),
loadRegistryCollections()
loadKnownDidRegistries({ client: didRegistries })
]);

setLoading(false);
Expand Down
60 changes: 14 additions & 46 deletions app/hooks/useVerifyCredential.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
import { useState, useCallback } from 'react';
import { ResultLog, verifyCredential } from '../lib/validate';
import { CredentialError } from '../types/credential';
import { CredentialRecordRaw } from '../model';
import { useFocusEffect } from '@react-navigation/native';
import { LruCache } from '@digitalcredentials/lru-memoize';
import {useCallback, useContext, useState} from 'react';
import {CredentialError} from '../types/credential';
import {CredentialRecordRaw} from '../model';
import {useFocusEffect} from '@react-navigation/native';
import {DidRegistryContext} from '../init/registries';
import {
VerificationResult,
verificationResultFor
} from '../lib/verifiableObject';

/* Verification expiration = 30 days */
const VERIFICATION_EXPIRATION = 1000 * 30;
const DEFAULT_ERROR_MESSAGE = 'An error was encountered while verifying this credential.';
const lruCache = new LruCache({ maxAge: VERIFICATION_EXPIRATION });

export type VerificationResult = {
timestamp: number | null;
log: ResultLog[];
verified: boolean | null;
error?: Error;
}

export type VerifyPayload = {
loading: boolean;
Expand All @@ -31,12 +24,15 @@ export function useVerifyCredential(rawCredentialRecord?: CredentialRecordRaw, f
const [result, setResult] = useState<VerifyPayload['result']>(initialResult);
const [error, setError] = useState<VerifyPayload['error']>(null);

const registries = useContext(DidRegistryContext);

if (rawCredentialRecord === undefined) {
return null;
}

const verify = useCallback(async () => {
const verificationResult = await verificationResultFor(rawCredentialRecord, forceFresh);
const verificationResult = await verificationResultFor({
rawCredentialRecord, forceFresh, registries});
setResult(verificationResult);

if (verificationResult.error) {
Expand All @@ -46,7 +42,7 @@ export function useVerifyCredential(rawCredentialRecord?: CredentialRecordRaw, f

setError(errorMessage);
}

setLoading(false);
}, []);

Expand All @@ -56,31 +52,3 @@ export function useVerifyCredential(rawCredentialRecord?: CredentialRecordRaw, f

return { loading, error, result };
}

export async function verificationResultFor(rawCredentialRecord: CredentialRecordRaw, forceFresh = false): Promise<VerificationResult> {
const cachedRecordId = String(rawCredentialRecord._id);

if (!forceFresh) {
const cachedResult = await lruCache.memoize({
key: cachedRecordId,
fn: () => { return verifyCredential(rawCredentialRecord.credential); }
}) as VerificationResult;
return cachedResult;
}

let response, error;
try {
response = await verifyCredential(rawCredentialRecord.credential);
} catch (err) {
error = err as Error;
}

const result: VerificationResult = {
verified: response?.verified ?? false,
log: response?.results ? response.results[0].log : [],
timestamp: Date.now(),
error,
};

return result;
}
15 changes: 15 additions & 0 deletions app/init/registries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { createContext } from 'react';
import { RegistryClient } from '@digitalcredentials/issuer-registry-client';
import { KnownDidRegistries } from '../config';

const registries = new RegistryClient();

export const DidRegistryContext = createContext(registries);

/**
* Loads remote Known Issuer / Known Verifier DID registries from config.
*/
export async function loadKnownDidRegistries ({ client }: { client: RegistryClient }) {
await client.load({ config: KnownDidRegistries });
// now available for usage through useContext(DidRegistryContext)
}
24 changes: 0 additions & 24 deletions app/lib/credentialRequest.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import { authorize } from 'react-native-app-auth';

import { ChapiCredentialRequest, ChapiCredentialRequestParams } from '../types/chapi';
import { Credential } from '../types/credential';
import { DidRecordRaw } from '../model';

import { createVerifiablePresentation } from './present';
import { issuerAuthRegistry } from './issuerAuth';
import { parseResponseBody } from './parseResponse';
import { extractCredentialsFrom, verifyVerifiableObject, VerifiableObject } from './verifiableObject';

Expand Down Expand Up @@ -56,7 +53,6 @@ export function isChapiCredentialRequest(request: any): request is ChapiCredenti
export async function requestCredential(credentialRequestParams: CredentialRequestParams, didRecord: DidRecordRaw): Promise<Credential[]> {
const {
auth_type = 'code',
issuer,
vc_request_url,
challenge,
} = credentialRequestParams;
Expand All @@ -66,26 +62,6 @@ export async function requestCredential(credentialRequestParams: CredentialReque
let accessToken;

switch (auth_type) {
case 'code': {
if (!issuerAuthRegistry.isInRegistry(issuer)) {
throw new Error(`Issuer "${issuer}" not found in registry.`);
}
const oidcConfig = issuerAuthRegistry.entryFor(issuer);
// There needs to be a delay before authenticating or the app errors out.
await new Promise((res) => setTimeout(res, 1000));
console.log('Launching OIDC auth:', oidcConfig);

try {
console.log('authorize() called with:', oidcConfig);
({accessToken} = await authorize(oidcConfig));
console.log('Received access token, requesting credential.');
} catch (err) {
console.error(err);
throw new Error(
'Unable to receive credential: Authorization with the issuer failed');
}
break;
}
case 'bearer':
// Bearer token - do nothing. The 'challenge' param will be passed in the VP
break;
Expand Down
82 changes: 0 additions & 82 deletions app/lib/issuerAuth.ts

This file was deleted.

18 changes: 9 additions & 9 deletions app/lib/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { VerifiablePresentation, PresentationError } from '../types/presentation
import { Credential, CredentialError } from '../types/credential';

import { securityLoader } from '@digitalcredentials/security-document-loader';
import { registryCollections } from '@digitalcredentials/issuer-registry-client';
import { extractCredentialsFrom } from './verifiableObject';
import { RegistryClient } from '@digitalcredentials/issuer-registry-client';
import {extractCredentialsFrom, issuerInRegistries} from './verifiableObject';

const documentLoader = securityLoader({ fetchRemoteContexts: true }).build();
const suite = new Ed25519Signature2020();
Expand Down Expand Up @@ -36,7 +36,8 @@ export async function verifyPresentation(
unsignedPresentation = true,
): Promise<VerifyResponse> {
try {
const hasRevocation = extractCredentialsFrom(presentation)?.find(vc => vc.credentialStatus);
const hasRevocation = extractCredentialsFrom(presentation)?.find(
vc => vc.credentialStatus);
const result = await vc.verify({
presentation,
presentationPurpose,
Expand All @@ -58,23 +59,22 @@ export async function verifyPresentation(
}
}

export async function verifyCredential(credential: Credential): Promise<VerifyResponse> {
export async function verifyCredential(credential: Credential, registries: RegistryClient): Promise<VerifyResponse> {
const { issuer } = credential;
const issuerDid = typeof issuer === 'string' ? issuer : issuer.id;

const isInRegistry = await registryCollections.issuerDid.isInRegistryCollection(issuerDid);
const isInRegistry = issuerInRegistries({ issuer, registries });
if (!isInRegistry) {
throw new Error(CredentialError.DidNotInRegistry);
}

try {
const hasStatusProperty = extractCredentialsFrom(credential)?.find(vc => vc.credentialStatus);
const hasStatusProperty = extractCredentialsFrom(credential)?.find(
vc => vc.credentialStatus);
const result = await vc.verifyCredential({
credential,
suite,
documentLoader,
// Only check revocation status if VC has a 'credentialStatus' property
checkStatus: hasStatusProperty ? checkStatus : undefined
checkStatus: hasStatusProperty ? checkStatus : undefined,
});

// This logic catches the case where the verify response does not contain a `log` value
Expand Down
Loading

0 comments on commit 6ec33fd

Please sign in to comment.