@@ -25,13 +25,15 @@ import type {
2525 FilterSettingsValue ,
2626 GitifyError ,
2727 GitifyNotification ,
28+ Hostname ,
2829 SettingsState ,
2930 SettingsValue ,
3031 Status ,
3132 Token ,
3233} from '../types' ;
3334import { FetchType } from '../types' ;
3435import type {
36+ DeviceFlowSession ,
3537 LoginOAuthWebOptions ,
3638 LoginPersonalAccessTokenOptions ,
3739} from '../utils/auth/types' ;
@@ -42,10 +44,11 @@ import {
4244 exchangeAuthCodeForAccessToken ,
4345 getAccountUUID ,
4446 hasAccounts ,
45- performGitHubDeviceOAuth ,
4647 performGitHubWebOAuth ,
48+ pollGitHubDeviceFlow ,
4749 refreshAccount ,
4850 removeAccount ,
51+ startGitHubDeviceFlow ,
4952} from '../utils/auth/utils' ;
5053import {
5154 decryptValue ,
@@ -76,6 +79,12 @@ export interface AppContextState {
7679 auth : AuthState ;
7780 isLoggedIn : boolean ;
7881 loginWithGitHubApp : ( ) => Promise < void > ;
82+ startGitHubDeviceFlow : ( ) => Promise < DeviceFlowSession > ;
83+ pollGitHubDeviceFlow : ( session : DeviceFlowSession ) => Promise < Token | null > ;
84+ completeGitHubDeviceLogin : (
85+ token : Token ,
86+ hostname ?: Hostname ,
87+ ) => Promise < void > ;
7988 loginWithOAuthApp : ( data : LoginOAuthWebOptions ) => Promise < void > ;
8089 loginWithPersonalAccessToken : (
8190 data : LoginPersonalAccessTokenOptions ,
@@ -396,17 +405,61 @@ export const AppProvider = ({ children }: { children: ReactNode }) => {
396405 return hasAccounts ( auth ) ;
397406 } , [ auth ] ) ;
398407
408+ /**
409+ * Start a GitHub device flow session.
410+ */
411+ const startGitHubDeviceFlowWithDefaults = useCallback (
412+ async ( ) => await startGitHubDeviceFlow ( ) ,
413+ [ ] ,
414+ ) ;
415+
416+ /**
417+ * Poll GitHub device flow session for completion.
418+ */
419+ const pollGitHubDeviceFlowWithSession = useCallback (
420+ async ( session : DeviceFlowSession ) => await pollGitHubDeviceFlow ( session ) ,
421+ [ ] ,
422+ ) ;
423+
424+ /**
425+ * Persist GitHub app login after device flow completes.
426+ */
427+ const completeGitHubDeviceLogin = useCallback (
428+ async (
429+ token : Token ,
430+ hostname : Hostname = Constants . OAUTH_DEVICE_FLOW . hostname ,
431+ ) => {
432+ const updatedAuth = await addAccount ( auth , 'GitHub App' , token , hostname ) ;
433+
434+ persistAuth ( updatedAuth ) ;
435+ } ,
436+ [ auth , persistAuth ] ,
437+ ) ;
438+
399439 /**
400440 * Login with GitHub App using device flow so the client secret is never bundled or persisted.
401441 */
402442 const loginWithGitHubApp = useCallback ( async ( ) => {
403- const token = await performGitHubDeviceOAuth ( ) ;
404- const hostname = Constants . OAUTH_DEVICE_FLOW . hostname ;
443+ const session = await startGitHubDeviceFlowWithDefaults ( ) ;
444+ const intervalMs = Math . max ( 5000 , session . intervalSeconds * 1000 ) ;
405445
406- const updatedAuth = await addAccount ( auth , 'GitHub App' , token , hostname ) ;
446+ while ( Date . now ( ) < session . expiresAt ) {
447+ const token = await pollGitHubDeviceFlowWithSession ( session ) ;
407448
408- persistAuth ( updatedAuth ) ;
409- } , [ auth , persistAuth ] ) ;
449+ if ( token ) {
450+ await completeGitHubDeviceLogin ( token , session . hostname ) ;
451+ return ;
452+ }
453+
454+ await new Promise ( ( resolve ) => setTimeout ( resolve , intervalMs ) ) ;
455+ }
456+
457+ throw new Error ( 'Device code expired before authorization completed' ) ;
458+ } , [
459+ startGitHubDeviceFlowWithDefaults ,
460+ pollGitHubDeviceFlowWithSession ,
461+ completeGitHubDeviceLogin ,
462+ ] ) ;
410463
411464 /**
412465 * Login with custom GitHub OAuth App.
@@ -487,6 +540,9 @@ export const AppProvider = ({ children }: { children: ReactNode }) => {
487540 auth,
488541 isLoggedIn,
489542 loginWithGitHubApp,
543+ startGitHubDeviceFlow : startGitHubDeviceFlowWithDefaults ,
544+ pollGitHubDeviceFlow : pollGitHubDeviceFlowWithSession ,
545+ completeGitHubDeviceLogin,
490546 loginWithOAuthApp,
491547 loginWithPersonalAccessToken,
492548 logoutFromAccount,
@@ -517,6 +573,9 @@ export const AppProvider = ({ children }: { children: ReactNode }) => {
517573 auth ,
518574 isLoggedIn ,
519575 loginWithGitHubApp ,
576+ startGitHubDeviceFlowWithDefaults ,
577+ pollGitHubDeviceFlowWithSession ,
578+ completeGitHubDeviceLogin ,
520579 loginWithOAuthApp ,
521580 loginWithPersonalAccessToken ,
522581 logoutFromAccount ,
0 commit comments