Add fingerprint to user requests

pull/3813/head
Kamran Ahmed 2 years ago
parent 7dbb00a306
commit 0da417735e
  1. 1
      package.json
  2. 8
      pnpm-lock.yaml
  3. 47
      src/components/AuthenticationFlow/EmailSignupForm.tsx
  4. 20
      src/lib/http.ts

@ -23,6 +23,7 @@
"@astrojs/preact": "^2.1.0", "@astrojs/preact": "^2.1.0",
"@astrojs/sitemap": "^1.2.2", "@astrojs/sitemap": "^1.2.2",
"@astrojs/tailwind": "^3.1.1", "@astrojs/tailwind": "^3.1.1",
"@fingerprintjs/fingerprintjs": "^3.4.1",
"@nanostores/preact": "^0.3.1", "@nanostores/preact": "^0.3.1",
"astro": "^2.2.3", "astro": "^2.2.3",
"astro-compress": "^1.1.35", "astro-compress": "^1.1.35",

@ -4,6 +4,7 @@ specifiers:
'@astrojs/preact': ^2.1.0 '@astrojs/preact': ^2.1.0
'@astrojs/sitemap': ^1.2.2 '@astrojs/sitemap': ^1.2.2
'@astrojs/tailwind': ^3.1.1 '@astrojs/tailwind': ^3.1.1
'@fingerprintjs/fingerprintjs': ^3.4.1
'@nanostores/preact': ^0.3.1 '@nanostores/preact': ^0.3.1
'@playwright/test': ^1.32.3 '@playwright/test': ^1.32.3
'@tailwindcss/typography': ^0.5.9 '@tailwindcss/typography': ^0.5.9
@ -31,6 +32,7 @@ dependencies:
'@astrojs/preact': 2.1.0_preact@10.13.2 '@astrojs/preact': 2.1.0_preact@10.13.2
'@astrojs/sitemap': 1.2.2 '@astrojs/sitemap': 1.2.2
'@astrojs/tailwind': 3.1.1_ooi4vwztktmr2nba6g3hokplre '@astrojs/tailwind': 3.1.1_ooi4vwztktmr2nba6g3hokplre
'@fingerprintjs/fingerprintjs': 3.4.1
'@nanostores/preact': 0.3.1_ntvucyavaortwycasiweu74jd4 '@nanostores/preact': 0.3.1_ntvucyavaortwycasiweu74jd4
astro: 2.2.3 astro: 2.2.3
astro-compress: 1.1.35 astro-compress: 1.1.35
@ -634,6 +636,12 @@ packages:
dev: false dev: false
optional: true optional: true
/@fingerprintjs/fingerprintjs/3.4.1:
resolution: {integrity: sha512-i3TqlaIdF+4qDHP6OStMtLXCnvnoo2C156rNPm9/kIglUtnLAF45+sGkZZmVQB+58dDF0mNpaiqvKHNSR8J1Hg==}
dependencies:
tslib: 2.5.0
dev: false
/@gar/promisify/1.1.3: /@gar/promisify/1.1.3:
resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==}
dev: false dev: false

