Skip to content

Commit 5e73d51

Browse files
Feat/evault provisioning via phone (#188)
* feat: eid wallet basic ui for verification * chore: evault provisioning * feat: working wallet with provisioning * feat: restrict people on dupes * πŸ“ Add docstrings to `feat/evault-provisioning-via-phone` (#189) Docstrings generation was requested by @coodos. * #188 (comment) The following files were modified: * `infrastructure/eid-wallet/src/lib/utils/capitalize.ts` * `infrastructure/evault-provisioner/src/utils/hmac.ts` Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
1 parent 98b7ba3 commit 5e73d51

File tree

37 files changed

+1786
-440
lines changed

37 files changed

+1786
-440
lines changed

β€Žinfrastructure/eid-wallet/package.jsonβ€Ž

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,16 @@
2727
"@tauri-apps/plugin-biometric": "^2.2.0",
2828
"@tauri-apps/plugin-opener": "^2",
2929
"@tauri-apps/plugin-store": "^2.2.0",
30+
"@veriff/incontext-sdk": "^2.4.0",
31+
"@veriff/js-sdk": "^1.5.1",
32+
"axios": "^1.6.7",
3033
"clsx": "^2.1.1",
34+
"dotenv": "^16.5.0",
3135
"flag-icons": "^7.3.2",
32-
"tailwind-merge": "^3.0.2"
36+
"import": "^0.0.6",
37+
"svelte-loading-spinners": "^0.3.6",
38+
"tailwind-merge": "^3.0.2",
39+
"uuid": "^11.1.0"
3340
},
3441
"devDependencies": {
3542
"@biomejs/biome": "^1.9.4",

β€Žinfrastructure/eid-wallet/src-tauri/gen/apple/eid-wallet.xcodeproj/project.pbxprojβ€Ž

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@
377377
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
378378
CODE_SIGN_ENTITLEMENTS = "eid-wallet_iOS/eid-wallet_iOS.entitlements";
379379
CODE_SIGN_IDENTITY = "iPhone Developer";
380-
DEVELOPMENT_TEAM = 7F2T2WK6DR;
380+
DEVELOPMENT_TEAM = 3FS4B734X5;
381381
ENABLE_BITCODE = NO;
382382
"EXCLUDED_ARCHS[sdk=iphoneos*]" = "arm64-sim x86_64";
383383
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -430,7 +430,7 @@
430430
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
431431
CODE_SIGN_ENTITLEMENTS = "eid-wallet_iOS/eid-wallet_iOS.entitlements";
432432
CODE_SIGN_IDENTITY = "iPhone Developer";
433-
DEVELOPMENT_TEAM = 7F2T2WK6DR;
433+
DEVELOPMENT_TEAM = 3FS4B734X5;
434434
ENABLE_BITCODE = NO;
435435
"EXCLUDED_ARCHS[sdk=iphoneos*]" = "arm64-sim x86_64";
436436
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
Lines changed: 109 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,103 +1,145 @@
11
<script lang="ts">
2-
import * as Button from "$lib/ui/Button";
3-
import { cn } from "$lib/utils";
4-
import {
5-
CheckmarkBadge02Icon,
6-
Upload03Icon,
7-
ViewIcon,
8-
} from "@hugeicons/core-free-icons";
9-
import { HugeiconsIcon } from "@hugeicons/svelte";
10-
import type { HTMLAttributes } from "svelte/elements";
2+
import * as Button from "$lib/ui/Button";
3+
import { cn } from "$lib/utils";
4+
import {
5+
CheckmarkBadge02Icon,
6+
Upload03Icon,
7+
ViewIcon,
8+
} from "@hugeicons/core-free-icons";
9+
import { HugeiconsIcon } from "@hugeicons/svelte";
10+
import type { HTMLAttributes } from "svelte/elements";
1111
12-
interface userData {
13-
[fieldName: string]: string;
14-
}
15-
interface IIdentityCard extends HTMLAttributes<HTMLElement> {
16-
variant?: "eName" | "ePassport" | "eVault";
17-
userId?: string;
18-
viewBtn?: () => void;
19-
shareBtn?: () => void;
20-
userData?: userData;
21-
totalStorage?: number;
22-
usedStorage?: number;
23-
}
12+
interface userData {
13+
[fieldName: string]: string;
14+
}
15+
interface IIdentityCard extends HTMLAttributes<HTMLElement> {
16+
variant?: "eName" | "ePassport" | "eVault";
17+
userId?: string;
18+
viewBtn?: () => void;
19+
shareBtn?: () => void;
20+
userData?: userData;
21+
totalStorage?: number;
22+
usedStorage?: number;
23+
}
2424
25-
const {
26-
variant = "eName",
27-
userId,
28-
viewBtn,
29-
shareBtn,
30-
userData,
31-
totalStorage = 0,
32-
usedStorage = 0,
33-
...restProps
34-
}: IIdentityCard = $props();
35-
const state = $state({
36-
progressWidth: "0%",
37-
});
25+
const {
26+
variant = "eName",
27+
userId,
28+
viewBtn,
29+
shareBtn,
30+
userData,
31+
totalStorage = 0,
32+
usedStorage = 0,
33+
...restProps
34+
}: IIdentityCard = $props();
35+
const state = $state({
36+
progressWidth: "0%",
37+
});
3838
39-
$effect(() => {
40-
state.progressWidth =
41-
usedStorage > 0 ? `${(usedStorage / totalStorage) * 100}%` : "0%";
42-
});
39+
$effect(() => {
40+
state.progressWidth =
41+
usedStorage > 0 ? `${(usedStorage / totalStorage) * 100}%` : "0%";
42+
});
4343
44-
const baseClasses = `relative ${variant === "eName" ? "bg-black-900" : variant === "ePassport" ? "bg-primary" : "bg-gray"} rounded-3xl w-full min-h-[150px] text-white overflow-hidden`;
44+
const baseClasses = `relative ${variant === "eName" ? "bg-black-900" : variant === "ePassport" ? "bg-primary" : "bg-gray"} rounded-3xl w-full min-h-[150px] text-white overflow-hidden`;
4545
</script>
4646

4747
<div {...restProps} class={cn(baseClasses, restProps.class)}>
48-
<div class="w-full h-full pointer-events-none flex gap-13 justify-end absolute right-15 bottom-20">
49-
<div class="w-10 {variant === 'eVault' ? "bg-white/40" : "bg-white/10"} h-[300%] rotate-40"></div>
50-
<div class="w-10 {variant === 'eVault' ? "bg-white/40" : "bg-white/10"} h-[300%] rotate-40"></div>
48+
<div
49+
class="w-full h-full pointer-events-none flex gap-13 justify-end absolute right-15 bottom-20"
50+
>
51+
<div
52+
class="w-10 {variant === 'eVault'
53+
? 'bg-white/40'
54+
: 'bg-white/10'} h-[300%] rotate-40"
55+
></div>
56+
<div
57+
class="w-10 {variant === 'eVault'
58+
? 'bg-white/40'
59+
: 'bg-white/10'} h-[300%] rotate-40"
60+
></div>
5161
</div>
5262
<div class="p-5 flex flex-col gap-2">
5363
<div class="flex justify-between">
54-
{#if variant === 'eName'}
55-
<HugeiconsIcon size={30} strokeWidth={2} className="text-secondary" icon={CheckmarkBadge02Icon} />
56-
<div class="flex gap-3 items-center">
57-
{#if shareBtn}
58-
<Button.Icon icon={Upload03Icon} iconColor={"white"} strokeWidth={2} onclick={shareBtn} />
64+
{#if variant === "eName"}
65+
<HugeiconsIcon
66+
size={30}
67+
strokeWidth={2}
68+
className="text-secondary"
69+
icon={CheckmarkBadge02Icon}
70+
/>
71+
<div class="flex gap-3 items-center">
72+
{#if shareBtn}
73+
<Button.Icon
74+
icon={Upload03Icon}
75+
iconColor={"white"}
76+
strokeWidth={2}
77+
onclick={shareBtn}
78+
/>
5979
{/if}
6080
{#if viewBtn}
61-
<Button.Icon icon={ViewIcon} iconColor={"white"} strokeWidth={2} onclick={viewBtn} />
81+
<Button.Icon
82+
icon={ViewIcon}
83+
iconColor={"white"}
84+
strokeWidth={2}
85+
onclick={viewBtn}
86+
/>
6287
{/if}
63-
</div>
64-
{:else if variant === 'ePassport'}
65-
<p class="bg-white text-black flex items-center leading-0 justify-center rounded-full h-7 px-5 text-xs font-medium">HIGH SECURITY</p>
88+
</div>
89+
{:else if variant === "ePassport"}
90+
<p
91+
class="bg-white text-black flex items-center leading-0 justify-center rounded-full h-7 px-5 text-xs font-medium"
92+
>
93+
HIGH SECURITY
94+
</p>
6695
{#if viewBtn}
67-
<Button.Icon icon={ViewIcon} iconColor={"white"} strokeWidth={2} onclick={viewBtn} />
96+
<Button.Icon
97+
icon={ViewIcon}
98+
iconColor={"white"}
99+
strokeWidth={2}
100+
onclick={viewBtn}
101+
/>
68102
{/if}
69-
{:else if variant === 'eVault'}
70-
<h3 class="text-black-300 text-3xl font-semibold mb-3 z-[1]">{state.progressWidth} Used</h3>
103+
{:else if variant === "eVault"}
104+
<h3 class="text-black-300 text-3xl font-semibold mb-3 z-[1]">
105+
{state.progressWidth} Used
106+
</h3>
71107
{/if}
72108
</div>
73109
<div>
74-
{#if variant === "eName"}
110+
{#if variant === "eName"}
75111
<p class="text-gray font-normal">Your eName</p>
76112
<div class="flex items-center justify-between w-full">
77-
<p class="text-white w-full font-medium">@{userId}</p>
113+
<p class="text-white w-full font-medium">{userId}</p>
78114
</div>
79115
{:else if variant === "ePassport"}
80116
<div class="flex gap-2 flex-col">
81117
{#if userData}
82-
{#each Object.entries(userData) as [fieldName, value] }
118+
{#each Object.entries(userData) as [fieldName, value]}
83119
<div class="flex justify-between">
84-
<p class="text-gray">{fieldName}</p>
120+
<p class="text-gray capitalize">{fieldName}</p>
85121
<p class=" font-medium text-white">{value}</p>
86122
</div>
87123
{/each}
88124
{/if}
89125
</div>
90126
{:else if variant === "eVault"}
91-
<div>
92-
<div class="flex justify-between mb-1 ">
93-
<p class="z-[1]">{usedStorage}GB Used</p>
94-
<p class="z-[1]">{totalStorage}GB total storage</p>
95-
</div>
96-
<div class="relative w-full h-3 rounded-full overflow-hidden bg-primary-400">
97-
<div class="h-full bg-secondary rounded-full" style={`width: calc(${state.progressWidth})`}></div>
127+
<div>
128+
<div class="flex justify-between mb-1">
129+
<p class="z-[1]">{usedStorage}GB Used</p>
130+
<p class="z-[1]">{totalStorage}GB total storage</p>
131+
</div>
132+
<div
133+
class="relative w-full h-3 rounded-full overflow-hidden bg-primary-400"
134+
>
135+
<div
136+
class="h-full bg-secondary rounded-full"
137+
style={`width: calc(${state.progressWidth})`}
138+
></div>
139+
</div>
98140
</div>
99-
</div>
100141
{/if}
101142
</div>
102143
</div>
103-
</div>
144+
</div>
145+
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import type { Store } from "@tauri-apps/plugin-store";
2+
3+
export class VaultController {
4+
#store: Store;
5+
constructor(store: Store) {
6+
this.#store = store;
7+
}
8+
9+
set vault(
10+
vault:
11+
| Promise<Record<string, string> | undefined>
12+
| Record<string, string>
13+
| undefined,
14+
) {
15+
if (vault instanceof Promise) {
16+
vault
17+
.then((resolvedUser) => {
18+
this.#store.set("vault", resolvedUser);
19+
})
20+
.catch((error) => {
21+
console.error("Failed to set vault:", error);
22+
});
23+
} else {
24+
this.#store.set("vault", vault);
25+
}
26+
}
27+
28+
get vault() {
29+
return this.#store
30+
.get<Record<string, string>>("vault")
31+
.then((vault) => {
32+
if (!vault) {
33+
return undefined;
34+
}
35+
return vault;
36+
})
37+
.catch((error) => {
38+
console.error("Failed to get vault:", error);
39+
return undefined;
40+
});
41+
}
42+
}

β€Žinfrastructure/eid-wallet/src/lib/global/controllers/user.tsβ€Ž

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,12 @@ export class UserController {
5454
* ```
5555
* @throws {Error} If the user state cannot be set in the store
5656
*/
57-
set user(user:
58-
| Promise<Record<string, string> | undefined>
59-
| Record<string, string>
60-
| undefined) {
57+
set user(
58+
user:
59+
| Promise<Record<string, string> | undefined>
60+
| Record<string, string>
61+
| undefined,
62+
) {
6163
if (user instanceof Promise) {
6264
user.then((resolvedUser) => {
6365
this.#store.set("user", resolvedUser);
@@ -83,4 +85,38 @@ export class UserController {
8385
return undefined;
8486
});
8587
}
88+
89+
set document(
90+
document:
91+
| Promise<Record<string, string> | undefined>
92+
| Record<string, string>
93+
| undefined,
94+
) {
95+
if (document instanceof Promise) {
96+
document
97+
.then((resolvedDoc) => {
98+
this.#store.set("doc", resolvedDoc);
99+
})
100+
.catch((error) => {
101+
console.error("Failed to set doc:", error);
102+
});
103+
} else {
104+
this.#store.set("doc", document);
105+
}
106+
}
107+
108+
get document() {
109+
return this.#store
110+
.get<Record<string, string>>("doc")
111+
.then((user) => {
112+
if (!user) {
113+
return undefined;
114+
}
115+
return user;
116+
})
117+
.catch((error) => {
118+
console.error("Failed to get doc:", error);
119+
return undefined;
120+
});
121+
}
86122
}

β€Žinfrastructure/eid-wallet/src/lib/global/state.tsβ€Ž

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Store } from "@tauri-apps/plugin-store";
22
import { SecurityController } from "./controllers/security";
33
import { UserController } from "./controllers/user";
4+
import { VaultController } from "./controllers/evault";
45
/**
56
* @author SoSweetHam <[email protected]>
67
* @description A centralized state that can be used to control the global state of the application, meant to be used as a singleton through the main layout component.
@@ -23,10 +24,13 @@ export class GlobalState {
2324
#store: Store;
2425
securityController: SecurityController;
2526
userController: UserController;
27+
vaultController: VaultController;
28+
2629
private constructor(store: Store) {
2730
this.#store = store;
2831
this.securityController = new SecurityController(store);
2932
this.userController = new UserController(store);
33+
this.vaultController = new VaultController(store);
3034
}
3135

3236
/**

β€Žinfrastructure/eid-wallet/src/lib/ui/Button/ButtonAction.svelteβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ const classes = $derived({
106106
</div>
107107
</button>
108108

109-
<!--
109+
<!--
110110
@component
111111
export default ButtonAction
112112
@description
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/**
2+
* Returns a new string with the first letter of each word in uppercase and the remaining letters in lowercase.
3+
*
4+
* @param str - The input string to capitalize.
5+
* @returns The capitalized string.
6+
*/
7+
export function capitalize(str: string) {
8+
return str
9+
.toLowerCase()
10+
.split(" ")
11+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
12+
.join(" ");
13+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from "./mergeClasses";
22
export * from "./clickOutside";
3+
export * from "./capitalize";

0 commit comments

Comments
Β (0)