I wanted to make a simple app that would ask user’s permission to create and modify spreadsheets on their drive. The client would log in, and then send their log-in details to the Express server to handle api calls on behalf of the user. As far as I’m aware, this is possible with GAPI.
I begun following these instructions and implementing them into my code:
- Resource 1
- Resource 2
I went onto G Cloud Platform and created the project and enabled Sheets Api. I created OAuth 2.0 creds and an api key. I also set the “Authorized JavaScript origins” and “Authorized redirect URIs” to my public site’s IP and tried setting it to allow from localhost:8000 but THAT part doesnt work. So to test changes, I have to publish EVERY commit to heroku. but thats not the real problem.
The real problem is that I have no idea how to allow the server to modify the client’s sheets in their place. I follow this resource to get started with the code, since it looked mostly backend, but then it gets the OAuth2.0 token by generating a link and asking the user to “go to it and insert the code found on the link” (the link is a google sign in page that will redirect).
Upon signing in, nothing happens except the URL gets a code parameter. Problem is, I couldn’t get the server to redirect the user to the site, then back, to get the url params without CORS throwing a fit. This attempt was a simple Express route. When a user calls /sheets, it would first try to authenticate, then create a sheet. I had a auth method prepared that looks like this:
const {google} = require('googleapis');
const sheets = google.sheets('v4');
const readline=require("readline")
const fs = require("fs")
const TOKEN_PATH = 'token.json';
// If modifying these scopes, delete token.json.
const SCOPES = ['https://www.googleapis.com/auth/spreadsheets'];
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
const auth=(req,res,next)=>{
// Load client secrets from a local file.
fs.readFile('./utils/credentials.json', (err, content) => {
if (err) return console.log('Error loading client secret file:', err);
// Authorize a client with credentials, then call the Google Sheets API.
const {client_secret, client_id, redirect_uris} = JSON.parse(content);
const oAuth2Client = new google.auth.OAuth2(
client_id, client_secret, redirect_uris[0]
);
// Check if we have previously stored a token.
fs.readFile(TOKEN_PATH, (err, token) => {
console.log(err,token);
if (err){
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES,
});
console.log("redirecting");
console.log(authUrl);
// res.header("Access-Control-Allow-Origin", "*")//attempt to bypass cors. doesnt work
res.redirect(authUrl)//redirect to URL generated to auth. wont work because of CORS.
}else{
oAuth2Client.setCredentials(JSON.parse(token));
// callback(oAuth2Client);
next()
}
});
});
}
module.exports=auth
but this was just my first attempt. I know this site practically bans people for asking questions, so I wanted to give it as many attempts as possible before posting and logging EVERYTHING. The second attempt was to follow how to auth through the client. The next resource I used was this: resource 4. On here, the scripts would create an auth button and a log out button and then display some basic data from a spreadsheet, courtesy of Google. This works, but its not feasible to have the client make the calls to gapi for my project. So, I created a button that would create the spreadsheet if the user is logged in. The code is this:
async function main () {
const googleAuth = gapi.auth2.getAuthInstance()
const googleUser=googleAuth.currentUser.get()
console.log(googleUser);
const TOKEN =googleUser.getAuthResponse().id_token
console.log("token: "+TOKEN);
const response = await fetch(`/sheet`, {
method: 'POST',
body: JSON.stringify({
token:TOKEN
}),
});
if (response.ok) {
console.log("success");
} else {
alert(response.statusText);
}
}
document.getElementById("create_spreadsheet_btn").addEventListener("click",(event)=>{
event.preventDefault()
main();
})
now, the resource that easily explains how to create a spreadsheet is this: resource 5, but in it, it requires the user’s auth/log/whatever. I honestly dont know, and the documentation refers to this: resource 6 to authenticate a user. But that is where thats just resource 3 all over again. I just couldnt get express to redirect the user to the auth screen because of CORS.
Honestly, I love the amount of simple document Google has, but I feel like they left out the MOST important part; the authentication! it sucks when resource 5 has almost everything but the way to get the server to act as the user with their credentials (the bottom part).
How is something like this supposed to work? I’m assuming that signing it through the client side is super easy, and their examples do that just fine, but how is an express server supposed to know you’re signed in? And how is it supposed to make API calls without your credentials? I even tried getting the current user’s id_token and passing that through, but it still says the “user is not logged in” on the server side. How do I pass through a logged in user on the server side?
So far, all I got is this:
Client side:
async function main () {
const googleAuth = gapi.auth2.getAuthInstance()
const googleUser=googleAuth.currentUser.get()
console.log(googleUser);
const TOKEN =googleUser.getAuthResponse().id_token
console.log("token: "+TOKEN);
const response = await fetch(`/sheet`, {
method: 'POST',
body: JSON.stringify({
token:TOKEN
}),
});
if (response.ok) {
console.log("success");
} else {
alert(response.statusText);
}
}
document.getElementById("create_spreadsheet_btn").addEventListener("click",(event)=>{
event.preventDefault()
main();
})
server side, express routing:
const Router=require("express").Router()
//const auth=require("../utils/auth") couldnt redirect user.
const {google} = require('googleapis');
const sheets = google.sheets('v4');
Router.get("/",(req,res)=>{
res.render("home")
})
Router.post("/sheet",async (req,res)=>{
try {
console.log("req.body: "+req.body);
console.log("req.body.token: "+req.body.token);
const request = {
resource: {
// TODO: Add desired properties to the request body.
},
auth: req.body.token, //WHAT IS THE AUTH? WHAT DOES THIS EVEN LOOK LIKE?
};
// google.client.setToken({
// access_token: req.body.token
// })
const response = (await sheets.spreadsheets.create(request)).data;
// TODO: Change code below to process the `response` object:
console.log(JSON.stringify(response, null, 2));
res.json("User is logged in")
} catch (error) {
console.error(error);
res.json(error)
}
})
module.exports=Router
I appreciate any advice! Please dont downvote, I really did my research. (there like 20 stackoverflow resources, but none of them are bring new insight)