update dev app

This commit is contained in:
Balázs Orbán
2023-05-05 14:32:40 +02:00
parent 436d46ab89
commit bb4d5ce29b
22 changed files with 263 additions and 443 deletions

View File

@@ -0,0 +1,10 @@
"use client"
import { useEffect } from "react"
export default function Client({ session }: any) {
useEffect(() => {
console.log(window.location)
})
return <div>{JSON.stringify(session)}</div>
}

View File

@@ -1,14 +0,0 @@
.footer {
margin-top: 2rem;
}
.navItems {
margin-bottom: 1rem;
padding: 0;
list-style: none;
}
.navItem {
display: inline-block;
margin-right: 1rem;
}

View File

@@ -1,90 +0,0 @@
/* Set min-height to avoid page reflow while session loading */
.signedInStatus {
display: flex;
align-items: center;
min-height: 4rem;
width: 100%;
}
.loading,
.loaded {
position: relative;
top: 0;
opacity: 1;
overflow: hidden;
border-radius: 0 0 0.6rem 0.6rem;
padding: 0.6rem 1rem;
margin: 0;
background-color: rgba(0, 0, 0, 0.05);
transition: all 0.2s ease-in;
}
.loading {
top: -2rem;
opacity: 0;
}
.signedInText,
.notSignedInText {
padding-top: 0.8rem;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
z-index: 1;
line-height: 1.3rem;
flex: 1;
}
.signedInText {
padding-top: 0rem;
left: 4.6rem;
}
.avatar {
border-radius: 2rem;
float: left;
height: 2.8rem;
width: 2.8rem;
margin-right: 1rem;
background-color: white;
background-size: cover;
background-repeat: no-repeat;
}
.button,
.buttonPrimary {
justify-self: end;
font-weight: 500;
border-radius: 0.3rem;
cursor: pointer;
font-size: 1rem;
line-height: 1.4rem;
padding: 0.7rem 0.8rem;
position: relative;
z-index: 10;
background-color: transparent;
color: #555;
}
.buttonPrimary {
background-color: #346df1;
border-color: #346df1;
color: #fff;
text-decoration: none;
padding: 0.7rem 1.4rem;
}
.buttonPrimary:hover {
box-shadow: inset 0 0 5rem rgba(0, 0, 0, 0.2);
}
.navItems {
margin-bottom: 2rem;
padding: 0;
list-style: none;
}
.navItem {
display: inline-block;
margin-right: 1rem;
}

View File

@@ -1,75 +0,0 @@
import { auth } from "auth"
import { cookies, headers } from "next/headers"
import Link from "next/link"
import styles from "./header.module.css"
function SignIn({ id, ...props }: any) {
const $cookies = cookies()
const csrfToken = $cookies.get("next-auth.csrf-token")?.value.split("|")[0]
const action = id ? `/api/auth/signin/${id}` : "/api/auth/signin"
return (
<form action={action} method="post">
<button {...props} />
<input type="hidden" name="csrfToken" value={csrfToken} />
</form>
)
}
function SignOut(props: any) {
const $cookies = cookies()
const csrfToken = $cookies.get("next-auth.csrf-token")?.value.split("|")[0]
return (
<form action="/api/auth/signout" method="post">
<button {...props} />
<input type="hidden" name="csrfToken" value={csrfToken} />
</form>
)
}
// The approach used in this component shows how to built a sign in and sign out
// component that works on pages which support both client and server side
// rendering, and avoids any flash incorrect content on initial page load.
export default async function Header() {
const session = await auth(headers())
return (
<header>
<div className={styles.signedInStatus}>
{!session && (
<>
<span className={styles.notSignedInText}>
You are not signed in
</span>
<SignIn className={styles.buttonPrimary}>Sign in</SignIn>
</>
)}
{session && (
<>
{session.token.picture && (
<img src={session.token.picture} className={styles.avatar} />
)}
<span className={styles.signedInText}>
<small>Signed in as</small>
<br />
<strong>{session.token.email} </strong>
{session.token.name ? `(${session.token.name})` : null}
</span>
<SignOut className={styles.button}>Sign out</SignOut>
</>
)}
</div>
<nav>
<ul className={styles.navItems}>
<li className={styles.navItem}>
<Link href="/">Home</Link>
</li>
<li className={styles.navItem}>
<Link href="/dashboard">Dashboard (app)</Link>
</li>
<li className={styles.navItem}>
<Link href="/policy">Policy (pages)</Link>
</li>
</ul>
</nav>
</header>
)
}

