11import type { IncomingMessage , ServerResponse } from "node:http" ;
22import { generateId , json } from "@delego/utils" ;
33import * as authService from "../src/auth/authService.js" ;
4+ import * as oauthService from "../src/auth/oauthService.js" ;
45import {
56 publishAuthAuditEvent ,
67 AUTH_AUDIT_ACTIONS ,
@@ -9,6 +10,7 @@ import {
910 validateSchema ,
1011 RegisterSchema ,
1112 LoginSchema ,
13+ OAuthCallbackSchema ,
1214} from "../src/validation.js" ;
1315import {
1416 readJsonBody ,
@@ -27,6 +29,9 @@ export const authDependencies = {
2729 loginUser : authService . loginUser ,
2830 refreshAccessToken : authService . refreshAccessToken ,
2931 logoutUser : authService . logoutUser ,
32+ handleOAuthCallback : oauthService . handleOAuthCallback ,
33+ buildAuthorizationUrl : oauthService . buildAuthorizationUrl ,
34+ validateProvider : oauthService . validateProvider ,
3035} ;
3136
3237function resolveRequestId ( req : IncomingMessage ) : string {
@@ -271,3 +276,86 @@ export async function logoutHandler(
271276 error : null ,
272277 } ) ;
273278}
279+
280+ export async function oauthCallbackHandler (
281+ req : IncomingMessage ,
282+ res : ServerResponse ,
283+ ) : Promise < void > {
284+ const requestId = resolveRequestId ( req ) ;
285+
286+ try {
287+ const body = await readJsonBody ( req ) ;
288+ const validation = validateSchema ( OAuthCallbackSchema , body ) ;
289+ if ( ! validation . valid ) {
290+ publishAuthAuditEvent ( {
291+ action : AUTH_AUDIT_ACTIONS . OAUTH_LOGIN ,
292+ success : false ,
293+ requestId,
294+ } ) ;
295+ badRequest ( res , "Invalid request body" , req , validation . errors ) ;
296+ return ;
297+ }
298+
299+ const { provider, code, state } = body ;
300+ const redirectUri = process . env . OAUTH_REDIRECT_URI ?? "" ;
301+
302+ const result = await authDependencies . handleOAuthCallback ( provider , code , redirectUri ) ;
303+
304+ publishAuthAuditEvent ( {
305+ action : result . isNewUser ? AUTH_AUDIT_ACTIONS . OAUTH_REGISTER : AUTH_AUDIT_ACTIONS . OAUTH_LOGIN ,
306+ success : true ,
307+ requestId,
308+ userId : result . user . id ,
309+ email : result . user . email ,
310+ } ) ;
311+
312+ setRefreshTokenCookie ( res , result . refreshToken ) ;
313+ json ( res , 200 , {
314+ data : {
315+ user : result . user ,
316+ accessToken : result . accessToken ,
317+ expiresIn : result . expiresIn ,
318+ isNewUser : result . isNewUser ,
319+ } ,
320+ error : null ,
321+ } ) ;
322+ } catch ( err : any ) {
323+ publishAuthAuditEvent ( {
324+ action : AUTH_AUDIT_ACTIONS . OAUTH_LOGIN ,
325+ success : false ,
326+ requestId,
327+ } ) ;
328+ if ( err instanceof InvalidJsonError || err instanceof BodyTooLargeError ) {
329+ badRequest ( res , err . message , req ) ;
330+ } else {
331+ sendApiError ( res , 400 , "OAUTH_ERROR" , err . message , req ) ;
332+ }
333+ }
334+ }
335+
336+ export async function oauthAuthorizeHandler (
337+ req : IncomingMessage ,
338+ res : ServerResponse ,
339+ ) : Promise < void > {
340+ const url = new URL ( req . url ?? "/" , `http://${ req . headers . host ?? "localhost" } ` ) ;
341+ const provider = url . searchParams . get ( "provider" ) ;
342+ const redirectUri = url . searchParams . get ( "redirect_uri" ) ?? process . env . OAUTH_REDIRECT_URI ?? "" ;
343+
344+ if ( ! provider ) {
345+ sendApiError ( res , 400 , "VALIDATION_ERROR" , "provider query parameter is required" , req ) ;
346+ return ;
347+ }
348+
349+ try {
350+ const validatedProvider = authDependencies . validateProvider ( provider ) ;
351+ const state = generateId ( ) ;
352+ const authorizationUrl = authDependencies . buildAuthorizationUrl ( validatedProvider , redirectUri , state ) ;
353+
354+ json ( res , 200 , {
355+ data : { authorizationUrl, state } ,
356+ error : null ,
357+ } ) ;
358+ } catch ( err : any ) {
359+ sendApiError ( res , 400 , "INVALID_PROVIDER" , err . message , req ) ;
360+ }
361+ }
0 commit comments