From 451c825c77cd68eafeb262eb1ea5cbfc21dce550 Mon Sep 17 00:00:00 2001
From: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com>
Date: Sat, 13 Feb 2021 01:21:36 +0300
Subject: Dynamically Show Discord OAuth Button

Dynamically displays an auth button in place of the submit button if
needed, and adds full authorization flow.

Signed-off-by: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com>
---
 src/pages/FormPage.tsx | 65 ++++++++++++++++++++++++--------------------------
 1 file changed, 31 insertions(+), 34 deletions(-)

(limited to 'src/pages/FormPage.tsx')

diff --git a/src/pages/FormPage.tsx b/src/pages/FormPage.tsx
index c49b9fd..4237e86 100644
--- a/src/pages/FormPage.tsx
+++ b/src/pages/FormPage.tsx
@@ -9,10 +9,12 @@ import HeaderBar from "../components/HeaderBar";
 import RenderedQuestion from "../components/Question";
 import Loading from "../components/Loading";
 import ScrollToTop from "../components/ScrollToTop";
+import OAuth2Button  from "../components/OAuth2Button";
 
 import { Form, FormFeatures, getForm } from "../api/forms";
+import { OAuthScopes, checkScopes } from "../api/auth";
 import colors from "../colors";
-import { unselectable }  from "../commonStyles";
+import { submitStyles, unselectable } from "../commonStyles";
 
 
 interface PathParams {
@@ -20,10 +22,13 @@ interface PathParams {
 }
 
 interface NavigationProps {
-    form_state: boolean  // Whether the form is open or not
+    form_state: boolean,  // Whether the form is open or not
+    scopes: OAuthScopes[]
 }
 
 class Navigation extends React.Component<NavigationProps> {
+    PAGE_PATH = "/form"
+
     containerStyles = css`
       margin: auto;
       width: 50%;
@@ -38,7 +43,7 @@ class Navigation extends React.Component<NavigationProps> {
         width: 50%;
       }
 
-      @media (max-width: 850px) {
+      @media (max-width: 870px) {
         width: 100%;
 
         > div {
@@ -62,13 +67,13 @@ class Navigation extends React.Component<NavigationProps> {
       height: 0;
       display: none;
 
-      @media (max-width: 850px) {
+      @media (max-width: 870px) {
         display: block;
       }
     `;
 
     returnStyles = css`
-      padding: 0.5rem 2rem;
+      padding: 0.55rem 2.2rem;
       border-radius: 8px;
 
       color: white;
@@ -83,36 +88,21 @@ class Navigation extends React.Component<NavigationProps> {
     }
     `;
 
-    submitStyles = css`
-      text-align: right;
-
-      button {
-        padding: 0.5rem 4rem;
-        cursor: pointer;
-
-        border: none;
-        border-radius: 8px;
-
-        color: white;
-        font: inherit;
-
-        background-color: ${colors.blurple};
-        transition: background-color 300ms;
-      }
-
-      button:hover {
-        background-color: ${colors.darkerBlurple};
-      }
-    `;
+    constructor(props: NavigationProps) {
+        super(props);
+        this.setState({"logged_in": false});
+    }
 
     render(): JSX.Element {
         let submit = null;
+
         if (this.props.form_state) {
-            submit = (
-                <div css={this.submitStyles}>
-                    <button form="form" type="submit">Submit</button>
-                </div>
-            );
+            if (this.props.scopes.includes(OAuthScopes.Identify) && !checkScopes(this.props.scopes, this.PAGE_PATH)) {
+                // Render OAuth button if login is required, and the scopes needed are not available
+                submit = <OAuth2Button path={this.PAGE_PATH} scopes={this.props.scopes} rerender={() => this.setState({"logged_in": true})}/>;
+            } else {
+                submit = <button form="form" type="submit">Submit</button>;
+            }
         }
 
         return (
@@ -121,7 +111,7 @@ class Navigation extends React.Component<NavigationProps> {
                     <Link to="/" css={this.returnStyles}>Return Home</Link>
                 </div>
                 <br css={this.separatorStyles}/>
-                { submit }
+                <div css={submitStyles}>{ submit }</div>
             </div>
         );
     }
@@ -146,7 +136,7 @@ const closedHeaderStyles = css`
   font-size: 1.5rem;
 
   background-color: ${colors.error};
-  
+
   @media (max-width: 500px) {
     padding: 1rem 1.5rem;
   }
@@ -186,6 +176,13 @@ function FormPage(): JSX.Element {
     }
 
     const open: boolean = form.features.includes(FormFeatures.Open);
+    const require_auth: boolean = form.features.includes(FormFeatures.RequiresLogin);
+
+    const scopes = [];
+    if (require_auth) {
+        scopes.push(OAuthScopes.Identify);
+        if (form.features.includes(FormFeatures.CollectEmail)) { scopes.push(OAuthScopes.Email); }
+    }
 
     let closed_header = null;
     if (!open) {
@@ -201,7 +198,7 @@ function FormPage(): JSX.Element {
                     { closed_header }
                     { questions }
                 </form>
-                <Navigation form_state={open}/>
+                <Navigation form_state={open} scopes={scopes}/>
             </div>
 
             <div css={css`margin-bottom: 10rem`}/>
-- 
cgit v1.2.3


From fba316f235a3871743427f37b3bbd07bea6d77bd Mon Sep 17 00:00:00 2001
From: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com>
Date: Wed, 17 Feb 2021 09:30:47 +0300
Subject: Removes Path From Auth

Signed-off-by: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com>
---
 src/api/auth.ts                 | 23 +++++++++++------------
 src/components/OAuth2Button.tsx |  5 ++---
 src/pages/FormPage.tsx          |  6 ++----
 3 files changed, 15 insertions(+), 19 deletions(-)

(limited to 'src/pages/FormPage.tsx')

diff --git a/src/api/auth.ts b/src/api/auth.ts
index ad97e67..cfaa563 100644
--- a/src/api/auth.ts
+++ b/src/api/auth.ts
@@ -91,11 +91,11 @@ function ensureMinimumScopes(scopes: unknown, expected: OAuthScopes | OAuthScope
 /**
  * Return true if the program has the requested scopes or higher.
  */
-export function checkScopes(scopes?: OAuthScopes[], path = ""): boolean {
+export function checkScopes(scopes?: OAuthScopes[]): boolean {
     const cleanedScopes = ensureMinimumScopes(scopes, OAuthScopes.Identify);
 
     // Get Active Scopes And Ensure Type
-    const cookies = new Cookies().get(CookieNames.Scopes + path);
+    const cookies = new Cookies().get(CookieNames.Scopes);
     if (!cookies || !Array.isArray(cookies)) {
         return false;
     }
@@ -169,7 +169,7 @@ export async function getDiscordCode(scopes: OAuthScopes[]): Promise<{code: stri
 }
 
 /**
- * Sends a discord code from a given path to the backend,
+ * Sends a discord code to the backend,
  * and returns the resultant JWT and expiry date.
  *
  * @throws { APIErrors } On error, the APIErrors.Message is set, and an APIErrors object is thrown.
@@ -218,27 +218,26 @@ export async function requestBackendJWT(code: string): Promise<JWTResponse> {
 }
 
 /**
- * Handle a full authorization flow. Sets a token for the specified path with the JWT and scopes.
+ * Handle a full authorization flow. Sets a cookie with the JWT and scopes.
  *
  * @param scopes The scopes that should be authorized for the application.
  * @param disableFunction An optional function that can disable a component while processing.
- * @param path The site path to save the token under.
  *
  * @throws { APIErrors } See documentation on { requestBackendJWT }.
  */
-export default async function authorize(scopes: OAuthScopes[] = [], disableFunction?: (newState: boolean) => void, path = "/"): Promise<void> {
-    if (!checkScopes(scopes, path)) {
+export default async function authorize(scopes: OAuthScopes[] = [], disableFunction?: (newState: boolean) => void): Promise<void> {
+    if (!checkScopes(scopes)) {
         const cookies = new Cookies;
-        cookies.remove(CookieNames.Token + path);
-        cookies.remove(CookieNames.Scopes + path);
+        cookies.remove(CookieNames.Token);
+        cookies.remove(CookieNames.Scopes);
 
         if (disableFunction) { disableFunction(true); }
         await getDiscordCode(scopes).then(async discord_response =>{
             await requestBackendJWT(discord_response.code).then(backend_response => {
-                const options: CookieSetOptions = {sameSite: "strict", expires: backend_response.Expiry, secure: PRODUCTION, path: path};
+                const options: CookieSetOptions = {sameSite: "strict", expires: backend_response.Expiry, secure: PRODUCTION};
 
-                cookies.set(CookieNames.Token + path, backend_response.JWT, options);
-                cookies.set(CookieNames.Scopes + path, discord_response.cleanedScopes, options);
+                cookies.set(CookieNames.Token, backend_response.JWT, options);
+                cookies.set(CookieNames.Scopes, discord_response.cleanedScopes, options);
             });
         }).finally(() => {
             if (disableFunction) { disableFunction(false); }
diff --git a/src/components/OAuth2Button.tsx b/src/components/OAuth2Button.tsx
index 1ee456c..25c5871 100644
--- a/src/components/OAuth2Button.tsx
+++ b/src/components/OAuth2Button.tsx
@@ -11,7 +11,6 @@ import { selectable } from "../commonStyles";
 
 interface OAuth2ButtonProps {
     scopes?: OAuthScopes[],
-    path?: string,
     rerender: () => void
 }
 
@@ -47,7 +46,7 @@ const errorStyles =  css`
 `;
 
 async function login(props: OAuth2ButtonProps, errorDialog: React.RefObject<HTMLDivElement>, setDisabled: (newState: boolean) => void) {
-    await authenticate(props.scopes, setDisabled, props.path).catch((reason: APIErrors) => {
+    await authenticate(props.scopes, setDisabled).catch((reason: APIErrors) => {
         // Display Error Message
         if (errorDialog.current) {
             errorDialog.current.style.visibility = "visible";
@@ -60,7 +59,7 @@ async function login(props: OAuth2ButtonProps, errorDialog: React.RefObject<HTML
         throw reason.Error;
     });
 
-    if (checkScopes(props.scopes, props.path)) {
+    if (checkScopes(props.scopes)) {
         props.rerender();
     }
 }
diff --git a/src/pages/FormPage.tsx b/src/pages/FormPage.tsx
index 4237e86..4e13b8a 100644
--- a/src/pages/FormPage.tsx
+++ b/src/pages/FormPage.tsx
@@ -27,8 +27,6 @@ interface NavigationProps {
 }
 
 class Navigation extends React.Component<NavigationProps> {
-    PAGE_PATH = "/form"
-
     containerStyles = css`
       margin: auto;
       width: 50%;
@@ -97,9 +95,9 @@ class Navigation extends React.Component<NavigationProps> {
         let submit = null;
 
         if (this.props.form_state) {
-            if (this.props.scopes.includes(OAuthScopes.Identify) && !checkScopes(this.props.scopes, this.PAGE_PATH)) {
+            if (this.props.scopes.includes(OAuthScopes.Identify) && !checkScopes(this.props.scopes)) {
                 // Render OAuth button if login is required, and the scopes needed are not available
-                submit = <OAuth2Button path={this.PAGE_PATH} scopes={this.props.scopes} rerender={() => this.setState({"logged_in": true})}/>;
+                submit = <OAuth2Button scopes={this.props.scopes} rerender={() => this.setState({"logged_in": true})}/>;
             } else {
                 submit = <button form="form" type="submit">Submit</button>;
             }
-- 
cgit v1.2.3


From 9f04d5f5effd07534c9eb5b9f8886c8f970d0dc6 Mon Sep 17 00:00:00 2001
From: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com>
Date: Sun, 7 Mar 2021 00:46:47 +0300
Subject: Fixes Return Home Button Centering

Fixes the centering of the return home button on closed forms, by
removing the style wrapper.

Signed-off-by: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com>
---
 src/components/OAuth2Button.tsx | 6 +++---
 src/pages/FormPage.tsx          | 8 +++++---
 2 files changed, 8 insertions(+), 6 deletions(-)

(limited to 'src/pages/FormPage.tsx')

diff --git a/src/components/OAuth2Button.tsx b/src/components/OAuth2Button.tsx
index 25c5871..885c080 100644
--- a/src/components/OAuth2Button.tsx
+++ b/src/components/OAuth2Button.tsx
@@ -31,16 +31,16 @@ const errorStyles =  css`
   visibility: hidden;
   width: 100%;
   left: 0;
-  
+
   text-align: center;
   white-space: normal;
   box-sizing: border-box;
-  
+
   padding: 0 15rem;
   @media (max-width: 750px) {
     padding: 0 5rem;
   }
-  
+
   color: red;
   margin-top: 2.5rem;
 `;
diff --git a/src/pages/FormPage.tsx b/src/pages/FormPage.tsx
index fa63282..8852ac5 100644
--- a/src/pages/FormPage.tsx
+++ b/src/pages/FormPage.tsx
@@ -96,12 +96,14 @@ class Navigation extends React.Component<NavigationProps> {
         let submit = null;
 
         if (this.props.form_state) {
+            let inner_submit;
             if (this.props.scopes.includes(OAuthScopes.Identify) && !checkScopes(this.props.scopes)) {
                 // Render OAuth button if login is required, and the scopes needed are not available
-                submit = <OAuth2Button scopes={this.props.scopes} rerender={() => this.setState({"logged_in": true})}/>;
+                inner_submit = <OAuth2Button scopes={this.props.scopes} rerender={() => this.setState({"logged_in": true})}/>;
             } else {
-                submit = <button form="form" type="submit">Submit</button>;
+                inner_submit = <button form="form" type="submit">Submit</button>;
             }
+            submit = <div css={submitStyles}>{ inner_submit }</div>;
         }
 
         return (
@@ -110,7 +112,7 @@ class Navigation extends React.Component<NavigationProps> {
                     <Link to="/" css={Navigation.returnStyles}>Return Home</Link>
                 </div>
                 <br css={this.separatorStyles}/>
-                <div css={submitStyles}>{ submit }</div>
+                { submit }
             </div>
         );
     }
-- 
cgit v1.2.3

