Skip to content

Commit

Permalink
feat: ios app builds
Browse files Browse the repository at this point in the history
  • Loading branch information
vonovak committed Jan 21, 2024
1 parent 38dd012 commit 53b1d7d
Show file tree
Hide file tree
Showing 40 changed files with 5,982 additions and 24,811 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,12 @@ buck-out/
*.jsbundle

# CocoaPods
/ios/Pods/
/ios_old/Pods/

# Expo
.expo/
web-build/

# @end expo-cli

android/app/release/
android_old/app/release/
22 changes: 19 additions & 3 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"displayName": "Learner Credential Wallet",
"expo": {
"name": "Learner Credential Wallet",
"slug": "Learner Credential Wallet",
"slug": "learner-credential-wallet",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./app/assets/icon.png",
Expand All @@ -20,7 +20,13 @@
],
"ios": {
"supportsTablet": true,
"bundleIdentifier": "edu.wallet"
"bundleIdentifier": "edu.mit.eduwallet",
"entitlements": {
"com.apple.security.application-groups": [
"group.edu.mit.eduwallet"
]
},
"associatedDomains": ["applinks:lcw.app/mobile"]
},
"android": {
"adaptiveIcon": {
Expand All @@ -31,6 +37,16 @@
},
"web": {
"favicon": "./app/assets/favicon.png"
}
},
"plugins": [
[
"react-native-vision-camera",
{
"cameraPermissionText": "$(PRODUCT_NAME) needs access to your Camera to scan QR codes.",
"enableMicrophonePermission": false,
"enableCodeScanner": true
}
]
]
}
}
11 changes: 6 additions & 5 deletions app/components/ConfirmModal/ConfirmModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ import React, { useEffect, useState } from 'react';
import { Modal, Text, View, TouchableWithoutFeedback, AccessibilityInfo } from 'react-native';
import { Button } from 'react-native-elements';

import { useAccessibilityFocus, useDynamicStyles } from '../../hooks';
import { useDynamicStyles } from '../../hooks/useDynamicStyles';
import { useAccessibilityFocus } from '../../hooks/useAccessibilityFocus';
import { ConfirmModalProps } from './ConfirmModal.d';

import dynamicStyleSheet from './ConfirmModal.style';

/**
* TODO: Right now the accessibility focus throws errors on Android
/**
* TODO: Right now the accessibility focus throws errors on Android
* when returning from the share UI. Those errors must be resolved before
* this can be enabled. This disable flag is a temporary fix.
*/
*/
const ENABLE_ACCESSIBILITY_FOCUS = false;

