-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathHasRecoveryKit.svelte
125 lines (105 loc) · 3.66 KB
/
HasRecoveryKit.svelte
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
<script lang="ts">
import * as uint8arrays from 'uint8arrays'
import { sessionStore } from '$src/stores'
import {
RECOVERY_STATES,
USERNAME_STORAGE_KEY,
loadAccount,
prepareUsername
} from '$lib/auth/account'
import Check from '$components/icons/CheckIcon.svelte'
import RecoveryKitButton from './RecoveryKitButton.svelte'
let state = $sessionStore.session
? RECOVERY_STATES.Done
: RECOVERY_STATES.Ready
/**
* Parse the user's `username` and `readKey` from the uploaded recovery kit and pass them into
* webnative to recover the user's account and populate the `session` and `filesystem` stores
* @param files
*/
export const handleFileInput: (
files: FileList
) => Promise<void> = async files => {
const reader = new FileReader()
reader.onload = async event => {
state = RECOVERY_STATES.Processing
try {
const {
authStrategy,
program: {
components: { crypto, reference, storage }
}
} = $sessionStore
const parts = (event.target.result as string)
.split('username: ')[1]
.split('key: ')
const readKey = uint8arrays.fromString(
// Trim any whitespace from the parsed readKey
parts[1].replace(/(\r\n|\n|\r)/gm, ''),
'base64pad'
)
// Trim any whitespace from the parsed username
const oldUsername = parts[0].replace(/(\r\n|\n|\r)/gm, '')
const hashedOldUsername = await prepareUsername(oldUsername)
const newRootDID = await $sessionStore.program.agentDID()
// Construct a new username using the old `trimmed` name and `newRootDID`
const newUsername = `${oldUsername.split('#')[0]}#${newRootDID}`
const hashedNewUsername = await prepareUsername(newUsername)
storage.setItem(USERNAME_STORAGE_KEY, newUsername)
// Recover user's file system
const { success } = await $sessionStore.program.recoverFileSystem({
newUsername: hashedNewUsername,
oldUsername: hashedOldUsername,
readKey
})
if (!success) {
throw new Error('Failed to recover file system')
}
// Load account data into sessionStore
await loadAccount(hashedNewUsername, newUsername)
state = RECOVERY_STATES.Done
} catch (error) {
console.error(error)
state = RECOVERY_STATES.Error
}
}
reader.onerror = error => {
console.error(error)
state = RECOVERY_STATES.Error
}
reader.readAsText(files[0])
}
</script>
<div
class="min-h-[calc(100vh-96px)] flex flex-col items-start justify-center max-w-[590px] m-auto gap-6 pb-5 text-sm"
>
<h1 class="text-xl">Recover your account</h1>
{#if state === RECOVERY_STATES.Done}
<h3 class="flex items-center gap-2 font-normal text-base text-green-600">
<Check /> Account recovered!
</h3>
<p>
Welcome back <strong>{$sessionStore.username.trimmed}.</strong>
We were able to successfully recover all of your private data.
</p>
{:else}
<p>
If you’ve lost access to all of your connected devices, you can use your
recovery kit to restore access to your private data.
</p>
{/if}
{#if state === RECOVERY_STATES.Error}
<p class="text-red-600">
We were unable to recover your account. Please double check that you
uploaded the correct file.
</p>
{/if}
<div class="flex flex-col gap-2">
<RecoveryKitButton {handleFileInput} {state} />
{#if state !== RECOVERY_STATES.Done}
<p class="text-xxs">
{`It should be a file named Webnative-RecoveryKit-{yourUsername}.txt`}
</p>
{/if}
</div>
</div>