import useSWR, { SWRConfiguration, SWRResponse } from 'swr'; import { useStoreState } from '@/state/hooks'; import http, { FractalResponseList } from '@/api/http'; import Transformers from '@transformers'; import { SecurityKey } from '@models'; import { AxiosError } from 'axios'; import { base64Decode, bufferDecode, bufferEncode, decodeSecurityKeyCredentials } from '@/helpers'; import { LoginResponse } from '@/api/auth/login'; const useSecurityKeys = (config?: SWRConfiguration): SWRResponse => { const uuid = useStoreState(state => state.user.data!.uuid); return useSWR( [ 'account', uuid, 'security-keys' ], async (): Promise => { const { data } = await http.get('/api/client/account/security-keys'); return (data as FractalResponseList).data.map((datum) => Transformers.toSecurityKey(datum.attributes)); }, config, ); }; const deleteSecurityKey = async (uuid: string): Promise => { await http.delete(`/api/client/account/security-keys/${uuid}`); }; const registerCredentialForAccount = async (name: string, tokenId: string, credential: PublicKeyCredential): Promise => { const { data } = await http.post('/api/client/account/security-keys/register', { name, token_id: tokenId, registration: { id: credential.id, type: credential.type, rawId: bufferEncode(credential.rawId), response: { attestationObject: bufferEncode((credential.response as AuthenticatorAttestationResponse).attestationObject), clientDataJSON: bufferEncode(credential.response.clientDataJSON), }, }, }); return Transformers.toSecurityKey(data.attributes); }; const registerSecurityKey = async (name: string): Promise => { const { data } = await http.get('/api/client/account/security-keys/register'); const publicKey = data.data.credentials; publicKey.challenge = bufferDecode(base64Decode(publicKey.challenge)); publicKey.user.id = bufferDecode(publicKey.user.id); if (publicKey.excludeCredentials) { publicKey.excludeCredentials = decodeSecurityKeyCredentials(publicKey.excludeCredentials); } const credentials = await navigator.credentials.create({ publicKey }); if (!credentials || credentials.type !== 'public-key') { throw new Error(`Unexpected type returned by navigator.credentials.create(): expected "public-key", got "${credentials?.type}"`); } return await registerCredentialForAccount(name, data.data.token_id, credentials); }; // eslint-disable-next-line camelcase const authenticateSecurityKey = async (data: { confirmation_token: string; data: string }): Promise => { const response = await http.post('/auth/login/checkpoint/key', data); return { complete: response.data.complete, intended: response.data.data?.intended || null, }; }; export { useSecurityKeys, deleteSecurityKey, registerSecurityKey, authenticateSecurityKey };