Implemented component version of authentication guard.
This commit is contained in:
		@@ -1,6 +1,6 @@
 | 
				
			|||||||
import "./styles/Layout.css";
 | 
					import "./styles/Layout.css";
 | 
				
			||||||
import "./styles/extra.css";
 | 
					import "./styles/extra.css";
 | 
				
			||||||
import { useEffect, useState } from "react";
 | 
					import { useContext } from "react";
 | 
				
			||||||
import { NavLink, Route, Routes, useNavigate } from "react-router-dom";
 | 
					import { NavLink, Route, Routes, useNavigate } from "react-router-dom";
 | 
				
			||||||
import Welcome from "./pages/Welcome";
 | 
					import Welcome from "./pages/Welcome";
 | 
				
			||||||
import Navbar from "react-bootstrap/Navbar";
 | 
					import Navbar from "react-bootstrap/Navbar";
 | 
				
			||||||
@@ -9,30 +9,14 @@ import NavbarToggle from "react-bootstrap/esm/NavbarToggle";
 | 
				
			|||||||
import NavbarCollapse from "react-bootstrap/esm/NavbarCollapse";
 | 
					import NavbarCollapse from "react-bootstrap/esm/NavbarCollapse";
 | 
				
			||||||
import Dashboard from "./pages/Dashboard";
 | 
					import Dashboard from "./pages/Dashboard";
 | 
				
			||||||
import Login from "./pages/Login";
 | 
					import Login from "./pages/Login";
 | 
				
			||||||
import { apiClient } from "./utils/httpClients";
 | 
					 | 
				
			||||||
