import logger , { setLogger } from "../lib/logger" ; import * as routes from "./routes" ; import renderPage from "./pages" ; import { init } from "./init" ; import { assertConfig } from "./lib/assert" ; import { SessionStore } from "./lib/cookie" ; import type { NextAuthOptions } from "./types" ; import type { NextAuthAction } from "../lib/types" ; import type { Cookie } from "./lib/cookie" ; import type { ErrorType } from "./pages/error" ; export interface IncomingRequest { default host ? : string ; method ? : string ; cookies ? : Record < string , string > ; headers ? : Record < string , any > ; query ? : Record < string , any > ; body ? : Record < string , any > ; action : NextAuthAction ; providerId ? : string ; error ? : string ; } export interface NextAuthHeader { key : string ; value : string ; } export interface OutgoingResponse < Body extends string | Record < string , any > | any [ ] = any > { status ? : number ; headers ? : NextAuthHeader [ ] ; body ? : Body ; redirect ? : string ; cookies ? : Cookie [ ] ; } export interface NextAuthHandlerParams { req : IncomingRequest ; options : NextAuthOptions ; } export async function NextAuthHandler < Body extends string | Record < string , any > | any [ ] > ( params ) { const { options : userOptions , req } = params ; setLogger ( userOptions . logger , userOptions . debug ) ; const assertionResult = assertConfig ( params ) ; if ( typeof assertionResult === "string" ) { logger . warn ( assertionResult ) ; } else if ( assertionResult instanceof Error ) { const { pages , theme } = userOptions ; logger . error ( assertionResult . code , assertionResult ) ; if ( pages ?. error ) { return { redirect : `${ pages . error }?error=Configuration` , } ; } const render = renderPage ( { theme } ) ; return render . error ( { error : "configuration" } ) ; } const { action , providerId , error , method = "GET" } = req ; const { options , cookies } = await init ( { userOptions , action , providerId , host : req . host , callbackUrl : req . body ?. callbackUrl ?? req . query ?. callbackUrl , csrfToken : req . body ?. csrfToken , cookies : req . cookies , isPost : method === "POST" , } ) ; const sessionStore = new SessionStore ( options . cookies . sessionToken , req , options . logger ) ; if ( method === "GET" ) { const render = renderPage ( { ... options , query : req . query , cookies } ) ; const { pages } = options ; switch ( action ) { case "providers" : return ( await routes . providers ( options . providers ) ) as any ; case "session" : { const session = await routes . session ( { options , sessionStore } ) ; if ( session . cookies ) cookies . push ( ... session . cookies ) ; return { ... session , cookies } as any ; } case "csrf" : return { headers : [ { key : "Content-Type" , value : "application/json" } ] , body : { csrfToken : options . csrfToken } as any , cookies , } ; case "signin" : if ( pages . signIn ) { let signinUrl = `${ pages . signIn }${ pages . signIn . includes ( "?" ) ? "&" : "?" }callbackUrl=${ options . callbackUrl }` ; if ( error ) signinUrl = `${ signinUrl }&error=${ error }` ; return { redirect : signinUrl , cookies } ; } return render . signin ( ) ; case "signout" : if ( pages . signOut ) return { redirect : pages . signOut , cookies } ; return render . signout ( ) ; case "callback" : if ( options . provider ) { const callback = await routes . callback ( { body : req . body , query : req . query , headers : req . headers , cookies : req . cookies , method , options , sessionStore , } ) ; if ( callback . cookies ) cookies . push ( ... callback . cookies ) ; return { ... callback , cookies } ; } break ; case "verify-request" : if ( pages . verifyRequest ) { return { redirect : pages . verifyRequest , cookies } ; } return render . verifyRequest ( ) ; case "error" : if ( [ "Signin" , "OAuthSignin" , "OAuthCallback" , "OAuthCreateAccount" , "EmailCreateAccount" , "Callback" , "OAuthAccountNotLinked" , "EmailSignin" , "CredentialsSignin" , "SessionRequired" , ] . includes ( error as string ) ) { return { redirect : `${ options . url }/signin?error=${ error }` , cookies } ; } if ( pages . error ) { return { redirect : `${ pages . error }${ pages . error . includes ( "?" ) ? "&" : "?" }error=${ error }` , cookies , } ; } return render . error ( { error : error as ErrorType } ) ; default : } } else if ( method === "POST" ) { switch ( action ) { case "signin" : if ( options . csrfTokenVerified && options . provider ) { const signin = await routes . signin ( { query : req . query , body : req . body , options , } ) ; if ( signin . cookies ) cookies . push ( ... signin . cookies ) ; return { ... signin , cookies } ; } return { redirect : `${ options . url }/signin?csrf=true` , cookies } ; case "signout" : if ( options . csrfTokenVerified ) { const signout = await routes . signout ( { options , sessionStore } ) ; if ( signout . cookies ) cookies . push ( ... signout . cookies ) ; return { ... signout , cookies } ; } return { redirect : `${ options . url }/signout?csrf=true` , cookies } ; case "callback" : if ( options . provider ) { if ( options . provider . type === "credentials" && ! options . csrfTokenVerified ) { return { redirect : `${ options . url }/signin?csrf=true` , cookies } ; } const callback = await routes . callback ( { body : req . body , query : req . query , headers : req . headers , cookies : req . cookies , method , options , sessionStore , } ) ; if ( callback . cookies ) cookies . push ( ... callback . cookies ) ; return { ... callback , cookies } ; } break ; case "_log" : if ( userOptions . logger ) { try { const { code , level , ... metadata } = req . body ?? { } ; logger [ level ] ( code , metadata ) ; } catch ( error ) { logger . error ( "LOGGER_ERROR" , error as Error ) ; } } return { } ; default : } } return { status : 400 , body : `Error: Action ${ action } with HTTP ${ method } is not supported by NextAuth.js` as any , } ; }