Skip to content

Instantly share code, notes, and snippets.

@IronSavior
Last active February 17, 2022 00:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save IronSavior/493f64c0084f45dbf14f2668e42b2c15 to your computer and use it in GitHub Desktop.
Save IronSavior/493f64c0084f45dbf14f2668e42b2c15 to your computer and use it in GitHub Desktop.
Google OAuth2 for local app
const fs = require('fs')
const { google } = require('googleapis')
const http = require('http')
const open = require('open')
const { promisify } = require('util')
const config = {
auth_scopes: [
'https://www.googleapis.com/auth/drive.readonly',
],
oauth_redirect_url: new URL('http://localhost:3300'),
oauth_client_file: '.oauth_client.json', // The JSON file you download from GCP console
oauth_token_file: '.refresh_token.json',
oauth_http_content: 'Done. You may close this window.',
}
const readFile = promisify(fs.readFile)
const writeFile = promisify(fs.writeFile)
// Open browser to authorization screen and listen for the code
function prompt_authorization(auth_url) {
return new Promise((pass, fail) => {
const live_socks = new Set()
const server = http.createServer((req, res) => {
const shutdown = _ => {
live_socks.forEach(sock => sock.destroy())
server.close()
}
const url = new URL(req.url, config.oauth_redirect_url)
const code = url.searchParams.get('code')
if (code === undefined || code === null || code == '') {
res.statusCode = 500
res.write('invalid code')
res.end()
setImmediate(shutdown)
fail(new Error(`invalid code "${code}"`))
return
}
res.write(config.oauth_http_content)
res.end()
pass(code)
setImmediate(shutdown)
})
server.on('listening', () => open(auth_url))
server.on('connection', sock => {
sock.on('close', sock => live_socks.delete(sock))
live_socks.add(sock)
})
server.listen(parseInt(config.oauth_redirect_url.port))
})
}
async function load_oauth_client() {
if (!fs.existsSync(config.oauth_client_file)) {
console.error(`credential file ${config.oauth_client_file} not found`)
process.exit(1)
}
return JSON.parse(await readFile(config.oauth_client_file)).installed
}
// Set up OAuth2, prompting the user for authorization if necessary
async function OAuth2(scope, redirect_url) {
const { client_id, client_secret } = await load_oauth_client()
const auth = new google.auth.OAuth2(client_id, client_secret, redirect_url.toString())
// Save refresh tokens when we receive them
auth.on('tokens', tokens => {
if (tokens.refresh_token) {
const token_json = JSON.stringify(tokens.refresh_token)
writeFile(config.oauth_token_file, token_json).catch(e => console.error('cannot save refresh token: %s', e))
}
})
if (fs.existsSync(config.oauth_token_file)) {
// try to load refresh token from previous run
const token = JSON.parse(await readFile(config.oauth_token_file))
auth.setCredentials({ refresh_token: token })
console.error(`Loaded refresh token`)
}
else {
// not authorized yet
console.error("OAuth2 user authorization is required, opening browser")
const url = auth.generateAuthUrl({ access_type: 'offline', scope })
const code = await prompt_authorization(url)
const { tokens } = await auth.getToken(code)
auth.setCredentials(tokens)
}
return auth
}
async function main() {
google.options({ auth: await OAuth2(config.auth_scopes, config.oauth_redirect_url) })
const res = await google.drive('v3').drives.list()
console.log(JSON.stringify(res.data))
}
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment