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