1- import { createHmac , timingSafeEqual } from 'crypto'
1+ import { createHash , createHmac , timingSafeEqual } from 'crypto'
22import type { NextRequest , NextResponse } from 'next/server'
33import { env } from '@/lib/core/config/env'
44import { isDev } from '@/lib/core/config/feature-flags'
@@ -12,16 +12,31 @@ function signPayload(payload: string): string {
1212 return createHmac ( 'sha256' , env . BETTER_AUTH_SECRET ) . update ( payload ) . digest ( 'hex' )
1313}
1414
15- function generateAuthToken ( deploymentId : string , type : string ) : string {
16- const payload = `${ deploymentId } :${ type } :${ Date . now ( ) } `
15+ function passwordSlot ( encryptedPassword ?: string | null ) : string {
16+ if ( ! encryptedPassword ) return ''
17+ return createHash ( 'sha256' ) . update ( encryptedPassword ) . digest ( 'hex' ) . slice ( 0 , 8 )
18+ }
19+
20+ function generateAuthToken (
21+ deploymentId : string ,
22+ type : string ,
23+ encryptedPassword ?: string | null
24+ ) : string {
25+ const payload = `${ deploymentId } :${ type } :${ Date . now ( ) } :${ passwordSlot ( encryptedPassword ) } `
1726 const sig = signPayload ( payload )
1827 return Buffer . from ( `${ payload } :${ sig } ` ) . toString ( 'base64' )
1928}
2029
2130/**
22- * Validates an HMAC-signed authentication token for a deployment (chat or form)
31+ * Validates an HMAC-signed authentication token for a deployment (chat or form).
32+ * Includes a password-derived slot so changing the deployment password immediately
33+ * invalidates existing sessions.
2334 */
24- export function validateAuthToken ( token : string , deploymentId : string ) : boolean {
35+ export function validateAuthToken (
36+ token : string ,
37+ deploymentId : string ,
38+ encryptedPassword ?: string | null
39+ ) : boolean {
2540 try {
2641 const decoded = Buffer . from ( token , 'base64' ) . toString ( )
2742 const lastColon = decoded . lastIndexOf ( ':' )
@@ -39,10 +54,14 @@ export function validateAuthToken(token: string, deploymentId: string): boolean
3954 }
4055
4156 const parts = payload . split ( ':' )
42- const [ storedId , _type , timestamp ] = parts
57+ if ( parts . length < 4 ) return false
58+ const [ storedId , _type , timestamp , storedPwSlot ] = parts
4359
4460 if ( storedId !== deploymentId ) return false
4561
62+ const expectedPwSlot = passwordSlot ( encryptedPassword )
63+ if ( storedPwSlot !== expectedPwSlot ) return false
64+
4665 const createdAt = Number . parseInt ( timestamp )
4766 const expireTime = 24 * 60 * 60 * 1000
4867 if ( Date . now ( ) - createdAt > expireTime ) return false
@@ -60,9 +79,10 @@ export function setDeploymentAuthCookie(
6079 response : NextResponse ,
6180 cookiePrefix : 'chat' | 'form' ,
6281 deploymentId : string ,
63- authType : string
82+ authType : string ,
83+ encryptedPassword ?: string | null
6484) : void {
65- const token = generateAuthToken ( deploymentId , authType )
85+ const token = generateAuthToken ( deploymentId , authType , encryptedPassword )
6686 response . cookies . set ( {
6787 name : `${ cookiePrefix } _auth_${ deploymentId } ` ,
6888 value : token ,
0 commit comments