improving AuthContextProvider.tsx

This commit is contained in:
Myzel394 2022-12-11 22:14:19 +01:00
parent d5e9234f3c
commit cf21360509
2 changed files with 89 additions and 59 deletions

View File

@ -1,17 +1,18 @@
import {ReactElement, ReactNode, useCallback, useEffect, useMemo} from "react" import {ReactElement, ReactNode, useCallback, useEffect} from "react"
import {AxiosError} from "axios" import {AxiosError} from "axios"
import {decrypt, readMessage, readPrivateKey} from "openpgp" import {decrypt, readMessage, readPrivateKey} from "openpgp"
import {useLocalStorage} from "react-use" import {useLocalStorage} from "react-use"
import AuthContext, {AuthContextType, EncryptionStatus} from "./AuthContext" import AuthContext from "./AuthContext"
import useExtensionHandler from "~/AuthContext/use-extension-handler" import useExtensionHandler from "~/AuthContext/use-extension-handler"
import useMasterPassword from "~/AuthContext/use-master-password" import useMasterPassword from "~/AuthContext/use-master-password"
import {AuthenticationDetails, ServerUser, User} from "~/server-types" 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 {client} from "~/constants/axios-client"
import {useMutation, useQuery} from "@tanstack/react-query" import {useMutation, useQuery} from "@tanstack/react-query"
import PasswordShareConfirmationDialog from "~/AuthContext/PasswordShareConfirmationDialog" import PasswordShareConfirmationDialog from "~/AuthContext/PasswordShareConfirmationDialog"
import useContextValue from "~/AuthContext/use-context-value"
export interface AuthContextProviderProps { export interface AuthContextProviderProps {
children: ReactNode children: ReactNode
@ -41,6 +42,40 @@ export default function AuthContextProvider({children}: AuthContextProviderProps
onError: () => logout(), onError: () => logout(),
}) })
const decryptUsingPrivateKey = useCallback(
async (message: string): Promise<string> => {
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<AuthenticationDetails, AxiosError>(["get_me"], getMe, { useQuery<AuthenticationDetails, AxiosError>(["get_me"], getMe, {
refetchOnWindowFocus: "always", refetchOnWindowFocus: "always",
refetchOnReconnect: "always", refetchOnReconnect: "always",
@ -90,65 +125,11 @@ export default function AuthContextProvider({children}: AuthContextProviderProps
return () => client.interceptors.response.eject(interceptor) return () => client.interceptors.response.eject(interceptor)
}, [logout, refresh]) }, [logout, refresh])
const decryptUsingPrivateKey = useCallback(
async (message: string): Promise<string> => {
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<AuthContextType>(
() => ({
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 ( return (
<> <>
<AuthContext.Provider value={value}>{children}</AuthContext.Provider> <AuthContext.Provider value={contextValue}>{children}</AuthContext.Provider>
<PasswordShareConfirmationDialog <PasswordShareConfirmationDialog
open={Boolean(masterPassword && showDialog)} open={Boolean(_masterPassword && showDialog)}
onShare={sharePassword} onShare={sharePassword}
onClose={doNotAskAgain => { onClose={doNotAskAgain => {
closeDialog(doNotAskAgain) closeDialog(doNotAskAgain)

View File

@ -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<string>
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,
}
}