refactoring to use jwt with cookies

This commit is contained in:
Mikayla Dobson
2023-02-08 16:01:23 -06:00
parent 38f8ef23d0
commit 303c2d031e
18 changed files with 222 additions and 57 deletions

View File

@@ -3,6 +3,7 @@
"private": true,
"version": "0.0.0",
"type": "module",
"proxy": "http://localhost:8080",
"scripts": {
"dev": "vite",
"build": "vite build",

View File

@@ -1,10 +1,27 @@
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import API from './util/API';
import Home from './pages/Home';
import SingleItem from './pages/protected/SingleItem';
import Items from './pages/protected/Items';
import './App.css'
import { useCallback, useEffect, useState } from 'react';
import useAuthContext from './context/useAuthContext';
import jwtDecode from 'jwt-decode';
function App() {
const { setUser, token, setToken } = useAuthContext();
useEffect(() => {
if (document.cookie) {
console.log(document.cookie);
setToken(document.cookie.split("=")[1]);
setUser(jwtDecode(document.cookie.split("=")[1]).user);
console.log(token);
}
}, []);
return (
<BrowserRouter>
<div className="App">

View File

@@ -0,0 +1,4 @@
import { createContext } from "react";
const AuthContext = createContext();
export default AuthContext;

View File

@@ -0,0 +1,17 @@
import { useEffect, useState } from "react"
import AuthContext from "./authContext";
const AuthProvider = ({ children }) => {
const [user, setUser] = useState();
const [token, setToken] = useState();
const value = { user, setUser, token, setToken }
return (
<AuthContext.Provider value={ value }>
{ children }
</AuthContext.Provider>
)
}
export default AuthProvider;

View File

@@ -0,0 +1,14 @@
import { useContext, useEffect } from "react"
import AuthContext from "./authContext"
const useAuthContext = () => {
const value = useContext(AuthContext);
useEffect(() => {
console.log(value);
}, [value])
return value;
}
export default useAuthContext

View File

@@ -1,10 +1,13 @@
import React from 'react'
import AuthProvider from './context/AuthProvider'
import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>,
<AuthProvider>
<React.StrictMode>
<App />
</React.StrictMode>
</AuthProvider>
)

View File

@@ -1,10 +1,23 @@
import { useState } from "react";
import { LoginForm, RegisterForm } from "../components/Form";
import useAuthContext from "../context/useAuthContext";
import API from "../util/API";
function Auth({ handleLogin, handleRegister }) {
const [info, setInfo] = useState({ email: "", password: "" });
const [register, setRegister] = useState({ username: "", email: "", password: "" })
const { setUser, setToken } = useAuthContext();
async function handleLogin() {
if (!info.email || !info.password) return;
const response = await API.login(info);
setUser(response.user);
setToken(response.token);
}
return (
<section>
<LoginForm info={info} setInfo={setInfo} />

View File

@@ -1,25 +1,23 @@
import { useNavigate } from "react-router-dom";
import { useEffect, useState } from "react";
import jwt_decode from "jwt-decode";
import useAuthContext from "../context/useAuthContext"
import API from "../util/API";
import Auth from "./Auth";
import Welcome from "./protected/Welcome";
function Home() {
const [user, setUser] = useState(null);
const [contents, setContents] = useState(<></>);
const [update, setUpdate] = useState(false);
const { user, setUser, token, setToken } = useAuthContext();
const navigate = useNavigate();
async function handleLogin(info) {
if (!info.email || !info.password) return;
const response = await API.login(info);
const user = jwt_decode(response.token);
console.log(user);
localStorage.setItem('user', JSON.stringify(user));
localStorage.setItem('token', response.token);
setUser(response.user);
setToken(response.token);
setUpdate(!update);
}
@@ -46,16 +44,8 @@ function Home() {
}
useEffect(() => {
let item = localStorage.getItem('user');
if (item) {
item = JSON.parse(item);
setUser(item.user);
}
}, [update])
useEffect(() => {
setContents(user ? <Welcome user={user} handleLogout={handleLogout} /> : <Auth handleLogin={handleLogin} handleRegister={handleRegister} />)
}, [user, update])
setContents(user ? <Welcome handleLogout={handleLogout} /> : <Auth handleLogin={handleLogin} handleRegister={handleRegister} />)
}, [token])
return (
<main>

View File

@@ -1,15 +1,21 @@
import { useState, useEffect } from "react";
import API from "../../util/API";
import { v4 } from "uuid";
import useAuthContext from "../../context/useAuthContext";
function Items() {
const [items, setItems] = useState(<p>Loading...</p>);
const { token, setToken } = useAuthContext();
const [content, setContent] = useState();
useEffect(() => {
(async() => {
console.log(token);
token && (async() => {
try {
const data = await API.getItems();
const data = await API.getItems(token);
console.log(data);
setItems(data.map(item => (
<div className="item" key={v4()}>
<a href={`/item/${item.id}`}>{item.name}</a>
@@ -25,7 +31,7 @@ function Items() {
)
}
})();
}, [])
}, [token, setToken])
return (
<main>

View File

@@ -1,15 +1,17 @@
import { useState, useEffect } from "react";
import { useParams } from "react-router-dom"
import useAuthContext from "../../context/useAuthContext";
import API from "../../util/API";
function SingleItem() {
const [content, setContent] = useState(<p>Loading...</p>);
const { token, setToken } = useAuthContext();
const { id } = useParams();
useEffect(() => {
(async() => {
try {
const data = await API.getOneItem(id);
const data = await API.getOneItem(id, token);
console.log(data);
setContent(
<>
@@ -26,7 +28,7 @@ function SingleItem() {
)
}
})();
}, []);
}, [token, setToken]);
return (
<div>

View File

@@ -1,11 +1,21 @@
import { useNavigate } from "react-router-dom"
import API from "../../util/API";
import useAuthContext from "../../context/useAuthContext";
function Welcome({ user, handleLogout }) {
function Welcome() {
const navigate = useNavigate();
const { user, setUser, setToken } = useAuthContext();
async function handleLogout() {
await API.logout();
setUser(null);
setToken(null);
navigate('/');
}
return (
<section>
<h2>Welcome, {user.username}!</h2>
<h2>Welcome, {user?.username}!</h2>
<hr />
<h3>Check out some cool protected actions:</h3>

View File

@@ -1,11 +1,15 @@
import { default as _api } from './axiosInstance';
export default class API {
static async validate() {
static async validate(token) {
try {
const response = await _api.get('/');
const data = Promise.resolve(response.data);
console.log(data);
const response = await _api.get('/', {
headers: {
"Content-Type": "application/json",
"Authorization": ("Bearer " + token)
}
});
const data = Promise.resolve(response);
return data;
} catch (error) {
console.log(error);
@@ -15,7 +19,8 @@ export default class API {
static async login(data) {
try {
const response = await _api.post('/auth/login', data);
return Promise.resolve(response.data);
console.log(response);
return Promise.resolve(response);
} catch(err) {
console.log(err);
}
@@ -25,7 +30,10 @@ export default class API {
try {
const response = await _api.delete('/auth/logout');
console.log(response);
return Promise.resolve(response.data);
document.cookie = `token=;expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
console.log(document.cookie);
return null;
} catch(err) {
console.log(err);
}
@@ -40,21 +48,18 @@ export default class API {
}
}
static async getItems() {
const token = localStorage.getItem("token");
static async getItems(token) {
const response = await _api.get('/app/item', {
headers: {
"Content-Type": "application/json",
"Authorization": ("Bearer " + token)
"Authorization": `Bearer ${token}`
}
});
return Promise.resolve(response.data);
}
static async getOneItem(id) {
const token = localStorage.getItem("token");
static async getOneItem(id, token) {
const response = await _api.get(`/app/item/${id}`, {
headers: {
"Content-Type": "application/json",

View File

@@ -1,7 +1,28 @@
import axios from 'axios'
import jwt_decode from 'jwt-decode'
const apiUrl = import.meta.env.VITE_APIURL;
export default axios.create({
baseURL: "http://localhost:8080"
const instance = axios.create({
baseURL: apiUrl
});
instance.interceptors.response.use((res) => {
if (res?.data.token) {
document.cookie = `token=${res.data.token}`;
return {
token: res.data.token,
user: jwt_decode(res.data.token).user,
data: res.data.data || null
}
} else {
return {
data: res?.data || null
}
}
}, (err) => {
return Promise.reject(err);
})
export default instance;

View File

@@ -30,6 +30,8 @@ async function authRoute(app, passport) {
response = response.data;
}
console.log(response);
req.user = response;
req.session.user = response;
@@ -43,12 +45,14 @@ async function authRoute(app, passport) {
}
const token = jwt.sign({ user: safeUserData }, secret);
req.session.token = token;
req.session.save((err) => {
return next(err);
})
console.log(req.session);
res.cookie('token', token, { httpOnly: true });
res.json({ token });
}
} catch (error) {
@@ -60,7 +64,8 @@ async function authRoute(app, passport) {
try {
req.session = null;
req.user = null;
res.status(200).clearCookie('connect.sid');
res.clearCookie('connect.sid').clearCookie('token');
res.status(204).send("logout successful");
res.end();
} catch (error) {
console.log(error);

25
server/routes/base.js Normal file
View File

@@ -0,0 +1,25 @@
const router = require('express').Router();
require('dotenv').config();
const secret = process.env.SECRET;
async function baseRoute(app, passport) {
router.get('/', async (req, res, next) => {
try {
console.log(req.session);
const user = undefined;
if (!user) {
res.status(403).send("Not authorized");
} else {
res.status(200).send({ token: req.token, user: req.user });
}
} catch (error) {
console.log(error);
}
})
return router;
}
module.exports = baseRoute;

View File

@@ -1,22 +1,32 @@
const jwt = require('jsonwebtoken');
const authRoute = require("./auth");
const itemRoute = require("./item");
const jwtRoute = require('./jwt');
const baseRoute = require('./base');
async function routesLoader(app, passport) {
app.use('/app', (req, res, next) => {
const token = req.headers['authorization'].split(" ")[1];
jwt.verify(token, process.env.SECRET, (err, data) => {
if (err) {
res.status(403).send(err);
} else {
req.user = data;
next();
}
})
const token = req.headers['authorization']?.split(" ")[1];
if (!token) {
res.status(403).send("Unauthorized");
} else {
jwt.verify(token, process.env.SECRET, (err, data) => {
if (err) {
res.status(403).send(err);
} else {
req.user = data;
next();
}
})
}
})
app.use('/', await baseRoute(app, passport));
app.use('/auth', await authRoute(app, passport));
app.use('/app', await itemRoute(app, passport));
app.use('/jwt', await jwtRoute(app, passport));
}
module.exports = routesLoader;

View File

@@ -1,10 +1,10 @@
// const jwt = require('jsonwebtoken');
const jwt = require('jsonwebtoken');
require('dotenv').config();
const router = require('express').Router();
const ItemController = require('../controllers/ItemController');
async function itemRoute(app, passport) {
/* router.use('/', (req, res, next) => {
router.use('/', (req, res, next) => {
const token = req.headers['authorization'].split(" ")[1];
jwt.verify(token, process.env.SECRET, (err, data) => {
if (err) {
@@ -14,7 +14,7 @@ async function itemRoute(app, passport) {
next();
}
})
}) */
})
router.get('/item', async (req, res) => {
const response = await ItemController.getAll();

22
server/routes/jwt.js Normal file
View File

@@ -0,0 +1,22 @@
const router = require('express').Router();
const jwt = require('jsonwebtoken');
require('dotenv').config();
const secret = process.env.SECRET;
async function jwtRoute(app, passport) {
router.get('/', (req, res) => {
const user = req.user;
if (!user) {
res.status(403).send("Unauthorized");
} else {
const token = jwt.sign({ user: req.user }, secret);
res.cookie('token', token, { httpOnly: true });
res.json({ token });
}
});
return router;
}
module.exports = jwtRoute;