From e19fb6eb22f3c2aeb98d901dbc85eb9820c328f0 Mon Sep 17 00:00:00 2001 From: Juraj Uhlar Date: Tue, 15 Oct 2024 10:04:35 +0200 Subject: [PATCH 1/3] fix: allow GET in event endpoint --- src/app/api/event/[requestId]/route.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/app/api/event/[requestId]/route.ts b/src/app/api/event/[requestId]/route.ts index 1410981c..95a34035 100644 --- a/src/app/api/event/[requestId]/route.ts +++ b/src/app/api/event/[requestId]/route.ts @@ -30,10 +30,18 @@ export async function OPTIONS(request: NextRequest) { return new NextResponse(null, { status: 204 }); } -// Main handler export async function POST(request: NextRequest, { params }: { params: { requestId: string } }) { + return await handleRequest(request, params.requestId); +} + +// For backward compatibility with mobile applications, accept GET requests as well +export async function GET(request: NextRequest, { params }: { params: { requestId: string } }) { + return await handleRequest(request, params.requestId); +} + +// Main handler +const handleRequest = async (request: NextRequest, requestId: string | undefined | null) => { const origin = request.headers.get('origin'); - const requestId = params.requestId; // In production, validate the origin if (IS_PRODUCTION && (!origin || !allowedOrigins.includes(origin))) { @@ -59,7 +67,7 @@ export async function POST(request: NextRequest, { params }: { params: { request } return NextResponse.json(result.data, { headers: getCorsHeaders(origin) }); -} +}; async function tryGetFingerprintEvent( requestId: string, From 1a22d6714a6fe49f26261afc9f82fe24ba47099c Mon Sep 17 00:00:00 2001 From: Juraj Uhlar Date: Tue, 15 Oct 2024 10:36:49 +0200 Subject: [PATCH 2/3] chore: unify error responses --- src/app/api/event/[requestId]/route.ts | 48 ++++++++++++-------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/src/app/api/event/[requestId]/route.ts b/src/app/api/event/[requestId]/route.ts index 95a34035..06b190bc 100644 --- a/src/app/api/event/[requestId]/route.ts +++ b/src/app/api/event/[requestId]/route.ts @@ -1,6 +1,6 @@ import { NextRequest, NextResponse } from 'next/server'; import { EventResponse, isEventError } from '@fingerprintjs/fingerprintjs-pro-server-api'; -import { OUR_ORIGINS } from '../../../../server/checks'; +import { OUR_ORIGINS, Severity } from '../../../../server/checks'; import { IS_PRODUCTION } from '../../../../envShared'; import { fingerprintServerApiClient } from '../../../../server/fingerprint-server-api'; @@ -10,12 +10,14 @@ const allowedOrigins = [...OUR_ORIGINS, 'https://dev.fingerprint.com']; // Handle CORS const getCorsHeaders = (origin: string | null) => ({ 'Access-Control-Allow-Origin': String(origin), - 'Access-Control-Allow-Methods': 'POST, OPTIONS', + 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type', }); type CorsHeaders = ReturnType; +type GetEventPayload = EventResponse | { severity: Severity; message: string }; + export async function OPTIONS(request: NextRequest) { const origin = request.headers.get('origin'); @@ -40,24 +42,25 @@ export async function GET(request: NextRequest, { params }: { params: { requestI } // Main handler -const handleRequest = async (request: NextRequest, requestId: string | undefined | null) => { +const handleRequest = async ( + request: NextRequest, + requestId: string | undefined | null, +): Promise> => { const origin = request.headers.get('origin'); // In production, validate the origin if (IS_PRODUCTION && (!origin || !allowedOrigins.includes(origin))) { - return new NextResponse(null, { - status: 403, - statusText: `Origin "${origin}" is not allowed to call this endpoint`, - headers: getCorsHeaders(origin), - }); + return NextResponse.json( + { severity: 'error', message: `Origin "${origin}" is not allowed to call this endpoint,` }, + { status: 403, headers: getCorsHeaders(origin) }, + ); } if (!requestId) { - return new NextResponse(null, { - status: 400, - statusText: 'Missing requestId parameter', - headers: getCorsHeaders(origin), - }); + return NextResponse.json( + { severity: 'error', message: 'Missing `requestId` parameter.' }, + { status: 400, headers: getCorsHeaders(origin) }, + ); } const result = await tryGetFingerprintEvent(requestId); @@ -89,21 +92,16 @@ async function tryGetFingerprintEvent( } } -function sendErrorResponse(error: unknown, corsHeaders: CorsHeaders): NextResponse { +function sendErrorResponse(error: unknown, corsHeaders: CorsHeaders): NextResponse { if (isEventError(error)) { return NextResponse.json( - { message: error.message, code: error.errorCode }, - { - status: error.statusCode, - statusText: `${error.errorCode} - ${error.message}`, - headers: corsHeaders, - }, + { message: error.message, severity: 'error' }, + { status: error.statusCode, headers: corsHeaders }, ); } else { - return new NextResponse(null, { - status: 500, - statusText: `Something went wrong ${error}`, - headers: corsHeaders, - }); + return NextResponse.json( + { message: `Something went wrong ${error}`, severity: 'error' }, + { status: 500, headers: corsHeaders }, + ); } } From 4b80d4719c4766e88181818d861229a8b3baee71 Mon Sep 17 00:00:00 2001 From: Juraj Uhlar Date: Tue, 15 Oct 2024 10:56:29 +0200 Subject: [PATCH 3/3] chore: improve error responses --- src/app/api/event/[requestId]/route.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/app/api/event/[requestId]/route.ts b/src/app/api/event/[requestId]/route.ts index 06b190bc..dd5f46e3 100644 --- a/src/app/api/event/[requestId]/route.ts +++ b/src/app/api/event/[requestId]/route.ts @@ -50,16 +50,18 @@ const handleRequest = async ( // In production, validate the origin if (IS_PRODUCTION && (!origin || !allowedOrigins.includes(origin))) { + const message = `Origin "${origin}" is not allowed to call this endpoint,`; return NextResponse.json( - { severity: 'error', message: `Origin "${origin}" is not allowed to call this endpoint,` }, - { status: 403, headers: getCorsHeaders(origin) }, + { severity: 'error', message }, + { status: 403, statusText: message, headers: getCorsHeaders(origin) }, ); } if (!requestId) { + const message = 'Missing `requestId` parameter.'; return NextResponse.json( - { severity: 'error', message: 'Missing `requestId` parameter.' }, - { status: 400, headers: getCorsHeaders(origin) }, + { severity: 'error', message }, + { status: 400, statusText: message, headers: getCorsHeaders(origin) }, ); } @@ -96,12 +98,13 @@ function sendErrorResponse(error: unknown, corsHeaders: CorsHeaders): NextRespon if (isEventError(error)) { return NextResponse.json( { message: error.message, severity: 'error' }, - { status: error.statusCode, headers: corsHeaders }, + { status: error.statusCode, statusText: error.message, headers: corsHeaders }, ); } else { + const message = `Something went wrong ${error}`; return NextResponse.json( - { message: `Something went wrong ${error}`, severity: 'error' }, - { status: 500, headers: corsHeaders }, + { message, severity: 'error' }, + { status: 500, statusText: message, headers: corsHeaders }, ); } }