@@ -8,21 +8,32 @@ const GIRA_GRAPHQL_WS_ENDPOINT = "wss://c2g091p01.emel.pt/ws/graphql";
88const GIRA_AUTH_ENDPOINT = "https://api-auth.emel.pt/auth" ;
99const GIRA_TOKEN_REFRESH_ENDPOINT = "https://api-auth.emel.pt/token/refresh" ;
1010const GIRA_USER_ENDPOINT = "https://api-auth.emel.pt/user" ;
11+ const FIREBASE_TOKEN_URL = "https://luk.moe/girabot_tokens/exchange" ;
1112
1213const NUMBER_OF_RETRIES = 3 ;
1314const DEFAULT_PROXY = "https://corsproxy.afonsosousah.workers.dev/" ;
1415
16+ async function makeProxyRequest ( url , init ) {
17+ return fetch ( proxyURL ?? DEFAULT_PROXY , {
18+ ...init ,
19+ headers : {
20+ ...init . headers ,
21+ "X-Proxy-URL" : url ,
22+ } ,
23+ } ) ;
24+ }
25+
1526async function makePostRequest ( url , body , accessToken = null ) {
1627 // Increment current request try
1728 currentRequestTry += 1 ;
1829
19- const response = await fetch ( proxyURL ?? DEFAULT_PROXY , {
30+ const response = await makeProxyRequest ( url , {
2031 method : "POST" ,
2132 headers : {
22- "User-Agent" : "Gira/3.4.0 (Android 34)" ,
23- "X-Proxy-URL" : url ,
33+ "User-Agent" : "Gira/3.4.3 (Android 34)" ,
2434 "Content-Type" : "application/json" ,
2535 "X-Authorization" : `Bearer ${ accessToken } ` ,
36+ "X-Firebase-Token" : await encryptFirebaseToken ( user . firebaseToken , user . accessToken ) ,
2637 } ,
2738 body : body ,
2839 } ) ;
@@ -31,9 +42,14 @@ async function makePostRequest(url, body, accessToken = null) {
3142
3243 // refresh token
3344 accessToken = await tokenRefresh ( ) ;
45+ // se o token tiver expirado
46+ if ( ! getCookie ( "firebaseToken" ) ) {
47+ const firebaseToken = await fetchFirebaseToken ( user . accessToken ) ;
48+ if ( ! firebaseToken ) delete user . firebaseToken ;
49+ }
3450
35- // check if token refresh was successful
36- if ( typeof accessToken !== "undefined" ) {
51+ // check if token refresh was successful and there's a firebase token
52+ if ( typeof accessToken !== "undefined" && user . firebaseToken ) {
3753 // try to make request again
3854 return await retryPostRequest ( url , body , accessToken , "Erro da API (401)" ) ; // be sure to use latest available token
3955 }
@@ -156,6 +172,41 @@ async function makePostRequest(url, body, accessToken = null) {
156172 // return;
157173 // }
158174 }
175+ returnToDefaultState ( ) ;
176+ }
177+
178+ // source: trust me bro
179+ async function encryptFirebaseToken ( firebaseToken , authToken ) {
180+ const { sub, jti } = getJWTPayload ( authToken ) ;
181+
182+ // 1. Create key from sub (hex string)
183+ const key = new TextEncoder ( ) . encode ( sub . replaceAll ( "-" , "" ) ) ;
184+
185+ // 2. IV from jti.slice(0, 16) using UTF-8 encoding
186+ const iv = new TextEncoder ( ) . encode ( jti . slice ( 0 , 16 ) ) ;
187+
188+ // 3. Import key
189+ const cryptoKey = await crypto . subtle . importKey ( "raw" , key , { name : "AES-CBC" } , false , [ "encrypt" ] ) ;
190+
191+ // 4. Encrypt
192+ const tokenEncoded = new TextEncoder ( ) . encode ( firebaseToken ) ;
193+ const encryptedBuffer = await crypto . subtle . encrypt ( { name : "AES-CBC" , iv } , cryptoKey , tokenEncoded ) ;
194+
195+ // 5. Convert to base64
196+ return bufferToBase64 ( encryptedBuffer ) ;
197+ }
198+
199+ function hexStringToBytes ( hex ) {
200+ const bytes = new Uint8Array ( hex . length / 2 ) ;
201+ for ( let i = 0 ; i < bytes . length ; i ++ ) {
202+ bytes [ i ] = parseInt ( hex . substr ( i * 2 , 2 ) , 16 ) ;
203+ }
204+ return bytes ;
205+ }
206+
207+ function bufferToBase64 ( buffer ) {
208+ const binary = String . fromCharCode ( ...new Uint8Array ( buffer ) ) ;
209+ return btoa ( binary ) ;
159210}
160211
161212async function makeGetRequest ( url , accessToken = null ) {
@@ -339,25 +390,27 @@ async function retryPostRequest(url, body, accessToken, errorMessage) {
339390 // Warn user about the API error
340391 alert ( errorMessage ) ;
341392
342- // Return to app starting state
343- hideUserSettings ( ) ;
344- hidePlaceSearchMenu ( ) ;
345- let bikeListMenu = document . getElementById ( "bikeMenu" ) ;
346- if ( bikeListMenu ) {
347- bikeListMenu . classList . add ( "smooth-slide-to-bottom" ) ;
348- setTimeout ( ( ) => bikeListMenu . remove ( ) , 500 ) ; // remove element after animation
349- return ; // prevent station card from being hidden if there was a bike list menu
350- }
351- let menu = document . getElementById ( "stationMenu" ) ;
352- if ( menu ) {
353- menu . classList . add ( "smooth-slide-to-left" ) ;
354- setTimeout ( ( ) => menu . remove ( ) , 500 ) ; // remove element after animation
355- document . getElementById ( "zoomControls" ) . classList . add ( "smooth-slide-down-zoom-controls" ) ; // move zoom controls back down
356- }
393+ returnToDefaultState ( ) ;
357394
358395 // Reset currentRequestTry
359396 currentRequestTry = 0 ;
397+ }
398+ }
360399
361- return ;
400+ function returnToDefaultState ( ) {
401+ // Return to app starting state
402+ hideUserSettings ( ) ;
403+ hidePlaceSearchMenu ( ) ;
404+ let bikeListMenu = document . getElementById ( "bikeMenu" ) ;
405+ if ( bikeListMenu ) {
406+ bikeListMenu . classList . add ( "smooth-slide-to-bottom" ) ;
407+ setTimeout ( ( ) => bikeListMenu . remove ( ) , 500 ) ; // remove element after animation
408+ return ; // prevent station card from being hidden if there was a bike list menu
409+ }
410+ let menu = document . getElementById ( "stationMenu" ) ;
411+ if ( menu ) {
412+ menu . classList . add ( "smooth-slide-to-left" ) ;
413+ setTimeout ( ( ) => menu . remove ( ) , 500 ) ; // remove element after animation
414+ document . getElementById ( "zoomControls" ) . classList . add ( "smooth-slide-down-zoom-controls" ) ; // move zoom controls back down
362415 }
363416}
0 commit comments