From 823da8613b316551cf2f286f32c3361e31f7e622 Mon Sep 17 00:00:00 2001
From: thijsheijden <hi@thijsheijden.nl>
Date: Mon, 7 Feb 2022 14:28:00 +0100
Subject: [PATCH] feat(authorization): worked on adding the popup sign-in
 window

---
 apps/graphpolaris/src/app/app.tsx             | 158 +++++++++++-------
 .../src/assets/login-screen/github.png        | Bin 0 -> 7204 bytes
 .../src/assets/login-screen/google.png        | Bin 0 -> 8001 bytes
 apps/graphpolaris/src/main.tsx                |  11 +-
 .../src/web/components/login/loginScreen.tsx  | 122 ++++++++++++++
 .../src/web/components/login/popup.tsx        |  17 ++
 .../src/web/components/panels/Panel.tsx       |   5 +-
 .../data-access/authorization/src/index.ts    |   2 +-
 .../src/lib/authorizationHandler.ts           | 157 ++++++++++++++++-
 9 files changed, 410 insertions(+), 62 deletions(-)
 create mode 100644 apps/graphpolaris/src/assets/login-screen/github.png
 create mode 100644 apps/graphpolaris/src/assets/login-screen/google.png
 create mode 100644 apps/graphpolaris/src/web/components/login/loginScreen.tsx
 create mode 100644 apps/graphpolaris/src/web/components/login/popup.tsx

diff --git a/apps/graphpolaris/src/app/app.tsx b/apps/graphpolaris/src/app/app.tsx
index f0d382be6..4068d6b0b 100644
--- a/apps/graphpolaris/src/app/app.tsx
+++ b/apps/graphpolaris/src/app/app.tsx
@@ -1,72 +1,114 @@
 // eslint-disable-next-line @typescript-eslint/no-unused-vars
+import { AuthorizationHandler } from '@graphpolaris/shared/data-access/authorization';
 import {
   assignNewGraphQueryResult,
   useAppDispatch,
 } from '@graphpolaris/shared/data-access/store';
+import { useEffect, useState } from 'react';
 import GridLayout from 'react-grid-layout';
-import Panel from '../web/components/panels/panel';
-import { RawJSONVis } from '../web/components/visualisations/rawjsonvis/rawjsonvis';
+import Panel from '../web/components/panels/Panel';
+import { RawJSONVis } from '../web/components/visualisations/RawJSONVis/rawjsonvis';
+import LoginScreen from '../web/components/login/loginScreen';
 
 export function App() {
   const dispatch = useAppDispatch();
+
+  const [userAuthorized, setUserAuthorized] = useState<boolean>(false);
+
+  // Attempt to Authorize the user
+  const authorize = async () => {
+    const authorized = await AuthorizationHandler.instance().Authorize();
+    console.log('User authorized: ' + authorized);
+
+    // If user is authorized don't show login screen
+    if (authorized) {
+      setUserAuthorized(true);
+    }
+  };
+
+  useEffect(() => {
+    authorize();
+  }, []);
+
   return (
-    <GridLayout
-      className="layout"
-      cols={10}
-      rowHeight={30}
-      width={window.innerWidth}
-    >
-      <div
-        key="schema-panel"
-        data-grid={{ x: 0, y: 0, w: 3, h: 30, maxW: 5, isDraggable: false }}
-      >
-        <Panel content="Schema Panel" color="red"></Panel>
-      </div>
-      <div
-        key="query-panel"
-        data-grid={{ x: 3, y: 20, w: 5, h: 10, maxH: 20, isDraggable: false }}
-      >
-        <Panel content="Query Panel" color="blue"></Panel>
-      </div>
-      <div
-        key="visualisation-panel"
-        data-grid={{ x: 3, y: 0, w: 7, h: 20, isDraggable: false }}
-      >
-        <Panel content="Visualisation Panel" color="green">
-          <div>
-            <button
-              onClick={() =>
-                dispatch(
-                  assignNewGraphQueryResult({
-                    nodes: [
-                      { id: 'agent/007', attributes: { name: 'Daniel Craig' } },
-                    ],
-                    links: [],
-                  })
-                )
-              }
-            >
-              Load in mock result
-            </button>
-            <button
-              onClick={() =>
-                dispatch(assignNewGraphQueryResult({ nodes: [], links: [] }))
-              }
-            >
-              Remove mock result
-            </button>
-          </div>
-          <RawJSONVis />
-          <div />
-        </Panel>
-      </div>
-      <div
-        key="history-panel"
-        data-grid={{ x: 8, y: 20, w: 2, h: 10, isDraggable: false }}
+    <>
+      {!userAuthorized && <LoginScreen />}
+      <GridLayout
+        className="layout"
+        cols={10}
+        rowHeight={30}
+        width={window.innerWidth}
+        style={{ zIndex: 0 }}
       >
-        <Panel content="History Channel" color="purple"></Panel>
-      </div>
-    </GridLayout>
+        <div
+          key="schema-panel"
+          data-grid={{
+            x: 0,
+            y: 0,
+            w: 3,
+            h: 30,
+            maxW: 5,
+            isDraggable: false,
+          }}
+        >
+          <Panel content="Schema Panel" color="white"></Panel>
+        </div>
+        <div
+          key="query-panel"
+          data-grid={{
+            x: 3,
+            y: 20,
+            w: 5,
+            h: 10,
+            maxH: 20,
+            isDraggable: false,
+          }}
+        >
+          <Panel content="Query Panel" color="white"></Panel>
+        </div>
+        <div
+          key="visualisation-panel"
+          data-grid={{ x: 3, y: 0, w: 7, h: 20, isDraggable: false }}
+        >
+          <Panel content="Visualisation Panel" color="white">
+            <div>
+              <button
+                onClick={() =>
+                  dispatch(
+                    assignNewGraphQueryResult({
+                      nodes: [
+                        {
+                          id: 'agent/007',
+                          attributes: { name: 'Daniel Craig' },
+                        },
+                      ],
+                      links: [],
+                    })
+                  )
+                }
+              >
+                Load in mock result
+              </button>
+              <button
+                onClick={() =>
+                  dispatch(assignNewGraphQueryResult({ nodes: [], links: [] }))
+                }
+              >
+                Remove mock result
+              </button>
+            </div>
+            <RawJSONVis />
+            <div />
+          </Panel>
+        </div>
+        <div
+          key="history-panel"
+          data-grid={{ x: 8, y: 20, w: 2, h: 10, isDraggable: false }}
+        >
+          <Panel content="History Channel" color="white"></Panel>
+        </div>
+      </GridLayout>
+    </>
   );
 }
 
