to do: troubleshoot missing token in request headers

This commit is contained in:
Mikayla Dobson
2023-02-11 21:51:39 -06:00
parent 90a5bdf128
commit 1b73fa6b99
13 changed files with 109 additions and 114 deletions

View File

@@ -3,6 +3,7 @@ import { useEffect } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom'; import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { useAuthContext } from './context/AuthContext'; import { useAuthContext } from './context/AuthContext';
import jwtDecode from 'jwt-decode'; import jwtDecode from 'jwt-decode';
import API from './util/API';
// pages, ui, components, styles // pages, ui, components, styles
import Subscriptions from './components/pages/Subscriptions/Subscriptions'; import Subscriptions from './components/pages/Subscriptions/Subscriptions';
@@ -22,7 +23,7 @@ import { TokenType } from './util/types';
import './sass/App.scss'; import './sass/App.scss';
function App() { function App() {
const { setUser, setToken } = useAuthContext(); const { setUser, token, setToken } = useAuthContext();
useEffect(() => { useEffect(() => {
if (document.cookie) { if (document.cookie) {
@@ -30,7 +31,11 @@ function App() {
setToken(document.cookie.split("=")[1]); setToken(document.cookie.split("=")[1]);
setUser(extractedToken.user); setUser(extractedToken.user);
} }
}, []); }, [document.cookie]);
useEffect(() => {
token && API.Settings.setToken(token);
}, [token])
return ( return (
<BrowserRouter> <BrowserRouter>

View File

@@ -10,7 +10,7 @@ const AddRecipe = () => {
const getFormState = useCallback((data: IRecipe) => { const getFormState = useCallback((data: IRecipe) => {
setInput(data); setInput(data);
}, []) }, [input])
const handleCreate = () => { const handleCreate = () => {
for (let field of Object.keys(input)) { for (let field of Object.keys(input)) {
@@ -28,20 +28,6 @@ const AddRecipe = () => {
} }
}) })
}, [authContext]) }, [authContext])
useEffect(() => {
input.authoruserid && setForm(
new Form<IRecipe>({
parent: "AddRecipe",
keys: ["name", "preptime", "course", "cuisine", "ingredients", "description"],
labels: ["Recipe Name:", "Prep Time:", "Course:", "Cuisine:", "Ingredients:", "Description:"],
dataTypes: ['text', 'text', 'custom picker', 'custom picker', 'custom picker', 'TINYMCE'],
initialState: input,
getState: getFormState,
richTextInitialValue: "<p>Enter recipe details here!</p>"
}).mount()
)
}, [input.authoruserid])
useEffect(() => { useEffect(() => {
console.log(input); console.log(input);
@@ -53,7 +39,18 @@ const AddRecipe = () => {
<Divider /> <Divider />
<Panel> <Panel>
<Form parent={input} _config={{
parent: "AddRecipe",
keys: ["name", "preptime", "course", "cuisine", "ingredients", "description"],
labels: ["Recipe Name:", "Prep Time:", "Course:", "Cuisine:", "Ingredients:", "Description:"],
dataTypes: ['text', 'text', 'custom picker', 'custom picker', 'custom picker', 'TINYMCE'],
initialState: input,
getState: getFormState,
richTextInitialValue: "<p>Enter recipe details here!</p>"
}} />
{ form || <h2>Loading...</h2> } { form || <h2>Loading...</h2> }
<Button onClick={handleCreate}>Create Recipe!</Button> <Button onClick={handleCreate}>Create Recipe!</Button>
</Panel> </Panel>
</Page> </Page>

View File

@@ -9,8 +9,7 @@ import API from "../../util/API";
export default function Login() { export default function Login() {
const params = new URLSearchParams(window.location.search); const params = new URLSearchParams(window.location.search);
const redirect = params.get("redirect"); const redirect = params.get("redirect");
const { user, setUser } = useContext(AuthContext); const { user, setToken } = useContext(AuthContext);
const [form, setForm] = useState<JSX.Element>();
// setup and local state // setup and local state
const navigate = useNavigate(); const navigate = useNavigate();
@@ -25,11 +24,12 @@ export default function Login() {
const handleLogin = async () => { const handleLogin = async () => {
if (!input.email || !input.password) return; if (!input.email || !input.password) return;
const result = await new API.Auth().login(input); const result = await new API.Auth().login(input);
console.log(result);
// const { data, ok } = await attemptLogin(input); // setting token will trigger ui update
// if (ok) setUser(data); setToken(result.token);
// navigate(`/${redirect ?? ''}`);
// if there is a redirect, go there, else go home
navigate(`/${redirect ?? ''}`);
} }
// check for logged in user and mount form // check for logged in user and mount form
@@ -37,10 +37,6 @@ export default function Login() {
if (user) navigate('/'); if (user) navigate('/');
}, []) }, [])
useEffect(() => {
console.log(input);
}, [getFormState])
return ( return (
<Page> <Page>
<h1>Hello! Nice to see you again.</h1> <h1>Hello! Nice to see you again.</h1>
@@ -54,7 +50,7 @@ export default function Login() {
dataTypes: Object.keys(input), dataTypes: Object.keys(input),
initialState: input, initialState: input,
getState: getFormState getState: getFormState
} as FormConfig<typeof input>} /> }} />
<Button onClick={handleLogin}>Log In</Button> <Button onClick={handleLogin}>Log In</Button>

View File

@@ -14,8 +14,7 @@ export default function Profile() {
return ( return (
<Protect redirect="profile"> <Protect redirect="profile">
<div className="profile-authenticated"> <div className="profile-authenticated">
<h1>{user?.firstname}'s Profile</h1> <h1>{user && user.firstname}'s Profile</h1>
<p>Things and stuff!</p>
<Friends /> <Friends />
</div> </div>
</Protect> </Protect>

View File

@@ -5,9 +5,10 @@ import { RegisterVariantType, VariantLabel } from ".";
import { useAuthContext } from "../../../context/AuthContext"; import { useAuthContext } from "../../../context/AuthContext";
import { IUser, IUserAuth } from "../../../schemas"; import { IUser, IUserAuth } from "../../../schemas";
import { attemptLogin, attemptRegister } from "../../../util/apiUtils"; import { attemptLogin, attemptRegister } from "../../../util/apiUtils";
import API from "../../../util/API";
import { Button, Page, Panel } from "../../ui"; import { Button, Page, Panel } from "../../ui";
import Divider from "../../ui/Divider"; import Divider from "../../ui/Divider";
import Form, { FormConfig } from "../../ui/Form"; import Form from "../../ui/Form";
const blankUser: IUser = { const blankUser: IUser = {
firstname: '', firstname: '',
@@ -20,51 +21,31 @@ const blankUser: IUser = {
} }
const AboutYou: RegisterVariantType = ({ transitionDisplay }) => { const AboutYou: RegisterVariantType = ({ transitionDisplay }) => {
const auth = new API.Auth();
const navigate = useNavigate(); const navigate = useNavigate();
const authContext = useAuthContext(); const { user, setToken } = useAuthContext();
const [form, setForm] = useState<JSX.Element>(<p key={v4()}>Loading content...</p>);
const [input, setInput] = useState<IUser>(blankUser); const [input, setInput] = useState<IUser>(blankUser);
const [regSuccess, setRegSuccess] = useState<any>();
const getFormState = useCallback((received: IUser) => { const getFormState = useCallback((received: IUser) => {
setInput(received); setInput(received);
}, []); }, []);
useEffect(() => { useEffect(() => {
if (authContext.user) navigate('/'); if (user) navigate('/');
}, [authContext]); }, [user]);
async function handleRegister() { async function handleRegister() {
const res = await attemptRegister(input); const res = await auth.register(input);
if (res.ok) { if (res.ok) {
setTimeout(async () => {
const result = await auth.login(input);
setToken(result.token);
}, 750);
transitionDisplay(VariantLabel.InitialCollection, input); transitionDisplay(VariantLabel.InitialCollection, input);
} }
} }
async function unwrapLogin() {
const data: IUserAuth = { email: input.email, password: input.password || "" }
const login = await attemptLogin(data);
if (login) {
authContext.user = login.user;
}
navigate('/');
}
useEffect(() => {
setForm(new Form<IUser>({
parent: "register",
keys: ['firstname', 'lastname', 'handle', 'email', 'password'],
initialState: input,
labels: ['First Name', 'Last Name', 'Handle', 'Email', "Password"],
dataTypes: ['text', 'text', 'text', 'email', 'password'],
getState: getFormState
}).mount());
}, [])
useEffect(() => {
if (regSuccess) unwrapLogin();
}, [regSuccess])
return ( return (
<Page> <Page>
<h1>Hi! Thanks for being here.</h1> <h1>Hi! Thanks for being here.</h1>
@@ -74,7 +55,16 @@ const AboutYou: RegisterVariantType = ({ transitionDisplay }) => {
<h2>Tell us a bit about yourself:</h2> <h2>Tell us a bit about yourself:</h2>
<Panel extraStyles="form-panel two-columns"> <Panel extraStyles="form-panel two-columns">
{ form || <h2>Loading...</h2> }
<Form parent={input} _config={{
parent: "register",
keys: ['firstname', 'lastname', 'handle', 'email', 'password'],
initialState: input,
labels: ['First Name', 'Last Name', 'Handle', 'Email', "Password"],
dataTypes: ['text', 'text', 'text', 'email', 'password'],
getState: getFormState
}} />
<Button onClick={handleRegister}>Register</Button> <Button onClick={handleRegister}>Register</Button>
</Panel> </Panel>
</Page> </Page>

View File

@@ -3,33 +3,23 @@ import { RegisterVariantType, VariantLabel } from ".";
import { useNow } from "../../../hooks/useNow"; import { useNow } from "../../../hooks/useNow";
import { ICollection, IUser, IUserAuth } from "../../../schemas"; import { ICollection, IUser, IUserAuth } from "../../../schemas";
import { attemptLogin, createNewCollection } from "../../../util/apiUtils"; import { attemptLogin, createNewCollection } from "../../../util/apiUtils";
import API from "../../../util/API";
import { Button, Divider, Page, Panel } from "../../ui"; import { Button, Divider, Page, Panel } from "../../ui";
import TextField from "../../ui/TextField"; import TextField from "../../ui/TextField";
import { useAuthContext } from "../../../context/AuthContext";
const InitialCollection: RegisterVariantType = ({ transitionDisplay, input }) => { const InitialCollection: RegisterVariantType = ({ transitionDisplay, input }) => {
const { user, setUser } = useAuthContext();
const [collectionName, setCollectionName] = useState<string>(); const [collectionName, setCollectionName] = useState<string>();
const [view, setView] = useState<JSX.Element>(<Page><h1>Loading...</h1></Page>); const [view, setView] = useState<JSX.Element>(<Page><h1>Loading...</h1></Page>);
const [user, setUser] = useState<IUser>();
const now = useNow(); const now = useNow();
async function unwrapLogin(data: IUser) { const collectionAPI = new API.Collection();
const userInfo: IUserAuth = { email: data.email, password: data.password! }
const login = await attemptLogin(userInfo);
setUser(login.user);
}
useEffect(() => {
if (input) {
setTimeout(() => {
unwrapLogin(input);
}, 750);
}
}, [])
const handleClick = async () => { const handleClick = async () => {
if (!user) return; if (!user) return;
const collection: ICollection = { const collection: ICollection = {
name: collectionName || (user.firstname + "'s Collection"), name: collectionName ?? (user.firstname + "'s Collection"),
active: true, active: true,
ismaincollection: true, ismaincollection: true,
ownerid: user.id!.toString(), ownerid: user.id!.toString(),
@@ -39,7 +29,7 @@ const InitialCollection: RegisterVariantType = ({ transitionDisplay, input }) =>
console.log(collection); console.log(collection);
const result = await createNewCollection(collection); const result = await collectionAPI.post(collection);
console.log(result); console.log(result);
if (result) transitionDisplay(VariantLabel.AddFriends); if (result) transitionDisplay(VariantLabel.AddFriends);
} }
@@ -58,7 +48,8 @@ const InitialCollection: RegisterVariantType = ({ transitionDisplay, input }) =>
<Divider /> <Divider />
<h3>What would you like to call your main collection?</h3> <h3>What would you like to call your main collection?</h3>
<TextField onChange={(e: ChangeEvent<HTMLInputElement>) => setCollectionName(e.target.value)} placeholder={user.firstname + 's Collection'} /> {/* <TextField onChange={(e: ChangeEvent<HTMLInputElement>) => setCollectionName(e.target.value)} placeholder={user.firstname + 's Collection'} /> */}
<input type="text" onChange={(e) => setCollectionName(e.target.value)} placeholder={user.firstname + 's Collection'}></input>
</Panel> </Panel>
<Button onClick={handleClick}>Next</Button> <Button onClick={handleClick}>Next</Button>

View File

@@ -1,7 +1,7 @@
import API from "../../../util/API"; import API from "../../../util/API";
import { NavbarType } from "../../../util/types"; import { NavbarType } from "../../../util/types";
import { Button, Dropdown } from '..' import { Button, Dropdown } from '..'
import { useState } from "react"; import { useEffect, useState } from "react";
import { useAuthContext } from "../../../context/AuthContext"; import { useAuthContext } from "../../../context/AuthContext";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
@@ -40,6 +40,10 @@ const LoggedIn = () => {
navigate(payload); navigate(payload);
} }
useEffect(() => {
console.log(user);
}, [])
return ( return (
<div> <div>
<div id="navbar"> <div id="navbar">
@@ -47,7 +51,7 @@ const LoggedIn = () => {
<a onClick={() => navigate('/')}>RECIPIN</a> <a onClick={() => navigate('/')}>RECIPIN</a>
</div> </div>
<div className="navbar-block"> <div className="navbar-block">
<p>Hi, {user?.firstname}.</p> <p>Hi, {user && user.firstname}.</p>
<span id="search-icon"></span> <span id="search-icon"></span>
<Button onClick={() => handleUIChange("SEARCH")}>Search</Button> <Button onClick={() => handleUIChange("SEARCH")}>Search</Button>
<Button onClick={() => handleUIChange("ACTIONS")}>Actions</Button> <Button onClick={() => handleUIChange("ACTIONS")}>Actions</Button>

View File

@@ -3,12 +3,15 @@ import { IUser } from "../../../schemas";
import { TextField, UserCard } from ".."; import { TextField, UserCard } from "..";
import { v4 } from "uuid"; import { v4 } from "uuid";
import { getAllUsers } from "../../../util/apiUtils"; import { getAllUsers } from "../../../util/apiUtils";
import API from "../../../util/API";
const FriendSearchWidget: FC<{}> = () => { const FriendSearchWidget: FC<{}> = () => {
const [searchTerm, setSearchTerm] = useState<string>(); const [searchTerm, setSearchTerm] = useState<string>();
const [userPool, setUserPool] = useState<IUser[]>([]); const [userPool, setUserPool] = useState<IUser[]>([]);
const [friendResults, setFriendResults] = useState<IUser[]>([]); const [friendResults, setFriendResults] = useState<IUser[]>([]);
const users = new API.User();
// this isn't really working right now i don't think // this isn't really working right now i don't think
const handleRequestSent = useCallback((targetid: string | number) => { const handleRequestSent = useCallback((targetid: string | number) => {
setUserPool((prev: IUser[]) => { setUserPool((prev: IUser[]) => {
@@ -23,7 +26,7 @@ const FriendSearchWidget: FC<{}> = () => {
// load available user pool on mount // load available user pool on mount
useEffect(() => { useEffect(() => {
(async function() { (async function() {
const result = await getAllUsers(); const result = await users.getAll();
if (result) setUserPool(result); if (result) setUserPool(result);
})(); })();
}, []) }, [])

View File

@@ -3,57 +3,61 @@ import { IUser, IUserAuth, IFriendship, IRecipe, IIngredient, ICollection, IGroc
import { default as _instance } from "./axiosInstance"; import { default as _instance } from "./axiosInstance";
module API { module API {
const APISTRING = import.meta.env.APISTRING || "http://localhost:8080"; export class Settings {
private static APISTRING = import.meta.env.APISTRING || "http://localhost:8080";
private static token?: string;
public static getAPISTRING() {
return Settings.APISTRING;
}
public static getToken() {
return Settings.token;
}
public static setToken(newToken: string) {
Settings.token = newToken;
}
}
abstract class RestController<T> { abstract class RestController<T> {
protected instance = _instance; protected instance = _instance;
protected endpoint: string; protected endpoint: string;
protected token?: string;
protected headers?: any protected headers?: any
constructor(endpoint: string, token?: string) { constructor(endpoint: string) {
this.endpoint = endpoint; this.endpoint = endpoint;
this.token = token;
if (token) { if (Settings.getToken()) {
this.headers = { this.headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
"Authorization": ("Bearer " + token) "Authorization": ("Bearer " + Settings.getToken())
}; };
} }
} }
async getAll() { async getAll() {
if (!this.token) return null;
const response = await this.instance.get(this.endpoint, this.headers); const response = await this.instance.get(this.endpoint, this.headers);
return Promise.resolve(response.data); return Promise.resolve(response.data);
} }
async getByID(id: string) { async getByID(id: string) {
if (!this.token) return null;
const response = await this.instance.get(this.endpoint + "/" + id, this.headers); const response = await this.instance.get(this.endpoint + "/" + id, this.headers);
return Promise.resolve(response.data); return Promise.resolve(response.data);
} }
async postOne(data: T) { async post(data: T) {
if (!this.token) return null; console.log(data);
const response = await this.instance.post(this.endpoint, data, this.headers); const response = await this.instance.post(this.endpoint, data, this.headers);
return Promise.resolve(response.data); return Promise.resolve(response.data);
} }
async put(id: string, data: T | Partial<T>) { async put(id: string, data: T | Partial<T>) {
if (!this.token) return null; const response = await this.instance.put(this.endpoint + "/" + id, JSON.stringify(data), this.headers);
const response = await this.instance.put(this.endpoint + "/" + id, data, this.headers);
return Promise.resolve(response.data); return Promise.resolve(response.data);
} }
async delete(id: string) { async delete(id: string) {
if (!this.token) return null;
const response = await this.instance.delete(this.endpoint + '/' + id, this.headers); const response = await this.instance.delete(this.endpoint + '/' + id, this.headers);
return Promise.resolve(response.data); return Promise.resolve(response.data);
} }
@@ -61,7 +65,7 @@ module API {
export class Auth { export class Auth {
private instance = _instance; private instance = _instance;
private endpoint = APISTRING + "/auth"; private endpoint = Settings.getAPISTRING() + "/auth";
async login(data: IUserAuth | Partial<IUser>) { async login(data: IUserAuth | Partial<IUser>) {
try { try {
@@ -109,18 +113,16 @@ module API {
export class User extends RestController<IUser> { export class User extends RestController<IUser> {
constructor() { constructor() {
super(APISTRING + "/app/users"); super(Settings.getAPISTRING() + "/app/users");
} }
} }
export class Friendship extends RestController<IFriendship> { export class Friendship extends RestController<IFriendship> {
constructor() { constructor() {
super(APISTRING + "/app/friends"); super(Settings.getAPISTRING() + "/app/friends");
} }
async getPendingFriendRequests() { async getPendingFriendRequests() {
if (!this.token) return null;
const response = await this.instance.get(this.endpoint + "?pending=true", this.headers); const response = await this.instance.get(this.endpoint + "?pending=true", this.headers);
return Promise.resolve(response.data); return Promise.resolve(response.data);
} }
@@ -128,25 +130,25 @@ module API {
export class Recipe extends RestController<IRecipe> { export class Recipe extends RestController<IRecipe> {
constructor() { constructor() {
super(APISTRING + "/app/recipes"); super(Settings.getAPISTRING() + "/app/recipes");
} }
} }
export class Ingredient extends RestController<IIngredient> { export class Ingredient extends RestController<IIngredient> {
constructor() { constructor() {
super(APISTRING + "/app/ingredients"); super(Settings.getAPISTRING() + "/app/ingredients");
} }
} }
export class Collection extends RestController<ICollection> { export class Collection extends RestController<ICollection> {
constructor() { constructor() {
super(APISTRING + "/app/collections"); super(Settings.getAPISTRING() + "/app/collection");
} }
} }
export class GroceryList extends RestController<IGroceryList> { export class GroceryList extends RestController<IGroceryList> {
constructor() { constructor() {
super(APISTRING + "/app/grocery-list") super(Settings.getAPISTRING() + "/app/grocery-list")
} }
} }
} }

View File

@@ -1,4 +1,4 @@
import { Express } from 'express'; import express, { Express } from 'express';
import bodyParser from 'body-parser'; import bodyParser from 'body-parser';
import cookieParser from 'cookie-parser'; import cookieParser from 'cookie-parser';
import morgan from 'morgan'; import morgan from 'morgan';
@@ -19,8 +19,8 @@ declare module 'express-session' {
export const expressLoader = async (app: Express) => { export const expressLoader = async (app: Express) => {
app.use(cors({ origin: origin })); app.use(cors({ origin: origin }));
app.use(bodyParser.json()); app.use(express.json());
app.use(bodyParser.urlencoded({ extended: true })); app.use(express.urlencoded({ extended: true }));
app.use(cookieParser()); app.use(cookieParser());
app.use(morgan('tiny')); app.use(morgan('tiny'));
app.use(requireSessionSecret); app.use(requireSessionSecret);

View File

@@ -56,6 +56,8 @@ export const authRoute = (app: Express) => {
const safeUserData = { const safeUserData = {
id: user.id, id: user.id,
firstname: user.firstname,
lastname: user.lastname,
handle: user.handle, handle: user.handle,
email: user.email, email: user.email,
datecreated: user.datecreated, datecreated: user.datecreated,

View File

@@ -8,6 +8,12 @@ const router = Router();
export const collectionRoute = (app: Express) => { export const collectionRoute = (app: Express) => {
app.use('/app/collection', router); app.use('/app/collection', router);
router.use((req, res, next) => {
console.log('what gives');
console.log(req.body);
next();
})
router.get('/:id', async (req, res, next) => { router.get('/:id', async (req, res, next) => {
const { id } = req.params; const { id } = req.params;
try { try {
@@ -30,7 +36,7 @@ export const collectionRoute = (app: Express) => {
router.post('/', async (req, res, next) => { router.post('/', async (req, res, next) => {
const data = req.body; const data = req.body;
console.log(data); console.log(req.body ?? "sanity check");
try { try {
const result = await CollectionInstance.post(data); const result = await CollectionInstance.post(data);

View File

@@ -25,7 +25,7 @@ export const routes = async (app: Express) => {
console.log(token); console.log(token);
if (!token) { if (!token) {
res.status(403).send("Unauthorized"); res.status(403).send("Unauthorized, did not receive token");
} else { } else {
jwt.verify(token, process.env.SESSIONSECRET as string, (err, data) => { jwt.verify(token, process.env.SESSIONSECRET as string, (err, data) => {
if (err) { if (err) {