aboutsummaryrefslogtreecommitdiff
path: root/src/server/apis/google/GoogleApiServerUtils.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/apis/google/GoogleApiServerUtils.ts')
-rw-r--r--src/server/apis/google/GoogleApiServerUtils.ts145
1 files changed, 97 insertions, 48 deletions
diff --git a/src/server/apis/google/GoogleApiServerUtils.ts b/src/server/apis/google/GoogleApiServerUtils.ts
index 8785cd974..c899c2ef2 100644
--- a/src/server/apis/google/GoogleApiServerUtils.ts
+++ b/src/server/apis/google/GoogleApiServerUtils.ts
@@ -1,11 +1,14 @@
-import { google, docs_v1, slides_v1 } from "googleapis";
+import { google } from "googleapis";
import { createInterface } from "readline";
import { readFile, writeFile } from "fs";
-import { OAuth2Client } from "google-auth-library";
+import { OAuth2Client, Credentials } from "google-auth-library";
import { Opt } from "../../../new_fields/Doc";
import { GlobalOptions } from "googleapis-common";
import { GaxiosResponse } from "gaxios";
-
+import request = require('request-promise');
+import * as qs from 'query-string';
+import Photos = require('googlephotos');
+import { Database } from "../../database";
/**
* Server side authentication for Google Api queries.
*/
@@ -20,6 +23,9 @@ export namespace GoogleApiServerUtils {
'presentations.readonly',
'drive',
'drive.file',
+ 'photoslibrary',
+ 'photoslibrary.appendonly',
+ 'photoslibrary.sharing'
];
export const parseBuffer = (data: Buffer) => JSON.parse(data.toString());
@@ -29,74 +35,124 @@ export namespace GoogleApiServerUtils {
Slides = "Slides"
}
-
- export interface CredentialPaths {
- credentials: string;
- token: string;
+ export interface CredentialInformation {
+ credentialsPath: string;
+ userId: string;
}
export type ApiResponse = Promise<GaxiosResponse>;
- export type ApiRouter = (endpoint: Endpoint, paramters: any) => ApiResponse;
- export type ApiHandler = (parameters: any) => ApiResponse;
+ export type ApiRouter = (endpoint: Endpoint, parameters: any) => ApiResponse;
+ export type ApiHandler = (parameters: any, methodOptions?: any) => ApiResponse;
export type Action = "create" | "retrieve" | "update";
export type Endpoint = { get: ApiHandler, create: ApiHandler, batchUpdate: ApiHandler };
export type EndpointParameters = GlobalOptions & { version: "v1" };
- export const GetEndpoint = async (sector: string, paths: CredentialPaths) => {
- return new Promise<Opt<Endpoint>>((resolve, reject) => {
- readFile(paths.credentials, (err, credentials) => {
+ export const GetEndpoint = (sector: string, paths: CredentialInformation) => {
+ return new Promise<Opt<Endpoint>>(resolve => {
+ RetrieveCredentials(paths).then(authentication => {
+ let routed: Opt<Endpoint>;
+ let parameters: EndpointParameters = { auth: authentication.client, version: "v1" };
+ switch (sector) {
+ case Service.Documents:
+ routed = google.docs(parameters).documents;
+ break;
+ case Service.Slides:
+ routed = google.slides(parameters).presentations;
+ break;
+ }
+ resolve(routed);
+ });
+ });
+ };
+
+ export const RetrieveAccessToken = (information: CredentialInformation) => {
+ return new Promise<string>((resolve, reject) => {
+ RetrieveCredentials(information).then(
+ credentials => resolve(credentials.token.access_token!),
+ error => reject(`Error: unable to authenticate Google Photos API request.\n${error}`)
+ );
+ });
+ };
+
+ export const RetrieveCredentials = (information: CredentialInformation) => {
+ return new Promise<TokenResult>((resolve, reject) => {
+ readFile(information.credentialsPath, async (err, credentials) => {
if (err) {
reject(err);
return console.log('Error loading client secret file:', err);
}
- return authorize(parseBuffer(credentials), paths.token).then(auth => {
- let routed: Opt<Endpoint>;
- let parameters: EndpointParameters = { auth, version: "v1" };
- switch (sector) {
- case Service.Documents:
- routed = google.docs(parameters).documents;
- break;
- case Service.Slides:
- routed = google.slides(parameters).presentations;
- break;
- }
- resolve(routed);
- });
+ authorize(parseBuffer(credentials), information.userId).then(resolve, reject);
});
});
};
+ export const RetrievePhotosEndpoint = (paths: CredentialInformation) => {
+ return new Promise<any>((resolve, reject) => {
+ RetrieveAccessToken(paths).then(
+ token => resolve(new Photos(token)),
+ reject
+ );
+ });
+ };
+ type TokenResult = { token: Credentials, client: OAuth2Client };
/**
* Create an OAuth2 client with the given credentials, and returns the promise resolving to the authenticated client
* @param {Object} credentials The authorization client credentials.
*/
- export function authorize(credentials: any, token_path: string): Promise<OAuth2Client> {
+ export function authorize(credentials: any, userId: string): Promise<TokenResult> {
const { client_secret, client_id, redirect_uris } = credentials.installed;
- const oAuth2Client = new google.auth.OAuth2(
- client_id, client_secret, redirect_uris[0]);
-
- return new Promise<OAuth2Client>((resolve, reject) => {
- readFile(token_path, (err, token) => {
- // Check if we have previously stored a token.
- if (err) {
- return getNewToken(oAuth2Client, token_path).then(resolve, reject);
+ const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]);
+ return new Promise<TokenResult>((resolve, reject) => {
+ // Attempting to authorize user (${userId})
+ Database.Auxiliary.GoogleAuthenticationToken.Fetch(userId).then(token => {
+ if (!token) {
+ // No token registered, so awaiting input from user
+ return getNewToken(oAuth2Client, userId).then(resolve, reject);
}
- oAuth2Client.setCredentials(parseBuffer(token));
- resolve(oAuth2Client);
+ if (token.expiry_date! < new Date().getTime()) {
+ // Token has expired, so submitting a request for a refreshed access token
+ return refreshToken(token, client_id, client_secret, oAuth2Client, userId).then(resolve, reject);
+ }
+ // Authentication successful!
+ oAuth2Client.setCredentials(token);
+ resolve({ token, client: oAuth2Client });
});
});
}
+ const refreshEndpoint = "https://oauth2.googleapis.com/token";
+ const refreshToken = (credentials: Credentials, client_id: string, client_secret: string, oAuth2Client: OAuth2Client, userId: string) => {
+ return new Promise<TokenResult>(resolve => {
+ let headerParameters = { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } };
+ let queryParameters = {
+ refreshToken: credentials.refresh_token,
+ client_id,
+ client_secret,
+ grant_type: "refresh_token"
+ };
+ let url = `${refreshEndpoint}?${qs.stringify(queryParameters)}`;
+ request.post(url, headerParameters).then(async response => {
+ let { access_token, expires_in } = JSON.parse(response);
+ const expiry_date = new Date().getTime() + (expires_in * 1000);
+ await Database.Auxiliary.GoogleAuthenticationToken.Update(userId, access_token, expiry_date);
+ credentials.access_token = access_token;
+ credentials.expiry_date = expiry_date;
+ oAuth2Client.setCredentials(credentials);
+ resolve({ token: credentials, client: oAuth2Client });
+ });
+ });
+ };
+
/**
* Get and store new token after prompting for user authorization, and then
* execute the given callback with the authorized OAuth2 client.
* @param {google.auth.OAuth2} oAuth2Client The OAuth2 client to get token for.
* @param {getEventsCallback} callback The callback for the authorized client.
*/
- function getNewToken(oAuth2Client: OAuth2Client, token_path: string) {
- return new Promise<OAuth2Client>((resolve, reject) => {
+ function getNewToken(oAuth2Client: OAuth2Client, userId: string) {
+ return new Promise<TokenResult>((resolve, reject) => {
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES.map(relative => prefix + relative),
@@ -108,21 +164,14 @@ export namespace GoogleApiServerUtils {
});
rl.question('Enter the code from that page here: ', (code) => {
rl.close();
- oAuth2Client.getToken(code, (err, token) => {
+ oAuth2Client.getToken(code, async (err, token) => {
if (err || !token) {
reject(err);
return console.error('Error retrieving access token', err);
}
oAuth2Client.setCredentials(token);
- // Store the token to disk for later program executions
- writeFile(token_path, JSON.stringify(token), (err) => {
- if (err) {
- console.error(err);
- reject(err);
- }
- console.log('Token stored to', token_path);
- });
- resolve(oAuth2Client);
+ await Database.Auxiliary.GoogleAuthenticationToken.Write(userId, token);
+ resolve({ token, client: oAuth2Client });
});
});
});