export default function ConfirmModal({
Expand Down Expand Up @@ -68,7 +69,7 @@ export default function ConfirmModal({
<View style={styles.modalBackground} />
</TouchableWithoutFeedback>
<View style={styles.modalContainer}>
<Text
<Text
style={styles.modalTitle}
ref={titleRef}
accessibilityRole="header"
Expand Down
5 changes: 3 additions & 2 deletions app/components/CredentialItem/CredentialItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import { MaterialCommunityIcons } from '@expo/vector-icons';

import dynamicStyleSheet from './CredentialItem.styles';
import type { CredentialItemProps } from './CredentialItem.d';
import { CredentialStatusBadges } from '../../components';
import { useDynamicStyles, useVerifyCredential } from '../../hooks';
import { useDynamicStyles } from '../../hooks/useDynamicStyles';
import { useVerifyCredential } from '../../hooks/useVerifyCredential';
import { credentialItemPropsFor } from '../../lib/credentialDisplay';
import { CardImage } from '../../lib/credentialDisplay/shared';
import CredentialStatusBadges from '../CredentialStatusBadges/CredentialStatusBadges';

export default function CredentialItem({
onSelect,
Expand Down
2 changes: 1 addition & 1 deletion app/components/VerificationCard/VerificationCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { View, Text, TouchableOpacity } from 'react-native';
import { MaterialCommunityIcons, MaterialIcons } from '@expo/vector-icons';

import { useDynamicStyles, useVerifyCredential } from '../../hooks';
import { VerifyPayload } from '../../lib/verifiableObject';
import dynamicStyleSheet from './VerificationCard.styles';
import { navigationRef } from '../../navigation';
import { CredentialRecordRaw } from '../../model';
import {VerifyPayload} from '../../lib/verificationResultFor';

type CommonProps = {
isButton?: boolean;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { VerifyPayload } from '../../lib/verifiableObject';
import { Credential } from '../../types/credential';
import type {VerifyPayload} from '../../lib/verificationResultFor';

export type VerificationStatusCardProps = {
credential: Credential;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import dynamicStyleSheet from './VerificationStatusCard.styles';
import { useDynamicStyles } from '../../hooks';
import { BulletList } from '../../components';
import { DidRegistryContext } from '../../init/registries';
import { issuerInRegistries } from '../../lib/verifiableObject';
import { issuerInRegistries } from '../../lib/issuerInRegistries';

const DATE_FORMAT = 'MMM D, YYYY';

Expand Down
6 changes: 3 additions & 3 deletions app/hooks/useAnimation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { useRef } from 'react';
import { Animated, Easing } from 'react-native';

type InterpolateRange = [number, number] | [string, string];
type UseAnimationType = Animated.CompositeAnimation & {
readonly value: Animated.AnimatedInterpolation
type UseAnimationType = Animated.CompositeAnimation & {
readonly value: Animated.AnimatedInterpolation<number | string>
};

export function useAnimation(
Expand All @@ -18,7 +18,7 @@ export function useAnimation(
...config,
})).current;

/* This method replaces the Animated library
/* This method replaces the Animated library
* reset() method that doesn't work.
*/
const reset = () => animationValue.setValue(0);
Expand Down
5 changes: 1 addition & 4 deletions app/hooks/useVerifyCredential.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ import { CredentialError } from '../types/credential';
import { CredentialRecordRaw } from '../model';
import { useFocusEffect } from '@react-navigation/native';
import { DidRegistryContext } from '../init/registries';
import {
VerifyPayload,
verificationResultFor
} from '../lib/verifiableObject';
import {verificationResultFor, VerifyPayload} from '../lib/verificationResultFor';

const DEFAULT_ERROR_MESSAGE = 'An error was encountered while verifying this credential.';

Expand Down
3 changes: 2 additions & 1 deletion app/lib/credentialRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { DidRecordRaw } from '../model';

import { createVerifiablePresentation } from './present';
import { parseResponseBody } from './parseResponse';
import { extractCredentialsFrom, verifyVerifiableObject, VerifiableObject } from './verifiableObject';
import { extractCredentialsFrom, VerifiableObject } from './verifiableObject';
import { RegistryClient } from '@digitalcredentials/issuer-registry-client';
import {verifyVerifiableObject} from './verifyVerifiableObject';

export type CredentialRequestParams = {
auth_type?: string;
Expand Down
4 changes: 2 additions & 2 deletions app/lib/decode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import qs from 'query-string';
import { securityLoader } from '@digitalcredentials/security-document-loader';
import { ChapiCredentialRequest, ChapiCredentialResponse, ChapiDidAuthRequest } from '../types/chapi';
import type { Credential, EducationalOperationalCredential, Subject } from '../types/credential';
import { VerifiablePresentation } from '../types/presentation';
import { CredentialRequestParams, getChapiCredentialRequest, isChapiCredentialRequestParams } from './credentialRequest';
import { isCredentialRequestParams } from './credentialRequest';
import { HumanReadableError } from './error';
import { isChapiCredentialResponse, isChapiDidAuthRequest, isVerifiableCredential, isVerifiablePresentation } from './verifiableObject';
import { CredentialRecordRaw } from '../model';
import { NavigationUtil } from './navigationUtil';
import { DidAuthRequestParams, performDidAuthRequest } from './didAuthRequest';
import {VerifiablePresentation} from '../types/presentation';

const documentLoader = securityLoader({ fetchRemoteContexts: true }).build();
export const regexPattern = {
Expand Down Expand Up @@ -109,7 +109,7 @@ async function credentialsFromJson(text: string): Promise<Credential[]> {

/**
* A method for decoding credentials from a variety text formats.
*
*
* @param text - A string containing a VPQR, URL, or JSON object.
* @returns {Promise<Credential[]>} - An array of credentials.
*/
Expand Down
2 changes: 1 addition & 1 deletion app/lib/dynamicStyles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ type StyleObjectsResolver = (dynamicStyles: DynamicStyles) => Record<string, Sty
export function createDynamicStyleSheet<R extends StyleObjectsResolver, S extends ReturnType<R>>(styleObjectsResolver: R): DynamicStyleSheet<S> {
return (dynamicStyles: DynamicStyles) => {
const styleObjects = styleObjectsResolver(dynamicStyles);
return StyleSheet.create(styleObjects) as S;
return StyleSheet.create(styleObjects as any) as S;
};
}
19 changes: 19 additions & 0 deletions app/lib/issuerInRegistries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type {RegistryClient} from '@digitalcredentials/issuer-registry-client';

/**
* Checks to see if a VC's issuer appears in any of the known DID registries.
*
* @returns A list of names of DID registries the issuer appears in.
*/
export function issuerInRegistries({ issuer, registries }: {
issuer: string | any,
registries: RegistryClient
}): string[] | null {
const issuerDid = typeof issuer === 'string' ? issuer : issuer.id;
const issuerInfo = registries.didEntry(issuerDid);
// See if the issuer DID appears in any of the known registries
// If yes, assemble a list of registries it appears in
return issuerInfo?.inRegistries
? Array.from(issuerInfo.inRegistries).map(r => r.name)
: null;
}
81 changes: 0 additions & 81 deletions app/lib/validate.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,5 @@
import { Ed25519Signature2020 } from '@digitalcredentials/ed25519-signature-2020';
import { purposes } from '@digitalcredentials/jsonld-signatures';
import { checkStatus } from '@digitalcredentials/vc-status-list';
import vc from '@digitalcredentials/vc';

import { VerifiablePresentation, PresentationError } from '../types/presentation';
import { Credential, CredentialError } from '../types/credential';

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

const documentLoader = securityLoader({ fetchRemoteContexts: true }).build();
const suite = new Ed25519Signature2020();
const presentationPurpose = new purposes.AssertionProofPurpose();

export type ResultLog = {
id: string,
valid: boolean
Expand All @@ -31,70 +17,3 @@ export type VerifyResponse = {
results: Result[];
}

export async function verifyPresentation(
presentation: VerifiablePresentation,
unsignedPresentation = true,
): Promise<VerifyResponse> {
try {
const hasRevocation = extractCredentialsFrom(presentation)?.find(
vc => vc.credentialStatus);
const result = await vc.verify({
presentation,
presentationPurpose,
suite,
documentLoader,
unsignedPresentation,
// Only check revocation status if any VC has a 'credentialStatus' property
checkStatus: hasRevocation ? checkStatus : undefined
});

if (!result.verified) {
console.warn('VP not verified:', JSON.stringify(result, null, 2));
}
return result;
} catch (err) {
console.warn(err);

throw new Error(PresentationError.CouldNotBeVerified);
}
}

export async function verifyCredential(credential: Credential, registries: RegistryClient): Promise<VerifyResponse> {
const { issuer } = credential;
const isInRegistry = issuerInRegistries({ issuer, registries });
if (!isInRegistry) {
throw new Error(CredentialError.DidNotInRegistry);
}

try {
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,
});

// This logic catches the case where the verify response does not contain a `log` value
if (result.results?.[0].log === undefined) {
throw result.error || new Error('Verify response does not a `log` value');
}

if (!result.verified) {
console.warn('VC not verified:', JSON.stringify(result, null, 2));
}

return result;
} catch (err) {
console.warn(err);
console.log(JSON.stringify(err, removeStackReplacer, 2));

throw new Error(CredentialError.CouldNotBeVerified);
}
}

function removeStackReplacer(key: string, value: unknown) {
return key === 'stack' ? '...' : value;
}
Loading

0 comments on commit 53b1d7d

Please sign in to comment.