Skip to content

Commit c645a19

Browse files
committed
feat: working wallet with provisioning
1 parent 2cd6d7b commit c645a19

File tree

14 files changed

+386
-207
lines changed

14 files changed

+386
-207
lines changed

infrastructure/eid-wallet/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@
3535
"flag-icons": "^7.3.2",
3636
"import": "^0.0.6",
3737
"svelte-loading-spinners": "^0.3.6",
38-
"tailwind-merge": "^3.0.2"
38+
"tailwind-merge": "^3.0.2",
39+
"uuid": "^11.1.0"
3940
},
4041
"devDependencies": {
4142
"@biomejs/biome": "^1.9.4",

infrastructure/eid-wallet/src/lib/fragments/IdentityCard/IdentityCard.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@
110110
{#if variant === "eName"}
111111
<p class="text-gray font-normal">Your eName</p>
112112
<div class="flex items-center justify-between w-full">
113-
<p class="text-white w-full font-medium">@{userId}</p>
113+
<p class="text-white w-full font-medium">{userId}</p>
114114
</div>
115115
{:else if variant === "ePassport"}
116116
<div class="flex gap-2 flex-col">
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/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/routes/(app)/main/+page.svelte

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import { getContext, onMount, type Snippet } from "svelte";
1010
1111
let userData: Record<string, unknown> = $state();
12+
let greeting = $state();
13+
let ename = $state();
1214
1315
let shareQRdrawerOpen = $state(false);
1416
@@ -21,12 +23,22 @@
2123
2224
onMount(async () => {
2325
userData = await globalState.userController.user;
26+
const vaultData = await globalState.vaultController.vault;
27+
ename = vaultData.ename;
28+
29+
const currentHour = new Date().getHours();
30+
greeting =
31+
currentHour > 17
32+
? "Good Evening"
33+
: currentHour > 12
34+
? "Good Afternoon"
35+
: "Good Morning";
2436
});
2537
</script>
2638

2739
<Hero
28-
title="Good morning!"
29-
subtitle="Don't forget to drink water."
40+
title={greeting ?? "Hi!"}
41+
subtitle="Welcome back to your eID Wallet"
3042
showSettings
3143
/>
3244

@@ -40,7 +52,7 @@
4052
{#snippet eName()}
4153
<IdentityCard
4254
variant="eName"
43-
userId="caa0f630-2413-5aceaa2c-4628ce93e497"
55+
userId={ename ?? "Loading..."}
4456
viewBtn={() => alert("View button clicked!")}
4557
shareBtn={() => (shareQRdrawerOpen = true)}
4658
/>

infrastructure/eid-wallet/src/routes/(auth)/e-passport/+page.svelte

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,19 @@
44
import IdentityCard from "$lib/fragments/IdentityCard/IdentityCard.svelte";
55
import type { GlobalState } from "$lib/global";
66
import { ButtonAction } from "$lib/ui";
7-
import { getContext } from "svelte";
7+
import { getContext, onMount } from "svelte";
88
9+
let userData = $state();
910
let globalState: GlobalState =
1011
getContext<() => GlobalState>("globalState")();
1112
1213
const handleFinish = async () => {
1314
await goto("/main");
1415
};
16+
17+
onMount(async () => {
18+
userData = await globalState.userController.user;
19+
});
1520
</script>
1621

1722
<main
@@ -23,10 +28,7 @@
2328
subtitle="Log into any W3DS platform without passwords. It’s tied to this phone; if lost, you’ll need to revoke and reissue it on a new device."
2429
class="mb-2"
2530
/>
26-
<IdentityCard
27-
variant="ePassport"
28-
userData={globalState.userController.user}
29-
/>
31+
<IdentityCard variant="ePassport" {userData} />
3032
</section>
3133
<section class="mt-[4svh] mb-[9svh]">
3234
<h4>Your eVault</h4>
@@ -35,7 +37,7 @@
3537
personal data. W3DS platforms access it directly, keeping you in
3638
control.
3739
</p>
38-
<IdentityCard variant="eVault" usedStorage={15} totalStorage={80} />
40+
<IdentityCard variant="eVault" usedStorage={0.1} totalStorage={10} />
3941
</section>
4042
<ButtonAction class="w-full" callback={handleFinish}>Finish</ButtonAction>
4143
</main>
Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,35 @@
11
<script lang="ts">
2-
import { goto } from "$app/navigation";
3-
import { Hero, IdentityCard } from "$lib/fragments";
4-
import { ButtonAction } from "$lib/ui";
2+
import { goto } from "$app/navigation";
3+
import { Hero, IdentityCard } from "$lib/fragments";
4+
import type { GlobalState } from "$lib/global";
5+
import { ButtonAction } from "$lib/ui";
6+
import axios from "axios";
7+
import { getContext, onMount } from "svelte";
58
6-
const handleNext = async () => {
7-
await goto("/e-passport");
8-
};
9+
let globalState = getContext<() => GlobalState>("globalState")();
10+
let ename = $state();
11+
12+
const handleNext = async () => {
13+
await goto("/e-passport");
14+
};
15+
16+
onMount(async () => {
17+
const vault = await globalState.vaultController.vault;
18+
ename = vault.ename;
19+
});
920
</script>
1021

11-
<main class="h-screen pt-[5.2svh] px-[5vw] pb-[4.5svh] flex flex-col justify-between">
22+
<main
23+
class="h-screen pt-[5.2svh] px-[5vw] pb-[4.5svh] flex flex-col justify-between"
24+
>
1225
<section>
1326
<Hero
1427
title="Here’s your eName"
1528
subtitle="This identifier is permanently yours, and it stays with you for your whole life."
1629
class="mb-4"
1730
/>
18-
<IdentityCard variant="eName" userId= "acbsdjk-14n14k43-12412"/>
31+
<IdentityCard variant="eName" userId={`${ename ?? "Loading..."}`} />
1932
</section>
2033
<ButtonAction class="w-full" callback={handleNext}>Next</ButtonAction>
21-
</main>
34+
</main>
35+

infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77
import { capitalize } from "$lib/utils";
88
import Drawer from "$lib/ui/Drawer/Drawer.svelte";
99
import axios from "axios";
10-
import { PUBLIC_PROVISIONER_URL } from "$env/static/public";
10+
import { v4 as uuidv4 } from "uuid";
11+
import {
12+
PUBLIC_PROVISIONER_URL,
13+
PUBLIC_REGISTRY_URL,
14+
} from "$env/static/public";
1115
import {
1216
DocFront,
1317
verificaitonId,
@@ -17,11 +21,14 @@
1721
} from "./store";
1822
import Passport from "./steps/passport.svelte";
1923
import Selfie from "./steps/selfie.svelte";
24+
import { load } from "@tauri-apps/plugin-store";
25+
import { Shadow } from "svelte-loading-spinners";
2026
2127
let globalState: GlobalState | undefined = $state(undefined);
2228
let showVeriffModal = $state(false);
2329
let person: Record<string, unknown>;
2430
let document: Record<string, unknown>;
31+
let loading = $state(false);
2532
2633
async function handleVerification() {
2734
const { data } = await axios.post(
@@ -68,6 +75,8 @@
6875
handleContinue = async () => {
6976
if ($status !== "approved") return verifStep.set(0);
7077
if (!globalState) throw new Error("Global state is not defined");
78+
79+
loading = true;
7180
globalState.userController.user = {
7281
name: capitalize(
7382
person.firstName.value + " " + person.lastName.value,
@@ -85,8 +94,28 @@
8594
).toDateString(),
8695
"Verified On": new Date().toDateString(),
8796
};
88-
console.log(globalState.userController.user);
89-
await goto("/register");
97+
const {
98+
data: { token: registryEntropy },
99+
} = await axios.get(
100+
new URL("/entropy", PUBLIC_REGISTRY_URL).toString(),
101+
);
102+
const { data } = await axios.post(
103+
new URL("/provision", PUBLIC_PROVISIONER_URL).toString(),
104+
{
105+
registryEntropy,
106+
namespace: uuidv4(),
107+
verificationId: $verificaitonId,
108+
},
109+
);
110+
if (data.success === true) {
111+
globalState.vaultController.vault = {
112+
uri: data.uri,
113+
ename: data.w3id,
114+
};
115+
}
116+
setTimeout(() => {
117+
goto("/register");
118+
}, 10_000);
90119
};
91120
});
92121
</script>
@@ -97,7 +126,8 @@
97126
<section>
98127
<Hero
99128
title="Verify your account"
100-
subtitle="Get your passport ready. You’ll be directed to a site where you can verify your account in a swift and secure process"
129+
subtitle="Get your passport ready. You’ll be directed to present
130+
your passport and take a quick selfie."
101131
/>
102132
<img class="mx-auto mt-20" src="images/Passport.svg" alt="passport" />
103133
</section>
@@ -109,6 +139,15 @@
109139
<Passport></Passport>
110140
{:else if $verifStep === 1}
111141
<Selfie></Selfie>
142+
{:else if loading}
143+
<div class="my-20">
144+
<div
145+
class="align-center flex w-full flex-col items-center justify-center gap-6"
146+
>
147+
<Shadow size={40} color="rgb(142, 82, 255);" />
148+
<h3>Generating your eName</h3>
149+
</div>
150+
</div>
112151
{:else}
113152
<div class="flex flex-col gap-6">
114153
{#if $status === "approved"}

infrastructure/evault-provisioner/src/controllers/VerificationController.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,18 +208,19 @@ export class VerificationController {
208208
];
209209
if (
210210
affirmativeStatusTypes.includes(
211-
body.data.verification.status,
211+
body.data.verification.decision,
212212
)
213213
) {
214214
const approved =
215-
body.data.verification.status === "approved";
215+
body.data.verification.decision === "approved";
216216
await this.verificationService.findByIdAndUpdate(id, {
217217
approved,
218218
data: {
219-
status: body.data.verification.status,
220219
person: body.data.verification.person,
220+
document: body.data.verification.document,
221221
},
222-
documentId: body.data.verification.document.id
222+
documentId:
223+
body.data.verification.document.number.value,
223224
});
224225
}
225226

infrastructure/evault-provisioner/src/index.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ interface ProvisionRequest {
5858
interface ProvisionResponse {
5959
success: boolean;
6060
uri?: string;
61+
w3id?: string;
6162
message?: string;
6263
error?: string | unknown;
6364
}
@@ -75,8 +76,8 @@ app.post(
7576
res: Response<ProvisionResponse>,
7677
) => {
7778
try {
78-
if (!process.env.PUBLIC_REGISTRY_URI)
79-
throw new Error("PUBLIC_REGISTRY_URI is not set");
79+
if (!process.env.PUBLIC_REGISTRY_URL)
80+
throw new Error("PUBLIC_REGISTRY_URL is not set");
8081
const { registryEntropy, namespace, verificationId } = req.body;
8182
if (!registryEntropy || !namespace || !verificationId) {
8283
return res.status(400).json({
@@ -97,7 +98,7 @@ app.post(
9798
const jwksResponse = await axios.get(
9899
new URL(
99100
`/.well-known/jwks.json`,
100-
process.env.PUBLIC_REGISTRY_URI,
101+
process.env.PUBLIC_REGISTRY_URL,
101102
).toString(),
102103
);
103104

@@ -119,7 +120,7 @@ app.post(
119120
await axios.post(
120121
new URL(
121122
"/register",
122-
process.env.PUBLIC_REGISTRY_URI,
123+
process.env.PUBLIC_REGISTRY_URL,
123124
).toString(),
124125
{
125126
ename: w3id,
@@ -135,6 +136,7 @@ app.post(
135136

136137
res.json({
137138
success: true,
139+
w3id,
138140
uri,
139141
});
140142
} catch (error) {

0 commit comments

Comments
 (0)