Skip to content

Commit

Permalink
Refactor app
Browse files Browse the repository at this point in the history
  • Loading branch information
mrruby committed Nov 27, 2023
1 parent e31809e commit 9a3dcdd
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 193 deletions.
183 changes: 22 additions & 161 deletions src/lib/queries/app-queries.ts
Original file line number Diff line number Diff line change
@@ -1,173 +1,34 @@
import { createMutation, useQueryClient, createQuery } from '@tanstack/svelte-query';
import { storageService } from '$services';
import { Password, SecureDataSchema, SessionStateSchema } from '$types';
import { useQueryClient } from '@tanstack/svelte-query';

import {
createSessionQuery,
createSetupDeviceKeyQuery,
createStoreDeviceKey
} from './sessionAndKey';
import {
LOCAL,
PASSWORD,
DEVICE_KEY,
SESSION,
SESSION_DATA,
SESSION_DATA_KEY,
SETUP_KEY,
SETUP_PASSWORD
} from '$const';
import { decryptData, encryptData, hashPassword } from '$helpers';
createChangePasswordWithDeviceKeyMutation,
createPasswordMutation,
createSetupPasswordQuery,
createSignInMutation
} from './password';

export function sessionStorageQueries() {
const queryClient = useQueryClient();
const sessionQuery = createQuery({
queryKey: [SESSION_DATA_KEY],
queryFn: async () => {
const data = await storageService.getWithoutCallback({
key: SESSION_DATA,
area: SESSION
});

const validatedData = SessionStateSchema.safeParse(data);
return validatedData.success ? validatedData.data : false;
}
});
const signInMutation = createMutation({
mutationFn: async (password: string) => {
const result = await storageService.getWithoutCallback({
key: PASSWORD,
area: LOCAL
});

const validatedResult = Password.safeParse(result);

if (validatedResult.success && (await hashPassword(password)) === validatedResult.data) {
return storageService.set({
key: SESSION_DATA,
value: true,
area: SESSION
});
}

throw new Error('Invalid password or data');
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [SESSION_DATA_KEY] });
}
});
const setupPasswordQuery = createQuery({
queryKey: [SETUP_PASSWORD],
queryFn: async () => {
const data = await storageService.getWithoutCallback({
key: PASSWORD,
area: LOCAL
});

const validatedData = Password.safeParse(data);
return validatedData.success;
}
});
const setupDeviceKeyQuery = createQuery({
queryKey: [SETUP_KEY],
queryFn: async () => {
const data = await storageService.getWithoutCallback({
key: DEVICE_KEY,
area: LOCAL
});

const validatedData = SecureDataSchema.safeParse(data);
return validatedData.success;
}
});
const createPassword = createMutation({
mutationFn: async (password: string) => {
const hash = await hashPassword(password);
storageService.set({
key: PASSWORD,
value: hash,
area: LOCAL
});
},

onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [SETUP_PASSWORD] });
}
});
const storeDeviceKey = createMutation({
mutationFn: async (deviceKey: Uint8Array) => {
const result = await storageService.getWithoutCallback({
key: PASSWORD,
area: LOCAL
});

const validatePassword = Password.safeParse(result);

if (validatePassword.success) {
storageService.set({
key: DEVICE_KEY,
value: await encryptData(deviceKey, validatePassword.data),
area: LOCAL
});
}
},

onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [SETUP_KEY] });
}
});

const changePasswordWithDeviceKeyMutation = createMutation({
mutationFn: async (mutationData: { newPassword: string; oldPassword: string }) => {
const oldHash = await hashPassword(mutationData.oldPassword);

const result = await storageService.getWithoutCallback({
key: PASSWORD,
area: LOCAL
});

const validatePassword = Password.safeParse(result);

if (!validatePassword.success || oldHash !== validatePassword.data) {
throw new Error('Invalid password');
}

const deviceKey = await storageService.getWithoutCallback({
key: DEVICE_KEY,
area: LOCAL
});

const validatedDeviceKey = SecureDataSchema.safeParse(deviceKey);

if (!validatedDeviceKey.success) {
throw new Error('Invalid device key');
}

const decryptedData = await decryptData(validatedDeviceKey.data, oldHash);
const newHash = await hashPassword(mutationData.newPassword);

storageService.set({
key: PASSWORD,
value: newHash,
area: LOCAL
});
storageService.set({
key: DEVICE_KEY,
value: await encryptData(decryptedData, newHash),
area: LOCAL
});
storageService.set({
key: SESSION_DATA,
value: false,
area: SESSION
});
},

onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [SESSION_DATA_KEY] });
}
});
const changePasswordWithDeviceKeyMutation =
createChangePasswordWithDeviceKeyMutation(queryClient);
const createPassword = createPasswordMutation(queryClient);
const sessionQuery = createSessionQuery();
const setupDeviceKeyQuery = createSetupDeviceKeyQuery();
const setupPasswordQuery = createSetupPasswordQuery();
const signInMutation = createSignInMutation(queryClient);
const storeDeviceKey = createStoreDeviceKey(queryClient);

