The client side implementation is currently this

 function initClient() {
        client = google.accounts.oauth2.initCodeClient({
          client_id: CLIENT_ID,
          scope: SCOPES,
          ux_mode: "popup",
          callback: async (response) => {
            var code_receiver_uri = "http://localhost:5000/oauth2callback";
            try {
              const { code, scope } = response; //
              const fetchResponse = await fetch(code_receiver_uri, {
                method: "POST",
                headers: {
                  "Content-Type": "application/x-www-form-urlencoded",
                },
                body: `code=${code}&scope=${scope}`,
              });
              result = await fetchResponse.json();
              accessToken = result.token;
              console.log(result);

              document.getElementById("signout_button").style.visibility =
                "visible";
              document.getElementById("authorize_button").innerText = "Refresh";
              await createPicker();
            } catch (error) {
              console.error("Error making request: ", error);
            }
          },
        });
        gisInited = true;
        maybeEnableButtons();
      }

      function gapiLoaded() {
        gapi.load("client:picker", initializePicker);
      }

      async function initializePicker() {
        await gapi.client.load(
          "https://www.googleapis.com/discovery/v1/apis/drive/v3/rest"
        );
        pickerInited = true;
        maybeEnableButtons();
      }

      function maybeEnableButtons() {
        if (pickerInited && gisInited) {
          document.getElementById("authorize_button").style.visibility =
            "visible";
        }
      }

      function createPicker() {
        // const view = new google.picker.View(google.picker.ViewId.DOCS);
        // view.setMimeTypes("image/png,image/jpeg,image/jpg");
        const picker = new google.picker.PickerBuilder()
          .enableFeature(google.picker.Feature.NAV_HIDDEN)
          .enableFeature(google.picker.Feature.MULTISELECT_ENABLED)
          .setDeveloperKey(API_KEY)
          .setAppId(APP_ID)
          .setOAuthToken(accessToken)
          .addView(new google.picker.DocsView()
          .setIncludeFolders(true)
          .setMimeTypes('application/pdf')
          .setOwnedByMe(true))
          .setCallback(pickerCallback)
          .build();
        picker.setVisible(true);
      }

      async function getAccessToken() {
          if (!accessToken || isAccessTokenExpired())
          {
            // Using refresh token to get the new access token 
            accessToken = await refreshAccessToken();
          }
          return accessToken;
      }
      // function to check if access token is expired or not 
      function isAccessTokenExpired()
      { 
        const fs = require('fs');

        // Read the contents of the JSON file
        const filePath = '../credentials.json';
        const fileContent = fs.readFileSync(filePath, 'utf-8');

        // Parse the JSON content
        const credentials = JSON.parse(fileContent);

        // Access the accessTokenExpirationTime from the credentials
        const accessTokenExpirationTime = credentials.expiry;
        // console log expiration time 
        console.log('access token expiration time', accessTokenExpirationTime)

        const currentTimestamp = Math.floor(Date.now()/1000);

        if (accessTokenExpirationTime <= currentTimestamp)
        {
          return True
        }
        else 
        {
          return False
        }
      }

    async function refreshAccessToken(){
        const response = await fetch("http://localhost:5000/get-token", {
        method: "POST",
        headers: {
            "Content-Type": "application/x-www-form-urlencoded",
        },
        body: `refresh_token=${refreshToken}`,
    });

    const result = await response.json();
    return result.token;
      }

      async function pickerCallback(data) {
        console.log("Here in picker callback!");
        if (data.action === google.picker.Action.PICKED) {
          const documents = data[google.picker.Response.DOCUMENTS];
          console.log("here in documentsssss");
          const documentPayload = documents.map((document) => ({
            fileId: document.id,
            fileName: document.name,
          }));
          console.log("Documents: ", documentPayload);
          try {
            console.log(JSON.stringify(documentPayload));
          } catch (error) {
            console.warn(error);
          }

          const download_endpoint = "http://localhost:5000/gdrive/download";
          const response = await fetch(download_endpoint, {
            method: "POST",
            headers: {
              "Content-Type": "application/json",
            },
            body: JSON.stringify(documentPayload),
          });

          console.log("Request sent......");
          const result = await response.json();
          console.log(result);
        }
      }

      function handleAuthClick() {
        if (accessToken)
        {
        client.requestCode();
      }
    </script>
    <script
      async
      defer
      src="https://apis.google.com/js/api.js"
      onload="gapiLoaded()"
    ></script>

    <script
      src="https://accounts.google.com/gsi/client"
      onload="initClient()"
      async
      defer
    ></script>

and server side implementation is currently this

import os

from flask import Flask, request, url_for, render_template
from flask_cors import CORS
from google_auth_oauthlib.flow import Flow
from googleapiclient.discovery import build
from googleapiclient.http import MediaIoBaseDownload
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request


app = Flask(__name__)
CORS(app)

app.secret_key = "c8a8fe7b5ace3e7c662c202ce4910834"


# @app.route("/oauth2callback", methods=["POST"])
# def oauth_endpoint():
#     authorization_code = request.form.get("code")
#     scopes = request.form.get("scope").split()

#     flow = Flow.from_client_secrets_file(
#         "client_secret.json",
#         scopes=scopes,
#         redirect_uri="postmessage",
#     )
# authorization_url, state = flow.authorization_url(
#     # Enable offline access so that you can refresh an access token without
#     # re-prompting the user for permission. Recommended for web server apps.
#     access_type='offline',
#     # Enable incremental authorization. Recommended as a best practice.
#     include_granted_scopes='true')

#     print(url_for("oauth_endpoint", _external=True))
#     flow.fetch_token(code=authorization_code)
#     credentials = flow.credentials
#     with open("../credentials.json", "w") as creds:
#         creds.write(credentials.to_json())
#     print("credentials written, returning")
#     return {
#         "token": credentials.token,
#         "refresh_token": credentials.refresh_token,
#         "token_uri": credentials.token_uri,
#         "client_id": credentials.client_id,
#         "client_secret": credentials.client_secret,
#         "scopes": credentials.scopes,
#     }



@app.route("/oauth2callback", methods=["POST"])
def oauth_endpoint():
    authorization_code = request.form.get("code")
    scopes = request.form.get("scope").split()

    # Load existing credentials or create a new flow if none exist
    credentials_file = "../credentials.json"
    if os.path.exists(credentials_file):
        print('credentials')
        credentials = Credentials.from_authorized_user_file(credentials_file, scopes)
    else:
        flow = Flow.from_client_secrets_file(
        "credentials.json",
        scopes=scopes,redirect_uri="postmessage",
        )
        authorization_url, state = flow.authorization_url(
        # Enable offline access so that you can refresh an access token without
        # re-prompting the user for permission. Recommended for web server apps.
        access_type='offline',
        # Enable incremental authorization. Recommended as a best practice.
        include_granted_scopes='true')
        credentials = flow.run_local_server(port=0)

    # Check if the credentials are expired, and refresh if necessary
    if credentials.expired and credentials.refresh_token:
        credentials.refresh(Request())

        # Save the updated credentials back to the file
        with open(credentials_file, "w") as creds:
            creds.write(credentials.to_json())

    return {
        "token": credentials.token,
        "refresh_token": credentials.refresh_token,
        "token_uri": credentials.token_uri,
        "client_id": credentials.client_id,
        "client_secret": credentials.client_secret,
        "scopes": credentials.scopes,
    }

# implement a function to retrieve refresh token 
def retrieve_refresh_token():
    credentials_file = "../credentials.json"
    if os.path.exists(credentials_file):
        credentials = Credentials.from_authorized_user_file(credentials_file)
        return credentials.refresh_token
    else:
        return None

@app.route("/get-token", methods=['POST'])
def refresh_access_token():
    refresh_token=retrieve_refresh_token()
    credentials= Credentials.from_client_secrets_file('client_secret.json', refresh_token=refresh_token)
    if credentials.expired:
        credentials.refresh(Request())
    return credentials.token


def download(id, name):
    credentials = Credentials.from_authorized_user_file("../credentials.json")
    try:
        service = build("drive", "v3", credentials=credentials)
        print("service initialized")
        media_request = service.files().get_media(fileId=id)
        # file = io.BytesIO()
        downloadPath = os.path.join("../downloads/", name)
        print("downloadPath: ", downloadPath)
        with open(downloadPath, "wb") as file:
            downloader = MediaIoBaseDownload(file, media_request)
            done = False
            while done is False:
                status, done = downloader.next_chunk()
                print(f"Download {int(status.progress() * 100)}.")
    except:
        raise


@app.route("/gdrive/download", methods=["POST"])
def gdrive_download():
    print("here in download file")
    request_files = request.get_json()

    failed_files = []
    successful_files = []

    for file in request_files:
        file_id = file.get("fileId")
        file_name = file.get("fileName")
        try:
            download(file_id, file_name)
            successful_files.append(file_name)
        except Exception as e:
            print(e)
            failed_files.append(file_name)
    response_data = {"successful": successful_files, "failed": failed_files}

    return response_data, 201


@app.route("/")
def home():
    return render_template('implicitindex.html')

When I launch the picker it succesfully executes and even selects the files succesfully but the problem is everytime I want to select new file it asks for sign in and consent everytime even in the same session with a valid access token. What could be the solution ??

I have tried several approaches present in GIS platform and could not get actual implementation of both server and client side for authorization code flow approach.

0

There are 0 best solutions below