11import { enableLogging , logDebugMessage } from "../logger" ;
22
3- import type { NextRequest , NextResponse , SuperTokensNextjsConfig } from "./types" ;
3+ import type { NextRequest , SuperTokensNextjsConfig , SuperTokensRequestToken } from "./types" ;
44
55const ACCESS_TOKEN_COOKIE_NAME = "sAccessToken" ;
66const ACCESS_TOKEN_HEADER_NAME = "st-access-token" ;
@@ -9,14 +9,30 @@ const FRONT_TOKEN_HEADER_NAME = "front-token";
99const REFRESH_TOKEN_COOKIE_NAME = "sRefreshToken" ;
1010const REFRESH_TOKEN_HEADER_NAME = "st-refresh-token" ;
1111const ANTI_CSRF_TOKEN_COOKIE_NAME = "sAntiCsrf" ;
12+ const ANTI_CSRF_TOKEN_HEADER_NAME = "anti-csrf" ;
1213
1314let AppInfo : SuperTokensNextjsConfig [ "appInfo" ] ;
1415
15- export async function refreshSession (
16- config : SuperTokensNextjsConfig ,
17- request : NextRequest ,
18- response : NextResponse
19- ) : Promise < NextResponse > {
16+ // export function superTokensMiddleware(config: SuperTokensNextjsConfig, request: NextRequest, response: NextResponse) {
17+ // AppInfo = config.appInfo;
18+ // if (config.enableDebugLogs) {
19+ // enableLogging();
20+ // }
21+ // const url = new URL(request.url);
22+ // if (url.pathname === "/auth/session/refresh") {
23+ // return refreshSession(request, response);
24+ // }
25+ //
26+ // // Save the current path so that we can use it during SSR
27+ // // Used to redirect the user to the correct path after login/refresh
28+ // return response.next({
29+ // headers: {
30+ // "x-current-path": url.pathname,
31+ // },
32+ // });
33+ // }
34+
35+ export async function refreshSession ( config : SuperTokensNextjsConfig , request : NextRequest ) : Promise < Response > {
2036 AppInfo = config . appInfo ;
2137 if ( config . enableDebugLogs ) {
2238 enableLogging ( ) ;
@@ -25,43 +41,73 @@ export async function refreshSession(
2541 request . cookies . get ( REFRESH_TOKEN_COOKIE_NAME ) ?. value || request . headers . get ( REFRESH_TOKEN_HEADER_NAME ) ;
2642 if ( ! refreshToken ) {
2743 logDebugMessage ( "Refresh token not found" ) ;
28- return redirectToAuthPage ( request , response ) ;
44+ return redirectToAuthPage ( request ) ;
2945 }
3046
3147 try {
3248 const tokens = await fetchNewTokens ( refreshToken ) ;
33- if ( ! tokens . accessToken || ! tokens . refreshToken || ! tokens . frontToken ) {
49+ const hasRequiredCookies = tokens . accessToken . cookie && tokens . refreshToken . cookie && tokens . frontToken . cookie ;
50+ const hasRequiredHeaders = tokens . accessToken . header && tokens . refreshToken . header && tokens . frontToken . header ;
51+ if ( ! hasRequiredCookies && ! hasRequiredHeaders ) {
3452 logDebugMessage ( "Missing tokens from refresh response" ) ;
35- return redirectToAuthPage ( request , response ) ;
53+ return redirectToAuthPage ( request ) ;
3654 }
3755
3856 const currentUrl = new URL ( request . url ) ;
3957 const redirectUrl = new URL ( currentUrl . searchParams . get ( "redirectTo" ) || "/" , request . url ) ;
40- const finalResponse = response . redirect ( redirectUrl ) ;
41- finalResponse . headers . set ( "x-current-path" , redirectUrl . pathname ) ;
42- // @ts -expect-error TS(2345) It complains about tokens being null although we check for that in the previous if condition
43- attachTokensToResponse ( finalResponse , tokens ) ;
58+ const finalResponse = new Response ( null , {
59+ status : 307 ,
60+ headers : {
61+ Location : redirectUrl . toString ( ) ,
62+ "x-current-path" : redirectUrl . pathname ,
63+ "x-st-ssr-session-refresh-attempt" : "0" ,
64+ } ,
65+ } ) ;
66+ if ( hasRequiredCookies ) {
67+ finalResponse . headers . append (
68+ "set-cookie" ,
69+ `${ tokens . accessToken . cookie as string } ,${ tokens . refreshToken . cookie as string } ,${
70+ tokens . frontToken . cookie as string
71+ } `
72+ ) ;
73+ finalResponse . headers . append ( "x-middleware-set-cookie" , tokens . accessToken . cookie as string ) ;
74+ finalResponse . headers . append ( "x-middleware-set-cookie" , tokens . refreshToken . cookie as string ) ;
75+ finalResponse . headers . append ( "x-middleware-set-cookie" , tokens . frontToken . cookie as string ) ;
76+ }
77+ if ( hasRequiredHeaders ) {
78+ finalResponse . headers . append ( REFRESH_TOKEN_HEADER_NAME , tokens . refreshToken . header as string ) ;
79+ finalResponse . headers . append ( ACCESS_TOKEN_HEADER_NAME , tokens . accessToken . header as string ) ;
80+ finalResponse . headers . append ( FRONT_TOKEN_HEADER_NAME , tokens . frontToken . header as string ) ;
81+ if ( tokens . antiCsrfToken . header ) {
82+ finalResponse . headers . append ( ANTI_CSRF_TOKEN_HEADER_NAME , tokens . antiCsrfToken . header ) ;
83+ }
84+ }
4485 logDebugMessage ( "Attached new tokens to response" ) ;
4586 return finalResponse ;
4687 } catch ( err ) {
4788 logDebugMessage ( "Error refreshing session" ) ;
4889 logDebugMessage ( err as unknown as string ) ;
49- return redirectToAuthPage ( request , response ) ;
90+ return redirectToAuthPage ( request ) ;
5091 }
5192}
5293
53- function redirectToAuthPage ( request : NextRequest , response : NextResponse ) : NextResponse {
94+ function redirectToAuthPage ( request : NextRequest ) : Response {
5495 const authPagePath = AppInfo . websiteBasePath || "/auth" ;
5596 const redirectUrl = new URL ( authPagePath , request . url ) ;
5697 logDebugMessage ( `Redirecting to: ${ redirectUrl } ` ) ;
57- return response . redirect ( redirectUrl ) ;
98+ return new Response ( null , {
99+ status : 307 ,
100+ headers : {
101+ Location : redirectUrl . toString ( ) ,
102+ } ,
103+ } ) ;
58104}
59105
60106async function fetchNewTokens ( currentRefreshToken : string ) : Promise < {
61- accessToken : string | null ;
62- refreshToken : string | null ;
63- frontToken : string | null ;
64- antiCsrf : string | null ;
107+ accessToken : SuperTokensRequestToken ;
108+ refreshToken : SuperTokensRequestToken ;
109+ frontToken : SuperTokensRequestToken ;
110+ antiCsrfToken : SuperTokensRequestToken ;
65111} > {
66112 const refreshApiURL = new URL ( `${ AppInfo . apiBasePath } /session/refresh` , AppInfo . apiDomain ) ;
67113 const refreshResponse = await fetch ( refreshApiURL , {
@@ -73,55 +119,38 @@ async function fetchNewTokens(currentRefreshToken: string): Promise<{
73119 credentials : "include" ,
74120 } ) ;
75121 logDebugMessage ( "Session refresh request completed" ) ;
76- const frontToken = refreshResponse . headers . get ( "front-token" ) ;
77-
78- let accessToken : string | null = null ;
79- let refreshToken : string | null = null ;
80- let antiCsrf : string | null = null ;
122+ const frontToken : SuperTokensRequestToken = {
123+ header : refreshResponse . headers . get ( "front-token" ) ,
124+ cookie : `${ FRONT_TOKEN_COOKIE_NAME } =${ refreshResponse . headers . get ( "front-token" ) } ; Path=/` ,
125+ } ;
126+ const accessToken : SuperTokensRequestToken = {
127+ header : refreshResponse . headers . get ( ACCESS_TOKEN_HEADER_NAME ) ,
128+ cookie : null ,
129+ } ;
130+ const refreshToken : SuperTokensRequestToken = {
131+ header : refreshResponse . headers . get ( REFRESH_TOKEN_HEADER_NAME ) ,
132+ cookie : null ,
133+ } ;
134+ const antiCsrfToken : SuperTokensRequestToken = {
135+ header : refreshResponse . headers . get ( ANTI_CSRF_TOKEN_HEADER_NAME ) ,
136+ cookie : null ,
137+ } ;
81138
82139 // getSetCookie was added in node 18 and our build target is ES5
83140 // This should not a problem here since the function runs in the Vercel edge runtime environment
84141 // @ts -expect-error TS(2339): Property 'getSetCookie' does not exist on type 'Headers'.
85142 const setCookieHeaders = refreshResponse . headers . getSetCookie ( ) ;
86- if ( ! setCookieHeaders . length ) {
87- return { accessToken, refreshToken, frontToken, antiCsrf } ;
88- }
89-
90143 for ( const header of setCookieHeaders ) {
91144 if ( header . includes ( ACCESS_TOKEN_COOKIE_NAME ) ) {
92- accessToken = getCookieValue ( header , ACCESS_TOKEN_COOKIE_NAME ) ;
145+ accessToken . cookie = header ;
93146 }
94147 if ( header . includes ( REFRESH_TOKEN_COOKIE_NAME ) ) {
95- refreshToken = getCookieValue ( header , REFRESH_TOKEN_COOKIE_NAME ) ;
148+ refreshToken . cookie = header ;
96149 }
97150 if ( header . includes ( ANTI_CSRF_TOKEN_COOKIE_NAME ) ) {
98- antiCsrf = getCookieValue ( header , ANTI_CSRF_TOKEN_COOKIE_NAME ) ;
151+ antiCsrfToken . cookie = header ;
99152 }
100153 }
101154
102- return { accessToken, refreshToken, frontToken, antiCsrf } ;
103- }
104-
105- function attachTokensToResponse (
106- response : NextResponse ,
107- tokens : { accessToken : string ; refreshToken : string ; frontToken : string ; antiCsrf : string | null }
108- ) {
109- response . headers . append ( "set-cookie" , `${ ACCESS_TOKEN_COOKIE_NAME } =${ tokens . accessToken } ` ) ;
110- response . headers . append ( ACCESS_TOKEN_HEADER_NAME , tokens . accessToken ) ;
111- response . cookies . set ( ACCESS_TOKEN_COOKIE_NAME , tokens . accessToken ) ;
112- response . headers . append ( "set-cookie" , `${ REFRESH_TOKEN_COOKIE_NAME } =${ tokens . refreshToken } ` ) ;
113- response . headers . append ( REFRESH_TOKEN_HEADER_NAME , tokens . refreshToken ) ;
114- response . cookies . set ( REFRESH_TOKEN_COOKIE_NAME , tokens . refreshToken ) ;
115- response . headers . append ( "set-cookie" , `${ FRONT_TOKEN_COOKIE_NAME } =${ tokens . frontToken } ` ) ;
116- response . headers . append ( FRONT_TOKEN_HEADER_NAME , tokens . frontToken ) ;
117- response . cookies . set ( FRONT_TOKEN_COOKIE_NAME , tokens . frontToken ) ;
118- if ( tokens . antiCsrf ) {
119- response . headers . append ( "set-cookie" , `${ ANTI_CSRF_TOKEN_COOKIE_NAME } =${ tokens . antiCsrf } ` ) ;
120- response . cookies . set ( ANTI_CSRF_TOKEN_COOKIE_NAME , tokens . antiCsrf ) ;
121- }
122- }
123-
124- export function getCookieValue ( header : string , name : string ) : string | null {
125- const match = header . match ( new RegExp ( `${ name } =([^;]+)` ) ) ;
126- return match ? match [ 1 ] : null ;
155+ return { accessToken, refreshToken, frontToken, antiCsrfToken } ;
127156}
0 commit comments