View File

@@ -1,5 +1,8 @@
import Header from "./header"
import Footer from "./footer"
import { auth } from "auth"
import Footer from "components/footer"
import { Header } from "components/header"
import { cookies, headers } from "next/headers"
import styles from "components/header.module.css"
import "./styles.css"
export default function RootLayout(props: { children: React.ReactNode }) {
@@ -7,10 +10,45 @@ export default function RootLayout(props: { children: React.ReactNode }) {
<html>
<head></head>
<body>
<Header />
{/* @ts-expect-error */}
<AppHeader />
<main>{props.children}</main>
<Footer />
</body>
</html>
)
}
function SignIn({ id, ...props }: any) {
const $cookies = cookies()
const csrfToken = $cookies.get("next-auth.csrf-token")?.value.split("|")[0]
const action = id ? `/api/auth/signin/${id}` : "/api/auth/signin"
return (
<form action={action} method="post">
<button {...props} />
<input type="hidden" name="csrfToken" value={csrfToken} />
</form>
)
}
function SignOut(props: any) {
const $cookies = cookies()
const csrfToken = $cookies.get("next-auth.csrf-token")?.value.split("|")[0]
return (
<form action="/api/auth/signout" method="post">
<button {...props} />
<input type="hidden" name="csrfToken" value={csrfToken} />
</form>
)
}
export async function AppHeader() {
const session = await auth(headers())
return (
<Header
session={session}
signIn={<SignIn className={styles.buttonPrimary}>Sign in</SignIn>}
signOut={<SignOut className={styles.button}>Sign out</SignOut>}
/>
)
}

View File

@@ -1,7 +0,0 @@
export default function Loading() {
return (
<>
<h1>Loading...</h1>
</>
)
}

View File