@ -1,5 +1,6 @@
import type { FunctionComponent } from 'preact'; import type { FunctionComponent } from 'preact';
import { useState } from 'preact/hooks'; import { useState } from 'preact/hooks';
import { httpPost } from '../../lib/http';
const EmailSignupForm: FunctionComponent = () => { const EmailSignupForm: FunctionComponent = () => {
const [email, setEmail] = useState(''); const [email, setEmail] = useState('');
@ -9,43 +10,33 @@ const EmailSignupForm: FunctionComponent = () => {
const [error, setError] = useState(''); const [error, setError] = useState('');
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const handleRegisterResponse = async (res: Response) => { const onSubmit = async (e: Event) => {
const json = await res.json();
if (!res.ok) {
setError(json.message || 'Something went wrong. Please try again later.');
setIsLoading(false);
return;
}
window.location.href = `/verification-pending?email=${encodeURIComponent(
email
)}`;
};
const onSubmit = (e: Event) => {
e.preventDefault(); e.preventDefault();
setIsLoading(true); setIsLoading(true);
setError(''); setError('');
fetch(`${import.meta.env.PUBLIC_API_URL}/v1-register`, { const { response, error } = await httpPost<{ status: 'ok' }>(
method: 'POST', `${import.meta.env.PUBLIC_API_URL}/v1-register`,
headers: { {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email, email,
password, password,
name, name,
}), }
}) );
.then(handleRegisterResponse)
.catch((err) => { if (error || response?.status !== 'ok') {
setIsLoading(false); setIsLoading(false);
setError('Something went wrong. Please try again later.'); setError(
}); error?.message || 'Something went wrong. Please try again later.'
);
return;
}
window.location.href = `/verification-pending?email=${encodeURIComponent(
email
)}`;
}; };
return ( return (

@ -1,12 +1,14 @@
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import fp from '@fingerprintjs/fingerprintjs';
import { TOKEN_COOKIE_NAME } from './jwt'; import { TOKEN_COOKIE_NAME } from './jwt';
type HttpOptionsType = RequestInit | { headers: Record<string, any> };
type AppResponse = Record<string, any>; type AppResponse = Record<string, any>;
type FetchError = { type FetchError = {
status: number; status: number;
message: string; message: string;
}; };
type AppError = { type AppError = {
status: number; status: number;
message: string; message: string;
@ -29,9 +31,12 @@ export async function httpCall<
ErrorType = AppError ErrorType = AppError
>( >(
url: string, url: string,
options?: RequestInit options?: HttpOptionsType
): Promise<ApiReturn<ResponseType, ErrorType>> { ): Promise<ApiReturn<ResponseType, ErrorType>> {
try { try {
const fingerprintPromise = await fp.load({ monitoring: false });
const fingerprint = await fingerprintPromise.get();
const response = await fetch(url, { const response = await fetch(url, {
credentials: 'include', credentials: 'include',
...options, ...options,
@ -39,6 +44,7 @@ export async function httpCall<
'Content-Type': 'application/json', 'Content-Type': 'application/json',
Accept: 'application/json', Accept: 'application/json',
Authorization: `Bearer ${Cookies.get(TOKEN_COOKIE_NAME)}`, Authorization: `Bearer ${Cookies.get(TOKEN_COOKIE_NAME)}`,
'x-fp': fingerprint.visitorId,
...(options?.headers ?? {}), ...(options?.headers ?? {}),
}), }),
}); });
@ -82,7 +88,7 @@ export async function httpPost<
>( >(
url: string, url: string,
body: Record<string, any>, body: Record<string, any>,
options?: RequestInit options?: HttpOptionsType
): Promise<ApiReturn<ResponseType, ErrorType>> { ): Promise<ApiReturn<ResponseType, ErrorType>> {
return httpCall<ResponseType, ErrorType>(url, { return httpCall<ResponseType, ErrorType>(url, {
...options, ...options,
@ -94,7 +100,7 @@ export async function httpPost<
export async function httpGet<ResponseType = AppResponse, ErrorType = AppError>( export async function httpGet<ResponseType = AppResponse, ErrorType = AppError>(
url: string, url: string,
queryParams?: Record<string, any>, queryParams?: Record<string, any>,
options?: RequestInit options?: HttpOptionsType
): Promise<ApiReturn<ResponseType, ErrorType>> { ): Promise<ApiReturn<ResponseType, ErrorType>> {
const searchParams = new URLSearchParams(queryParams).toString(); const searchParams = new URLSearchParams(queryParams).toString();
const queryUrl = searchParams ? `${url}?${searchParams}` : url; const queryUrl = searchParams ? `${url}?${searchParams}` : url;
@ -112,7 +118,7 @@ export async function httpPatch<
>( >(
url: string, url: string,
body: Record<string, any>, body: Record<string, any>,
options?: RequestInit options?: HttpOptionsType
): Promise<ApiReturn<ResponseType, ErrorType>> { ): Promise<ApiReturn<ResponseType, ErrorType>> {
return httpCall<ResponseType, ErrorType>(url, { return httpCall<ResponseType, ErrorType>(url, {
...options, ...options,
@ -124,7 +130,7 @@ export async function httpPatch<
export async function httpPut<ResponseType = AppResponse, ErrorType = AppError>( export async function httpPut<ResponseType = AppResponse, ErrorType = AppError>(
url: string, url: string,
body: Record<string, any>, body: Record<string, any>,
options?: RequestInit options?: HttpOptionsType
): Promise<ApiReturn<ResponseType, ErrorType>> { ): Promise<ApiReturn<ResponseType, ErrorType>> {
return httpCall<ResponseType, ErrorType>(url, { return httpCall<ResponseType, ErrorType>(url, {
...options, ...options,
@ -138,7 +144,7 @@ export async function httpDelete<
ErrorType = AppError ErrorType = AppError
>( >(
url: string, url: string,
options?: RequestInit options?: HttpOptionsType
): Promise<ApiReturn<ResponseType, ErrorType>> { ): Promise<ApiReturn<ResponseType, ErrorType>> {
return httpCall<ResponseType, ErrorType>(url, { return httpCall<ResponseType, ErrorType>(url, {
...options, ...options,

Loading…
Cancel
Save