return {
changePasswordWithDeviceKeyMutation,
createPassword,
sessionQuery,
setupDeviceKeyQuery,
setupPasswordQuery,
changePasswordWithDeviceKeyMutation,
sessionQuery,
signInMutation,
storeDeviceKey
};
Expand Down
113 changes: 113 additions & 0 deletions src/lib/queries/password.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import {
DEVICE_KEY,
LOCAL,
PASSWORD,
SESSION,
SESSION_DATA,
SESSION_DATA_KEY,
SETUP_PASSWORD
} from '$const';
import { decryptData, encryptData, hashPassword } from '$helpers';
import { storageService } from '$services';
import { Password, SecureDataSchema } from '$types';
import { createMutation, createQuery, type QueryClient } from '@tanstack/svelte-query';

const getPassword = async () => {
const data = await storageService.getWithoutCallback({ key: PASSWORD, area: LOCAL });
return Password.safeParse(data);
};

export function createSetupPasswordQuery() {
return createQuery({
queryKey: [SETUP_PASSWORD],
queryFn: async () => {
const validatedResult = await getPassword();
return validatedResult.success;
}
});
}

export function createPasswordMutation(queryClient: QueryClient) {
return createMutation({
mutationFn: async (password: string) => {
const hash = await hashPassword(password);
storageService.set({
key: PASSWORD,
value: hash,
area: LOCAL
});
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [SETUP_PASSWORD] });
}
});
}

export function createSignInMutation(queryClient: QueryClient) {
return createMutation({
mutationFn: async (password: string) => {
const validatedResult = await getPassword();

if (validatedResult.success && (await hashPassword(password)) === validatedResult.data) {
return storageService.set({
key: SESSION_DATA,
value: true,
area: SESSION
});
}

throw new Error('Invalid password or data');
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [SESSION_DATA_KEY] });
}
});
}

export function createChangePasswordWithDeviceKeyMutation(queryClient: QueryClient) {
return createMutation({
mutationFn: async (mutationData: { newPassword: string; oldPassword: string }) => {
const oldHash = await hashPassword(mutationData.oldPassword);

const validatePassword = await getPassword();

if (!validatePassword.success || oldHash !== validatePassword.data) {
throw new Error('Invalid password');
}

const deviceKey = await storageService.getWithoutCallback({
key: DEVICE_KEY,
area: LOCAL
});

const validatedDeviceKey = SecureDataSchema.safeParse(deviceKey);

if (!validatedDeviceKey.success) {
throw new Error('Invalid device key');
}

const decryptedData = await decryptData(validatedDeviceKey.data, oldHash);
const newHash = await hashPassword(mutationData.newPassword);

storageService.set({
key: PASSWORD,
value: newHash,
area: LOCAL
});
storageService.set({
key: DEVICE_KEY,
value: await encryptData(decryptedData, newHash),
area: LOCAL
});
storageService.set({
key: SESSION_DATA,
value: false,
area: SESSION
});
},

onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [SESSION_DATA_KEY] });
}
});
}
67 changes: 67 additions & 0 deletions src/lib/queries/sessionAndKey.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {
DEVICE_KEY,
LOCAL,
PASSWORD,
SESSION,
SESSION_DATA,
SESSION_DATA_KEY,
SETUP_KEY
} from '$const';
import { encryptData } from '$helpers';
import { storageService } from '$services';
import { Password, SecureDataSchema, SessionStateSchema } from '$types';
import { QueryClient, createMutation, createQuery } from '@tanstack/svelte-query';

export function createSessionQuery() {
return createQuery({
queryKey: [SESSION_DATA_KEY],
queryFn: async () => {
const data = await storageService.getWithoutCallback({
key: SESSION_DATA,
area: SESSION
});
const validatedData = SessionStateSchema.safeParse(data);
return validatedData.success ? validatedData.data : false;
}
});
}

export function createSetupDeviceKeyQuery() {
return createQuery({
queryKey: [SETUP_KEY],
queryFn: async () => {
const data = await storageService.getWithoutCallback({
key: DEVICE_KEY,
area: LOCAL
});
const validatedData = SecureDataSchema.safeParse(data);
return validatedData.success;
}
});
}

export function createStoreDeviceKey(queryClient: QueryClient) {
return createMutation({
mutationFn: async (deviceKey: Uint8Array) => {
const result = await storageService.getWithoutCallback({
key: PASSWORD,
area: LOCAL
});
const validatePassword = Password.safeParse(result);

if (validatePassword.success) {
storageService.set({
key: DEVICE_KEY,
value: await encryptData(deviceKey, validatePassword.data),
area: LOCAL
});
}

throw new Error('Something went wrong');
},

onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [SETUP_KEY] });
}
});
}
Loading

0 comments on commit 9a3dcdd

Please sign in to comment.