-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Set refresh token in /login route Also adds JWT_SECRET * Remove code that sets/gets token in localStorage * Fix dep cycle, refactor * Create auth router for login in, refresh, logout - Refactor code to reflect new route * Refactor * Return username from refresh endpoint * Set up refresh logic in frontend - Persists the logged in state by making request to refresh endpoint every page reload - Shows new loading view while that request is being made * Refactor Login RTL test * Add HTTP interceptor to catch 401s and refetch access token * Add test for App.tsx refresh request * Replace real store with test store in tests * Change expiration time of tokens * Prefetch getUsers in App.tsx instead of index.tsx * Add logout button on mobile, refactor * Fix bug related to updating username * Fix broken backend tests * Refactor tests for better scope/clarity and fix broken tests after adding refresh token feature * Add tests for logging out * Refactor cypress commands
- Loading branch information
1 parent
108ab48
commit 356ae7f
Showing
60 changed files
with
1,803 additions
and
922 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
import jwt from 'jsonwebtoken'; | ||
import bcrypt from 'bcrypt'; | ||
import express from 'express'; | ||
import fieldParsers from '../utils/field-parsers'; | ||
import logger from '../utils/logger'; | ||
import { User } from '../mongo'; | ||
import config from '../utils/config'; | ||
|
||
const router = express.Router(); | ||
const { JWT_SECRET } = config; | ||
|
||
router.post('/login', async (req, res) => { | ||
if (!JWT_SECRET) { | ||
return res.status(500).send({ error: 'JWT_SECRET is not set.' }); | ||
} | ||
|
||
const { body } = req; | ||
let loginFields: { | ||
username: string, | ||
password: string, | ||
}; | ||
|
||
try { | ||
loginFields = fieldParsers.proofLogInFields(body); | ||
} catch (error) { | ||
return res.status(400).send({ error: logger.getErrorMessage(error) }); | ||
} | ||
|
||
try { | ||
const user = await User.findOne({ username: loginFields.username }); | ||
const isUserValidated = !user | ||
? false | ||
: await bcrypt.compare(loginFields.password, user.passwordHash); | ||
|
||
if (!isUserValidated) { | ||
return res.status(401).send({ | ||
error: 'Invalid username or password.', | ||
}); | ||
} | ||
|
||
const userTokenInfo = { | ||
username: user.username, | ||
id: user.id, | ||
}; | ||
|
||
const accessToken = jwt.sign( | ||
userTokenInfo, | ||
JWT_SECRET, | ||
{ expiresIn: '15m' }, | ||
); | ||
const refreshToken = jwt.sign( | ||
userTokenInfo, | ||
JWT_SECRET, | ||
{ expiresIn: '30d' }, | ||
); | ||
|
||
res.cookie('refreshToken', refreshToken, { | ||
expires: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // 30 days | ||
httpOnly: true, | ||
secure: process.env.NODE_ENV === 'production', | ||
}); | ||
|
||
return res.status(200).send({ accessToken, username: user.username }); | ||
} catch (error) { | ||
return res.status(500).send({ error: 'Something went wrong!' }); | ||
} | ||
}); | ||
|
||
router.post('/refresh', async (req, res, next) => { | ||
if (!JWT_SECRET) { | ||
return res.status(500).send({ error: 'JWT_SECRET is not set.' }); | ||
} | ||
|
||
let decodedRefreshToken; | ||
const { refreshToken } = req.cookies; | ||
|
||
if (refreshToken) { | ||
try { | ||
decodedRefreshToken = jwt.verify( | ||
refreshToken, | ||
JWT_SECRET, | ||
) as jwt.JwtPayload; | ||
} catch (error) { | ||
res.clearCookie('refreshToken'); | ||
|
||
return next(error); | ||
} | ||
} | ||
|
||
if ( | ||
!decodedRefreshToken | ||
|| !decodedRefreshToken.id | ||
|| !decodedRefreshToken.username | ||
) { | ||
res.clearCookie('refreshToken'); | ||
|
||
return res.status(401).send({ | ||
error: 'refresh token missing or invalid.', | ||
}); | ||
} | ||
|
||
const user = await User.findById(decodedRefreshToken.id); | ||
|
||
if (!user) { | ||
res.clearCookie('refreshToken'); | ||
|
||
return res.status(401).send({ | ||
error: 'refresh token is not valid for any user.', | ||
}); | ||
} | ||
|
||
// generate new access token | ||
const userTokenInfo = { | ||
username: user.username, | ||
id: decodedRefreshToken.id, | ||
}; | ||
|
||
const newAccessToken = jwt.sign( | ||
userTokenInfo, | ||
JWT_SECRET, | ||
{ expiresIn: '15m' }, | ||
); | ||
|
||
return res.status(200).send( | ||
{ | ||
accessToken: newAccessToken, | ||
username: user.username, | ||
}, | ||
); | ||
}); | ||
|
||
router.post('/logout', (_req, res) => { | ||
res.clearCookie('refreshToken'); | ||
res.status(204).end(); | ||
}); | ||
|
||
export default router; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,53 +0,0 @@ | ||
import jwt from 'jsonwebtoken'; | ||
import bcrypt from 'bcrypt'; | ||
import express from 'express'; | ||
import fieldParsers from '../utils/field-parsers'; | ||
import logger from '../utils/logger'; | ||
import { User } from '../mongo'; | ||
|
||
const router = express.Router(); | ||
|
||
router.post('/', async (req, res) => { | ||
const { body } = req; | ||
let loginFields: { | ||
username: string, | ||
password: string, | ||
}; | ||
|
||
try { | ||
loginFields = fieldParsers.proofLogInFields(body); | ||
} catch (error) { | ||
return res.status(400).send({ error: logger.getErrorMessage(error) }); | ||
} | ||
|
||
try { | ||
// TODO: populate fields? frontend needs the image and posts of the logged in user. | ||
const user = await User.findOne({ username: loginFields.username }); | ||
const isUserValidated = !user | ||
? false | ||
: await bcrypt.compare(loginFields.password, user.passwordHash); | ||
|
||
if (!isUserValidated) { | ||
return res.status(401).send({ | ||
error: 'Invalid username or password.', | ||
}); | ||
} | ||
|
||
const userTokenInfo = { | ||
username: user.username, | ||
id: user.id, | ||
}; | ||
|
||
const token = jwt.sign( | ||
userTokenInfo, | ||
process.env.SECRET = 'scrt', | ||
{ expiresIn: 60 * 60 }, | ||
); | ||
|
||
return res.status(200).send({ token, username: user.username }); | ||
} catch (error) { | ||
return res.status(500).send({ error: 'Something went wrong!' }); | ||
} | ||
}); | ||
|
||
export default router; | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.