mirror of
https://github.com/Myxelium/Bridge-Multi.git
synced 2026-07-07 18:45:09 +00:00
Made CLIENT_ID and CLIENT_SECRET server configurable
This commit is contained in:
@@ -1,10 +1,12 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-misused-promises */
|
/* eslint-disable @typescript-eslint/no-misused-promises */
|
||||||
/* eslint-disable @typescript-eslint/camelcase */
|
/* eslint-disable @typescript-eslint/camelcase */
|
||||||
import { dataPath, CLIENT_ID, CLIENT_SECRET, REDIRECT_URI } from '../../shared/Paths'
|
import { dataPath, REDIRECT_URI } from '../../shared/Paths'
|
||||||
import { mainWindow } from '../../main'
|
import { mainWindow } from '../../main'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { readFile, writeFile } from 'jsonfile'
|
import { readFile, writeFile } from 'jsonfile'
|
||||||
import { google } from 'googleapis'
|
import { google } from 'googleapis'
|
||||||
|
import { Credentials } from 'googleapis/node_modules/google-auth-library/build/src/auth/credentials'
|
||||||
|
import { OAuth2Client } from 'googleapis-common/node_modules/google-auth-library/build/src/auth/oauth2client'
|
||||||
import * as needle from 'needle'
|
import * as needle from 'needle'
|
||||||
import { authServer } from './AuthServer'
|
import { authServer } from './AuthServer'
|
||||||
import { BrowserWindow } from 'electron'
|
import { BrowserWindow } from 'electron'
|
||||||
@@ -18,74 +20,61 @@ const TOKEN_PATH = join(dataPath, 'token.json')
|
|||||||
|
|
||||||
export class GoogleAuth {
|
export class GoogleAuth {
|
||||||
|
|
||||||
private hasTriedTokenFile = false
|
|
||||||
private hasAuthenticated = false
|
private hasAuthenticated = false
|
||||||
|
|
||||||
|
private oAuth2Client: OAuth2Client = null
|
||||||
|
private token: Credentials = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to authenticate the googleapis library using the token stored at `TOKEN_PATH`.
|
* Attempts to authenticate the googleapis library using the token stored at `TOKEN_PATH`.
|
||||||
* @returns `true` if the user is authenticated, and `false` otherwise.
|
* @returns `true` if the user is authenticated, and `false` otherwise.
|
||||||
*/
|
*/
|
||||||
async attemptToAuthenticate() {
|
async attemptToAuthenticate() {
|
||||||
|
|
||||||
// TODO remove this workaround when Google's API stops being dumb
|
if (this.hasAuthenticated) {
|
||||||
// if (this.hasAuthenticated) {
|
return true
|
||||||
// return true
|
|
||||||
// }
|
|
||||||
// return new Promise<boolean>(resolve => {
|
|
||||||
// needle.request(
|
|
||||||
// 'get',
|
|
||||||
// serverURL + `/api/data/temp`, null, (err, response) => {
|
|
||||||
// if (err) {
|
|
||||||
// resolve(false)
|
|
||||||
// } else {
|
|
||||||
// if (!response.body.includes || (response.body as string)?.includes('<!DOCTYPE html>')) {
|
|
||||||
// resolve(false)
|
|
||||||
// } else {
|
|
||||||
// google.options({ auth: response.body })
|
|
||||||
// this.hasAuthenticated = true
|
|
||||||
// resolve(true)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// })
|
|
||||||
|
|
||||||
if (this.hasTriedTokenFile) {
|
|
||||||
return this.hasAuthenticated
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = await this.getStoredToken()
|
// Get client info from server
|
||||||
if (token != null) {
|
if (!await this.getOAuth2Client()) {
|
||||||
// Token has been restored from a previous session
|
|
||||||
const oAuth2Client = new google.auth.OAuth2(CLIENT_ID, CLIENT_SECRET, REDIRECT_URI)
|
|
||||||
oAuth2Client.setCredentials(token)
|
|
||||||
google.options({ auth: oAuth2Client })
|
|
||||||
this.hasAuthenticated = true
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
// Token doesn't exist; user has not authenticated
|
|
||||||
this.hasAuthenticated = false
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get stored token
|
||||||
|
if (!await this.getStoredToken()) {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Token has been restored from a previous session
|
||||||
|
this.authenticateWithToken()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses OAuth2 to generate a token that can be used to authenticate download requests.
|
||||||
|
* Involves displaying a popup window to the user.
|
||||||
|
* @returns true if the auth token was generated, and false otherwise.
|
||||||
|
*/
|
||||||
async generateAuthToken() {
|
async generateAuthToken() {
|
||||||
|
|
||||||
if (await this.getStoredToken() != null) { return true }
|
if (this.hasAuthenticated) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
if (this.hasTriedTokenFile == false) {
|
// Get client info from server
|
||||||
// Token exists but couldn't be read
|
if (!await this.getOAuth2Client()) {
|
||||||
console.log('Auth token exists but could not be loaded. Check file permissions.')
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const oAuth2Client = new google.auth.OAuth2(CLIENT_ID, CLIENT_SECRET, REDIRECT_URI)
|
|
||||||
let popupWindow: BrowserWindow
|
let popupWindow: BrowserWindow
|
||||||
let gotAuthCode = false
|
|
||||||
|
|
||||||
return new Promise<boolean>(resolve => {
|
return new Promise<boolean>(resolve => {
|
||||||
authServer.on('listening', () => {
|
authServer.on('listening', () => {
|
||||||
const authUrl = oAuth2Client.generateAuthUrl({
|
const authUrl = this.oAuth2Client.generateAuthUrl({
|
||||||
access_type: 'offline',
|
access_type: 'offline',
|
||||||
|
// This scope is too broad, but is the only one that will actually download files for some dumb reason.
|
||||||
|
// If you want this fixed, please upvote/star my issue on the Google bug tracker so they will fix it faster:
|
||||||
|
// https://issuetracker.google.com/issues/168687448
|
||||||
scope: ['https://www.googleapis.com/auth/drive.readonly'],
|
scope: ['https://www.googleapis.com/auth/drive.readonly'],
|
||||||
redirect_uri: REDIRECT_URI
|
redirect_uri: REDIRECT_URI
|
||||||
})
|
})
|
||||||
@@ -105,16 +94,15 @@ export class GoogleAuth {
|
|||||||
})
|
})
|
||||||
popupWindow.loadURL(authUrl, { userAgent: 'Chrome' })
|
popupWindow.loadURL(authUrl, { userAgent: 'Chrome' })
|
||||||
popupWindow.on('ready-to-show', () => popupWindow.show())
|
popupWindow.on('ready-to-show', () => popupWindow.show())
|
||||||
popupWindow.on('closed', () => resolve(gotAuthCode))
|
popupWindow.on('closed', () => resolve(this.hasAuthenticated))
|
||||||
})
|
})
|
||||||
|
|
||||||
authServer.on('authCode', async (authCode) => {
|
authServer.on('authCode', async (authCode) => {
|
||||||
const { tokens } = await oAuth2Client.getToken(authCode)
|
this.token = (await this.oAuth2Client.getToken(authCode)).tokens
|
||||||
oAuth2Client.setCredentials(tokens)
|
writeFile(TOKEN_PATH, this.token).catch(err => console.log('Got token, but failed to write it to TOKEN_PATH:', err))
|
||||||
google.options({ auth: oAuth2Client })
|
|
||||||
await writeFile(TOKEN_PATH, tokens)
|
this.authenticateWithToken()
|
||||||
this.hasTriedTokenFile = false
|
|
||||||
gotAuthCode = true
|
|
||||||
popupWindow.close()
|
popupWindow.close()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -122,29 +110,71 @@ export class GoogleAuth {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this.token as the credentials for this.oAuth2Client, and make the google library use this authentication.
|
||||||
|
* Assumes these have already been defined correctly.
|
||||||
|
*/
|
||||||
|
private authenticateWithToken() {
|
||||||
|
this.oAuth2Client.setCredentials(this.token)
|
||||||
|
google.options({ auth: this.oAuth2Client })
|
||||||
|
this.hasAuthenticated = true
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns the previously stored auth token, or `null` if it doesn't exist or can't be accessed.
|
* Attempts to get Bridge's client info from the server.
|
||||||
|
* @returns true if this.clientID and this.clientSecret have been set, and false if that failed.
|
||||||
|
*/
|
||||||
|
private async getOAuth2Client() {
|
||||||
|
if (this.oAuth2Client != null) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return new Promise<boolean>(resolve => {
|
||||||
|
needle.request(
|
||||||
|
'get',
|
||||||
|
serverURL + `/api/data/client`, null, (err, response) => {
|
||||||
|
if (err) {
|
||||||
|
console.log('Could not authenticate because client info could not be retrieved from the server.')
|
||||||
|
resolve(false)
|
||||||
|
} else {
|
||||||
|
this.oAuth2Client = new google.auth.OAuth2(response.body.CLIENT_ID, response.body.CLIENT_SECRET, REDIRECT_URI)
|
||||||
|
resolve(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to retrieve a previously stored auth token at `TOKEN_PATH`.
|
||||||
|
* Note: will not try again if this.token === undefined.
|
||||||
|
* @returns true if this.token has been set, and false if that failed or the token didn't exist.
|
||||||
*/
|
*/
|
||||||
private async getStoredToken() {
|
private async getStoredToken() {
|
||||||
this.hasTriedTokenFile = true
|
if (this.token === undefined) {
|
||||||
|
return false // undefined means no token file was found
|
||||||
|
} else if (this.token !== null) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
try {
|
try {
|
||||||
return await readFile(TOKEN_PATH)
|
this.token = await readFile(TOKEN_PATH)
|
||||||
|
return true
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err && err.code && err.code != 'ENOENT') {
|
if (err?.code && err?.code != 'ENOENT') {
|
||||||
// Failed to access the file; next attempt should try again
|
this.token = null // File exists but could not be accessed; next attempt should try again
|
||||||
this.hasTriedTokenFile = false
|
} else {
|
||||||
|
this.token = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* removes the previously stored auth token.
|
* Removes a previously stored auth token from `TOKEN_PATH`.
|
||||||
*/
|
*/
|
||||||
async deleteStoredToken() {
|
async deleteStoredToken() {
|
||||||
this.hasTriedTokenFile = false
|
this.token = undefined
|
||||||
this.hasAuthenticated = false
|
this.hasAuthenticated = false
|
||||||
try {
|
try {
|
||||||
await unlink(TOKEN_PATH)
|
await unlink(TOKEN_PATH)
|
||||||
|
|||||||
@@ -11,9 +11,7 @@ export const themesPath = join(dataPath, 'themes')
|
|||||||
// URL
|
// URL
|
||||||
export const serverURL = 'bridge-db.net'
|
export const serverURL = 'bridge-db.net'
|
||||||
|
|
||||||
// Google Project ID (More info on why these are here: https://developers.google.com/identity/protocols/oauth2#installed)
|
// OAuth callback server
|
||||||
export const CLIENT_ID = '668064259105-vkm77i5lcoo2oumk2eulik7bae8k5agf.apps.googleusercontent.com'
|
|
||||||
export const CLIENT_SECRET = 'RU69Ubr9CidGcI0Z23Ttn2ZV'
|
|
||||||
export const SERVER_PORT = 42813
|
export const SERVER_PORT = 42813
|
||||||
export const REDIRECT_BASE = `http://127.0.0.1:${SERVER_PORT}`
|
export const REDIRECT_BASE = `http://127.0.0.1:${SERVER_PORT}`
|
||||||
export const REDIRECT_PATH = `/oauth2callback`
|
export const REDIRECT_PATH = `/oauth2callback`
|
||||||
|
|||||||
Reference in New Issue
Block a user