@@ -4,6 +4,22 @@ import type { FastifyReply, FastifyRequest } from 'fastify';
44import { z } from 'zod' ;
55import { LogError } from '@/utils/errors' ;
66
7+ const OAUTH_SENSITIVE_KEYS = [ 'code' , 'state' ] ;
8+
9+ function sanitizeOAuthQuery (
10+ query : Record < string , unknown > | null | undefined
11+ ) : Record < string , string > {
12+ if ( ! query || typeof query !== 'object' ) {
13+ return { } ;
14+ }
15+ return Object . fromEntries (
16+ Object . entries ( query ) . map ( ( [ k , v ] ) => [
17+ k ,
18+ OAUTH_SENSITIVE_KEYS . includes ( k ) ? '<redacted>' : String ( v ) ,
19+ ] )
20+ ) ;
21+ }
22+
723export async function gscGoogleCallback (
824 req : FastifyRequest ,
925 reply : FastifyReply
@@ -16,27 +32,35 @@ export async function gscGoogleCallback(
1632
1733 const query = schema . safeParse ( req . query ) ;
1834 if ( ! query . success ) {
19- throw new LogError ( 'Invalid GSC callback query params' , {
20- error : query . error ,
21- query : req . query ,
22- } ) ;
35+ throw new LogError (
36+ 'Invalid GSC callback query params' ,
37+ sanitizeOAuthQuery ( req . query as Record < string , unknown > )
38+ ) ;
2339 }
2440
2541 const { code, state } = query . data ;
2642 const storedState = req . cookies . gsc_oauth_state ?? null ;
2743 const codeVerifier = req . cookies . gsc_code_verifier ?? null ;
2844 const projectId = req . cookies . gsc_project_id ?? null ;
2945
30- if ( ! storedState || ! codeVerifier || ! projectId ) {
46+ const hasStoredState = storedState !== null ;
47+ const hasCodeVerifier = codeVerifier !== null ;
48+ const hasProjectId = projectId !== null ;
49+ const hasAllCookies = hasStoredState && hasCodeVerifier && hasProjectId ;
50+ if ( ! hasAllCookies ) {
3151 throw new LogError ( 'Missing GSC OAuth cookies' , {
32- storedState : storedState === null ,
33- codeVerifier : codeVerifier === null ,
34- projectId : projectId === null ,
52+ storedState : ! hasStoredState ,
53+ codeVerifier : ! hasCodeVerifier ,
54+ projectId : ! hasProjectId ,
3555 } ) ;
3656 }
3757
3858 if ( state !== storedState ) {
39- throw new LogError ( 'GSC OAuth state mismatch' , { state, storedState } ) ;
59+ throw new LogError ( 'GSC OAuth state mismatch' , {
60+ hasState : true ,
61+ hasStoredState : true ,
62+ stateMismatch : true ,
63+ } ) ;
4064 }
4165
4266 const tokens = await googleGsc . validateAuthorizationCode (
@@ -91,6 +115,9 @@ export async function gscGoogleCallback(
91115 return reply . redirect ( redirectUrl ) ;
92116 } catch ( error ) {
93117 req . log . error ( error ) ;
118+ reply . clearCookie ( 'gsc_oauth_state' ) ;
119+ reply . clearCookie ( 'gsc_code_verifier' ) ;
120+ reply . clearCookie ( 'gsc_project_id' ) ;
94121 return redirectWithError ( reply , error ) ;
95122 }
96123}
0 commit comments