@@ -80,22 +80,19 @@ function credentialFromURL(url: URL, request: HttpServerRequest.HttpServerReques
8080 return Effect . succeed ( emptyCredential ( ) )
8181}
8282
83- function validateRawCredential < A , E , R > (
84- effect : Effect . Effect < A , E , R > ,
85- credential : ServerAuth . DecodedCredentials ,
86- config : ServerAuth . Info ,
87- ) {
88- if ( ! ServerAuth . required ( config ) ) return effect
89- if ( ! ServerAuth . authorized ( credential , config ) )
90- return Effect . succeed (
91- HttpServerResponse . empty ( {
92- status : UNAUTHORIZED ,
93- headers : { "www-authenticate" : WWW_AUTHENTICATE } ,
94- } ) ,
95- )
96- return effect
83+ function setCookieHeader (
84+ response : HttpServerResponse . HttpServerResponse ,
85+ token : string ,
86+ ) : HttpServerResponse . HttpServerResponse {
87+ return HttpServerResponse . setHeader (
88+ response ,
89+ "set-cookie" ,
90+ `${ AUTH_TOKEN_COOKIE } =${ token } ; Path=/; HttpOnly; SameSite=Strict` ,
91+ )
9792}
9893
94+ // Router middleware for all routes except the SPA catch-all. Requires auth for
95+ // non-public paths and sets a persistent cookie when auth_token is in the URL.
9996export const authorizationRouterMiddleware = HttpRouter . middleware ( ) (
10097 Effect . gen ( function * ( ) {
10198 const config = yield * ServerAuth . Config
@@ -109,20 +106,41 @@ export const authorizationRouterMiddleware = HttpRouter.middleware()(
109106 if ( hasPtyConnectTicketURL ( url ) ) return yield * effect
110107 const token = url . searchParams . get ( AUTH_TOKEN_QUERY )
111108 const credential = yield * credentialFromURL ( url , request )
112- // When auth_token comes via URL and is valid, set a persistent cookie so the
113- // browser can navigate to SPA subpaths without repeating the query param.
114- if ( token && ServerAuth . authorized ( credential , config ) ) {
115- yield * HttpEffect . appendPreResponseHandler ( ( _req , response ) =>
116- Effect . succeed (
117- HttpServerResponse . setHeader (
118- response ,
119- "set-cookie" ,
120- `${ AUTH_TOKEN_COOKIE } =${ token } ; Path=/; HttpOnly; SameSite=Strict` ,
121- ) ,
122- ) ,
123- )
109+ if ( ! ServerAuth . authorized ( credential , config ) ) {
110+ return HttpServerResponse . empty ( {
111+ status : UNAUTHORIZED ,
112+ headers : { "www-authenticate" : WWW_AUTHENTICATE } ,
113+ } )
124114 }
125- return yield * validateRawCredential ( effect , credential , config )
115+ // Auth passed — get the response and attach cookie if token came via URL.
116+ // Direct header manipulation avoids appendPreResponseHandler which does not
117+ // fire reliably in the raw router middleware context.
118+ const response = yield * ( effect as unknown as Effect . Effect < HttpServerResponse . HttpServerResponse , never , never > )
119+ return token ? setCookieHeader ( response , token ) : response
120+ } )
121+ } ) ,
122+ )
123+
124+ // Router middleware for the SPA catch-all route (/*). Always serves content so
125+ // the browser can load the app shell at any subpath without a credential prompt.
126+ // API routes have their own auth layer; the SPA handles auth internally once loaded.
127+ // Sets a persistent cookie when a valid auth_token query param is supplied so that
128+ // subsequent SPA navigation (without the query param) remains authenticated.
129+ export const uiRouterMiddleware = HttpRouter . middleware ( ) (
130+ Effect . gen ( function * ( ) {
131+ const config = yield * ServerAuth . Config
132+ if ( ! ServerAuth . required ( config ) ) return ( effect ) => effect
133+
134+ return ( effect ) =>
135+ Effect . gen ( function * ( ) {
136+ const request = yield * HttpServerRequest . HttpServerRequest
137+ const url = new URL ( request . url , "http://localhost" )
138+ const token = url . searchParams . get ( AUTH_TOKEN_QUERY )
139+ // Always serve — the SPA handles auth internally via stored credentials.
140+ const response = yield * ( effect as unknown as Effect . Effect < HttpServerResponse . HttpServerResponse , never , never > )
141+ if ( ! token ) return response
142+ const credential = yield * credentialFromURL ( url , request )
143+ return ServerAuth . authorized ( credential , config ) ? setCookieHeader ( response , token ) : response
126144 } )
127145 } ) ,
128146)
0 commit comments