@@ -36,22 +36,22 @@ const ROOT_ID = 'root';
36
36
const MIME_SOURCE = 'text/plain' ;
37
37
// const MIME_FOLDER = 'application/vnd.google-apps.folder';
38
38
39
- // TODO: fix all calls to (window.google as any).accounts
39
+ // GIS Token Client
40
+ let tokenClient : google . accounts . oauth2 . TokenClient ;
41
+
40
42
export function * persistenceSaga ( ) : SagaIterator {
41
- // Starts the function* () for every dispatched LOGOUT_GOOGLE action
42
- // Same for all takeLatest() calls below, with respective types from PersistenceTypes
43
43
yield takeLatest ( LOGOUT_GOOGLE , function * ( ) {
44
44
yield put ( actions . playgroundUpdatePersistenceFile ( undefined ) ) ;
45
45
yield call ( ensureInitialised ) ;
46
46
yield gapi . client . setToken ( null ) ;
47
47
yield handleUserChanged ( null ) ;
48
+ yield localStorage . removeItem ( "gsi-access-token" ) ;
48
49
} ) ;
49
50
50
51
yield takeLatest ( PERSISTENCE_OPEN_PICKER , function * ( ) : any {
51
52
let toastKey : string | undefined ;
52
53
try {
53
54
yield call ( ensureInitialisedAndAuthorised ) ;
54
-
55
55
const { id, name, picked } = yield call ( pickFile , 'Pick a file to open' ) ;
56
56
if ( ! picked ) {
57
57
return ;
@@ -311,138 +311,125 @@ const initialisationPromise: Promise<void> = new Promise(res => {
311
311
startInitialisation = res ;
312
312
} ) . then ( initialise ) ;
313
313
314
- const getUserProfileData = async ( accessToken : string ) => {
315
- const headers = new Headers ( )
316
- headers . append ( 'Authorization' , `Bearer ${ accessToken } ` )
314
+ async function getUserProfileData ( accessToken : string ) {
315
+ const headers = new Headers ( ) ;
316
+ headers . append ( 'Authorization' , `Bearer ${ accessToken } ` ) ;
317
317
const response = await fetch ( 'https://www.googleapis.com/oauth2/v3/userinfo' , {
318
318
headers
319
- } )
319
+ } ) ;
320
320
const data = await response . json ( ) ;
321
321
return data ;
322
322
}
323
323
324
+ async function isOAuthTokenValid ( accessToken : string ) {
325
+ const userProfileData = await getUserProfileData ( accessToken ) ;
326
+ return userProfileData . error ? false : true ;
327
+ }
328
+
329
+ // updates store and localStorage
324
330
async function handleUserChanged ( accessToken : string | null ) {
325
- if ( accessToken === null ) { // TODO: check if access token is invalid instead of null
331
+ // logs out if null
332
+ if ( accessToken === null ) {
326
333
store . dispatch ( actions . setGoogleUser ( undefined ) ) ;
327
- }
328
- else {
334
+ } else {
329
335
const userProfileData = await getUserProfileData ( accessToken )
330
336
const email = userProfileData . email ;
331
- console . log ( "handleUserChanged" , email ) ;
332
337
store . dispatch ( actions . setGoogleUser ( email ) ) ;
338
+ localStorage . setItem ( "gsi-access-token" , accessToken ) ;
333
339
}
334
340
}
335
341
336
- let tokenClient : any ;
337
-
338
- async function initialise ( ) {
342
+ async function initialise ( ) { // only called once
339
343
// load GIS script
340
- await new Promise < void > ( ( resolve , reject ) => {
344
+ // adapted from https://github.com/MomenSherif/react-oauth
345
+ await new Promise < void > ( ( resolve , reject ) => {
341
346
const scriptTag = document . createElement ( 'script' ) ;
342
347
scriptTag . src = 'https://accounts.google.com/gsi/client' ;
343
348
scriptTag . async = true ;
344
349
scriptTag . defer = true ;
345
- //scriptTag.nonce = nonce;
346
- scriptTag . onload = ( ) => {
347
- console . log ( "success" ) ;
348
- resolve ( ) ;
349
- //setScriptLoadedSuccessfully(true);
350
- //onScriptLoadSuccessRef.current?.();
351
- } ;
350
+ scriptTag . onload = ( ) => resolve ( ) ;
352
351
scriptTag . onerror = ( ev ) => {
353
- console . log ( "failure" ) ;
354
352
reject ( ev ) ;
355
- //setScriptLoadedSuccessfully(false);
356
- //onScriptLoadErrorRef.current?.();
357
353
} ;
358
-
359
354
document . body . appendChild ( scriptTag ) ;
360
355
} ) ;
361
356
362
357
// load and initialize gapi.client
363
- await new Promise < void > ( ( resolve , reject ) =>
364
- gapi . load ( 'client' , { callback : ( ) => { console . log ( "gapi.client loaded" ) ; resolve ( ) ; } , onerror : reject } )
358
+ await new Promise < void > ( ( resolve , reject ) =>
359
+ gapi . load ( 'client' , {
360
+ callback : resolve ,
361
+ onerror : reject
362
+ } )
365
363
) ;
366
364
await gapi . client . init ( {
367
365
discoveryDocs : DISCOVERY_DOCS
368
366
} ) ;
369
367
370
- // juju
371
- // TODO: properly fix types here
372
- await new Promise ( ( resolve , reject ) => {
373
- //console.log("At least ur here");
374
- resolve ( ( window . google as any ) . accounts . oauth2 . initTokenClient ( {
375
- client_id : Constants . googleClientId ,
368
+ // initialize GIS client
369
+ await new Promise < google . accounts . oauth2 . TokenClient > ( ( resolve , reject ) => {
370
+ resolve ( window . google . accounts . oauth2 . initTokenClient ( {
371
+ client_id : Constants . googleClientId ! ,
376
372
scope : SCOPES ,
377
- callback : ''
373
+ callback : ( ) => void 0 // will be updated in getToken()
378
374
} ) ) ;
379
375
} ) . then ( ( c ) => {
380
- //console.log(c);
381
376
tokenClient = c ;
382
- //console.log(tokenClient.requestAccessToken);
383
377
} ) ;
384
378
385
- //await console.log("tokenClient", tokenClient);
386
-
387
-
388
- //await gapi.client.init({
389
- //apiKey: Constants.googleApiKey,
390
- //clientId: Constants.googleClientId,
391
- //discoveryDocs: DISCOVERY_DOCS,
392
- //scope: SCOPES
393
- //});
394
- //gapi.auth2.getAuthInstance().currentUser.listen(handleUserChanged);
395
- //handleUserChanged(gapi.auth2.getAuthInstance().currentUser.get());
379
+ // check for stored token
380
+ // if it exists and is valid, load manually
381
+ // leave checking whether it is valid or not to ensureInitialisedAndAuthorised
382
+ if ( localStorage . getItem ( "gsi-access-token" ) ) {
383
+ await loadToken ( localStorage . getItem ( "gsi-access-token" ) ! ) ;
384
+ }
396
385
}
397
386
398
- // TODO: fix types
399
387
// adapted from https://developers.google.com/identity/oauth2/web/guides/migration-to-gis
400
- async function getToken ( err : any ) {
401
-
402
- //if (err.result.error.code == 401 || (err.result.error.code == 403) &&
403
- // (err.result.error.status == "PERMISSION_DENIED")) {
404
- if ( err ) { //TODO: fix after debugging
388
+ async function getToken ( ) {
389
+ await new Promise ( ( resolve , reject ) => {
390
+ try {
391
+ // Settle this promise in the response callback for requestAccessToken()
392
+ // as any used here cos of limitations of the type declaration library
393
+ ( tokenClient as any ) . callback = ( resp : google . accounts . oauth2 . TokenResponse ) => {
394
+ if ( resp . error !== undefined ) {
395
+ reject ( resp ) ;
396
+ }
397
+ // GIS has automatically updated gapi.client with the newly issued access token.
398
+ handleUserChanged ( gapi . client . getToken ( ) . access_token ) ;
399
+ resolve ( resp ) ;
400
+ } ;
401
+ tokenClient . requestAccessToken ( ) ;
402
+ } catch ( err ) {
403
+ reject ( err ) ;
404
+ }
405
+ } ) ;
406
+ }
405
407
406
- // The access token is missing, invalid, or expired, prompt for user consent to obtain one.
407
- await new Promise ( ( resolve , reject ) => {
408
- try {
409
- // Settle this promise in the response callback for requestAccessToken()
410
- tokenClient . callback = ( resp : any ) => {
411
- if ( resp . error !== undefined ) {
412
- reject ( resp ) ;
413
- }
414
- // GIS has automatically updated gapi.client with the newly issued access token.
415
- console . log ( 'gapi.client access token: ' + JSON . stringify ( gapi . client . getToken ( ) ) ) ;
416
- resolve ( resp ) ;
417
- } ;
418
- console . log ( tokenClient . requestAccessToken ) ;
419
- tokenClient . requestAccessToken ( ) ;
420
- } catch ( err ) {
421
- console . log ( err )
422
- }
423
- } ) ;
424
- } else {
425
- // Errors unrelated to authorization: server errors, exceeding quota, bad requests, and so on.
426
- throw new Error ( err ) ;
427
- }
408
+ // manually load token, when token is not gotten from getToken()
409
+ // but instead from localStorage
410
+ async function loadToken ( accessToken : string ) {
411
+ gapi . client . setToken ( { access_token : accessToken } ) ;
412
+ return handleUserChanged ( accessToken ) ;
428
413
}
429
414
430
415
function * ensureInitialised ( ) {
431
416
startInitialisation ( ) ;
432
417
yield initialisationPromise ;
433
418
}
434
419
435
- function * ensureInitialisedAndAuthorised ( ) {
420
+ function * ensureInitialisedAndAuthorised ( ) { // called multiple times
436
421
yield call ( ensureInitialised ) ;
437
- // only call getToken if there is no token in gapi
438
- console . log ( gapi . client . getToken ( ) ) ;
439
- if ( gapi . client . getToken ( ) === null ) {
440
- yield getToken ( true ) ;
441
- yield handleUserChanged ( gapi . client . getToken ( ) . access_token ) ;
442
- } //TODO: fix after debugging
443
- //if (!gapi.auth2.getAuthInstance().isSignedIn.get()) {
444
- // yield gapi.auth2.getAuthInstance().signIn();
445
- //}
422
+ const currToken = gapi . client . getToken ( ) ;
423
+
424
+ if ( currToken === null ) {
425
+ yield call ( getToken ) ;
426
+ } else {
427
+ // check if loaded token is still valid
428
+ const isValid : boolean = yield call ( isOAuthTokenValid , currToken . access_token ) ;
429
+ if ( ! isValid ) {
430
+ yield call ( getToken ) ;
431
+ }
432
+ }
446
433
}
447
434
448
435
type PickFileResult =
0 commit comments