aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Wilkins <samwilkins333@gmail.com>2019-10-27 21:05:01 -0400
committerSam Wilkins <samwilkins333@gmail.com>2019-10-27 21:05:01 -0400
commitf0f3dddbe1d3ac54d3754bb913b8ecd9eb6fcc63 (patch)
tree9eee41196ab94669013cad9ffafc9fb90ffcef9d
parentfeec691275ec83e4ddd8fd8ea803f004a371cf11 (diff)
further cleanup for oauth, separated access token function from client function
-rw-r--r--src/server/apis/google/GoogleApiServerUtils.ts65
-rw-r--r--src/server/apis/google/GooglePhotosUploadUtils.ts19
-rw-r--r--src/server/database.ts2
-rw-r--r--src/server/index.ts24
4 files changed, 47 insertions, 63 deletions
diff --git a/src/server/apis/google/GoogleApiServerUtils.ts b/src/server/apis/google/GoogleApiServerUtils.ts
index c0824cfb7..88f0f3377 100644
--- a/src/server/apis/google/GoogleApiServerUtils.ts
+++ b/src/server/apis/google/GoogleApiServerUtils.ts
@@ -15,7 +15,6 @@ const path = require("path");
*/
export namespace GoogleApiServerUtils {
- // If modifying these scopes, delete token.json.
const prefix = 'https://www.googleapis.com/auth/';
const SCOPES = [
'documents.readonly',
@@ -54,7 +53,7 @@ export namespace GoogleApiServerUtils {
export const GetEndpoint = (sector: string, userId: string) => {
return new Promise<Opt<Endpoint>>(resolve => {
- authorize(userId).then(({ client: auth }) => {
+ retrieveOAuthClient(userId).then(auth => {
let routed: Opt<Endpoint>;
let parameters: EndpointParameters = { auth, version: "v1" };
switch (sector) {
@@ -70,10 +69,23 @@ export namespace GoogleApiServerUtils {
});
};
- export const RetrieveAccessToken = (userId: string): Promise<string> => {
+ export const retrieveAccessToken = (userId: string): Promise<string> => {
return new Promise<string>((resolve, reject) => {
- authorize(userId).then(
- ({ token: { access_token } }) => resolve(access_token!),
+ retrieveCredentials(userId).then(
+ ({ access_token }) => resolve(access_token!),
+ error => reject(`Error: unable to authenticate Google Photos API request.\n${error}`)
+ );
+ });
+ };
+
+ export const retrieveOAuthClient = (userId: string): Promise<OAuth2Client> => {
+ return new Promise<OAuth2Client>((resolve, reject) => {
+ retrieveCredentials(userId).then(
+ credentials => {
+ const client = generateClient();
+ client.setCredentials(credentials);
+ resolve(client);
+ },
error => reject(`Error: unable to authenticate Google Photos API request.\n${error}`)
);
});
@@ -82,7 +94,7 @@ export namespace GoogleApiServerUtils {
let installed: OAuth2ClientOptions;
let worker: OAuth2Client;
- export const LoadOAuthClient = async () => {
+ export const loadClientSecret = async () => {
return new Promise<void>((resolve, reject) => {
readFile(path.join(__dirname, "../../credentials/google_docs_credentials.json"), async (err, credentials) => {
if (err) {
@@ -90,7 +102,7 @@ export namespace GoogleApiServerUtils {
return console.log('Error loading client secret file:', err);
}
installed = parseBuffer(credentials).installed;
- worker = new google.auth.OAuth2(installed);
+ worker = generateClient();
resolve();
});
});
@@ -98,7 +110,7 @@ export namespace GoogleApiServerUtils {
const generateClient = () => new google.auth.OAuth2(installed);
- export const GenerateAuthenticationUrl = async (information: CredentialInformation) => {
+ export const generateAuthenticationUrl = async () => {
return worker.generateAuthUrl({
access_type: 'offline',
scope: SCOPES.map(relative => prefix + relative),
@@ -110,7 +122,7 @@ export namespace GoogleApiServerUtils {
avatar: string;
name: string;
}
- export const ProcessClientSideCode = async (userId: string, authenticationCode: string): Promise<GoogleAuthenticationResult> => {
+ export const processNewUser = async (userId: string, authenticationCode: string): Promise<GoogleAuthenticationResult> => {
return new Promise<GoogleAuthenticationResult>((resolve, reject) => {
worker.getToken(authenticationCode, async (err, token) => {
if (err || !token) {
@@ -158,35 +170,25 @@ export namespace GoogleApiServerUtils {
sub: string;
}
- export const authorize = async (userId: string): Promise<AuthenticationResult> => {
- return Database.Auxiliary.GoogleAuthenticationToken.Fetch(userId).then(token => {
- return new Promise<AuthenticationResult>((resolve, reject) => {
- const client = generateClient();
- if (token!.expiry_date! < new Date().getTime()) {
+ const retrieveCredentials = async (userId: string): Promise<Credentials> => {
+ return new Promise<Credentials>((resolve, reject) => {
+ Database.Auxiliary.GoogleAuthenticationToken.Fetch(userId).then(credentials => {
+ if (!credentials) {
+ return reject();
+ }
+ if (credentials!.expiry_date! < new Date().getTime()) {
// Token has expired, so submitting a request for a refreshed access token
- return refreshToken(token!, client, userId).then(resolve, reject);
+ return refreshAccessToken(credentials!, userId).then(resolve, reject);
}
// Authentication successful!
- client.setCredentials(token!);
- resolve({ token: token!, client });
+ resolve(credentials);
});
});
};
- export const RetrievePhotosEndpoint = (userId: string) => {
- return new Promise<any>((resolve, reject) => {
- RetrieveAccessToken(userId).then(
- token => resolve(new Photos(token)),
- reject
- );
- });
- };
-
- type AuthenticationResult = { token: Credentials, client: OAuth2Client };
-
const refreshEndpoint = "https://oauth2.googleapis.com/token";
- const refreshToken = (credentials: Credentials, oAuth2Client: OAuth2Client, userId: string) => {
- return new Promise<AuthenticationResult>(resolve => {
+ const refreshAccessToken = (credentials: Credentials, userId: string) => {
+ return new Promise<Credentials>(resolve => {
let headerParameters = { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } };
let queryParameters = {
refreshToken: credentials.refresh_token,
@@ -200,8 +202,7 @@ export namespace GoogleApiServerUtils {
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 });
+ resolve(credentials);
});
});
};
diff --git a/src/server/apis/google/GooglePhotosUploadUtils.ts b/src/server/apis/google/GooglePhotosUploadUtils.ts
index 4a67e57cc..d704faa71 100644
--- a/src/server/apis/google/GooglePhotosUploadUtils.ts
+++ b/src/server/apis/google/GooglePhotosUploadUtils.ts
@@ -20,19 +20,12 @@ export namespace GooglePhotosUploadUtils {
}
const prepend = (extension: string) => `https://photoslibrary.googleapis.com/v1/${extension}`;
- const headers = (type: string) => ({
+ const headers = (type: string, token: string) => ({
'Content-Type': `application/${type}`,
- 'Authorization': Bearer,
+ 'Authorization': token,
});
- let Bearer: string;
-
- export const initialize = async (information: GoogleApiServerUtils.CredentialInformation) => {
- const token = await GoogleApiServerUtils.RetrieveAccessToken(information);
- Bearer = `Bearer ${token}`;
- };
-
- export const DispatchGooglePhotosUpload = async (url: string) => {
+ export const DispatchGooglePhotosUpload = async (bearerToken: string, url: string) => {
if (!DashUploadUtils.imageFormats.includes(path.extname(url))) {
return undefined;
}
@@ -40,7 +33,7 @@ export namespace GooglePhotosUploadUtils {
const parameters = {
method: 'POST',
headers: {
- ...headers('octet-stream'),
+ ...headers('octet-stream', bearerToken),
'X-Goog-Upload-File-Name': path.basename(url),
'X-Goog-Upload-Protocol': 'raw'
},
@@ -56,13 +49,13 @@ export namespace GooglePhotosUploadUtils {
}));
};
- export const CreateMediaItems = async (newMediaItems: NewMediaItem[], album?: { id: string }): Promise<MediaItemCreationResult> => {
+ export const CreateMediaItems = async (bearerToken: string, newMediaItems: NewMediaItem[], album?: { id: string }): Promise<MediaItemCreationResult> => {
const newMediaItemResults = await BatchedArray.from(newMediaItems, { batchSize: 50 }).batchedMapPatientInterval(
{ magnitude: 100, unit: TimeUnit.Milliseconds },
async (batch: NewMediaItem[]) => {
const parameters = {
method: 'POST',
- headers: headers('json'),
+ headers: headers('json', bearerToken),
uri: prepend('mediaItems:batchCreate'),
body: { newMediaItems: batch } as any,
json: true
diff --git a/src/server/database.ts b/src/server/database.ts
index 12626e594..79dd26b7d 100644
--- a/src/server/database.ts
+++ b/src/server/database.ts
@@ -298,7 +298,7 @@ export namespace Database {
export type StoredCredentials = Credentials & { _id: string };
- export const Fetch = async (userId: string, removeId = true) => {
+ export const Fetch = async (userId: string, removeId = true): Promise<Opt<StoredCredentials>> => {
return SanitizedSingletonQuery<StoredCredentials>({ userId }, GoogleAuthentication, removeId);
};
diff --git a/src/server/index.ts b/src/server/index.ts
index 3220a9533..24866a5e5 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -65,7 +65,7 @@ async function PreliminaryFunctions() {
resolve();
});
});
- await GoogleApiServerUtils.LoadOAuthClient();
+ await GoogleApiServerUtils.loadClientSecret();
await DashUploadUtils.createIfNotExists(pdfDirectory);
await Database.tryInitializeConnection();
}
@@ -553,8 +553,6 @@ function routeSetter(router: RouteManager) {
}
});
- const credentialsPath = path.join(__dirname, "./credentials/google_docs_credentials.json");
-
const EndpointHandlerMap = new Map<GoogleApiServerUtils.Action, GoogleApiServerUtils.ApiRouter>([
["create", (api, params) => api.create(params)],
["retrieve", (api, params) => api.get(params)],
@@ -588,11 +586,10 @@ function routeSetter(router: RouteManager) {
onValidation: async ({ user, res }) => {
const userId = user.id;
const token = await Database.Auxiliary.GoogleAuthenticationToken.Fetch(userId);
- const information = { credentialsPath, userId };
if (!token) {
- return res.send(await GoogleApiServerUtils.GenerateAuthenticationUrl(information));
+ return res.send(await GoogleApiServerUtils.generateAuthenticationUrl());
}
- GoogleApiServerUtils.RetrieveAccessToken(userId).then(token => res.send(token));
+ GoogleApiServerUtils.retrieveAccessToken(userId).then(token => res.send(token));
}
});
@@ -600,35 +597,28 @@ function routeSetter(router: RouteManager) {
method: Method.POST,
subscription: RouteStore.writeGoogleAccessToken,
onValidation: async ({ user, req, res }) => {
- res.send(await GoogleApiServerUtils.ProcessClientSideCode(user.id, req.body.authenticationCode));
+ res.send(await GoogleApiServerUtils.processNewUser(user.id, req.body.authenticationCode));
}
});
const tokenError = "Unable to successfully upload bytes for all images!";
const mediaError = "Unable to convert all uploaded bytes to media items!";
- const userIdError = "Unable to parse the identification of the user!";
router.addSupervisedRoute({
method: Method.POST,
subscription: RouteStore.googlePhotosMediaUpload,
onValidation: async ({ user, req, res }) => {
const { media } = req.body;
- const userId = user.id;
- if (!userId) {
- return _error(res, userIdError);
- }
-
- await GooglePhotosUploadUtils.initialize({ credentialsPath, userId });
let failed: number[] = [];
-
+ const token = await GoogleApiServerUtils.retrieveAccessToken(user.id);
const newMediaItems = await BatchedArray.from<GooglePhotosUploadUtils.MediaInput>(media, { batchSize: 25 }).batchedMapPatientInterval(
{ magnitude: 100, unit: TimeUnit.Milliseconds },
async (batch: GooglePhotosUploadUtils.MediaInput[]) => {
const newMediaItems: NewMediaItem[] = [];
for (let index = 0; index < batch.length; index++) {
const element = batch[index];
- const uploadToken = await GooglePhotosUploadUtils.DispatchGooglePhotosUpload(element.url);
+ const uploadToken = await GooglePhotosUploadUtils.DispatchGooglePhotosUpload(token, element.url);
if (!uploadToken) {
failed.push(index);
} else {
@@ -647,7 +637,7 @@ function routeSetter(router: RouteManager) {
console.error(`Unable to upload ${failedCount} image${failedCount === 1 ? "" : "s"} to Google's servers`);
}
- GooglePhotosUploadUtils.CreateMediaItems(newMediaItems, req.body.album).then(
+ GooglePhotosUploadUtils.CreateMediaItems(token, newMediaItems, req.body.album).then(
result => _success(res, { results: result.newMediaItemResults, failed }),
error => _error(res, mediaError, error)
);