import { globalContext } from "./context.js";
 | 
					import { globalContext } from "./context.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function layout() {
 | 
					export default function layout() {
 | 
				
			||||||
    const navigate = useNavigate();
 | 
					    const navigate = useNavigate();
 | 
				
			||||||
    const [state, setState] = useState({
 | 
					 | 
				
			||||||
        user: null,
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(async () => {
 | 
					    const context = useContext(globalContext);
 | 
				
			||||||
        await updateAuthStatus();
 | 
					    let identityDisplay = (
 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    async function updateAuthStatus() {
 | 
					 | 
				
			||||||
        const getUserResponse = await apiClient.get("/user");
 | 
					 | 
				
			||||||
        if (getUserResponse !== 200) {
 | 
					 | 
				
			||||||
            setState({ user: null });
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            setState({ user: getUserResponse.data });
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let indentityDisplay = (
 | 
					 | 
				
			||||||
        <Nav>
 | 
					        <Nav>
 | 
				
			||||||
            <li className="nav-item">
 | 
					            <li className="nav-item">
 | 
				
			||||||
                <NavLink className="nav-link" to="/login" >Login</NavLink>
 | 
					                <NavLink className="nav-link" to="/login" >Login</NavLink>
 | 
				
			||||||
@@ -43,8 +27,8 @@ export default function layout() {
 | 
				
			|||||||
        </Nav>
 | 
					        </Nav>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (state.user) {
 | 
					    if (context.user) {
 | 
				
			||||||
        indentityDisplay = (
 | 
					        identityDisplay = (
 | 
				
			||||||
            <Nav>
 | 
					            <Nav>
 | 
				
			||||||
                <li className="nav-item">
 | 
					                <li className="nav-item">
 | 
				
			||||||
                    <NavLink className="nav-link" to="/" >Hi, {this.state.user.firstName}</NavLink>
 | 
					                    <NavLink className="nav-link" to="/" >Hi, {this.state.user.firstName}</NavLink>
 | 
				
			||||||
@@ -58,7 +42,7 @@ export default function layout() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <div id="app">
 | 
					        <div id="app">
 | 
				
			||||||
            <globalContext.Provider value={{ navigate: navigate }}>
 | 
					            <globalContext.Provider value={{ navigate: navigate, user: null }}>
 | 
				
			||||||
                <header>
 | 
					                <header>
 | 
				
			||||||
                    <Navbar bg="light" expand="md">
 | 
					                    <Navbar bg="light" expand="md">
 | 
				
			||||||
                        <Container>
 | 
					                        <Container>
 | 
				
			||||||
@@ -70,7 +54,7 @@ export default function layout() {
 | 
				
			|||||||
                                        <NavLink className="nav-link" to="/" >Home</NavLink>
 | 
					                                        <NavLink className="nav-link" to="/" >Home</NavLink>
 | 
				
			||||||
                                    </li>
 | 
					                                    </li>
 | 
				
			||||||
                                </Nav>
 | 
					                                </Nav>
 | 
				
			||||||
                                {indentityDisplay}
 | 
					                                {identityDisplay}
 | 
				
			||||||
                            </NavbarCollapse>
 | 
					                            </NavbarCollapse>
 | 
				
			||||||
                        </Container>
 | 
					                        </Container>
 | 
				
			||||||
                    </Navbar>
 | 
					                    </Navbar>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										30
									
								
								sports-matcher/client/src/components/AuthenticationGuard.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								sports-matcher/client/src/components/AuthenticationGuard.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					import React from "react";
 | 
				
			||||||
 | 
					import { Navigate } from "react-router-dom";
 | 
				
			||||||
 | 
					import { globalContext } from "../context";
 | 
				
			||||||
 | 
					import { apiClient } from "../utils/httpClients";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class AuthenticationGuard extends React.Component {
 | 
				
			||||||
 | 
					    constructor(props) {
 | 
				
			||||||
 | 
					        super(props);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async componentDidMount() {
 | 
				
			||||||
 | 
					        if (!this.context.user) {
 | 
				
			||||||
 | 
					            let userDataResponse = await apiClient.get("/user/");
 | 
				
			||||||
 | 
					            if (userDataResponse.status === 200) {
 | 
				
			||||||
 | 
					                this.context.user = userDataResponse.data;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static contextType = globalContext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    render() {
 | 
				
			||||||
 | 
					        if (!this.context.user) {
 | 
				
			||||||
 | 
					            return (
 | 
				
			||||||
 | 
					                <Navigate to="/signup" replace="true" />
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -5,7 +5,7 @@ import { apiClient } from "../utils/httpClients.js";
 | 
				
			|||||||
import MatchInfoCardDisplay from "../components/MatchInfoCardDisplay";
 | 
					import MatchInfoCardDisplay from "../components/MatchInfoCardDisplay";
 | 
				
			||||||
import SportInfoCardDisplay from "../components/SportInfoCardDisplay";
 | 
					import SportInfoCardDisplay from "../components/SportInfoCardDisplay";
 | 
				
			||||||
import { globalContext } from "../context";
 | 
					import { globalContext } from "../context";
 | 
				
			||||||
import { needUser } from "../utils/routing";
 | 
					import AuthenticationGuard from "../components/AuthenticationGuard";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class Dashboard extends React.Component {
 | 
					export default class Dashboard extends React.Component {
 | 
				
			||||||
    constructor(props) {
 | 
					    constructor(props) {
 | 
				
			||||||
@@ -21,7 +21,7 @@ export default class Dashboard extends React.Component {
 | 
				
			|||||||
    static contextType = globalContext;
 | 
					    static contextType = globalContext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async componentDidMount() {
 | 
					    async componentDidMount() {
 | 
				
			||||||
        await needUser(this.context.navigate);
 | 
					        this.setState({ user: this.context.user });
 | 
				
			||||||
        await this.latestMatches();
 | 
					        await this.latestMatches();
 | 
				
			||||||
        await this.availableSports();
 | 
					        await this.availableSports();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -41,29 +41,32 @@ export default class Dashboard extends React.Component {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    render() {
 | 
					    render() {
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            <React.Fragment>
 | 
					            <div className="page-root">
 | 
				
			||||||
                <h1></h1>
 | 
					                <AuthenticationGuard />
 | 
				
			||||||
                <InputGroup className="w-50">
 | 
					                <React.Fragment>
 | 
				
			||||||
                    <FormControl
 | 
					                    <h1></h1>
 | 
				
			||||||
                        placeholder="Search for Matches"
 | 
					                    <InputGroup className="w-50">
 | 
				
			||||||
                        aria-label="Search Bar"
 | 
					                        <FormControl
 | 
				
			||||||
                        aria-describedby="basic-addon2"
 | 
					                            placeholder="Search for Matches"
 | 
				
			||||||
                    />
 | 
					                            aria-label="Search Bar"
 | 
				
			||||||
                    <Button variant="outline-secondary" id="button-addon2">
 | 
					                            aria-describedby="basic-addon2"
 | 
				
			||||||
                        Search
 | 
					                        />
 | 
				
			||||||
                    </Button>
 | 
					                        <Button variant="outline-secondary" id="button-addon2">
 | 
				
			||||||
                </InputGroup>
 | 
					                            Search
 | 
				
			||||||
                <div className="p-4">
 | 
					                        </Button>
 | 
				
			||||||
                    <h2>Available Matches</h2>
 | 
					                    </InputGroup>
 | 
				
			||||||
                    <MatchInfoCardDisplay recommendedmatches={this.state.displayedMatches} />
 | 
					                    <div className="p-4">
 | 
				
			||||||
                </div>
 | 
					                        <h2>Available Matches</h2>
 | 
				
			||||||
                <div className="p-4">
 | 
					                        <MatchInfoCardDisplay recommendedmatches={this.state.displayedMatches} />
 | 
				
			||||||
                    <h2>Available Sports</h2>
 | 
					                    </div>
 | 
				
			||||||
                    <SportInfoCardDisplay recommendedsports={this.state.displayedSports} />
 | 
					                    <div className="p-4">
 | 
				
			||||||
                </div>
 | 
					                        <h2>Available Sports</h2>
 | 
				
			||||||
 | 
					                        <SportInfoCardDisplay recommendedsports={this.state.displayedSports} />
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            </React.Fragment>
 | 
					                </React.Fragment>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -2,7 +2,7 @@ import React from "react";
 | 
				
			|||||||
import { Alert, Button, Card, Container, Form } from "react-bootstrap";
 | 
					import { Alert, Button, Card, Container, Form } from "react-bootstrap";
 | 
				
			||||||
import { globalContext } from "../context";
 | 
					import { globalContext } from "../context";
 | 
				
			||||||
import { apiClient } from "../utils/httpClients";
 | 
					import { apiClient } from "../utils/httpClients";
 | 
				
			||||||
import { guard } from "../utils/routing";
 | 
					import AuthenticationGuard from "../components/AuthenticationGuard";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class Login extends React.Component {
 | 
					export default class Login extends React.Component {
 | 
				
			||||||
    constructor(props) {
 | 
					    constructor(props) {
 | 
				
			||||||
@@ -19,14 +19,6 @@ export default class Login extends React.Component {
 | 
				
			|||||||
    static contextType = globalContext;
 | 
					    static contextType = globalContext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async componentDidMount() {
 | 
					    async componentDidMount() {
 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            const getUserResponse = await apiClient.get("/user");
 | 
					 | 
				
			||||||
            guard(this.context.navigate, () => getUserResponse.status === 401, "/dashboard"); // If it's not 401, then we redirect to dashboard.
 | 
					 | 
				
			||||||
        } catch (error) {
 | 
					 | 
				
			||||||
            if (error.message !== "Request failed with status code 401") {
 | 
					 | 
				
			||||||
                throw error;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async attemptLogin(e) {
 | 
					    async attemptLogin(e) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,22 +0,0 @@
 | 
				
			|||||||
import { apiClient } from "./httpClients";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function guard(navigator, evaluator, redirect, navigateOptions, onRedirect) {
 | 
					 | 
				
			||||||
    if (!evaluator) throw new Error("evaluator required.");
 | 
					 | 
				
			||||||
    if (!redirect) throw new Error("redirect required.");
 | 
					 | 
				
			||||||
    if (!navigateOptions) {
 | 
					 | 
				
			||||||
        navigateOptions = {
 | 
					 | 
				
			||||||
            replace: true
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    let redirecting = !evaluator();
 | 
					 | 
				
			||||||
    if (redirecting) {
 | 
					 | 
				
			||||||
        if (onRedirect) onRedirect();
 | 
					 | 
				
			||||||
        navigator(redirect, navigateOptions);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export async function needUser(navigator) {
 | 
					 | 
				
			||||||
    let userDataResponse = await apiClient.get("/user");
 | 
					 | 
				
			||||||
    guard(navigator, () => userDataResponse.status === 200, "/login");
 | 
					 | 
				
			||||||
    return userDataResponse.data;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
		Reference in New Issue
	
	Block a user