@@ -8,6 +8,7 @@ import { CLIError } from "./output"
88const LOCAL_CALLBACK_HOST = "127.0.0.1"
99const LOCAL_CALLBACK_PATH = "/callback"
1010const DEFAULT_TIMEOUT_MS = 3 * 60 * 1000
11+ const ONE_TIME_TOKEN_APPLY_PATH = "/better-auth/one-time-token/apply"
1112const ONE_TIME_TOKEN_VERIFY_PATH = "/better-auth/one-time-token/verify"
1213const SESSION_CHECK_PATH = "/better-auth/get-session"
1314
@@ -114,6 +115,38 @@ const isRecord = (value: unknown): value is Record<string, unknown> => {
114115 return typeof value === "object" && value !== null && ! Array . isArray ( value )
115116}
116117
118+ const readSetCookieValues = ( response : Response ) : string [ ] => {
119+ if ( typeof response . headers . getSetCookie === "function" ) {
120+ return response . headers . getSetCookie ( )
121+ }
122+
123+ const setCookie = response . headers . get ( "set-cookie" )
124+ return setCookie ? [ setCookie ] : [ ]
125+ }
126+
127+ const extractSessionTokenFromSetCookie = ( setCookieValues : string [ ] ) : string | undefined => {
128+ for ( const setCookie of setCookieValues ) {
129+ const match = setCookie . match ( / (?: _ _ S e c u r e - ) ? b e t t e r - a u t h \. s e s s i o n _ t o k e n = ( [ ^ ; ] + ) / )
130+ if ( match ?. [ 1 ] ) {
131+ return match [ 1 ]
132+ }
133+ }
134+
135+ return undefined
136+ }
137+
138+ const extractSessionTokenFromBody = ( data : unknown ) : string | undefined => {
139+ if ( ! isRecord ( data ) ) {
140+ return undefined
141+ }
142+
143+ if ( isRecord ( data . session ) && typeof data . session . token === "string" ) {
144+ return data . session . token
145+ }
146+
147+ return undefined
148+ }
149+
117150const extractErrorMessage = async ( response : Response ) : Promise < string | undefined > => {
118151 const contentType = response . headers . get ( "content-type" ) ?? ""
119152
@@ -147,42 +180,80 @@ const hasValidSessionToken = async (apiUrl: string, token: string): Promise<bool
147180}
148181
149182export const resolveBrowserLoginToken = async ( apiUrl : string , token : string ) : Promise < string > => {
183+ const applyUrl = resolveAuthEndpointUrl ( apiUrl , ONE_TIME_TOKEN_APPLY_PATH )
150184 const verifyUrl = resolveAuthEndpointUrl ( apiUrl , ONE_TIME_TOKEN_VERIFY_PATH )
151185
152- let response : Response
186+ const requestBody = JSON . stringify ( { token } )
187+
188+ let response : Response | undefined
189+ let errorMessage : string | undefined
153190 try {
154- response = await fetch ( verifyUrl , {
191+ response = await fetch ( applyUrl , {
155192 method : "POST" ,
156193 headers : {
157194 "content-type" : "application/json" ,
158195 } ,
159- body : JSON . stringify ( { token } ) ,
196+ body : requestBody ,
160197 } )
161198 } catch ( error ) {
162199 throw new CLIError (
163200 "NETWORK_ERROR" ,
164- `Failed to verify browser login token: ${ ( error as Error ) . message } ` ,
201+ `Failed to apply browser login token: ${ ( error as Error ) . message } ` ,
165202 )
166203 }
167204
168205 if ( response . ok ) {
169206 const data = ( await response . json ( ) . catch ( ( ) => null ) ) as unknown
170207 const sessionToken =
171- isRecord ( data ) && isRecord ( data . session ) && typeof data . session . token === "string"
172- ? data . session . token
173- : undefined
208+ extractSessionTokenFromSetCookie ( readSetCookieValues ( response ) ) ??
209+ extractSessionTokenFromBody ( data )
174210
175211 if ( ! sessionToken ) {
176212 throw new CLIError (
177213 "UNAUTHORIZED" ,
178- "Browser login verification succeeded without returning a session token." ,
214+ "Browser login token apply succeeded without returning a session token." ,
179215 )
180216 }
181217
182218 return sessionToken
183219 }
184220
185- const errorMessage = await extractErrorMessage ( response )
221+ errorMessage = await extractErrorMessage ( response )
222+
223+ if ( response . status === 404 ) {
224+ try {
225+ response = await fetch ( verifyUrl , {
226+ method : "POST" ,
227+ headers : {
228+ "content-type" : "application/json" ,
229+ } ,
230+ body : requestBody ,
231+ } )
232+ } catch ( error ) {
233+ throw new CLIError (
234+ "NETWORK_ERROR" ,
235+ `Failed to verify browser login token: ${ ( error as Error ) . message } ` ,
236+ )
237+ }
238+
239+ if ( response . ok ) {
240+ const data = ( await response . json ( ) . catch ( ( ) => null ) ) as unknown
241+ const sessionToken =
242+ extractSessionTokenFromSetCookie ( readSetCookieValues ( response ) ) ??
243+ extractSessionTokenFromBody ( data )
244+
245+ if ( ! sessionToken ) {
246+ throw new CLIError (
247+ "UNAUTHORIZED" ,
248+ "Browser login verification succeeded without returning a session token." ,
249+ )
250+ }
251+
252+ return sessionToken
253+ }
254+
255+ errorMessage = await extractErrorMessage ( response )
256+ }
186257
187258 try {
188259 if ( await hasValidSessionToken ( apiUrl , token ) ) {
0 commit comments