11import { createServer , type IncomingMessage , type ServerResponse } from 'node:http' ;
2+ import { randomBytes , createHash } from 'node:crypto' ;
23import { URL } from 'node:url' ;
34import open from 'open' ;
45import { saveCredentials } from './store.js' ;
@@ -14,7 +15,18 @@ interface TokenResponse {
1415 token_type : string ;
1516}
1617
18+ function generateCodeVerifier ( ) : string {
19+ return randomBytes ( 32 ) . toString ( 'base64url' ) ;
20+ }
21+
22+ function generateCodeChallenge ( verifier : string ) : string {
23+ return createHash ( 'sha256' ) . update ( verifier ) . digest ( 'base64url' ) ;
24+ }
25+
1726export async function performOAuthLogin ( ) : Promise < boolean > {
27+ const codeVerifier = generateCodeVerifier ( ) ;
28+ const codeChallenge = generateCodeChallenge ( codeVerifier ) ;
29+
1830 return new Promise ( ( resolve ) => {
1931 const server = createServer ( async ( req : IncomingMessage , res : ServerResponse ) => {
2032 const url = new URL ( req . url ?? '/' , `http://localhost:${ CALLBACK_PORT } ` ) ;
@@ -46,6 +58,7 @@ export async function performOAuthLogin(): Promise<boolean> {
4658 code,
4759 redirect_uri : `http://localhost:${ CALLBACK_PORT } ${ CALLBACK_PATH } ` ,
4860 client_id : process . env . YAVY_CLIENT_ID ?? '01965e6a-0000-7000-8000-000000000001' ,
61+ code_verifier : codeVerifier ,
4962 } ) ,
5063 } ) ;
5164
@@ -81,6 +94,8 @@ export async function performOAuthLogin(): Promise<boolean> {
8194 authUrl . searchParams . set ( 'redirect_uri' , `http://localhost:${ CALLBACK_PORT } ${ CALLBACK_PATH } ` ) ;
8295 authUrl . searchParams . set ( 'response_type' , 'code' ) ;
8396 authUrl . searchParams . set ( 'scope' , '' ) ;
97+ authUrl . searchParams . set ( 'code_challenge' , codeChallenge ) ;
98+ authUrl . searchParams . set ( 'code_challenge_method' , 'S256' ) ;
8499
85100 open ( authUrl . toString ( ) ) ;
86101 } ) ;
0 commit comments