Skip to content

Commit

Permalink
Merge branch 'feature/web3-id' of github.com:Concordium/concordium-br…
Browse files Browse the repository at this point in the history
…owser-wallet into find-next-index-for-credential
  • Loading branch information
orhoj committed Aug 31, 2023
2 parents f794a5c + fa590bc commit a22a4be
Show file tree
Hide file tree
Showing 12 changed files with 209 additions and 7 deletions.
3 changes: 3 additions & 0 deletions packages/browser-wallet-api/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ function replacer(this: any, k: string, value: any) {
}
const rawValue = this[k];
if (rawValue instanceof Date) {
if (Number.isNaN(rawValue.getTime())) {
throw new Error(`Received a Date instance that was an invalid Date. Raw value was: [${rawValue}]`);
}
return { '@type': serializationTypes.Date, value };
}
if (Buffer.isBuffer(rawValue)) {
Expand Down
6 changes: 6 additions & 0 deletions packages/browser-wallet/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@

## Unreleased

### Changed

- Adjusted the schema validation for credential schemas to no longer require title and description. The type is now required to be 'object'.

### Fixed

- An issue where changing the credential metadata URL to an invalid URL, or a URL that does not contain a credential metadata file, would result in an empty screen.
- The wallet now ensures that the verifiable credential index used when adding a credential has not already been used in the contract.
- An issue where an invalid Date would result in the epoch timestamp instead of returning an error.
- Enabled ID statement checks for Web3 ID proof requests containing account credential statements.

## 1.1.3

Expand Down
5 changes: 4 additions & 1 deletion packages/browser-wallet/src/background/web3Id.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
verifyAtomicStatements,
isAccountCredentialStatement,
IDENTITY_SUBJECT_SCHEMA,
verifyIdstatement,
IdStatement,
} from '@concordium/web-sdk';
import {
sessionVerifiableCredentials,
Expand Down Expand Up @@ -178,7 +180,8 @@ export const runIfValidWeb3IdProof: RunCondition<MessageStatusWrapper<undefined>
// If a statement does not verify, an error is thrown.
statements.every((credStatement) =>
isAccountCredentialStatement(credStatement)
? verifyAtomicStatements(credStatement.statement, IDENTITY_SUBJECT_SCHEMA)
? verifyAtomicStatements(credStatement.statement, IDENTITY_SUBJECT_SCHEMA) &&
verifyIdstatement(credStatement.statement as IdStatement)
: verifyAtomicStatements(credStatement.statement)
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,9 @@ $header-height: rem(56px);
&__button {
margin: auto auto 20px;
}

&__import {
height: calc(100% - 32px);
margin: 20px;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { noOp } from 'wallet-common-helpers';
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 { VerifiableCredentialCardWithStatusFromChain } from '../VerifiableCredential/VerifiableCredentialList';
import { ExportFormat, VerifiableCredentialExport } from './utils';

Expand Down Expand Up @@ -126,7 +127,12 @@ export default function VerifiableCredentialImport() {
{imported && <DisplayResult imported={imported} />}
{!imported && (
<>
<input type="file" onChange={handleImport} />
<FileInput
className="verifiable-credential-import__import"
onChange={handleImport}
buttonTitle={t('importButton')}
value={null}
/>
{error && <p className="m-h-10 form-error-message">{error}</p>}
</>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const t: typeof en = {
title: 'Importer Web3 ID Credentials',
noImported: 'Ingen Web3 ID Credentials blev importeret',
error: 'Det var ikke muligt at importere den valgte fil. Filen skal være en backup lavet med en samme seed phrase.',
importButton: 'Vælg fil at importere',
close: 'Luk',
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ export default {
title: 'Import Web3 ID Credentials',
noImported: 'No Web3 ID Credentials were imported',
error: 'Unable to import the chosen file. The file must be a backup created with the same seed phrase.',
importButton: 'Select file to import',
close: 'Close',
};
103 changes: 103 additions & 0 deletions packages/browser-wallet/src/popup/shared/Form/FileInput/FileInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import clsx from 'clsx';
import React, { forwardRef, InputHTMLAttributes, useImperativeHandle, useMemo, useRef, useState } from 'react';
import Button from '../../Button';
import { CommonFieldProps, RequiredUncontrolledFieldProps } from '../common/types';
import ErrorMessage from '../ErrorMessage';
import { makeUncontrolled } from '../common/utils';

export interface FileInputRef {
reset(): void;
}

export type FileInputValue = FileList | null;

export type FileInputProps = Pick<
InputHTMLAttributes<HTMLInputElement>,
'accept' | 'multiple' | 'placeholder' | 'disabled' | 'className'
> &
RequiredUncontrolledFieldProps<HTMLInputElement> &
CommonFieldProps & {
buttonTitle: string;
value: FileInputValue;
disableFileNames?: boolean;
};

/**
* @description
* Component for handling file input. Parsing of file should be done externally. Supports drag and drop + click to browse.
*
* @example
* <FileInput value={files} onChange={setFiles} />
*/
export const FileInput = forwardRef<FileInputRef, FileInputProps>(
(
{
value,
onChange,
label,
valid,
error,
placeholder,
className,
buttonTitle,
disableFileNames = false,
...inputProps
},
ref
): JSX.Element => {
const inputRef = useRef<HTMLInputElement>(null);
const [dragOver, setDragOver] = useState<boolean>(false);
const files = useMemo(() => new Array(value?.length ?? 0).fill(0).map((_, i) => value?.item(i)), [value]);

const { disabled } = inputProps;

useImperativeHandle(ref, () => ({
reset: () => {
if (inputRef.current) {
inputRef.current.value = '';
}
},
}));

return (
<label
className={clsx(
'form-file-input__root',
!valid && 'form-file-input__invalid',
disabled && 'form-file-input__disabled',
dragOver && 'form-file-input__hovering',
className
)}
onDragOver={() => setDragOver(true)}
onDragLeave={() => setDragOver(false)}
>
{label && <label className="form-file-input__label">{label}</label>}
<div className="form-file-input__wrapper">
{files.length === 0 || disableFileNames
? placeholder && <div className="form-file-input__empty">{placeholder}</div>
: files.map((f, i) => (
// eslint-disable-next-line react/no-array-index-key
<div key={i} className="form-file-input__fileName">
{f?.name}
</div>
))}
<Button className="form-file-input__button" disabled={disabled}>
{buttonTitle}
</Button>
<input
className="form-file-input__input"
type="file"
onChange={onChange}
ref={inputRef}
{...inputProps}
/>
</div>
<ErrorMessage>{error}</ErrorMessage>
</label>
);
}
);

const FormFileInput = makeUncontrolled<HTMLInputElement, FileInputProps>(FileInput);

export default FormFileInput;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default, FileInput } from './FileInput';
72 changes: 72 additions & 0 deletions packages/browser-wallet/src/popup/shared/Form/Form.scss
Original file line number Diff line number Diff line change
Expand Up @@ -329,3 +329,75 @@ $handle-scale: scale(1.02);
}
}
}

.form-file-input {
&__root,
&__wrapper {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border-radius: 10px;
}

&__label {
margin-bottom: 5px;
}

&__root {
font-size: rem(12px);
}

&__disabled {
cursor: default;
}

&__wrapper {
flex: 1;
height: 100%;
width: 100%;
padding: 20px 30px;
position: relative;
border: 2px dashed $color-grey;

&__hovering & {
border-color: $color-cta;
}

&__invalid & {
border-color: $color-error;
}
}

&__fileName {
word-break: break-all;
text-align: center;
}

&__empty {
color: $color-grey;
}

&__input {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
opacity: 0;
width: 100%;
cursor: pointer;

&__disabled & {
cursor: default;
}
}

&__button {
pointer-events: none;

* + & {
margin-top: 30px;
}
}
}
5 changes: 4 additions & 1 deletion packages/browser-wallet/src/shared/storage/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,9 +319,12 @@ export type TimestampProperty = {
};

type CredentialSchemaAttributes = {
title?: string;
description?: string;
type: 'object';
properties: Record<string, CredentialSchemaProperty | TimestampProperty>;
required: string[];
} & CredentialSchemaProperty;
};

interface CredentialSchemaSubject {
type: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -409,9 +409,6 @@ const verifiableCredentialSchemaSchema = {
description: {
type: 'string',
},
format: {
type: 'string',
},
properties: {
additionalProperties: {
anyOf: [
Expand Down Expand Up @@ -451,10 +448,11 @@ const verifiableCredentialSchemaSchema = {
type: 'string',
},
type: {
const: 'object',
type: 'string',
},
},
required: ['description', 'properties', 'required', 'title', 'type'],
required: ['type', 'properties', 'required'],
type: 'object',
},
id: {
Expand Down

0 comments on commit a22a4be

Please sign in to comment.