Skip to content

Commit

Permalink
Merge pull request #374 from Concordium/validate-credential-with-schema
Browse files Browse the repository at this point in the history
Use the schema to validate a credential being added
  • Loading branch information
orhoj authored Sep 4, 2023
2 parents c66d0f4 + c34d402 commit 2048a6d
Show file tree
Hide file tree
Showing 21 changed files with 197 additions and 124 deletions.
2 changes: 1 addition & 1 deletion examples/add-example-Web3Id/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
"start": "live-server ./index.html --mount=/sdk.js:../../node_modules/@concordium/web-sdk/lib/concordium.min.js --mount=/helpers.js:../../packages/browser-wallet-api-helpers/lib/concordiumHelpers.min.js"
},
"dependencies": {
"@concordium/web-sdk": "^6.2.1"
"@concordium/web-sdk": "^6.3.0"
}
}
2 changes: 1 addition & 1 deletion examples/eSealing/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"license": "Apache-2.0",
"dependencies": {
"@concordium/react-components": "^0.3.0",
"@concordium/web-sdk": "^6.2.1",
"@concordium/web-sdk": "^6.3.0",
"@thi.ng/leb128": "^2.1.18",
"@types/sha256": "^0.2.0",
"@walletconnect/types": "^2.1.4",
Expand Down
2 changes: 1 addition & 1 deletion examples/nft-minting/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"packageManager": "[email protected]",
"dependencies": {
"@concordium/browser-wallet-api-helpers": "workspace:^",
"@concordium/web-sdk": "^6.2.1",
"@concordium/web-sdk": "^6.3.0",
"cors": "^2.8.5",
"express": "^4.18.1",
"express-fileupload": "^1.4.0",
Expand Down
2 changes: 1 addition & 1 deletion examples/piggybank/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"packageManager": "[email protected]",
"dependencies": {
"@concordium/browser-wallet-api-helpers": "workspace:^",
"@concordium/web-sdk": "^6.2.1",
"@concordium/web-sdk": "^6.3.0",
"react": "^18.1.0",
"react-dom": "^18.1.0"
},
Expand Down
2 changes: 1 addition & 1 deletion examples/two-step-transfer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
"start": "live-server ../two-step-transfer/index.html --mount=/sdk.js:../../node_modules/@concordium/web-sdk/lib/concordium.min.js --mount=/helpers.js:../../packages/browser-wallet-api-helpers/lib/concordiumHelpers.min.js"
},
"dependencies": {
"@concordium/web-sdk": "^6.2.1"
"@concordium/web-sdk": "^6.3.0"
}
}
2 changes: 1 addition & 1 deletion examples/voting/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"packageManager": "[email protected]",
"dependencies": {
"@concordium/browser-wallet-api-helpers": "^2.0.0",
"@concordium/web-sdk": "^6.2.1",
"@concordium/web-sdk": "^6.3.0",
"bootstrap": "^5.2.1",
"cross-env": "^7.0.3",
"moment": "^2.29.4",
Expand Down
2 changes: 1 addition & 1 deletion examples/wCCD/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"license": "Apache-2.0",
"dependencies": {
"@concordium/react-components": "^0.2.0",
"@concordium/web-sdk": "^6.2.1",
"@concordium/web-sdk": "^6.3.0",
"@thi.ng/leb128": "^2.1.18",
"@walletconnect/types": "^2.1.4",
"mathjs": "^11.4.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/browser-wallet-api-helpers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"url": "https://concordium.com"
},
"dependencies": {
"@concordium/web-sdk": "^6.2.1"
"@concordium/web-sdk": "^6.3.0"
},
"devDependencies": {
"@babel/core": "^7.17.10",
Expand Down
2 changes: 1 addition & 1 deletion packages/browser-wallet-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"license": "Apache-2.0",
"dependencies": {
"@concordium/browser-wallet-api-helpers": "workspace:^",
"@concordium/common-sdk": "^9.2.1",
"@concordium/common-sdk": "^9.3.0",
"@protobuf-ts/grpcweb-transport": "^2.8.2",
"@protobuf-ts/runtime-rpc": "^2.8.2",
"buffer": "^6.0.3",
Expand Down
11 changes: 11 additions & 0 deletions packages/browser-wallet/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Changelog

## Unreleased

### Changed

- Update according to the change to AttributeType from the SDK. In particular the timestamp type is now explicit, and therefore we have removed hhe special serialization/parsing of Dates when exporting/import verifiable credentials.

### Fixed

- Verifiable credentials are now validated according to the schema when being added. This will e.g. block setting an attribute as an integer if the schema defines it as a string.
- Refreshed the schema for credential schemas so that attribute types are now restricted as expected (`string`, `integer` and the special types are allowed).

## 1.1.5

### Added
Expand Down
2 changes: 1 addition & 1 deletion packages/browser-wallet/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"dependencies": {
"@concordium/browser-wallet-api": "workspace:^",
"@concordium/browser-wallet-api-helpers": "workspace:^",
"@concordium/web-sdk": "^6.2.1",
"@concordium/web-sdk": "^6.3.0",
"@noble/ed25519": "^1.7.0",
"@protobuf-ts/runtime-rpc": "^2.8.2",
"@scure/bip39": "^1.1.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ import {
fetchLocalization,
findNextUnusedVerifiableCredentialIndex,
getCredentialRegistryContractAddress,
withIdRemovedFromSchema,
} from '@shared/utils/verifiable-credential-helpers';
import { APIVerifiableCredential } from '@concordium/browser-wallet-api-helpers';
import { grpcClientAtom, networkConfigurationAtom } from '@popup/store/settings';
import { MetadataUrl } from '@concordium/browser-wallet-api-helpers/lib/wallet-api-types';
import { parse } from '@shared/utils/payload-helpers';
import { logError } from '@shared/utils/log-helpers';
import { addToastAtom } from '@popup/state';
import { Schema, Validator } from 'jsonschema';
import { VerifiableCredentialCard } from '../VerifiableCredential/VerifiableCredentialCard';

type Props = {
Expand Down Expand Up @@ -115,23 +117,32 @@ export default function AddWeb3IdCredential({ onAllow, onReject }: Props) {

useEffect(() => {
if (schema) {
// Ensure that all attributes required by the schema are in the attributes. If not, then
// the credential should not be allowed to be added.
const missingRequiredAttributeKeys = [];
const requiredAttributes = schema.properties.credentialSubject.properties.attributes.required;
for (const requiredAttribute of requiredAttributes) {
if (!Object.keys(credential.credentialSubject.attributes).includes(requiredAttribute)) {
missingRequiredAttributeKeys.push(requiredAttribute);
// Use the schema to validate the credential.
const validator = new Validator();
try {
const schemaWithNoId = withIdRemovedFromSchema(schema);
const validationResult = validator.validate(
{ credentialSubject: credential.credentialSubject },
schemaWithNoId as unknown as Schema
);
if (!validationResult.valid) {
setError(t('error.schemaValidation', { errors: validationResult.errors.toString() }));
setValidationComplete(true);
return;
}
}

if (missingRequiredAttributeKeys.length > 0) {
setError(t('error.attribute.required', { attributeKeys: missingRequiredAttributeKeys }));
} catch (e) {
logError(e);
setError(
t('error.schemaValidation', {
errors: 'An error occurred while attempting to validate credential.',
})
);
setValidationComplete(true);
return;
}

// Ensure that a credential with more attributes than listed by the schema cannot be added.
// The schema might not check this in the current iteration of schemas (additionalProperties: false).
const schemaAttributes = Object.keys(schema.properties.credentialSubject.properties.attributes.properties);
for (const credentialAttribute of Object.keys(credential.credentialSubject.attributes)) {
if (!schemaAttributes.includes(credentialAttribute)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const t: typeof en = {
attribute: en.error.attribute,
localization: en.error.localization,
findingNextIndex: en.error.findingNextIndex,
schemaValidation: en.error.schemaValidation,
},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const t = {
},
localization: 'Failed to get localization',
findingNextIndex: 'An error ocurred while attempting to add the credential. Please try again.',
schemaValidation: 'The credential did not validate according to the schema: [{{ errors }}]',
},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import { decrypt } from '@shared/utils/crypto';
import { useHdWallet } from '@popup/shared/utils/account-helpers';
import JSONBigInt from 'json-bigint';
import { FileInput } from '@popup/shared/Form/FileInput';
import { reviveDateFromTimeStampAttribute } from '@concordium/web-sdk';
import { VerifiableCredentialCardWithStatusFromChain } from '../VerifiableCredential/VerifiableCredentialList';
import { ExportFormat, VerifiableCredentialExport } from './utils';

Expand Down Expand Up @@ -47,7 +46,7 @@ async function parseExport(data: EncryptedData, encryptionKey: string): Promise<
const backup: ExportFormat = JSONBigInt({
alwaysParseAsBig: true,
useNativeBigInt: true,
}).parse(decrypted, reviveDateFromTimeStampAttribute);
}).parse(decrypted);
// Change index to number, due to parse changing all numbers to bigints.
backup.value.verifiableCredentials = backup.value.verifiableCredentials.map((v) => ({
...v,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
import { networkConfigurationAtom } from '@popup/store/settings';
import { saveData } from '@popup/shared/utils/file-helpers';
import { stringify } from 'json-bigint';
import { replaceDateWithTimeStampAttribute } from '@concordium/web-sdk';

export type VerifiableCredentialExport = {
verifiableCredentials: VerifiableCredential[];
Expand Down Expand Up @@ -47,7 +46,7 @@ function createExport(

// Use json-bigint to serialize bigints as json numbers.
// Ensure that Dates are stored as timestamps to not lose typing (otherwise they are serialized as strings).
return encrypt(stringify(exportContent, replaceDateWithTimeStampAttribute), encryptionKey);
return encrypt(stringify(exportContent), encryptionKey);
}

export function useVerifiableCredentialExport() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ export function DisplayStatementLine({ attribute, value, isRequirementMet, class
<div className="display-statement__line-value bodyM">
{value}
{isRequirementMet ? (
<CheckmarkIcon className="display-statement__line-check display-statement-checkmark " />
<CheckmarkIcon className="display-statement__line-check display-statement-checkmark" />
) : (
<CrossIcon className="display-statement__line-cross" />
<CrossIcon className="display-statement__line-cross display-statement-cross" />
)}
</div>
</li>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AttributeType, CredentialSchemaSubject } from '@concordium/web-sdk';
import { AttributeType, CredentialSchemaSubject, isTimestampAttribute } from '@concordium/web-sdk';
import { withDateAndTime } from '@shared/utils/time-helpers';
import { ClassName } from 'wallet-common-helpers';
import { SecretStatementV2 } from '../utils';
Expand All @@ -25,5 +25,7 @@ export type DisplayProps<StatementType, Attribute> = ClassName & {
};

export function defaultFormatAttribute<Attribute extends AttributeType>(_: string, value: Attribute) {
return value instanceof Date ? withDateAndTime(value) : value?.toString();
return value !== undefined && isTimestampAttribute(value)
? withDateAndTime(Date.parse(value.timestamp))
: value?.toString();
}
Original file line number Diff line number Diff line change
Expand Up @@ -267,3 +267,11 @@ $standard-box-shadow: 0 0 15px 0 rgb(0 0 0 / $color-shadow-alpha);
fill: $color-feedback-positive-base;
}
}

.display-statement-cross {
flex-shrink: 0;

path {
fill: $color-feedback-negative-base;
}
}
Loading

0 comments on commit 2048a6d

Please sign in to comment.