diff --git a/package.json b/package.json
index 0139002..84da454 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,6 @@
"homepage": "https://accessitech.github.io/tracker/",
"license": "MIT",
"dependencies": {
- "@react-oauth/google": "^0.5.1",
"@reduxjs/toolkit": "^1.8.2",
"formik": "^2.2.9",
"react": "^18.2.0",
diff --git a/src/App/App.tsx b/src/App/App.tsx
index 049aeee..8c4267e 100644
--- a/src/App/App.tsx
+++ b/src/App/App.tsx
@@ -1,6 +1,5 @@
import React, { FC, ReactElement, useEffect } from "react";
import { HashRouter, Routes, Route } from "react-router-dom";
-import { googleLogout, GoogleOAuthProvider } from '@react-oauth/google';
import { Home } from "../containers/Home";
import { Edit } from "../containers/Edit";
import { Log } from "../containers/Log";
@@ -10,31 +9,37 @@ import { EDIT_URL, EMPTY, ENTRY_EDIT_URL, ENTRY_URL, FIELD_URL, HOME_URL, LOG_ID
import { Toaster, ToastType } from "../components/Toaster";
import { deauthenticate, useSession } from "../store/Session/reducer";
import store from "../store/store";
-import { setLogoutTimer } from "../components/GoogleAuth";
+import { authenticateUser, deauthenticateUser, initGoogleAuth, setLogoutTimer } from "../components/GoogleApi";
export const App: FC = (): ReactElement => {
+ const apiKey = process.env.REACT_APP_G_API_KEY as string;
const clientId = process.env.REACT_APP_G_CLIENT_ID as string;
const session = useSession();
const { authenticated, expiresAt, data } = session;
const handleLogout = (): void => {
- googleLogout();
- store.dispatch(deauthenticate(''));
+ deauthenticateUser(() => {
+ store.dispatch(deauthenticate(''));
+ })
};
useEffect(() => {
- if (authenticated) {
- if (expiresAt && expiresAt < Date.now()) {
- handleLogout();
- } else {
- setLogoutTimer({
- logoutCallback: handleLogout,
- timeout: expiresAt - Date.now(),
- // autoRefresh,
- sessionData: data,
- });
+ initGoogleAuth({ apiKey, clientId }, () => {
+ if (authenticated) {
+ if (expiresAt && expiresAt < Date.now()) {
+ handleLogout();
+ } else {
+ authenticateUser(() => {
+ setLogoutTimer({
+ logoutCallback: handleLogout,
+ timeout: expiresAt - Date.now(),
+ // autoRefresh,
+ sessionData: data,
+ });
+ }, data.access_token && data);
+ }
}
- }
+ });
}, []);
const [toast, setToast] = React.useState({
@@ -45,7 +50,7 @@ export const App: FC = (): ReactElement => {
} as ToastType);
return (
-
+ <>
404} />
@@ -72,7 +77,7 @@ export const App: FC = (): ReactElement => {
-
+ >
);
};
diff --git a/src/components/GoogleApi/GoogleAuth.ts b/src/components/GoogleApi/GoogleAuth.ts
new file mode 100644
index 0000000..52a949b
--- /dev/null
+++ b/src/components/GoogleApi/GoogleAuth.ts
@@ -0,0 +1,164 @@
+import { EmptyFunction } from "./GoogleAuthButton";
+
+export const DISCOVERY_DOC =
+ "https://www.googleapis.com/discovery/v1/apis/drive/v3/rest";
+export const SCOPES = ["https://www.googleapis.com/auth/drive"];
+
+let apiInitialized = false;
+let googleInitialized = false;
+let tokenClient: any;
+let gapi: any;
+let google: any;
+
+export type ErrorCode =
+ | "invalid_request"
+ | "access_denied"
+ | "unauthorized_client"
+ | "unsupported_response_type"
+ | "invalid_scope"
+ | "server_error"
+ | "temporarily_unavailable";
+
+export interface TokenResponse {
+ /** The access token of a successful token response. */
+ access_token: string;
+ /** The lifetime in seconds of the access token. */
+ expires_in: number;
+ /** The hosted domain the signed-in user belongs to. */
+ hd?: string;
+ /** The prompt value that was used from the possible list of values specified by TokenClientConfig or OverridableTokenClientConfig */
+ prompt: string;
+ /** The type of the token issued. */
+ token_type: string;
+ /** A space-delimited list of scopes that are approved by the user. */
+ scope: string;
+ /** The string value that your application uses to maintain state between your authorization request and the response. */
+ state?: string;
+ /** A single ASCII error code. */
+ error?: ErrorCode;
+ /** Human-readable ASCII text providing additional information, used to assist the client developer in understanding the error that occurred. */
+ error_description?: string;
+ /** A URI identifying a human-readable web page with information about the error, used to provide the client developer with additional information about the error. */
+ error_uri?: string;
+}
+
+export interface InitGoogleAuthParams {
+ apiKey: string;
+ clientId: string;
+ discoveryDocs?: string[];
+}
+
+export const initGoogleAuth = ({
+ apiKey,
+ clientId,
+ discoveryDocs = [DISCOVERY_DOC],
+}: InitGoogleAuthParams, callback?:EmptyFunction): void => {
+ if (!apiInitialized) {
+ const gapiScript = document.createElement("script");
+ gapiScript.src = "https://apis.google.com/js/api.js";
+ gapiScript.onload = () => {
+ gapi = (window as any).gapi;
+ gapi.load("client", async () => {
+ await gapi.client.init({
+ apiKey,
+ discoveryDocs,
+ });
+ apiInitialized = true;
+
+ if (!googleInitialized) {
+ const googleScript = document.createElement("script");
+ googleScript.src = "https://accounts.google.com/gsi/client";
+ googleScript.onload = () => {
+ google = (window as any).google;
+ tokenClient = google.accounts.oauth2.initTokenClient({
+ client_id: clientId,
+ scope: SCOPES.join(" "),
+ callback: "",
+ });
+ googleInitialized = true;
+ if (callback) {
+ callback();
+ }
+ };
+ document.body.appendChild(googleScript);
+ }
+ });
+ };
+ document.body.appendChild(gapiScript);
+ }
+};
+
+export const authenticateUser = (
+ tokenCallback: (token: TokenResponse) => void,
+ tokenData?: TokenResponse
+) => {
+ tokenClient.callback = tokenCallback;
+ const prompt = tokenData ? "none" : "consent";
+ tokenClient.requestAccessToken({ prompt });
+};
+
+export const deauthenticateUser = (
+ signOutCallback: () => void,
+ tokenData?: TokenResponse,
+) => {
+ const token = tokenData || gapi.client.getToken();
+ google.accounts.oauth2.revoke(token.access_token);
+ signOutCallback();
+};
+
+export const getTokenClient = () => {
+ return tokenClient;
+};
+
+export const getApiClient = () => {
+ return gapi && gapi.client;
+};
+
+export const getGoogle = () => {
+ return google;
+};
+
+let logoutTimeout: any;
+
+export const clearLogoutTimer = () => {
+ clearTimeout(logoutTimeout);
+};
+
+export interface LogoutTimerProps {
+ logoutCallback: EmptyFunction;
+ autoRefresh?: boolean;
+ sessionData: { [key: string]: any };
+ timeout: number;
+}
+
+export const setLogoutTimer = ({
+ logoutCallback,
+ autoRefresh = false,
+ timeout,
+}: // sessionData,
+LogoutTimerProps) => {
+ // auto refresh doesn't work yet!
+ if (autoRefresh) {
+ logoutTimeout = setTimeout(async () => {
+ clearLogoutTimer();
+ // const refreshResponse = await fetchRefreshToken(sessionData.refresh_token);
+ const refreshResponse = {} as any; // todo: refresh token
+ if (refreshResponse.error) {
+ logoutCallback();
+ } else {
+ setLogoutTimer({
+ logoutCallback,
+ autoRefresh,
+ timeout: refreshResponse.expires_in * 1000 - 1000 * 60 * 5,
+ sessionData: refreshResponse,
+ });
+ }
+ }, timeout - 1000 * 60 * 5);
+ } else {
+ logoutTimeout = setTimeout(() => {
+ logoutCallback();
+ clearLogoutTimer();
+ }, timeout);
+ }
+};
+
diff --git a/src/components/GoogleApi/GoogleAuthButton.tsx b/src/components/GoogleApi/GoogleAuthButton.tsx
new file mode 100644
index 0000000..57b2849
--- /dev/null
+++ b/src/components/GoogleApi/GoogleAuthButton.tsx
@@ -0,0 +1,48 @@
+import React, { FC, ReactElement } from "react";
+import { Button } from "react-bootstrap";
+import { authenticateUser, deauthenticateUser, TokenResponse } from "./GoogleAuth";
+
+export type EmptyFunction = () => void;
+export type GoogleLoginSuccess = (tokenResponse: TokenResponse) => void;
+
+export interface GoogleAuthProps {
+ authenticated: boolean;
+ onLogout: EmptyFunction;
+ onLogin: GoogleLoginSuccess;
+ loginVariant?: string;
+ logoutVariant?: string;
+ loginText?: string;
+ logoutText?: string;
+ tokenData?: TokenResponse;
+}
+
+export const DEFAULT_LOGIN_VARIANT = "outline-primary";
+export const DEFAULT_LOGOUT_VARIANT = "outline-danger";
+export const DEFAULT_LOGIN_TEXT = "Log In";
+export const DEFAULT_LOGOUT_TEXT = "Log Out";
+
+export const GoogleAuthButton: FC = ({
+ authenticated,
+ onLogin,
+ onLogout,
+ loginVariant = DEFAULT_LOGIN_VARIANT,
+ logoutVariant = DEFAULT_LOGOUT_VARIANT,
+ loginText = DEFAULT_LOGIN_TEXT,
+ logoutText = DEFAULT_LOGOUT_TEXT,
+ tokenData,
+}): ReactElement => {
+
+ const login = () => authenticateUser(onLogin);
+ const logout = () => deauthenticateUser(onLogout, tokenData);
+
+ return (
+
+ );
+};
+
+
diff --git a/src/components/GoogleApi/GoogleDrive.ts b/src/components/GoogleApi/GoogleDrive.ts
new file mode 100644
index 0000000..d1705af
--- /dev/null
+++ b/src/components/GoogleApi/GoogleDrive.ts
@@ -0,0 +1,18 @@
+import { getApiClient } from "./GoogleAuth";
+
+export const listFiles = async () => {
+ const { drive } = getApiClient();
+ const response = await drive.files.list({
+ pageSize: 10,
+ fields: "nextPageToken, files(id, name)",
+ });
+ const files = response.result.files;
+ if (files && files.length > 0) {
+ console.log("Files:");
+ files.map((file: any) => {
+ console.log(`${file.name} (${file.id})`);
+ });
+ } else {
+ console.log("No files found.");
+ }
+}
\ No newline at end of file
diff --git a/src/components/GoogleApi/index.ts b/src/components/GoogleApi/index.ts
new file mode 100644
index 0000000..3560b63
--- /dev/null
+++ b/src/components/GoogleApi/index.ts
@@ -0,0 +1,38 @@
+export {
+ GoogleAuthButton,
+ DEFAULT_LOGIN_TEXT,
+ DEFAULT_LOGOUT_TEXT,
+ DEFAULT_LOGIN_VARIANT,
+ DEFAULT_LOGOUT_VARIANT,
+
+} from "./GoogleAuthButton";
+export type {
+ GoogleAuthProps,
+ EmptyFunction,
+ GoogleLoginSuccess,
+} from "./GoogleAuthButton";
+
+export {
+ DISCOVERY_DOC,
+ SCOPES,
+
+ initGoogleAuth,
+ authenticateUser,
+ deauthenticateUser,
+ getTokenClient,
+ getApiClient,
+ getGoogle,
+
+ setLogoutTimer,
+ clearLogoutTimer,
+} from './GoogleAuth';
+
+export type {
+ ErrorCode,
+ TokenResponse,
+ InitGoogleAuthParams,
+} from './GoogleAuth';
+
+export {
+ listFiles,
+} from './GoogleDrive';
diff --git a/src/components/GoogleAuth/GoogleAuthC.tsx b/src/components/GoogleAuth/GoogleAuthC.tsx
deleted file mode 100644
index 6f0bd44..0000000
--- a/src/components/GoogleAuth/GoogleAuthC.tsx
+++ /dev/null
@@ -1,117 +0,0 @@
-import React, { FC, ReactElement } from "react";
-import {
- googleLogout,
- useGoogleLogin,
- TokenResponse,
-} from "@react-oauth/google";
-import { Button } from "react-bootstrap";
-
-export type EmptyFunction = () => void;
-export type GoogleLoginSuccess = (tokenResponse: TokenResponse) => void;
-
-export interface GoogleAuthProps {
- authenticated: boolean;
- onLogout: EmptyFunction;
- onLogin: GoogleLoginSuccess;
- loginVariant?: string;
- logoutVariant?: string;
- loginText?: string;
- logoutText?: string;
-}
-
-export const DEFAULT_LOGIN_VARIANT = "outline-primary";
-export const DEFAULT_LOGOUT_VARIANT = "outline-danger";
-export const DEFAULT_LOGIN_TEXT = "Log In";
-export const DEFAULT_LOGOUT_TEXT = "Log Out";
-
-export const GoogleAuthButton: FC = ({
- authenticated,
- onLogin,
- onLogout,
- loginVariant = DEFAULT_LOGIN_VARIANT,
- logoutVariant = DEFAULT_LOGOUT_VARIANT,
- loginText = DEFAULT_LOGIN_TEXT,
- logoutText = DEFAULT_LOGOUT_TEXT,
-}): ReactElement => {
- const login = useGoogleLogin({
- onSuccess: (tokenResponse: TokenResponse) => {
- onLogin(tokenResponse);
- },
- onError: (error) => {
- console.log(error);
- },
- });
-
- const logout = () => {
- onLogout();
- googleLogout();
- };
-
- return (
-
- );
-};
-
-// todo: update this to use the new auth system once it is published
-export const fetchRefreshToken = async (refreshToken: string) => {
- const response = await fetch(
- `https://securetoken.googleapis.com/v1/token?key=${process.env.REACT_APP_G_CLIENT_ID as string}`,
- {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/x-www-form-urlencoded',
- },
- body: `grant_type=refresh_token&refresh_token=${refreshToken}`,
- }
- );
- return response.json();
-};
-
-let logoutTimeout: any;
-
-export const clearLogoutTimer = () => {
- clearTimeout(logoutTimeout);
-}
-
-export interface LogoutTimerProps {
- logoutCallback: EmptyFunction;
- autoRefresh?: boolean;
- sessionData: {[key: string]: any};
- timeout: number;
-}
-
-export const setLogoutTimer = ({
- logoutCallback,
- autoRefresh = false,
- timeout,
- sessionData,
-}: LogoutTimerProps) => {
- if (autoRefresh) {
- logoutTimeout = setTimeout(async () => {
- clearLogoutTimer();
- const refreshResponse = await fetchRefreshToken(sessionData.refresh_token);
- if (refreshResponse.error) {
- logoutCallback();
- } else {
- setLogoutTimer({
- logoutCallback,
- autoRefresh,
- timeout: (refreshResponse.expires_in * 1000) - (1000 * 60 * 5),
- sessionData: refreshResponse,
- });
- }
- }, timeout - (1000 * 60 * 5));
- } else {
- logoutTimeout = setTimeout(() => {
- logoutCallback();
- clearLogoutTimer();
- }, timeout);
- }
-};
-
-
diff --git a/src/components/GoogleAuth/index.ts b/src/components/GoogleAuth/index.ts
deleted file mode 100644
index a2747c9..0000000
--- a/src/components/GoogleAuth/index.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-export {
- GoogleAuthButton,
- DEFAULT_LOGIN_TEXT,
- DEFAULT_LOGOUT_TEXT,
- DEFAULT_LOGIN_VARIANT,
- DEFAULT_LOGOUT_VARIANT,
- setLogoutTimer,
- fetchRefreshToken,
-} from "./GoogleAuthC";
-export type {
- GoogleAuthProps,
- EmptyFunction,
- GoogleLoginSuccess,
-} from "./GoogleAuthC";
diff --git a/src/components/Sidebar/Sidebar.tsx b/src/components/Sidebar/Sidebar.tsx
index d33195f..3250332 100644
--- a/src/components/Sidebar/Sidebar.tsx
+++ b/src/components/Sidebar/Sidebar.tsx
@@ -1,9 +1,9 @@
import React, { useEffect, useState } from "react";
import { FC, ReactElement } from "react";
import { Button, Offcanvas } from "react-bootstrap";
-import { ABOUT_APP_HEADER, END, LINK_SECONDARY } from "../../strings";
+import { ABOUT_APP_HEADER, END, LINK_SECONDARY, PRIMARY } from "../../strings";
import "./sidebar.scss";
-import { GoogleAuthButton, setLogoutTimer } from "../GoogleAuth";
+import { GoogleAuthButton, listFiles, setLogoutTimer } from "../GoogleApi";
import { AboutModal } from "../AboutModal";
import store from "../../store/store";
import {
@@ -11,8 +11,7 @@ import {
deauthenticate,
useSession,
} from "../../store/Session";
-import { TokenResponse } from "@react-oauth/google";
-import { clearLogoutTimer } from "../GoogleAuth/GoogleAuthC";
+import { clearLogoutTimer, TokenResponse } from "../GoogleApi";
/**
* Sidebar Component
@@ -33,7 +32,7 @@ export const Sidebar: FC = ({
toggleSidebar,
}): ReactElement => {
const session = useSession();
- const { authenticated: isAuthenticated } = session;
+ const { authenticated: isAuthenticated, data } = session;
const [authenticated, setAuthenticated] = useState(isAuthenticated);
// const [rememberMe, setRememberMe] = useState(autoRefresh);
@@ -78,6 +77,7 @@ export const Sidebar: FC = ({
authenticated={authenticated}
onLogin={handleLogin}
onLogout={handleLogout}
+ tokenData={data.access_token && data}
/>
{/*