@@ -1,6 +1,12 @@
export default function Page() {
import { auth } from "auth"
import { headers } from "next/headers"
import Client from "./client"
export default async function Page() {
const session = await auth(headers())
return (
<>
<Client session={session} />
<h1>NextAuth.js Example</h1>
<p>
This is an example site to demonstrate how to use{" "}

View File

@@ -4,15 +4,27 @@ import Facebook from "@auth/core/providers/facebook"
import GitHub from "@auth/core/providers/github"
import Google from "@auth/core/providers/google"
import Twitter from "@auth/core/providers/twitter"
import Credentials from "@auth/core/providers/credentials"
export const { handlers, auth } = NextAuth({
export const { handlers, auth, getServerSession } = NextAuth({
debug: true,
providers: [GitHub, Auth0, Facebook, Google, Twitter],
providers: [
GitHub,
Auth0,
Facebook,
Google,
Twitter,
Credentials({
credentials: { password: { label: "Password", type: "password" } },
authorize(c) {
if (c.password !== "password") return null
return { id: "test", name: "Test User", email: "test@example.com" }
},
}),
],
callbacks: {
async authorized({ request, auth }) {
if (request.nextUrl.pathname === "/dashboard") {
return !!auth.token
}
async authorized({ request: { nextUrl }, auth }) {
if (nextUrl.pathname === "/dashboard") return !!auth.user
return true
},
},

View File

@@ -1,4 +1,4 @@
import { signIn } from "next-auth/react"
import { signIn } from "@auth/nextjs/react"
export default function AccessDenied() {
return (

View File

@@ -1,28 +0,0 @@
import Link from "next/link"
import styles from "./footer.module.css"
import packageJSON from "@auth/nextjs/package.json"
export default function Footer() {
return (
<footer className={styles.footer}>
<hr />
<ul className={styles.navItems}>
<li className={styles.navItem}>
<a href="https://authjs.dev">Documentation</a>
</li>
<li className={styles.navItem}>
<a href="https://www.npmjs.com/package/@auth/core">NPM</a>
</li>
<li className={styles.navItem}>
<a href="https://github.com/nextauthjs/next-auth-example">GitHub</a>
</li>
<li className={styles.navItem}>
<Link href="/policy">Policy</Link>
</li>
<li className={styles.navItem}>
<em>{packageJSON.version}</em>
</li>
</ul>
</footer>
)
}

View File

@@ -1,83 +0,0 @@
import Link from "next/link"
import { useSession } from "next-auth/react"
import styles from "./header.module.css"
// The approach used in this component shows how to built a sign in and sign out
// component that works on pages which support both client and server side
// rendering, and avoids any flash incorrect content on initial page load.
export default function Header() {
const { data: session, status } = useSession()
return (
<header>
<noscript>
<style>{".nojs-show { opacity: 1; top: 0; }"}</style>
</noscript>
<div className={styles.signedInStatus}>
<p
className={`nojs-show ${
!session && status === "loading" ? styles.loading : styles.loaded
}`}
>
{!session && (
<>
<span className={styles.notSignedInText}>
You are not signed in
</span>
<a href="/api/auth/signin" className={styles.buttonPrimary}>
Sign in
</a>
</>
)}
{session && (
<>
{session.user.image && (
<img src={session.user.image} className={styles.avatar} />
)}
<span className={styles.signedInText}>
<small>Signed in as</small>
<br />
<strong>{session.user.email} </strong>
{session.user.name ? `(${session.user.name})` : null}
</span>
<a href="/api/auth/signout" className={styles.button}>
Sign out
</a>
</>
)}
</p>
</div>
<nav>
<ul className={styles.navItems}>
<li className={styles.navItem}>
<Link href="/">Home</Link>
</li>
<li className={styles.navItem}>
<Link href="/client">Client</Link>
</li>
<li className={styles.navItem}>
<Link href="/server">Server</Link>
</li>
<li className={styles.navItem}>
<Link href="/protected">Protected</Link>
</li>
<li className={styles.navItem}>
<Link href="/protected-ssr">Protected(SSR)</Link>
</li>
<li className={styles.navItem}>
<Link href="/api-example">API</Link>
</li>
<li className={styles.navItem}>
<Link href="/credentials">Credentials</Link>
</li>
<li className={styles.navItem}>
<Link href="/email">Email</Link>
</li>
<li className={styles.navItem}>
<Link href="/middleware-protected">Middleware protected</Link>
</li>
</ul>
</nav>
</header>
)
}

View File

@@ -1,6 +1,7 @@
/* Set min-height to avoid page reflow while session loading */
.signedInStatus {
display: block;
display: flex;
align-items: center;
min-height: 4rem;
width: 100%;
}
@@ -25,16 +26,13 @@
.signedInText,
.notSignedInText {
position: absolute;
padding-top: 0.8rem;
left: 1rem;
right: 6.5rem;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
display: inherit;
z-index: 1;
line-height: 1.3rem;
flex: 1;
}
.signedInText {
@@ -47,6 +45,7 @@
float: left;
height: 2.8rem;
width: 2.8rem;
margin-right: 1rem;
background-color: white;
background-size: cover;
background-repeat: no-repeat;
@@ -54,10 +53,11 @@
.button,
.buttonPrimary {
float: right;
margin-right: -0.4rem;
justify-self: end;
font-weight: 500;
border-radius: 0.3rem;
border: none;
font-weight: bold;
cursor: pointer;
font-size: 1rem;
line-height: 1.4rem;

View File

@@ -0,0 +1,55 @@
import Link from "next/link"
import styles from "./header.module.css"
export function Header({ session, signIn, signOut }: any) {
return (
<header>
<div className={styles.signedInStatus}>
{!session && (
<>
<span className={styles.notSignedInText}>
You are not signed in
</span>
{signIn}
</>
)}
{session && (
<>
{session.user.picture && (
<img src={session.user.picture} className={styles.avatar} />
)}
<span className={styles.signedInText}>
<small>Signed in as</small>
<br />
<strong>{session.user.email} </strong>
{session.user.name ? `(${session.user.name})` : null}
</span>
{signOut}
</>
)}
</div>
<nav>
<ul className={styles.navItems}>
<li className={styles.navItem}>
<Link href="/">Home (app)</Link>
</li>
<li className={styles.navItem}>
<Link href="/dashboard">Dashboard (app)</Link>
</li>
<li className={styles.navItem}>
<Link href="/policy">Policy (pages)</Link>
</li>
<li className={styles.navItem}>
<Link href="/credentials">Credentials (pages)</Link>
</li>
<li className={styles.navItem}>
<Link href="/protected-ssr">getServerSideProps (pages)</Link>
</li>
<li className={styles.navItem}>
<Link href="/api/examples/protected">API Route (pages)</Link>
</li>
</ul>
</nav>
</header>
)
}

View File

@@ -1,12 +0,0 @@
import Header from "components/header"
import Footer from "components/footer"
export default function Layout({ children }) {
return (
<>
<Header />
<main>{children}</main>
<Footer />
</>
)
}

View File

@@ -1,10 +0,0 @@
import { SessionProvider } from "next-auth/react"
import "./styles.css"
export default function App({ Component, pageProps }) {
return (
<SessionProvider session={pageProps.session}>
<Component {...pageProps} />
</SessionProvider>
)
}

View File

@@ -0,0 +1,39 @@
import {
SessionProvider,
signIn,
signOut,
useSession,
} from "@auth/nextjs/react"
import "./styles.css"
import { Header } from "components/header"
import styles from "components/header.module.css"
import Footer from "components/footer"
export default function App({ Component, pageProps }) {
return (
<SessionProvider session={pageProps.session}>
<PagesHeader />
<Component {...pageProps} />
<Footer />
</SessionProvider>
)
}
function PagesHeader() {
const { data: session } = useSession()
return (
<Header
session={session}
signIn={
<button onClick={() => signIn()} className={styles.buttonPrimary}>
Sign in
</button>
}
signOut={
<button onClick={() => signOut()} className={styles.button}>
Sign out
</button>
}
/>
)
}

View File

@@ -1,9 +1,8 @@
// This is an example of to protect an API route
import { authConfig } from "../auth-old/[...nextauth]"
import { getServerSession } from "next-auth/next"
import { getServerSession } from "auth"
export default async (req, res) => {
const session = await getServerSession(req, res, authConfig as any)
const session = await getServerSession(req, res)
if (session) {
res.send({

View File

@@ -1,67 +0,0 @@
// eslint-disable-next-line no-use-before-define
import * as React from "react"
import { signIn, signOut, useSession } from "next-auth/react"
import Layout from "components/layout"
export default function Page() {
const [response, setResponse] = React.useState(null)
const handleLogin = (options) => async () => {
if (options.redirect) {
return signIn("credentials", options)
}
const response = await signIn("credentials", options)
setResponse(response)
}
const handleLogout = (options) => async () => {
if (options.redirect) {
return signOut(options)
}
const response = await signOut(options)
setResponse(response)
}
const { data: session } = useSession()
if (session) {
return (
<Layout>
<h1>Test different flows for Credentials logout</h1>
<span className="spacing">Default:</span>
<button onClick={handleLogout({ redirect: true })}>Logout</button>
<br />
<span className="spacing">No redirect:</span>
<button onClick={handleLogout({ redirect: false })}>Logout</button>
<br />
<p>Response:</p>
<pre style={{ background: "#eee", padding: 16 }}>
{JSON.stringify(response, null, 2)}
</pre>
</Layout>
)
}
return (
<Layout>
<h1>Test different flows for Credentials login</h1>
<span className="spacing">Default:</span>
<button onClick={handleLogin({ redirect: true, password: "password" })}>
Login
</button>
<br />
<span className="spacing">No redirect:</span>
<button onClick={handleLogin({ redirect: false, password: "password" })}>
Login
</button>
<br />
<span className="spacing">No redirect, wrong password:</span>
<button onClick={handleLogin({ redirect: false, password: "" })}>
Login
</button>
<p>Response:</p>
<pre style={{ background: "#eee", padding: 16 }}>
{JSON.stringify(response, null, 2)}
</pre>
</Layout>
)
}

View File

@@ -0,0 +1,67 @@
import * as React from "react"
import { signIn, signOut, useSession } from "@auth/nextjs/react"
import { SignInResponse, SignOutResponse } from "@auth/nextjs/lib/client"
export default function Page() {
const [response, setResponse] = React.useState<
SignInResponse | SignOutResponse
>()
const { data: session } = useSession()
if (session) {
return (
<>
<h1>Test different flows for Credentials logout</h1>
<span className="spacing">Default: </span>
<button onClick={() => signOut()}>Logout</button>
<br />
<span className="spacing">No redirect: </span>
<button onClick={() => signOut({ redirect: false }).then(setResponse)}>
Logout
</button>
<br />
<p>{response ? "Response:" : "Session:"}</p>
<pre style={{ background: "#eee", padding: 16 }}>
{JSON.stringify(response ?? session, null, 2)}
</pre>
</>
)
}
return (
<>
<h1>Test different flows for Credentials login</h1>
<span className="spacing">Default: </span>
<button onClick={() => signIn("credentials", { password: "password" })}>
Login
</button>
<br />
<span className="spacing">No redirect: </span>
<button
onClick={() =>
signIn("credentials", { redirect: false, password: "password" }).then(
setResponse
)
}
>
Login
</button>
<br />
<span className="spacing">No redirect, wrong password: </span>
<button
onClick={() =>
signIn("credentials", { redirect: false, password: "wrong" }).then(
setResponse
)
}
>
Login
</button>
<p>Response:</p>
<pre style={{ background: "#eee", padding: 16 }}>
{JSON.stringify(response, null, 2)}
</pre>
</>
)
}

View File

@@ -1,8 +1,6 @@
import Layout from "../components/layout"
export default function Page() {
return (
<Layout>
<>
<p>
This is an example site to demonstrate how to use{" "}
<a href="https://authjs.dev">Auth.js</a> for authentication.
@@ -18,15 +16,11 @@ export default function Page() {
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
</p>
<h2>Privacy Policy</h2>
<p>
This site uses JSON Web Tokens and an in-memory database which resets
every ~2 hours.
</p>
<p>
Data provided to this site is exclusively used to support signing in and
is not passed to any third party services, other than via SMTP or OAuth
for the purposes of authentication.
</p>
</Layout>
</>
)
}

View File

@@ -1,48 +1,34 @@
// This is an example of how to protect content using server rendering
import { getServerSession } from "next-auth/next"
import { authConfig } from "./api/auth-old/[...nextauth]"
import Layout from "../components/layout"
import AccessDenied from "../components/access-denied"
import { getServerSession } from "auth"
import AccessDenied from "components/access-denied"
export default function Page({ content, session }) {
// If no session exists, display access denied message
if (!session) {
return (
<Layout>
<AccessDenied />
</Layout>
)
}
if (!session) return <AccessDenied />
// If session exists, display content
return (
<Layout>
<>
<h1>Protected Page</h1>
<p>
<strong>{content}</strong>
</p>
</Layout>
</>
)
}
export async function getServerSideProps(context) {
const session = await getServerSession(context.req, context.res, authConfig)
let content = null
const session = await getServerSession(context.req, context.res)
if (session) {
// Note usually you don't need to fetch from an API route in getServerSideProps
// This is done here to demonstrate how you can fetch from a third-party API
// with a valid session. Likely you would also not pass cookies but an `Authorization` header
const hostname = process.env.NEXTAUTH_URL || "http://localhost:3000"
const options = { headers: { cookie: context.req.headers.cookie } }
const res = await fetch(`${hostname}/api/examples/protected`, options)
const json = await res.json()
if (json.content) {
content = json.content
}
const res = await fetch(`${hostname}/api/examples/protected`, {
headers: { cookie: context.req.headers.cookie },
})
return { props: { session, content: (await res.json()).content } }
}
return {
props: {
session,
content,
},
}
return { props: {} }
}