@@ -275,6 +275,7 @@ if (!playwright) {
275275 try {
276276 let authUrlCalls = 0 ;
277277 let signinupBody = null ;
278+ let signinupHeaders = null ;
278279
279280 // The provider, stubbed: send the browser straight back to our own
280281 // callback with a code, so nothing external is contacted.
@@ -302,6 +303,7 @@ if (!playwright) {
302303 // the app genuinely transitions to its authenticated view.
303304 await page . route ( '**/auth/signinup' , async ( route ) => {
304305 signinupBody = JSON . parse ( route . request ( ) . postData ( ) || '{}' ) ;
306+ signinupHeaders = route . request ( ) . headers ( ) ;
305307 await context . addCookies ( [ {
306308 name : COOKIE_NAME , value : cookieFor ( user ) , url : BASE_URL , httpOnly : true ,
307309 } ] ) ;
@@ -341,6 +343,14 @@ if (!playwright) {
341343 // Never oAuthTokens - the server refuses those (rejectRawOAuthTokens).
342344 assert ( ! signinupBody . oAuthTokens , 'the client must not submit raw oAuthTokens' ) ;
343345
346+ // Without this header SuperTokens returns the session in response
347+ // headers instead of cookies, signinup still says OK, and the next
348+ // /api/me is a 401. That is what v1.9.0 shipped.
349+ assert (
350+ signinupHeaders [ 'st-auth-mode' ] === 'cookie' ,
351+ `signinup must ask for cookie transport, got ${ signinupHeaders [ 'st-auth-mode' ] } ` ,
352+ ) ;
353+
344354 // The spent code must be replaced out of the URL: a reload that re-POSTs
345355 // a burned authorisation code fails and bounces the player to login.
346356 const url = new URL ( page . url ( ) ) ;
@@ -367,6 +377,35 @@ if (!playwright) {
367377 } finally { await context . close ( ) ; }
368378 } ) ;
369379
380+ await check ( 'a signin the server calls OK but that sets no session says so' , async ( ) => {
381+ // The v1.9.0 failure mode, reproduced: signinup answers status OK and no
382+ // session cookie is set. The player must be told, not silently returned to
383+ // a login screen with nothing wrong on it - that is what made the missing
384+ // st-auth-mode header survive a release and a production deploy.
385+ const { context, page } = await newPage ( ) ;
386+ try {
387+ await page . route ( '**/auth/authorisationurl*' , ( route ) => route . fulfill ( {
388+ status : 200 ,
389+ contentType : 'application/json' ,
390+ body : JSON . stringify ( {
391+ status : 'OK' ,
392+ urlWithQueryParams : `${ BASE_URL } /auth/callback/github?code=fake-code` ,
393+ } ) ,
394+ } ) ) ;
395+
396+ // OK, but deliberately no cookie - exactly what header transport does.
397+ await page . route ( '**/auth/signinup' , ( route ) => route . fulfill ( {
398+ status : 200 ,
399+ contentType : 'application/json' ,
400+ body : JSON . stringify ( { status : 'OK' , user : { id : 'github:1' } } ) ,
401+ } ) ) ;
402+
403+ await page . goto ( `${ BASE_URL } /` ) ;
404+ await page . locator ( 'button' , { hasText : 'Continue with GitHub' } ) . click ( ) ;
405+ await page . locator ( 'text=/did not set a session/i' ) . waitFor ( { timeout : 15000 } ) ;
406+ } finally { await context . close ( ) ; }
407+ } ) ;
408+
370409 await check ( 'an unconfigured provider shows a message, not a blank screen' , async ( ) => {
371410 const { context, page } = await newPage ( ) ;
372411 try {
0 commit comments