-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d641239
commit c77288f
Showing
16 changed files
with
656 additions
and
2 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
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,76 @@ | ||
import { Typography } from "@material-ui/core"; | ||
/** | ||
* TODO: the useUser hook doesn't seem to be updated on route change when the component is put into _app | ||
*/ | ||
// Taken from Next Passport example | ||
import Link from "next/link"; | ||
import { useUser } from "~/components/user/hooks"; | ||
|
||
const Header = () => { | ||
const user = useUser(); | ||
return ( | ||
<footer> | ||
<nav> | ||
<ul> | ||
<li> | ||
<Link href="/"> | ||
<a>Home</a> | ||
</Link> | ||
</li> | ||
{user ? ( | ||
<> | ||
<li> | ||
<Link href="/profile"> | ||
<a>Profile</a> | ||
</Link> | ||
</li> | ||
<li> | ||
<a href="/api/logout">Logout</a> | ||
</li> | ||
</> | ||
) : ( | ||
<li> | ||
<Link href="/login"> | ||
<a>Login</a> | ||
</Link> | ||
</li> | ||
)} | ||
</ul> | ||
</nav> | ||
<style jsx>{` | ||
nav { | ||
max-width: 42rem; | ||
margin: 0 auto; | ||
padding: 0.2rem 1.25rem; | ||
max-width: 1000px; | ||
margin: auto; | ||
} | ||
ul { | ||
display: flex; | ||
list-style: none; | ||
margin-left: 0; | ||
padding-left: 0; | ||
} | ||
li { | ||
margin-right: 1rem; | ||
} | ||
li:first-child { | ||
margin-left: auto; | ||
} | ||
a { | ||
color: violet; | ||
text-decoration: none; | ||
} | ||
footer { | ||
color: #000; | ||
border-top: 1px solid; | ||
border-image-source: linear-gradient(10deg, #e1009855, #3f77fa55); | ||
border-image-slice: 1; | ||
border-color: #3f77fa; | ||
} | ||
`}</style> | ||
</footer> | ||
); | ||
}; | ||
|
||
export default Header; |
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,82 @@ | ||
import Link from "next/link"; | ||
|
||
const UserForm = ({ isLogin, errorMessage, onSubmit }) => ( | ||
<form onSubmit={onSubmit}> | ||
<label> | ||
<span>Email</span> | ||
<input type="text" name="email" required /> | ||
</label> | ||
<label> | ||
<span>Password</span> | ||
<input type="password" name="password" required /> | ||
</label> | ||
{!isLogin && ( | ||
<label> | ||
<span>Repeat password</span> | ||
<input type="password" name="rpassword" required /> | ||
</label> | ||
)} | ||
|
||
<div className="submit"> | ||
{isLogin ? ( | ||
<> | ||
<Link href="/signup"> | ||
<a>I don't have an account</a> | ||
</Link> | ||
<button type="submit">Login</button> | ||
</> | ||
) : ( | ||
<> | ||
<Link href="/login"> | ||
<a>I already have an account</a> | ||
</Link> | ||
<button type="submit">Signup</button> | ||
</> | ||
)} | ||
</div> | ||
|
||
{errorMessage && <p className="error">{errorMessage}</p>} | ||
|
||
<style jsx>{` | ||
form, | ||
label { | ||
display: flex; | ||
flex-flow: column; | ||
} | ||
label > span { | ||
font-weight: 600; | ||
} | ||
input { | ||
padding: 8px; | ||
margin: 0.3rem 0 1rem; | ||
border: 1px solid #ccc; | ||
border-radius: 4px; | ||
} | ||
.submit { | ||
display: flex; | ||
justify-content: flex-end; | ||
align-items: center; | ||
justify-content: space-between; | ||
} | ||
.submit > a { | ||
text-decoration: none; | ||
} | ||
.submit > button { | ||
padding: 0.5rem 1rem; | ||
cursor: pointer; | ||
background: #fff; | ||
border: 1px solid #ccc; | ||
border-radius: 4px; | ||
} | ||
.submit > button:hover { | ||
border-color: #888; | ||
} | ||
.error { | ||
color: brown; | ||
margin: 1rem 0 0; | ||
} | ||
`}</style> | ||
</form> | ||
); | ||
|
||
export default UserForm; |
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,39 @@ | ||
// It won't reload if there are no remount => we need to find a way to mutate on login | ||
// @see https://github.com/vercel/next.js/discussions/19601 | ||
import { useEffect } from "react"; | ||
import Router from "next/router"; | ||
import useSWR from "swr"; | ||
|
||
const fetcher = (url) => | ||
fetch(url) | ||
.then((r) => r.json()) | ||
.then((data) => { | ||
return { user: data?.user || null }; | ||
}); | ||
|
||
export function useUser({ | ||
redirectTo, | ||
redirectIfFound, | ||
}: { | ||
redirectTo?: string; | ||
redirectIfFound?: boolean; | ||
} = {}) { | ||
const { data, error } = useSWR("/api/user", fetcher); | ||
const user = data?.user; | ||
const finished = Boolean(data); | ||
const hasUser = Boolean(user); | ||
|
||
useEffect(() => { | ||
if (!redirectTo || !finished) return; | ||
if ( | ||
// If redirectTo is set, redirect if the user was not found. | ||
(redirectTo && !redirectIfFound && !hasUser) || | ||
// If redirectIfFound is also set, redirect if the user was found | ||
(redirectIfFound && hasUser) | ||
) { | ||
Router.push(redirectTo); | ||
} | ||
}, [redirectTo, redirectIfFound, finished, hasUser]); | ||
|
||
return error ? null : user; | ||
} |
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,38 @@ | ||
import Head from "next/head"; | ||
import Footer from "~/components/layout/Footer"; | ||
|
||
const Layout = (props) => ( | ||
<> | ||
<Head> | ||
<title>With Cookies</title> | ||
</Head> | ||
|
||
<main> | ||
<div className="container">{props.children}</div> | ||
</main> | ||
|
||
{/*<Footer />*/} | ||
|
||
<style jsx global>{` | ||
*, | ||
*::before, | ||
*::after { | ||
box-sizing: border-box; | ||
} | ||
body { | ||
margin: 0; | ||
color: #333; | ||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, | ||
"Helvetica Neue", Arial, Noto Sans, sans-serif, "Apple Color Emoji", | ||
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; | ||
} | ||
.container { | ||
max-width: 42rem; | ||
margin: 0 auto; | ||
padding: 2rem 1.25rem; | ||
} | ||
`}</style> | ||
</> | ||
); | ||
|
||
export default Layout; |
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,39 @@ | ||
import passport from "passport"; | ||
import nextConnect from "next-connect"; | ||
import { localStrategy } from "~/api/passport/password-local"; | ||
import { encryptSession } from "~/api/passport/iron"; | ||
import { setTokenCookie } from "~/api/passport/auth-cookies"; | ||
import { NextApiRequest, NextApiResponse } from "next"; | ||
|
||
const authenticate = (method, req, res): Promise<any> => | ||
new Promise((resolve, reject) => { | ||
passport.authenticate(method, { session: false }, (error, token) => { | ||
if (error) { | ||
reject(error); | ||
} else { | ||
resolve(token); | ||
} | ||
})(req, res); | ||
}); | ||
|
||
passport.use(localStrategy); | ||
|
||
// NOTE: adding NextApiRequest, NextApiResponse is required to get the right typings in next-connect | ||
// this is the normal behaviour | ||
export default nextConnect<NextApiRequest, NextApiResponse>() | ||
.use(passport.initialize()) | ||
.post(async (req, res) => { | ||
try { | ||
const user = await authenticate("local", req, res); | ||
// session is the payload to save in the token, it may contain basic info about the user | ||
const session = { ...user }; | ||
// The token is a string with the encrypted session | ||
const token = await encryptSession(session); | ||
|
||
setTokenCookie(res, token); | ||
res.status(200).send({ done: true }); | ||
} catch (error) { | ||
console.error(error); | ||
res.status(401).send(error.message); | ||
} | ||
}); |
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,11 @@ | ||
import { NextApiRequest, NextApiResponse } from "next"; | ||
import { removeTokenCookie } from "~/api/passport/auth-cookies"; | ||
|
||
export default async function logout( | ||
req: NextApiRequest, | ||
res: NextApiResponse | ||
) { | ||
removeTokenCookie(res); | ||
res.writeHead(302, { Location: "/" }); | ||
res.end(); | ||
} |
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,24 @@ | ||
import { Request } from "express"; | ||
import { createMutator } from "@vulcanjs/graphql"; | ||
import { NextApiRequest, NextApiResponse } from "next"; | ||
import { User } from "~/models/user"; | ||
|
||
// TODO: factor the context creation so we can reuse it for graphql and REST endpoints | ||
import { contextFromReq } from "~/api/context"; | ||
export default async function signup( | ||
req: NextApiRequest, | ||
res: NextApiResponse | ||
) { | ||
try { | ||
// NOTE: the mutator is the function used by the create mutations in Vulcan | ||
// we need to use it to ensure that we run all callbacks associated to the user collection | ||
const user = req.body; | ||
// TODO: check if this is ok to compute the context from a NextApiRequest like this | ||
const context = await contextFromReq(req as unknown as Request) | ||
await createMutator({ model: User, data: user, context }); | ||
res.status(200).send({ done: true }); | ||
} catch (error) { | ||
console.error(error); | ||
res.status(500).end(error.message); | ||
} | ||
} |
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,11 @@ | ||
import { getSession } from "~/api/passport/iron"; | ||
import { UserConnector } from "~/models/user"; | ||
|
||
export default async function user(req, res) { | ||
const session = await getSession(req); | ||
// Get fresh data about the user | ||
const user = session?._id | ||
? await UserConnector.findOneById(session._id) | ||
: null; | ||
res.status(200).json({ user: user || null }); | ||
} |
Oops, something went wrong.