From c56b602e892707dbc7e22be2edba75f49a465ec7 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Mon, 28 Oct 2019 15:48:54 -0400 Subject: server utils beginning commenting --- src/server/apis/google/GoogleApiServerUtils.ts | 168 ++++++++++++++++++++----- 1 file changed, 139 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/server/apis/google/GoogleApiServerUtils.ts b/src/server/apis/google/GoogleApiServerUtils.ts index 4e5175a2b..9071b0485 100644 --- a/src/server/apis/google/GoogleApiServerUtils.ts +++ b/src/server/apis/google/GoogleApiServerUtils.ts @@ -7,10 +7,21 @@ import { GaxiosResponse } from "gaxios"; import request = require('request-promise'); import * as qs from 'query-string'; import { Database } from "../../database"; -const path = require("path"); +import path from "path"; +/** + * + */ const prefix = 'https://www.googleapis.com/auth/'; + +/** + * + */ const refreshEndpoint = "https://oauth2.googleapis.com/token"; + +/** + * + */ const SCOPES = [ 'documents.readonly', 'documents', @@ -25,23 +36,31 @@ const SCOPES = [ ]; /** - * Server side authentication for Google Api queries. + * This namespace manages server side authentication for Google API queries, either + * from the standard v1 APIs or the Google Photos REST API. */ export namespace GoogleApiServerUtils { - export type EnrichedCredentials = Credentials & { userInfo: UserInfo }; - + /** + * + */ export interface GoogleAuthenticationResult { access_token: string; avatar: string; name: string; } + /** + * + */ export interface CredentialsResult { credentials: Credentials; refreshed: boolean; } + /** + * + */ export interface UserInfo { at_hash: string; aud: string; @@ -57,9 +76,74 @@ export namespace GoogleApiServerUtils { sub: string; } + /** + * + */ + export enum Service { + Documents = "Documents", + Slides = "Slides" + } + + /** + * + */ + export interface CredentialInformation { + credentialsPath: string; + userId: string; + } + + /** + * + */ let installed: OAuth2ClientOptions; + + /** + * This is a global authorization client that is never + * passed around, and whose credentials are never set. + * Its job is purely to generate new authentication urls + * (users will follow to get to Google's permissions GUI) + * and to use the codes returned from that process to generate the + * initial credentials. + */ let worker: OAuth2Client; + /** + * + */ + export type ApiResponse = Promise; + + /** + * + */ + export type ApiRouter = (endpoint: Endpoint, parameters: any) => ApiResponse; + + /** + * + */ + export type ApiHandler = (parameters: any, methodOptions?: any) => ApiResponse; + + /** + * + */ + export type Action = "create" | "retrieve" | "update"; + + /** + * + */ + export interface Endpoint { + get: ApiHandler; + create: ApiHandler; + batchUpdate: ApiHandler; + } + + /** + * + */ + export type EndpointParameters = GlobalOptions & { version: "v1" }; + + /** + * + */ export const loadClientSecret = async () => { return new Promise((resolve, reject) => { readFile(path.join(__dirname, "../../credentials/google_docs_credentials.json"), async (err, projectCredentials) => { @@ -68,36 +152,28 @@ export namespace GoogleApiServerUtils { return console.log('Error loading client secret file:', err); } const { client_secret, client_id, redirect_uris } = JSON.parse(projectCredentials.toString()).installed; - worker = new google.auth.OAuth2({ + // initialize the global authorization client + installed = { clientId: client_id, clientSecret: client_secret, redirectUri: redirect_uris[0] - }); + }; + worker = generateClient(); resolve(); }); }); }; + /** + * + */ const authenticationClients = new Map(); - export enum Service { - Documents = "Documents", - Slides = "Slides" - } - - export interface CredentialInformation { - credentialsPath: string; - userId: string; - } - - export type ApiResponse = Promise; - 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" }; - + /** + * + * @param sector + * @param userId + */ export const GetEndpoint = (sector: string, userId: string) => { return new Promise>(resolve => { retrieveOAuthClient(userId).then(auth => { @@ -116,6 +192,10 @@ export namespace GoogleApiServerUtils { }); }; + /** + * + * @param userId + */ export const retrieveAccessToken = (userId: string): Promise => { return new Promise((resolve, reject) => { retrieveCredentials(userId).then( @@ -125,13 +205,17 @@ export namespace GoogleApiServerUtils { }); }; + /** + * + * @param userId + */ export const retrieveOAuthClient = (userId: string): Promise => { return new Promise((resolve, reject) => { retrieveCredentials(userId).then( ({ credentials, refreshed }) => { let client = authenticationClients.get(userId); if (!client) { - authenticationClients.set(userId, client = generateClientWith(credentials)); + authenticationClients.set(userId, client = generateClient(credentials)); } else if (refreshed) { client.setCredentials(credentials); } @@ -142,12 +226,19 @@ export namespace GoogleApiServerUtils { }); }; - function generateClientWith(credentials: Credentials) { + /** + * + * @param credentials + */ + function generateClient(credentials?: Credentials) { const client = new google.auth.OAuth2(installed); - client.setCredentials(credentials); + credentials && client.setCredentials(credentials); return client; } + /** + * + */ export const generateAuthenticationUrl = async () => { return worker.generateAuthUrl({ access_type: 'offline', @@ -155,6 +246,11 @@ export namespace GoogleApiServerUtils { }); }; + /** + * + * @param userId + * @param authenticationCode + */ export const processNewUser = async (userId: string, authenticationCode: string): Promise => { return new Promise((resolve, reject) => { worker.getToken(authenticationCode, async (err, credentials) => { @@ -174,6 +270,11 @@ export namespace GoogleApiServerUtils { }); }; + /** + * + */ + export type EnrichedCredentials = Credentials & { userInfo: UserInfo }; + /** * It's pretty cool: the credentials id_token is split into thirds by periods. * The middle third contains a base64-encoded JSON string with all the @@ -187,6 +288,10 @@ export namespace GoogleApiServerUtils { return { ...credentials, userInfo }; }; + /** + * + * @param userId + */ const retrieveCredentials = async (userId: string): Promise => { return new Promise((resolve, reject) => { Database.Auxiliary.GoogleAuthenticationToken.Fetch(userId).then(credentials => { @@ -203,13 +308,18 @@ export namespace GoogleApiServerUtils { }); }; + /** + * + * @param credentials + * @param userId + */ const refreshAccessToken = (credentials: Credentials, userId: string) => { return new Promise(resolve => { let headerParameters = { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }; let queryParameters = { refreshToken: credentials.refresh_token, - ...installed, - grant_type: "refresh_token" + grant_type: "refresh_token", + ...installed }; let url = `${refreshEndpoint}?${qs.stringify(queryParameters)}`; request.post(url, headerParameters).then(async response => { -- cgit v1.2.3-70-g09d2