diff --git a/src/AuthContext/AuthContextProvider.tsx b/src/AuthContext/AuthContextProvider.tsx index 859d336..c5c3b61 100644 --- a/src/AuthContext/AuthContextProvider.tsx +++ b/src/AuthContext/AuthContextProvider.tsx @@ -1,17 +1,18 @@ -import {ReactElement, ReactNode, useCallback, useEffect, useMemo} from "react" +import {ReactElement, ReactNode, useCallback, useEffect} from "react" import {AxiosError} from "axios" import {decrypt, readMessage, readPrivateKey} from "openpgp" import {useLocalStorage} from "react-use" -import AuthContext, {AuthContextType, EncryptionStatus} from "./AuthContext" +import AuthContext from "./AuthContext" import useExtensionHandler from "~/AuthContext/use-extension-handler" import useMasterPassword from "~/AuthContext/use-master-password" import {AuthenticationDetails, ServerUser, User} from "~/server-types" -import {getMe, REFRESH_TOKEN_URL, refreshToken, RefreshTokenResult} from "~/apis" +import {REFRESH_TOKEN_URL, RefreshTokenResult, getMe, refreshToken} from "~/apis" import {client} from "~/constants/axios-client" import {useMutation, useQuery} from "@tanstack/react-query" import PasswordShareConfirmationDialog from "~/AuthContext/PasswordShareConfirmationDialog" +import useContextValue from "~/AuthContext/use-context-value" export interface AuthContextProviderProps { children: ReactNode @@ -41,6 +42,40 @@ export default function AuthContextProvider({children}: AuthContextProviderProps onError: () => logout(), }) + const decryptUsingPrivateKey = useCallback( + async (message: string): Promise => { + if (!user) { + throw new Error("User not set.") + } + + if (!user.isDecrypted) { + throw new Error("User is not decrypted.") + } + + return ( + await decrypt({ + message: await readMessage({ + armoredMessage: message, + }), + decryptionKeys: await readPrivateKey({ + armoredKey: user.notes.privateKey, + }), + }) + ).data.toString() + }, + [user], + ) + + const contextValue = useContextValue({ + decryptUsingPrivateKey, + encryptUsingMasterPassword, + decryptUsingMasterPassword, + setDecryptionPassword, + logout, + login: setUser, + user: user || null, + }) + useQuery(["get_me"], getMe, { refetchOnWindowFocus: "always", refetchOnReconnect: "always", @@ -90,65 +125,11 @@ export default function AuthContextProvider({children}: AuthContextProviderProps return () => client.interceptors.response.eject(interceptor) }, [logout, refresh]) - const decryptUsingPrivateKey = useCallback( - async (message: string): Promise => { - if (!user) { - throw new Error("User not set.") - } - - if (!user.isDecrypted) { - throw new Error("User is not decrypted.") - } - - return ( - await decrypt({ - message: await readMessage({ - armoredMessage: message, - }), - decryptionKeys: await readPrivateKey({ - armoredKey: user.notes.privateKey, - }), - }) - ).data.toString() - }, - [user], - ) - - const value = useMemo( - () => ({ - user: user ?? null, - login: setUser, - encryptionStatus: (() => { - if (!user) { - return EncryptionStatus.Unavailable - } - - if (!user.encryptedPassword) { - return EncryptionStatus.Unavailable - } - - if (user.isDecrypted) { - return EncryptionStatus.Available - } - - return EncryptionStatus.PasswordRequired - })(), - logout, - isAuthenticated: user !== null, - _encryptUsingMasterPassword: encryptUsingMasterPassword, - _decryptUsingMasterPassword: decryptUsingMasterPassword, - _decryptUsingPrivateKey: decryptUsingPrivateKey, - _setDecryptionPassword: updateDecryptionPassword, - _updateUser: setUser, - }), - [user, logout, encryptUsingMasterPassword, decryptUsingMasterPassword], - ) - return ( <> - {children} + {children} { closeDialog(doNotAskAgain) diff --git a/src/AuthContext/use-context-value.ts b/src/AuthContext/use-context-value.ts new file mode 100644 index 0000000..34bcd6c --- /dev/null +++ b/src/AuthContext/use-context-value.ts @@ -0,0 +1,49 @@ +import {AuthContextType, EncryptionStatus} from "~/AuthContext/AuthContext" +import {ServerUser, User} from "~/server-types" + +export interface UseContextValueData { + user: User | ServerUser | null + login: (user: User | ServerUser) => void + logout: () => void + encryptUsingMasterPassword: (content: string) => string + decryptUsingMasterPassword: (content: string) => string + decryptUsingPrivateKey: (message: string) => Promise + setDecryptionPassword: (password: string) => void +} + +export default function useContextValue({ + user, + login, + logout, + encryptUsingMasterPassword, + decryptUsingMasterPassword, + setDecryptionPassword, + decryptUsingPrivateKey, +}: UseContextValueData): AuthContextType { + return { + user, + login, + logout, + isAuthenticated: Boolean(user), + encryptionStatus: (() => { + if (!user) { + return EncryptionStatus.Unavailable + } + + if (!user.encryptedPassword) { + return EncryptionStatus.Unavailable + } + + if (user.isDecrypted) { + return EncryptionStatus.Available + } + + return EncryptionStatus.PasswordRequired + })(), + _updateUser: login, + _setDecryptionPassword: setDecryptionPassword, + _encryptUsingMasterPassword: encryptUsingMasterPassword, + _decryptUsingMasterPassword: decryptUsingMasterPassword, + _decryptUsingPrivateKey: decryptUsingPrivateKey, + } +}