diff --git a/apps/graphpolaris/src/assets/login-screen/github.png b/apps/graphpolaris/src/assets/login-screen/github.png
new file mode 100644
index 0000000000000000000000000000000000000000..55f90676824d4901f6fe7a50ed11f6d4af3d6fef
GIT binary patch
literal 7204
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

literal 0
HcmV?d00001

diff --git a/apps/graphpolaris/src/assets/login-screen/google.png b/apps/graphpolaris/src/assets/login-screen/google.png
new file mode 100644
index 0000000000000000000000000000000000000000..f27bb2433042aea5fc34e19fcf90944430ec331b
GIT binary patch
literal 8001
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

literal 0
HcmV?d00001

diff --git a/apps/graphpolaris/src/main.tsx b/apps/graphpolaris/src/main.tsx
index 0eb1b52ef..f26b53e97 100644
--- a/apps/graphpolaris/src/main.tsx
+++ b/apps/graphpolaris/src/main.tsx
@@ -1,14 +1,23 @@
+import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
 import { store } from '@graphpolaris/shared/data-access/store';
 import { StrictMode } from 'react';
 import * as ReactDOM from 'react-dom';
 import { Provider } from 'react-redux';
 
 import App from './app/app';
+import LoginPopupComponent from './web/components/login/popup';
 
 ReactDOM.render(
   <StrictMode>
     <Provider store={store}>
-      <App />
+      <Router>
+        <Routes>
+          {/* Route to auth component in popup */}
+          <Route path="/auth" element={<LoginPopupComponent />}></Route>
+          {/* App */}
+          <Route path="/" element={<App />}></Route>
+        </Routes>
+      </Router>
     </Provider>
   </StrictMode>,
   document.getElementById('root')
diff --git a/apps/graphpolaris/src/web/components/login/loginScreen.tsx b/apps/graphpolaris/src/web/components/login/loginScreen.tsx
new file mode 100644
index 000000000..903b4eba2
--- /dev/null
+++ b/apps/graphpolaris/src/web/components/login/loginScreen.tsx
@@ -0,0 +1,122 @@
+import styled from 'styled-components';
+
+const Wrapper = styled.div`
+  font-family: 'Arial';
+  position: absolute;
+  left: 0;
+  top: 0;
+  // Cover the screen
+  width: 100vw;
+  height: 100vh;
+
+  display: flex;
+  justify-content: center;
+  align-items: center;
+`;
+
+const Background = styled.div`
+  position: absolute;
+
+  width: 100%;
+  height: 100%;
+
+  z-index: 1;
+
+  // Blur
+  background: rgba(
+    0,
+    0,
+    0,
+    0.4
+  ); // Make sure this color has an opacity of less than 1
+  backdrop-filter: blur(8px); // This be the blur
+`;
+
+const Content = styled.div`
+  background-color: white;
+  box-shadow: 0 3px 10px rgb(0 0 0 / 0.2);
+  padding: 2em;
+  z-index: 2;
+  border-radius: 8px;
+
+  display: flex;
+  flex-direction: column;
+  gap: 1em;
+  align-items: center;
+  justify-content: center;
+
+  // Give children 0 padding and margin
+  * {
+    display: flex;
+    margin: 0;
+    padding: 0;
+
+    // Same width flexbox items
+    flex: 1 1 0px;
+
+    max-height: 3em;
+
+    &:hover {
+      cursor: pointer;
+    }
+  }
+`;
+
+const LoginScreen = () => {
+  const openSignInWindow = (url: string) => {
+    // remove any existing event listeners
+    window.removeEventListener('message', receiveMessage);
+
+    // window features
+    const strWindowFeatures =
+      'toolbar=no, menubar=no, width=600, height=700, top=100, left=100';
+
+    window.open(url, 'Google Oauth', strWindowFeatures);
+
+    // add the listener for receiving a message from the popup
+    window.addEventListener('message', (event) => receiveMessage(event), false);
+  };
+
+  const receiveMessage = (event: any) => {
+    // Do we trust the sender of this message? (might be
+    // different from what we originally opened)
+    if (event.origin !== 'http://localhost:4200') {
+      return;
+    }
+    console.log(event);
+  };
+
+  return (
+    <Wrapper>
+      <Background></Background>
+      <Content>
+        <h1>Sign In</h1>
+        <img
+          onClick={() =>
+            openSignInWindow('http://localhost:3000/sign-in?provider=1')
+          }
+          src="assets/login-screen/google.png"
+          alt="sign up with google"
+        />
+        <img
+          onClick={() =>
+            openSignInWindow('http://localhost:3000/sign-in?provider=2')
+          }
+          src="assets/login-screen/github.png"
+          alt="sign up with github"
+        />
+        <p
+          onClick={() =>
+            openSignInWindow(
+              'https://datastrophe.science.uu.nl/user/create_free/'
+            )
+          }
+        >
+          Developer
+        </p>
+      </Content>
+    </Wrapper>
+  );
+};
+
+export default LoginScreen;
diff --git a/apps/graphpolaris/src/web/components/login/popup.tsx b/apps/graphpolaris/src/web/components/login/popup.tsx
new file mode 100644
index 000000000..d895b7cfd
--- /dev/null
+++ b/apps/graphpolaris/src/web/components/login/popup.tsx
@@ -0,0 +1,17 @@
+const LoginPopupComponent = () => {
+  if (window.opener) {
+    // Get the access token from the query params
+    const urlParams = new URLSearchParams(window.location.search);
+    const accessToken = urlParams.get('access_token');
+
+    // Send the access token to the parent window
+    window.opener.postMessage(accessToken, '*');
+
+    // Close this window
+    window.close();
+  }
+
+  return <h1>Loading...</h1>;
+};
+
+export default LoginPopupComponent;
diff --git a/apps/graphpolaris/src/web/components/panels/Panel.tsx b/apps/graphpolaris/src/web/components/panels/Panel.tsx
index bf43cca74..901772219 100644
--- a/apps/graphpolaris/src/web/components/panels/Panel.tsx
+++ b/apps/graphpolaris/src/web/components/panels/Panel.tsx
@@ -8,7 +8,10 @@ interface Props {
 
 const Wrapper = styled.div<{ color: string }>`
   background-color: ${(props) => props.color};
-  font: 'Arial';
+  font-family: 'Arial';
+
+  // Light shadow
+  box-shadow: 0 3px 10px rgb(0 0 0 / 0.2);
 
   height: 100%;
   width: 100%;
diff --git a/libs/shared/data-access/authorization/src/index.ts b/libs/shared/data-access/authorization/src/index.ts
index 39bd8dd17..0309d93b4 100644
--- a/libs/shared/data-access/authorization/src/index.ts
+++ b/libs/shared/data-access/authorization/src/index.ts
@@ -1 +1 @@
-export * from './lib/authorizationHandler';
+export { AuthorizationHandler } from './lib/authorizationHandler';
diff --git a/libs/shared/data-access/authorization/src/lib/authorizationHandler.ts b/libs/shared/data-access/authorization/src/lib/authorizationHandler.ts
index 68ae8fb25..8f2455e22 100644
--- a/libs/shared/data-access/authorization/src/lib/authorizationHandler.ts
+++ b/libs/shared/data-access/authorization/src/lib/authorizationHandler.ts
@@ -1 +1,156 @@
-export const test = 'hey!';
+export class AuthorizationHandler {
+  private static _instance: AuthorizationHandler;
+  private accessToken = '';
+  private authorized = false;
+  private userID = '';
+  private sessionID = '';
+
+  // instance gets the AuthorizationHandler singleton instance
+  public static instance(): AuthorizationHandler {
+    if (!AuthorizationHandler._instance) {
+      AuthorizationHandler._instance = new AuthorizationHandler();
+    }
+
+    return AuthorizationHandler._instance;
+  }
+
+  // MARK: Authorization code
+
+  /**
+   * Authorize attempts to authorize using a refresh-token set as a cookie. If the user has been inactive for more than 7 days this cookie will be gone.
+   * @returns true is authorization was successful, else returns false
+   */
+  public async Authorize(): Promise<boolean> {
+    // Attempt to log in with a refresh-token
+    const authResponse = await this.getNewAccessToken();
+
+    // If the request was a success, we have an accessToken, userID and sessionID
+    if (authResponse.success) {
+      // Store them
+      this.accessToken = authResponse.accessToken ?? '';
+      this.userID = authResponse.userID ?? '';
+      this.sessionID = authResponse.sessionID ?? '';
+
+      // Start the automatic refreshing every 10 minutes
+      setInterval(() => {
+        this.refreshTokens();
+      }, 10 * 60 * 1000);
+    }
+
+    return new Promise((resolve) => {
+      resolve(authResponse.success);
+    });
+  }
+
+  /**
+   * getNewAccessToken gets a new access token using the refresh-token cookie
+   * @returns an authResponse containing details
+   */
+  private async getNewAccessToken(): Promise<authResponse> {
+    // If we have an access token already, append it to the url as a query param to keep sessionID the same
+    let url = 'https://datastrophe.science.uu.nl/auth/refresh';
+    if (this.accessToken != '') {
+      url += '?access_token=' + this.accessToken;
+    }
+
+    return new Promise<authResponse>((resolve) => {
+      fetch(url, {
+        method: 'GET',
+        credentials: 'include',
+      })
+        .then((response) => {
+          if (!response.ok) {
+            throw Error(response.statusText);
+          }
+          return response.json();
+        })
+        .then((responseJSON) => {
+          resolve({
+            success: true,
+            accessToken: responseJSON.accessToken,
+            userID: responseJSON.userID,
+            sessionID: responseJSON.sessionID,
+          });
+        })
+        .catch(() => {
+          // User is not authorized
+          resolve({ success: false });
+        });
+    });
+  }
+
+  /**
+   * refreshTokens refreshes tokens
+   */
+  private async refreshTokens() {
+    // Get a new access + refresh token pair
+    const authResponse = await this.getNewAccessToken();
+
+    if (authResponse.success) {
+      // Set the new access token
+      this.accessToken = authResponse.accessToken ?? '';
+
+      // Initialise the new refresh token
+      this.initialiseRefreshToken();
+    }
+  }
+
+  /**
+   * initialiseRefreshToken attempts to initialise a refresh token
+   */
+  private async initialiseRefreshToken() {
+    fetch('https://datastrophe.science.uu.nl/auth/refresh', {
+      method: 'POST',
+      credentials: 'include',
+    })
+      .then((response) => {
+        if (!response.ok) {
+          throw Error(response.statusText);
+        }
+      })
+      .catch((error) => {
+        console.error(error);
+      });
+  }
+
+  // MARK: Getters
+
+  /**
+   * Authorized returns the current authorization status
+   * @returns true if authorized
+   */
+  Authorized(): boolean {
+    return this.authorized;
+  }
+
+  /**
+   * AccessToken returns the current access token
+   * @returns token
+   */
+  AccessToken(): string {
+    return this.accessToken;
+  }
+
+  /**
+   * UserID returns the current user' ID
+   * @returns id
+   */
+  UserID(): string {
+    return this.userID;
+  }
+
+  /**
+   * SessionID returns the current session' ID
+   * @returns id
+   */
+  SessionID(): string {
+    return this.sessionID;
+  }
+}
+
+type authResponse = {
+  success: boolean;
+  accessToken?: string;
+  userID?: string;
+  sessionID?: string;
+};
-- 
GitLab