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.