11import { base } from '$app/paths' ;
2- import { browser , dev } from '$app/environment' ;
2+ import { browser } from '$app/environment' ;
33import { env as publicEnv } from '$env/dynamic/public' ;
44import { z } from 'zod' ;
55import { setGithubToken } from '../../stores/githubAuth' ;
66
77const SESSION_PREFIX = 'github:oauth:pkce:' ;
8+ const CALLBACK_KEY = 'github:oauth:pkce:callback' ;
9+ const CALLBACK_TTL_MS = 2 * 60 * 1000 ;
810
911const GithubPkceSessionSchema = z . object ( {
1012 verifier : z . string ( ) . min ( 32 ) ,
@@ -62,11 +64,6 @@ export function getGithubPkceAvailability(): GithubPkceAvailability {
6264
6365export type GithubPkceCodePayload = { code : string ; state : string } ;
6466
65- export function getGithubManualTokenAllowed ( ) {
66- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
67- return Boolean ( dev ) || Boolean ( publicEnv . PUBLIC_GITHUB_ALLOW_MANUAL_TOKEN ) ;
68- }
69-
7067export type GithubPkceStartResult =
7168 | { ok : true }
7269 | { ok : false ; errorKey : 'panels.sync.githubMissing' | 'errors.githubPopupBlocked' | 'errors.githubPkceUnsupported' } ;
@@ -264,6 +261,7 @@ function attachPopupCallbackCoordinator(popup: Window, redirectUri: string) {
264261 if ( done ) return ;
265262 done = true ;
266263 window . removeEventListener ( 'message' , handleMessage ) ;
264+ window . removeEventListener ( 'storage' , handleStorage ) ;
267265 channel ?. removeEventListener ( 'message' , handleChannelMessage as any ) ;
268266 channel ?. close ( ) ;
269267 clearInterval ( interval ) ;
@@ -309,13 +307,45 @@ function attachPopupCallbackCoordinator(popup: Window, redirectUri: string) {
309307 cleanup ( ) ;
310308 } ;
311309
310+ const readCallbackFromStorage = ( ) => {
311+ try {
312+ const raw = localStorage . getItem ( CALLBACK_KEY ) ;
313+ if ( ! raw ) return null ;
314+ const parsed = JSON . parse ( raw ) ;
315+ const code = typeof parsed ?. code === 'string' ? parsed . code . trim ( ) : '' ;
316+ const state = typeof parsed ?. state === 'string' ? parsed . state . trim ( ) : '' ;
317+ const createdAt = typeof parsed ?. createdAt === 'number' ? parsed . createdAt : 0 ;
318+ if ( ! code || ! state ) return null ;
319+ if ( ! createdAt || Date . now ( ) - createdAt > CALLBACK_TTL_MS ) return null ;
320+ return { code, state } ;
321+ } catch {
322+ return null ;
323+ }
324+ } ;
325+
326+ const clearCallbackStorage = ( ) => {
327+ try {
328+ localStorage . removeItem ( CALLBACK_KEY ) ;
329+ } catch {
330+ // ignore
331+ }
332+ } ;
333+
312334 const handleMessage = ( event : MessageEvent ) => {
313335 if ( event . origin !== window . location . origin ) return ;
314336 if ( event . data ?. type === 'github-oauth-code' && event . data . code && event . data . state ) {
315337 void onCallback ( { code : event . data . code , state : event . data . state } ) ;
316338 }
317339 } ;
318340
341+ const handleStorage = ( event : StorageEvent ) => {
342+ if ( event . key !== CALLBACK_KEY ) return ;
343+ const payload = readCallbackFromStorage ( ) ;
344+ if ( ! payload ) return ;
345+ clearCallbackStorage ( ) ;
346+ void onCallback ( payload ) ;
347+ } ;
348+
319349 const handleChannelMessage = ( event : MessageEvent ) => {
320350 const data = ( event as MessageEvent ) . data as any ;
321351 if ( data ?. type === 'github-oauth-code' && data . code && data . state ) {
@@ -324,6 +354,7 @@ function attachPopupCallbackCoordinator(popup: Window, redirectUri: string) {
324354 } ;
325355
326356 window . addEventListener ( 'message' , handleMessage ) ;
357+ window . addEventListener ( 'storage' , handleStorage ) ;
327358 channel ?. addEventListener ( 'message' , handleChannelMessage as any ) ;
328359
329360 const interval = window . setInterval ( ( ) => {
@@ -344,6 +375,12 @@ function attachPopupCallbackCoordinator(popup: Window, redirectUri: string) {
344375 } catch {
345376 // ignore (cross-origin until it returns to our site)
346377 }
378+
379+ const payload = readCallbackFromStorage ( ) ;
380+ if ( payload ) {
381+ clearCallbackStorage ( ) ;
382+ void onCallback ( payload ) ;
383+ }
347384 } , 350 ) ;
348385
349386 const timeout = window . setTimeout ( ( ) => cleanup ( ) , 2 * 60 * 1000 ) ;
0 commit comments