Compare commits

...

67 Commits

Author SHA1 Message Date
ndom91
657dd57ef7 fix(docs): clean up v5 migration intro 2023-05-07 14:30:01 +02:00
ndom91
39691313fb feat(docs): initial v5 next-auth upgrade guide doc 2023-05-07 14:17:32 +02:00
Balázs Orbán
a7e02266cf fix value 2023-05-05 16:14:16 +02:00
Balázs Orbán
bac8b8d13a add getServerSession, simplify auth() return 2023-05-05 14:33:45 +02:00
Balázs Orbán
bb4d5ce29b update dev app 2023-05-05 14:32:40 +02:00
Balázs Orbán
436d46ab89 tweak docs 2023-05-05 02:57:19 +02:00
Balázs Orbán
b17dc76a70 Merge branch 'main' into feat/nextjs-auth 2023-05-05 02:38:57 +02:00
Balázs Orbán
b96f01319c chore: tweak manual release version 2023-05-05 02:38:47 +02:00
Balázs Orbán
5fb4caa485 whitespace 2023-05-05 02:32:04 +02:00
Balázs Orbán
24e8100a61 move 2023-05-05 02:31:52 +02:00
Balázs Orbán
b17c5deafb add installation 2023-05-05 02:31:40 +02:00
Balázs Orbán
7da32563d6 docs 2023-05-05 02:30:33 +02:00
Balázs Orbán
c470960d93 docs 2023-05-05 02:23:00 +02:00
Balázs Orbán
8cfd1aeb15 improve docs 2023-05-05 02:20:41 +02:00
Balázs Orbán
af81776e4b upgrade dev app 2023-05-05 01:57:23 +02:00
Balázs Orbán
3ba510c0e0 forward all set-cookie headers 2023-05-05 01:57:00 +02:00
Balázs Orbán
65fe0ef363 fix imports 2023-05-05 00:14:16 +02:00
Balázs Orbán
587d3666fc Merge branch 'main' into feat/nextjs-auth 2023-05-04 23:59:06 +02:00
Balázs Orbán
8f416b68ec chore: tweaks 2023-05-04 23:58:54 +02:00
Balázs Orbán
e4d568c220 fix redirects 2023-05-04 23:55:36 +02:00
Balázs Orbán
aaff906c68 add readme 2023-05-04 23:52:39 +02:00
Balázs Orbán
8f40883de5 Merge branch 'main' into feat/nextjs-auth 2023-05-04 23:51:00 +02:00
Balázs Orbán
eaf5080721 chore: tweak 2023-05-04 23:50:49 +02:00
Balázs Orbán
601c8c915f Merge branch 'main' into feat/nextjs-auth 2023-05-04 23:42:20 +02:00
Balázs Orbán
e0b5f18c5b chore: skip test for manual release 2023-05-04 23:42:05 +02:00
Balázs Orbán
7fae8744b2 Merge branch 'main' into feat/nextjs-auth 2023-05-04 23:41:33 +02:00
Balázs Orbán
99247ce446 chore: separate manual release job 2023-05-04 23:40:37 +02:00
Balázs Orbán
0afb1797ba Merge branch 'main' into feat/nextjs-auth 2023-05-04 23:25:39 +02:00
Balázs Orbán
d6bc65f0d8 chore: support release any package as experimental 2023-05-04 23:25:29 +02:00
Balázs Orbán
0b80c65a0c update lock file 2023-05-04 22:45:58 +02:00
Balázs Orbán
a7991aed15 revert 2023-05-04 22:38:54 +02:00
Balázs Orbán
c47a2eb7ac Merge branch 'main' into feat/nextjs-auth 2023-05-04 22:22:00 +02:00
Balázs Orbán
6f5a50313f chore: use @ts-ignore 2023-05-04 22:21:36 +02:00
Balázs Orbán
3c23a4f4e2 add docs 2023-05-04 22:14:15 +02:00
Balázs Orbán
52c04fe89e update turbo 2023-05-04 22:11:43 +02:00
Balázs Orbán
440317aa2f update turbo 2023-05-04 22:10:39 +02:00
Balázs Orbán
18a4e8b255 update turbo 2023-05-04 22:09:20 +02:00
Balázs Orbán
c411c6f5dc update gitignore 2023-05-04 22:06:08 +02:00
Balázs Orbán
46f622a5b4 Merge branch 'main' into feat/nextjs-auth 2023-05-04 22:05:27 +02:00
Balázs Orbán
1b984f09ab fix docs references 2023-05-04 21:51:11 +02:00
Balázs Orbán
fbc1ed857c space 2023-05-04 21:43:43 +02:00
Balázs Orbán
0d6ad6beb6 update lock file 2023-05-04 21:43:15 +02:00
Balázs Orbán
b972742171 Update settings.json 2023-05-04 20:42:31 +01:00
Balázs Orbán
01d6f86538 Update .prettierignore 2023-05-04 20:42:16 +01:00
Balázs Orbán
bf89d9fabb re-add next-auth 4 2023-05-04 21:41:36 +02:00
Balázs Orbán
90b01c4613 Merge branch 'feat/nextjs-auth' of github.com:nextauthjs/next-auth into feat/nextjs-auth 2023-05-04 21:39:40 +02:00
Balázs Orbán
47794e68cc move back to @auth/nextjs 2023-05-04 21:39:37 +02:00
Balázs Orbán
0c6b8581a7 revert 2023-05-04 20:30:08 +01:00
Balázs Orbán
7ece5a0a9d chore: fix framework builds 2023-05-04 21:26:53 +02:00
Balázs Orbán
828e38c976 ignore type errors 2023-05-04 21:19:11 +02:00
Balázs Orbán
47d4a3f80f mark import as type 2023-05-04 20:52:20 +02:00
Balázs Orbán
14f9388148 remove unnecessary type import 2023-05-04 20:30:23 +02:00
Balázs Orbán
46659f04b7 update deps 2023-05-04 19:55:45 +02:00
Balázs Orbán
fc4abd174f Merge branch 'main' into feat/nextjs-auth 2023-05-04 10:58:03 +02:00
Balázs Orbán
ebefd25bd6 improve docs 2023-05-04 10:57:42 +02:00
Balázs Orbán
e3bdb38df2 fix(docs): remove extra heading
Fixes #7426
2023-05-03 12:40:30 +02:00
Balázs Orbán
da9ce95677 add redirects 2023-05-01 14:18:56 +02:00
Balázs Orbán
8c99f5c9cf Merge branch 'main' into feat/nextjs-auth 2023-05-01 13:49:29 +02:00
Balázs Orbán
92a0fc42fa fix: allow handling OAuth callback error response
related #7407
2023-05-01 13:49:17 +02:00
Balázs Orbán
62e2ad115c chore: type fixes 2023-05-01 13:46:23 +02:00
Balázs Orbán
5551c4f147 add more providers 2023-05-01 13:42:35 +02:00
Balázs Orbán
e2ef07688c Merge branch 'main' into feat/nextjs-auth 2023-05-01 13:22:31 +02:00
Balázs Orbán
542c35d729 fix: loosen profile types 2023-05-01 13:22:16 +02:00
Balázs Orbán
e62fa3a0af infer issuer 2023-05-01 13:21:42 +02:00
Balázs Orbán
771e993ce9 fix import 2023-05-01 13:03:49 +02:00
Balázs Orbán
c4a15ae3dd bump versions 2023-05-01 12:56:32 +02:00
Balázs Orbán
80d1a8fd70 mention NA5 status in docs 2023-05-01 12:54:56 +02:00
261 changed files with 17883 additions and 1208 deletions

View File

@@ -5,14 +5,15 @@ const core = require("@actions/core")
try {
const packageJSONPath = path.join(
process.cwd(),
"packages/next-auth/package.json"
`packages/${process.env.PACKAGE_PATH || "next-auth"}/package.json`
)
const packageJSON = JSON.parse(fs.readFileSync(packageJSONPath, "utf8"))
const sha8 = process.env.GITHUB_SHA.substring(0, 8)
const prNumber = process.env.PR_NUMBER
const packageVersion = `0.0.0-pr.${prNumber}.${sha8}`
const prefix = "0.0.0-"
const pr = process.env.PR_NUMBER
const source = pr ? `pr.${pr}` : "manual"
const packageVersion = `${prefix}${source}.${sha8}`
packageJSON.version = packageVersion
core.setOutput("version", packageVersion)
fs.writeFileSync(packageJSONPath, JSON.stringify(packageJSON))

View File

@@ -8,6 +8,24 @@ on:
- next
- 3.x
pull_request:
# TODO: Support latest releases
workflow_dispatch:
inputs:
name:
type: choice
description: Package name (npm)
options:
- "@auth/nextjs"
- "@auth/core"
- "next-auth"
# TODO: Infer from package name
path:
type: choice
description: Directory name (packages/*)
options:
- "frameworks-nextjs"
- "core"
- "next-auth"
jobs:
test:
@@ -122,3 +140,34 @@ jobs:
env:
VERSION: ${{ steps.determine-version.outputs.version }}
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
release-manual:
name: Publish manually
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' }}
steps:
- name: Init
uses: actions/checkout@v3
- name: Install pnpm
uses: pnpm/action-setup@v2.2.4
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 18
- name: Install dependencies
run: pnpm install
- name: Determine version
uses: ./.github/version-pr
id: determine-version
env:
PACKAGE_PATH: ${{ github.event.inputs.path }}
- name: Publish to npm
run: |
cd packages/$PACKAGE_PATH
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> .npmrc
pnpm publish --no-git-checks --access public --tag experimental
echo "🎉 Experimental release published 📦️ on npm: https://npmjs.com/package/${{ github.event.inputs.name }}/v/${{ env.VERSION }}"
echo "Install via: pnpm add ${{ github.event.inputs.name }}@${{ env.VERSION }}"
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
PACKAGE_PATH: ${{ github.event.inputs.path }}
VERSION: ${{ steps.determine-version.outputs.version }}

14
.gitignore vendored
View File

@@ -26,7 +26,15 @@ dist
# Generated files
.docusaurus
.cache-loader
packages/next-auth/providers
packages/next-auth/src/providers/oauth-types.ts
packages/next-auth/client
packages/next-auth/css
packages/next-auth/utils
packages/next-auth/core
packages/next-auth/jwt
packages/next-auth/react
packages/next-auth/next
packages/*/*.js
packages/*/*.d.ts
packages/*/*.d.ts.map
@@ -77,10 +85,10 @@ packages/core/providers
packages/core/src/lib/pages/styles.ts
docs/docs/reference/core
docs/docs/reference/sveltekit
docs/docs/reference/next-auth
docs/docs/reference/nextjs
# Next.js
packages/next-auth/lib
packages/frameworks-nextjs/lib
# SvelteKit
packages/frameworks-sveltekit/index.*

View File

@@ -40,6 +40,10 @@ packages/core/src/lib/pages/styles.ts
packages/frameworks-sveltekit/package
packages/frameworks-sveltekit/vite.config.{js,ts}.timestamp-*
# next-auth
packages/next-auth/src/providers/oauth-types.ts
packages/next-auth/css/index.css
# Adapters
.branches

View File

@@ -1,8 +1,8 @@
{
"files.exclude": {
"packages/core/{lib,providers,*.js,*.d.ts*}": true,
"packages/next-auth/{lib,*.js,*.d.ts*}": true,
"packages/core/{lib,providers,*.js,*.d.ts,*.d.ts.map}": true,
"packages/next-auth/{client,core,css,jwt,next,providers,react,utils,*.js,*.d.ts}": true
},
"typescript.tsdk": "node_modules/typescript/lib",
"openInGitHub.remote.branch": "main"
}
}

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

@@ -0,0 +1,3 @@
export default function Page() {
return <h1>This page is protected.</h1>
}

View File

@@ -1,12 +1,54 @@
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
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 }) {
return (
<html>
<head></head>
<body>{children}</body>
<body>
{/* @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

@@ -0,0 +1,17 @@
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{" "}
<a href="https://nextjs.authjs.dev">NextAuth.js</a> for authentication.
</p>
</>
)
}

View File

@@ -1,37 +0,0 @@
import { auth } from "auth"
import { cookies, headers } from "next/headers"
function SignIn({ id, children }: any) {
const $cookies = cookies()
const csrfToken = $cookies.get("next-auth.csrf-token")?.value.split("|")[0]
return (
<form action={`/api/auth/signin/${id}`} method="post">
<button type="submit">{children}</button>
<input type="hidden" name="csrfToken" value={csrfToken} />
</form>
)
}
function SignOut({ children }: any) {
const $cookies = cookies()
const csrfToken = $cookies.get("next-auth.csrf-token")?.value.split("|")[0]
return (
<form action="/api/auth/signout" method="post">
<button type="submit">{children}</button>
<input type="hidden" name="csrfToken" value={csrfToken} />
</form>
)
}
export default async function Page() {
const session = await auth(headers())
if (session) {
return (
<>
<pre>{JSON.stringify(session, null, 2)}</pre>
<SignOut>Sign out</SignOut>
</>
)
}
return <SignIn id="github">Sign in with github</SignIn>
}

View File

@@ -0,0 +1,32 @@
body {
font-family: ui-sans-serif, system-ui, -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";
padding: 0 1rem 1rem 1rem;
max-width: 680px;
margin: 0 auto;
background: #fff;
color: var(--color-text);
}
li,
p {
line-height: 1.5rem;
}
a {
font-weight: 500;
}
hr {
border: 1px solid #ddd;
}
iframe {
background: #ccc;
border: 1px solid #ccc;
height: 10rem;
width: 100%;
border-radius: 0.5rem;
filter: invert(1);
}

View File

@@ -1,21 +1,31 @@
import { NextAuth } from "next-auth"
import NextAuth from "@auth/nextjs"
import Auth0 from "@auth/core/providers/auth0"
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({
providers: [GitHub],
export const { handlers, auth, getServerSession } = NextAuth({
debug: true,
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.method === "POST") {
// const [, token] = request.headers.get("Authorization")?.split(" ")
// const valid = validateToken(token)
// // If the request has a valid auth token, it is authorized
// if (valid) return true
// return NextResponse.json("Invalid auth token", { status: 401 })
// }
// Logged in users are authorized, otherwise, will redirect to login
// You could also return a custom redirect instead of the sign-in page
return !!auth
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,6 +1,6 @@
import Link from "next/link"
import styles from "./footer.module.css"
import packageJSON from "next-auth/package.json"
import packageJSON from "@auth/nextjs/package.json"
export default function Footer() {
return (

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 +1 @@
// export { auth as default } from "auth"
import { auth } from "auth"
import { NextResponse } from "next/server"
export default auth((req) => {
if (req.auth) return NextResponse.json(req.auth)
return NextResponse.json("Not authorized", { status: 401 })
})
export const config = { matcher: ["/middleware-protected"] }
export { auth as default } from "auth"

View File

@@ -15,7 +15,7 @@
"license": "ISC",
"dependencies": {
"@auth/core": "workspace:*",
"next-auth": "workspace:*",
"@auth/nextjs": "workspace:*",
"@next-auth/fauna-adapter": "workspace:*",
"@next-auth/prisma-adapter": "workspace:*",
"@next-auth/supabase-adapter": "workspace:*",
@@ -23,7 +23,7 @@
"@prisma/client": "^3",
"@supabase/supabase-js": "^2.0.5",
"faunadb": "^4",
"next": "13.3.2-canary.12",
"next": "13.4.0",
"next-auth": "workspace:*",
"nodemailer": "^6",
"react": "^18",

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,13 +0,0 @@
import Layout from "components/layout"
export default function Page() {
return (
<Layout>
<h1>NextAuth.js Example</h1>
<p>
This is an example site to demonstrate how to use{" "}
<a href="https://authjs.dev">NextAuth.js</a> for authentication.
</p>
</Layout>
)
}

View File

@@ -1,9 +0,0 @@
import Layout from "components/layout"
export default function Page() {
return (
<Layout>
<h1>Page protected by Middleware</h1>
</Layout>
)
}

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: {} }
}

View File

@@ -1,46 +0,0 @@
import { getServerSession } from "next-auth/next"
import Layout from "../components/layout"
import { authConfig } from "./api/auth-old/[...nextauth]"
export default function Page() {
// As this page uses Server Side Rendering, the `session` will be already
// populated on render without needing to go through a loading stage.
// This is possible because of the shared context configured in `_app.js` that
// is used by `useSession()`.
return (
<Layout>
<h1>Server Side Rendering</h1>
<p>
This page uses the <strong>getServerSession()</strong> method in{" "}
<strong>getServerSideProps()</strong>.
</p>
<p>
Using <strong>getServerSession()</strong> in{" "}
<strong>getServerSideProps()</strong> is currently the recommended
approach, although the API may still change, if you need to support
Server Side Rendering with authentication.
</p>
<p>
Using <strong>getSession()</strong> is still recommended on the client.
</p>
<p>
The advantage of Server Side Rendering is this page does not require
client side JavaScript.
</p>
<p>
The disadvantage of Server Side Rendering is that this page is slower to
render.
</p>
</Layout>
)
}
// Export the `session` prop to use sessions with Server Side Rendering
export async function getServerSideProps(context) {
return {
props: {
session: await getServerSession(context.req, context.res, authConfig),
},
}
}

View File

@@ -1,19 +1,17 @@
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET= # Linux: `openssl rand -hex 32` or go to https://generate-secret.vercel.app/32
AUTH_SECRET= # Linux: `openssl rand -hex 32` or go to https://generate-secret.vercel.app/32
AUTH_AUTH0_ID=
AUTH_AUTH0_SECRET=
AUTH_AUTH0_ISSUER=
AUTH0_ID=
AUTH0_SECRET=
AUTH0_ISSUER=
AUTH_FACEBOOK_ID=
AUTH_FACEBOOK_SECRET=
FACEBOOK_ID=
FACEBOOK_SECRET=
AUTH_GITHUB_ID=
AUTH_GITHUB_SECRET=
GITHUB_ID=
GITHUB_SECRET=
AUTH_GOOGLE_ID=
AUTH_GOOGLE_SECRET=
GOOGLE_ID=
GOOGLE_SECRET=
TWITTER_ID=
TWITTER_SECRET=
AUTH_TWITTER_ID=
AUTH_TWITTER_SECRET=

View File

@@ -1,8 +1,12 @@
import NextAuth from "next-auth"
import Auth0 from "@auth/core/providers/github"
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"
export const { handlers, auth } = NextAuth({
providers: [GitHub],
providers: [GitHub, Auth0, Facebook, Google, Twitter],
callbacks: {
async authorized({ request, auth }) {
// if (request.method === "POST") {

View File

@@ -0,0 +1,32 @@
---
title: Upgrade Guide (v5)
---
NextAuth.js version 5 will continue to be shipped as `next-auth` *for the Next.js version only*. We're here to help you upgrade your applications as smoothly as possible. It is possible to upgrade from any version of 4.x to the latest v5 release by following the migration steps below.
Upgrade to the latest version by running:
```bash npm2yarn2pnpm
npm install next-auth
```
## Getting Started
Below is a summary of the high-level API changes in `next-auth` v5.
```
| Where | Old | New |
| ------------------------- | --------------------------------------------------- | -------------- |
| API Route (Node) | getServerSession(req, res, authOptions) | auth() wrapper |
| API Route (Edge) | - | auth() wrapper |
| getServerSideProps | getServerSession(ctx.req, ctx.res, authOptions) | auth() wrapper |
| Middleware | withAuth(middleware, subset of authOptions) wrapper | auth() wrapper |
| Route Handler | - | auth() wrapper |
| Server Component | getServerSession(authOptions) | auth() call |
| Client Component | useSession() hook | useAuth() hook |
```
## Summary
We hope this migration goes smoothly for each and every one of you! If you have any questions or get stuck anywhere, feel free to create [a new issue](https://github.com/nextauthjs/next-auth/issues/new) on GitHub.

View File

@@ -12,9 +12,10 @@ The API reference is being migrated from the [old documentation page](https://ne
Here are the _currently_ planned and released packages under the `@auth/*` scope. This is not an exhaustive list, but the set of packages that we would like to focus on to begin with.
| Feature | Status |
| Package | Status |
| ------------------- | -------- |
| `@auth/nextjs` | Planned |
| `next-auth@4 ` | Stable. See [docs](https://next-auth.js.org/) |
| `@auth/nextjs` | Experimental, under `next-auth@5`. |
| `@auth/*-adapter` | Planned |
| `@auth/core` | Experimental |
| `@auth/sveltekit` | Experimental |

View File

@@ -249,7 +249,7 @@ const docusaurusConfig = {
plugins: [
typedocFramework("core", ["index.ts", "adapters.ts", "errors.ts", "jwt.ts", "types.ts"]),
typedocFramework("frameworks-sveltekit", ["lib/index.ts", "lib/client.ts"]),
typedocFramework("next-auth", ["index.ts", "react.tsx", "jwt.ts", "adapters.ts", "next.ts", "types.ts", "middleware.ts"]),
typedocFramework("frameworks-nextjs", ["index.ts", "react.tsx", "jwt.ts", "adapters.ts", "next.ts", "types.ts", "middleware.ts"]),
...(process.env.TYPEDOC_SKIP_ADAPTERS
? []
: [

View File

@@ -36,8 +36,8 @@ module.exports = {
{
type: "category",
label: "next-auth",
link: { type: "doc", id: "reference/next-auth/index" },
items: [{ type: "autogenerated", dirName: "reference/next-auth" }],
link: { type: "doc", id: "reference/nextjs/index" },
items: [{ type: "autogenerated", dirName: "reference/nextjs" }],
},
...(process.env.TYPEDOC_SKIP_ADAPTERS
? []

View File

@@ -66,6 +66,16 @@
"has": [{ "type": "host", "value": "solid-start.authjs.dev" }],
"destination": "https://authjs.dev/reference/solid-start"
},
{
"source": "/:path(.*)",
"has": [{ "type": "host", "value": "nextjs.authjs.dev" }],
"destination": "https://authjs.dev/reference/nextjs"
},
{
"source": "/v5",
"has": [{ "type": "host", "value": "nextjs.authjs.dev" }],
"destination": "https://authjs.dev/getting-started/upgrade-to-v5"
},
{
"source": "/:path(.*)",
"has": [{ "type": "host", "value": "errors.authjs.dev" }],

View File

@@ -42,8 +42,6 @@ import type { Adapter, AdapterAccount } from "next-auth/adapters"
* })
* ```
*
* ## Advanced usage
*
* ### Create the Prisma schema from scratch
*
* You need to use at least Prisma 2.26.0. Create a schema file in `prisma/schema.prisma` similar to this one:

View File

@@ -125,7 +125,12 @@ export class MissingSecret extends AuthError {}
*/
export class OAuthAccountNotLinked extends AuthError {}
/** @todo */
/**
* Thrown when an OAuth provider returns an error during the sign in process.
* This could happen for example if the user denied access to the application or there was a configuration error.
*
* For a full list of possible reasons, check out the specification [Authorization Code Grant: Error Response](https://www.rfc-editor.org/rfc/rfc6749#section-4.1.2.1)
*/
export class OAuthCallbackError extends AuthError {}
/** @todo */

View File

@@ -110,7 +110,6 @@ export async function AuthInternal<
if (
[
"Signin",
"OAuthCallback",
"OAuthCreateAccount",
"EmailCreateAccount",
"Callback",

View File

@@ -89,11 +89,9 @@ export async function handleOAuth(
/** https://www.rfc-editor.org/rfc/rfc6749#section-4.1.2.1 */
if (o.isOAuth2Error(codeGrantParams)) {
logger.debug("OAuthCallbackError", {
providerId: provider.id,
...codeGrantParams,
})
throw new OAuthCallbackError(codeGrantParams.error)
const cause = { providerId: provider.id, ...codeGrantParams }
logger.debug("OAuthCallbackError", cause)
throw new OAuthCallbackError("OAuth Provider returned an error", cause)
}
const codeVerifier = await checks.pkce.use(cookies, resCookies, options)

View File

@@ -11,7 +11,7 @@ const signinErrors: Record<
default: "Unable to sign in.",
signin: "Try signing in with a different account.",
oauthsignin: "Try signing in with a different account.",
oauthcallback: "Try signing in with a different account.",
oauthcallbackerror: "Try signing in with a different account.",
oauthcreateaccount: "Try signing in with a different account.",
emailcreateaccount: "Try signing in with a different account.",
callback: "Try signing in with a different account.",

View File

@@ -1,3 +1,4 @@
import { OAuthProfileParseError } from "../errors.js"
import { merge } from "./utils/merge.js"
import type {
@@ -90,8 +91,10 @@ function normalizeOAuth(
* @see https://openid.net/specs/openid-connect-core-1_0.html#UserInfo
*/
const defaultProfile: ProfileCallback<Profile> = (profile) => {
const id = profile.sub ?? profile.id
if (!id) throw new OAuthProfileParseError("Missing user id")
return stripUndefined({
id: profile.sub ?? profile.id,
id: id.toString(),
name: profile.name ?? profile.nickname ?? profile.preferred_username,
email: profile.email,
image: profile.picture,

View File

@@ -1,4 +1,8 @@
import { CallbackRouteError, Verification } from "../../errors.js"
import {
CallbackRouteError,
OAuthCallbackError,
Verification,
} from "../../errors.js"
import { handleLogin } from "../callback-handler.js"
import { handleOAuth } from "../oauth/callback.js"
import { handleState } from "../oauth/handle-state.js"
@@ -368,6 +372,15 @@ export async function callback(params: {
cookies,
}
} catch (e) {
if (e instanceof OAuthCallbackError) {
logger.error(e)
// REVIEW: Should we expose original error= and error_description=
// Should we use a different name for error= then, since we already use it for all kind of errors?
url.searchParams.set("error", OAuthCallbackError.name)
url.pathname += "/signin"
return { redirect: url.toString(), cookies }
}
const error = new CallbackRouteError(e as Error, { provider: provider.id })
logger.debug("callback route error details", { method, query, body })

View File

@@ -41,7 +41,7 @@ export async function session(
user: {
name: decodedToken?.name,
email: decodedToken?.email,
image: decodedToken?.picture,
picture: decodedToken?.picture,
},
expires: newExpires.toISOString(),
}
@@ -128,7 +128,7 @@ export async function session(
user: {
name: user.name,
email: user.email,
image: user.image,
picture: user.image,
},
expires: session.expires.toISOString(),
},

View File

@@ -65,6 +65,7 @@ export interface GitHubProfile {
space: number
private_repos: number
}
[claim: string]: unknown
}
/**

View File

@@ -263,11 +263,9 @@ export type OIDCConfigInternal<Profile> = OAuthConfigInternal<Profile> & {
export type OAuthUserConfig<Profile> = Omit<
Partial<OAuthConfig<Profile>>,
"options" | "type"
> &
Required<Pick<OAuthConfig<Profile>, "clientId" | "clientSecret">>
>
export type OIDCUserConfig<Profile> = Omit<
Partial<OIDCConfig<Profile>>,
"options" | "type"
> &
Required<Pick<OIDCConfig<Profile>, "clientId" | "clientSecret">>
>

View File

@@ -99,6 +99,7 @@ export interface TwitterProfile {
text: string
}>
}
[claims: string]: unknown
}
/**

View File

@@ -141,32 +141,32 @@ export interface Account extends Partial<OpenIDTokenEndpointResponse> {
* @see https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
*/
export interface Profile {
sub: string
name?: string
given_name?: string
family_name?: string
middle_name?: string
nickname?: string
preferred_username?: string
profile?: string
picture?: string
website?: string
email?: string
email_verified?: boolean
gender?: string
birthdate?: string
zoneinfo?: string
locale?: string
phone_number?: string
updated_at?: number
sub?: string | null
name?: string | null
given_name?: string | null
family_name?: string | null
middle_name?: string | null
nickname?: string | null
preferred_username?: string | null
profile?: string | null
picture?: string | null | any
website?: string | null
email?: string | null
email_verified?: boolean | null
gender?: string | null
birthdate?: string | null
zoneinfo?: string | null
locale?: string | null
phone_number?: string | null
updated_at?: Date | string | number | null
address?: {
formatted?: string
street_address?: string
locality?: string
region?: string
postal_code?: string
country?: string
}
formatted?: string | null
street_address?: string | null
locality?: string | null
region?: string | null
postal_code?: string | null
country?: string | null
} | null
[claim: string]: unknown
}
@@ -224,7 +224,7 @@ export interface CallbacksOptions<P = Profile, A = Account> {
* This callback is called whenever a session is checked.
* (Eg.: invoking the `/api/session` endpoint, using `useSession` or `getSession`)
*
* ⚠ By default, only a subset (email, name, image)
* ⚠ By default, only a subset (email, name, picture)
* of the token is returned for increased security.
*
* If you want to make something available you added to the token through the `jwt` callback,
@@ -337,7 +337,7 @@ export type ErrorPageParam = "Configuration" | "AccessDenied" | "Verification"
export type SignInPageErrorParam =
| "Signin"
| "OAuthSignin"
| "OAuthCallback"
| "OAuthCallbackError"
| "OAuthCreateAccount"
| "EmailCreateAccount"
| "Callback"
@@ -377,7 +377,7 @@ export interface DefaultSession {
user?: {
name?: string | null
email?: string | null
image?: string | null
picture?: string | null
}
expires: ISODateString
}

View File

@@ -0,0 +1,24 @@
<p align="center">
<br/>
<a href="https://authjs.dev" target="_blank"><img width="150px" src="https://authjs.dev/img/logo/logo-sm.png" /></a>
<h3 align="center">NextAuth.js</a></h3>
<h4 align="center">Authentication for Next.js.</h4>
<p align="center" style="align: center;">
<a href="https://npm.im/next-auth">
<img src="https://img.shields.io/badge/TypeScript-blue?style=flat-square" alt="TypeScript" />
</a>
<a href="https://npm.im/next-auth">
<img alt="npm" src="https://img.shields.io/npm/v/next-auth?color=green&label=next-auth&style=flat-square">
</a>
<a href="https://www.npmtrends.com/next-auth">
<img src="https://img.shields.io/npm/dm/next-auth?label=%20downloads&style=flat-square" alt="Downloads" />
</a>
<a href="https://github.com/nextauthjs/next-auth/stargazers">
<img src="https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square" alt="Github Stars" />
</a>
</p>
</p>
---
Check out the documentation at [nextjs.authjs.dev](https://nextjs.authjs.dev).

View File

@@ -0,0 +1,75 @@
{
"name": "@auth/nextjs",
"version": "0.0.1",
"description": "Authentication for Next.js",
"homepage": "https://nextjs.authjs.dev",
"repository": "https://github.com/nextauthjs/next-auth.git",
"author": "Balázs Orbán <info@balazsorban.com>",
"keywords": [
"react",
"nodejs",
"oauth",
"jwt",
"oauth2",
"authentication",
"nextjs",
"csrf",
"oidc",
"nextauth"
],
"type": "module",
"types": "./index.d.ts",
"exports": {
".": {
"types": "./index.d.ts",
"import": "./index.js"
},
"./adapters": {
"types": "./adapters.d.ts"
},
"./jwt": {
"types": "./jwt.d.ts",
"import": "./jwt.js"
},
"./middleware": {
"types": "./middleware.d.ts",
"import": "./middleware.js"
},
"./next": {
"types": "./next.d.ts",
"import": "./next.js"
},
"./providers": {
"types": "./providers.d.ts"
},
"./react": {
"types": "./react.d.ts",
"import": "./react.js"
},
"./package.json": "./package.json"
},
"scripts": {
"dev": "tsc -w",
"clean": "rm -rf *.js *.d.ts lib",
"build": "pnpm clean && tsc"
},
"files": [
"*.js",
"*.d.ts",
"lib",
"src"
],
"devDependencies": {
"@types/react": "18.0.37",
"typescript": "^4",
"next": "13.4.0"
},
"license": "ISC",
"dependencies": {
"@auth/core": "workspace:*"
},
"peerDependencies": {
"next": "^13.4.0",
"react": "^18.2.0"
}
}

View File

@@ -0,0 +1,47 @@
/**
* :::warning Deprecated
* This module is being replaced by [`@auth/core/adapters`](https://authjs.dev/reference/core/adapters) and only kept for backwards compatibility.
* :::
*
* @module adapters
*/
// TODO: remove this file and replace references with `@auth/core/adapters`
import type {
Adapter as CoreAdapter,
AdapterAccount as CoreAdapterAccount,
AdapterSession as CoreAdapterSession,
AdapterUser as CoreAdapterUser,
VerificationToken as CoreVerificationToken,
} from "@auth/core/adapters"
/**
* @deprecated use `@auth/core/adapters`
* Read more at: https://nextjs.authjs.dev/v5
*/
export type Adapter = CoreAdapter
/**
* @deprecated use `@auth/core/adapters`
* Read more at: https://nextjs.authjs.dev/v5
*/
export type AdapterAccount = CoreAdapterAccount
/**
* @deprecated use `@auth/core/adapters`
* Read more at: https://nextjs.authjs.dev/v5
*/
export type AdapterSession = CoreAdapterSession
/**
* @deprecated use `@auth/core/adapters`
* Read more at: https://nextjs.authjs.dev/v5
*/
export type AdapterUser = CoreAdapterUser
/**
* @deprecated use `@auth/core/adapters`
* Read more at: https://nextjs.authjs.dev/v5
*/
export type VerificationToken = CoreVerificationToken

View File

@@ -0,0 +1,176 @@
/**
*
* :::warning Note
* This is the documentation for `next-auth@5`, which is currently **experimental**. For the documentation of the latest stable version, see [next-auth@4](https://next-auth.js.org).
* :::
*
* If you are looking for the migration guide, visit the [`next-auth@5` Migration Guide](https://nextjs.authjs.dev/v5).
*
* ## Installation
*
* ```bash npm2yarn2pnpm
* npm install next-auth@5 @auth/core
* ```
*
* ## Signing in and signing out
*
* The App Router embraces Server Actions that can be leveraged to decrease the amount of JavaScript sent to the browser.
*
* :::info
* [Next.js Server Actions](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions) is **in alpha stage**. In the future, NextAuth.js will integrate with Server Actions and provide first-party APIs.
* Until then, here is how you can use NextAuth.js to log in and log out without JavaScript.
* :::
*
* ```ts title="app/auth-components.tsx"
* import { auth } from "../auth"
* import { cookies, headers } from "next/headers"
*
* function CSRF() {
* const value = cookies().get("next-auth.csrf-token")?.value.split("|")[0]
* return <input type="hidden" name="csrfToken" value={value} />
* }
*
* export function SignIn({ provider, ...props }: any) {
* return (
* <form action={`/api/auth/signin/${provider}`} method="post">
* <button {{...props}}/>
* <CSRF/>
* </form>
* )
* }
*
* export function SignOut(props: any) {
* return (
* <form action="/api/auth/signout" method="post">
* <button {...props}/>
* <CSRF/>
* </form>
* )
* }
* ```
*
* Alternatively, you can create client components, using the `signIn()` and `signOut` methods:
*
* ```ts title="app/auth-components.tsx"
* "use client"
* import { signIn, signOut } from "next-auth/react"
*
* export function SignIn({provider, ...props}: any) {
* return <button {...props} onClick={() => signIn(provider)}/>
* }
*
* export function SignOut(props: any) {
* return <button {...props} onClick={() => signOut()}/>
* }
* ```
*
* Then, you could for example use it like this:
*
* ```ts title=app/page.tsx
* import { headers } from "next/headers"
* import { SignIn, SignOut } from "./auth-components"
*
* export default async function Page() {
* const session = await auth(headers())
* if (session) {
* return (
* <>
* <pre>{JSON.stringify(session, null, 2)}</pre>
* <SignOut>Sign out</SignOut>
* </>
* )
* }
* return <SignIn id="github">Sign in with github</SignIn>
* }
* ```
*
* @module index
*/
import { Auth } from "@auth/core"
import { setEnvDefaults } from "./lib/env.js"
import { initAuth } from "./lib/index.js"
import type { NextRequest } from "next/server"
import type { NextAuthConfig } from "./lib/index.js"
import { getServerSession } from "./next"
type AppRouteHandlers = Record<
"GET" | "POST",
(req: NextRequest) => Promise<Response>
>
export type { NextAuthConfig }
/**
* The result of invoking {@link NextAuth}, initialized with the {@link NextAuthConfig}.
* It contains methods to set up and interact with NextAuth.js in your Next.js app.
*/
export interface NextAuthResult {
/**
* The NextAuth.js [Route Handler](https://beta.nextjs.org/docs/routing/route-handlers) methods. After initializing NextAuth.js in `auth.ts`,
* export these methods from `app/api/auth/[...nextauth]/route.ts`.
*
* :::note
* This is a workaround until we have integrated with [Next.js Server Actions](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions).
* :::
*
* @example
* ```ts title="app/api/auth/[...nextauth]/route.ts"
* import { handlers } from "../../../../auth"
* export const { GET, POST } = handlers
* export const runtime = "edge"
* ```
*/
handlers: AppRouteHandlers
/**
* A universal method to interact with NextAuth.js in your Next.js app.
* After initializing NextAuth.js in `auth.ts`, use this method in Middleware, Route Handlers or React Server Components.
*
* @example
* ```ts title="middleware.ts"
* export { auth as middleware } from "./auth"
* ```
* @example
* ```ts title="app/page.ts"
* import { auth } from "../auth"
* import { headers } from "next/headers"
* export default async function Page() {
* const { user } = await auth(headers())
* return <div>Hello {user?.name}</div>
* }
* ```
* @example
* ```ts title="app/api/route.ts"
* import { auth } from "../../auth"
*
* export const POST = auth((req) => {
* // req.auth
* })
* ```
*/
auth: ReturnType<typeof initAuth>
/** TODO: document */
getServerSession: ReturnType<typeof getServerSession>
}
/**
* Initialize NextAuth.js.
*
* @example
* ```ts title="auth.ts"
* import NextAuth from "next-auth"
* import GitHub from "@auth/core/providers/github"
*
* export const { handlers, auth } = NextAuth({ providers: [GitHub] })
* ```
*/
export default function NextAuth(config: NextAuthConfig): NextAuthResult {
setEnvDefaults(config)
const httpHandler = (req: NextRequest) => Auth(req, config)
return {
handlers: { GET: httpHandler, POST: httpHandler } as const,
auth: initAuth(config),
getServerSession: getServerSession(config),
}
}

View File

@@ -33,10 +33,8 @@ export interface UseSessionOptions<R extends boolean> {
onUnauthenticated?: () => void
}
/**
* Util type that matches some strings literally, but allows any other string as well.
* @source https://github.com/microsoft/TypeScript/issues/29729#issuecomment-832522611
*/
// Util type that matches some strings literally, but allows any other string as well.
// @source https://github.com/microsoft/TypeScript/issues/29729#issuecomment-832522611
export type LiteralUnion<T extends U, U = string> =
| T
| (U & Record<never, never>)
@@ -67,7 +65,10 @@ export interface SignInResponse {
url: string | null
}
/** Match `inputType` of `new URLSearchParams(inputType)` */
/**
* Match `inputType` of `new URLSearchParams(inputType)`
* @internal
*/
export type SignInAuthorizationParams =
| string
| string[][]
@@ -86,7 +87,12 @@ export interface SignOutParams<R extends boolean = true> {
redirect?: R
}
/** [Documentation](https://next-auth.js.org/getting-started/client#options) */
/**
* If you have session expiry times of 30 days (the default) or more, then you probably don't need to change any of the default options.
*
* However, if you need to customize the session behavior and/or are using short session expiry times, you can pass options to the provider to customize the behavior of the {@link useSession} hook.
*/
export interface SessionProviderProps {
children: React.ReactNode
session?: Session | null

View File

@@ -2,73 +2,73 @@ import type { AuthConfig } from "@auth/core"
import type { OAuthProviderType } from "@auth/core/providers"
// TODO: Generate this from the available providers
export const providersEnv: Record<OAuthProviderType, [string | undefined, string | undefined]> = {
"42-school": [process.env.AUTH_42_SCHOOL_ID, process.env.AUTH_42_SCHOOL_SECRET],
apple: [process.env.AUTH_APPLE_ID, process.env.AUTH_APPLE_SECRET],
asgardeo: [process.env.AUTH_ASGARDEO_ID, process.env.AUTH_ASGARDEO_SECRET],
atlassian: [process.env.AUTH_ATLASSIAN_ID, process.env.AUTH_ATLASSIAN_SECRET],
auth0: [process.env.AUTH_AUTH0_ID, process.env.AUTH_AUTH0_SECRET],
authentik: [process.env.AUTH_AUTHENTIK_ID, process.env.AUTH_AUTHENTIK_SECRET],
"azure-ad": [process.env.AUTH_AAD_ID, process.env.AUTH_AAD_SECRET],
"azure-ad-b2c": [process.env.AUTH_AAD_B2C_ID, process.env.AUTH_AAD_B2C_SECRET],
battlenet: [process.env.AUTH_BATTLENET_ID, process.env.AUTH_BATTLENET_SECRET],
beyondidentity: [process.env.AUTH_BEYOND_IDENTITY_ID, process.env.AUTH_BEYOND_IDENTITY_SECRET],
box: [process.env.AUTH_BOX_ID, process.env.AUTH_BOX_SECRET],
"boxyhq-saml": [process.env.AUTH_BOXYHQ_SAML_ID, process.env.AUTH_BOXYHQ_SAML_SECRET],
bungie: [process.env.AUTH_BUNGIE_ID, process.env.AUTH_BUNGIE_SECRET],
cognito: [process.env.AUTH_COGNITO_ID, process.env.AUTH_COGNITO_SECRET],
coinbase: [process.env.AUTH_COINBASE_ID, process.env.AUTH_COINBASE_SECRET],
discord: [process.env.AUTH_DISCORD_ID, process.env.AUTH_DISCORD_SECRET],
dropbox: [process.env.AUTH_DROPBOX_ID, process.env.AUTH_DROPBOX_SECRET],
"duende-identity-server6": [process.env.AUTH_IDS6_ID, process.env.AUTH_IDS6_SECRET],
eveonline: [process.env.AUTH_EVEONLINE_ID, process.env.AUTH_EVEONLINE_SECRET],
facebook: [process.env.AUTH_FACEBOOK_ID, process.env.AUTH_FACEBOOK_SECRET],
faceit: [process.env.AUTH_FACEIT_ID, process.env.AUTH_FACEIT_SECRET],
foursquare: [process.env.AUTH_FOURSQUARE_ID, process.env.AUTH_FOURSQUARE_SECRET],
freshbooks: [process.env.AUTH_FRESHBOOKS_ID, process.env.AUTH_FRESHBOOKS_SECRET],
fusionauth: [process.env.AUTH_FUSIONAUTH_ID, process.env.AUTH_FUSIONAUTH_SECRET],
github: [process.env.AUTH_GITHUB_ID, process.env.AUTH_GITHUB_SECRET],
gitlab: [process.env.AUTH_GITLAB_ID, process.env.AUTH_GITLAB_SECRET],
google: [process.env.AUTH_GOOGLE_ID, process.env.AUTH_GOOGLE_SECRET],
hubspot: [process.env.AUTH_HUBSPOT_ID, process.env.AUTH_HUBSPOT_SECRET],
"identity-server4": [process.env.AUTH_IDS4_ID, process.env.AUTH_IDS4_SECRET],
instagram: [process.env.AUTH_INSTAGRAM_ID, process.env.AUTH_INSTAGRAM_SECRET],
kakao: [process.env.AUTH_KAKAO_ID, process.env.AUTH_KAKAO_SECRET],
keycloak: [process.env.AUTH_KEYCLOAK_ID, process.env.AUTH_KEYCLOAK_SECRET],
line: [process.env.AUTH_LINE_ID, process.env.AUTH_LINE_SECRET],
linkedin: [process.env.AUTH_LINKEDIN_ID, process.env.AUTH_LINKEDIN_SECRET],
mailchimp: [process.env.AUTH_MAILCHIMP_ID, process.env.AUTH_MAILCHIMP_SECRET],
mailru: [process.env.AUTH_MAILRU_ID, process.env.AUTH_MAILRU_SECRET],
mattermost: [process.env.AUTH_MATTERMOST_ID, process.env.AUTH_MATTERMOST_SECRET],
medium: [process.env.AUTH_MEDIUM_ID, process.env.AUTH_MEDIUM_SECRET],
naver: [process.env.AUTH_NAVER_ID, process.env.AUTH_NAVER_SECRET],
netlify: [process.env.AUTH_NETLIFY_ID, process.env.AUTH_NETLIFY_SECRET],
notion: [process.env.AUTH_NOTION_ID, process.env.AUTH_NOTION_SECRET],
okta: [process.env.AUTH_OKTA_ID, process.env.AUTH_OKTA_SECRET],
onelogin: [process.env.AUTH_ONELOGIN_ID, process.env.AUTH_ONELOGIN_SECRET],
osso: [process.env.AUTH_OSSO_ID, process.env.AUTH_OSSO_SECRET],
osu: [process.env.AUTH_OSU_ID, process.env.AUTH_OSU_SECRET],
patreon: [process.env.AUTH_PATREON_ID, process.env.AUTH_PATREON_SECRET],
pinterest: [process.env.AUTH_PINTEREST_ID, process.env.AUTH_PINTEREST_SECRET],
pipedrive: [process.env.AUTH_PIPEDRIVE_ID, process.env.AUTH_PIPEDRIVE_SECRET],
reddit: [process.env.AUTH_REDDIT_ID, process.env.AUTH_REDDIT_SECRET],
salesforce: [process.env.AUTH_SALESFORCE_ID, process.env.AUTH_SALESFORCE_SECRET],
slack: [process.env.AUTH_SLACK_ID, process.env.AUTH_SLACK_SECRET],
spotify: [process.env.AUTH_SPOTIFY_ID, process.env.AUTH_SPOTIFY_SECRET],
strava: [process.env.AUTH_STRAVA_ID, process.env.AUTH_STRAVA_SECRET],
todoist: [process.env.AUTH_TODOIST_ID, process.env.AUTH_TODOIST_SECRET],
trakt: [process.env.AUTH_TRAKT_ID, process.env.AUTH_TRAKT_SECRET],
twitch: [process.env.AUTH_TWITCH_ID, process.env.AUTH_TWITCH_SECRET],
twitter: [process.env.AUTH_TWITTER_ID, process.env.AUTH_TWITTER_SECRET],
"united-effects": [process.env.AUTH_UNITED_EFFECTS_ID, process.env.AUTH_UNITED_EFFECTS_SECRET],
vk: [process.env.AUTH_VK_ID, process.env.AUTH_VK_SECRET],
wikimedia: [process.env.AUTH_WIKIMEDIA_ID, process.env.AUTH_WIKIMEDIA_SECRET],
wordpress: [process.env.AUTH_WORDPRESS_ID, process.env.AUTH_WORDPRESS_SECRET],
workos: [process.env.AUTH_WORKOS_ID, process.env.AUTH_WORKOS_SECRET],
yandex: [process.env.AUTH_YANDEX_ID, process.env.AUTH_YANDEX_SECRET],
zitadel: [process.env.AUTH_ZITADEL_ID, process.env.AUTH_ZITADEL_SECRET],
zoho: [process.env.AUTH_ZOHO_ID, process.env.AUTH_ZOHO_SECRET],
zoom: [process.env.AUTH_ZOOM_ID, process.env.AUTH_ZOOM_SECRET],
export const providersEnv: Record<OAuthProviderType, Array<string | undefined>> = {
"42-school": [process.env.AUTH_42_SCHOOL_ID, process.env.AUTH_42_SCHOOL_SECRET, process.env.AUTH_42_SCHOOL_ISSUER],
apple: [process.env.AUTH_APPLE_ID, process.env.AUTH_APPLE_SECRET, process.env.AUTH_APPLE_ISSUER],
asgardeo: [process.env.AUTH_ASGARDEO_ID, process.env.AUTH_ASGARDEO_SECRET, process.env.AUTH_ASGARDEO_ISSUER],
atlassian: [process.env.AUTH_ATLASSIAN_ID, process.env.AUTH_ATLASSIAN_SECRET, process.env.AUTH_ATLASSIAN_ISSUER],
auth0: [process.env.AUTH_AUTH0_ID, process.env.AUTH_AUTH0_SECRET, process.env.AUTH_AUTH0_ISSUER],
authentik: [process.env.AUTH_AUTHENTIK_ID, process.env.AUTH_AUTHENTIK_SECRET, process.env.AUTH_AUTHENTIK_ISSUER],
"azure-ad": [process.env.AUTH_AAD_ID, process.env.AUTH_AAD_SECRET, process.env.AUTH_AAD_ISSUER],
"azure-ad-b2c": [process.env.AUTH_AAD_B2C_ID, process.env.AUTH_AAD_B2C_SECRET, process.env.AUTH_AAD_B2C_ISSUER],
battlenet: [process.env.AUTH_BATTLENET_ID, process.env.AUTH_BATTLENET_SECRET, process.env.AUTH_BATTLENET_ISSUER],
beyondidentity: [process.env.AUTH_BEYOND_IDENTITY_ID, process.env.AUTH_BEYOND_IDENTITY_SECRET, process.env.AUTH_BEYOND_IDENTITY_ISSUER],
box: [process.env.AUTH_BOX_ID, process.env.AUTH_BOX_SECRET, process.env.AUTH_BOX_ISSUER],
"boxyhq-saml": [process.env.AUTH_BOXYHQ_SAML_ID, process.env.AUTH_BOXYHQ_SAML_SECRET, process.env.AUTH_BOXYHQ_SAML_ISSUER],
bungie: [process.env.AUTH_BUNGIE_ID, process.env.AUTH_BUNGIE_SECRET, process.env.AUTH_BUNGIE_ISSUER],
cognito: [process.env.AUTH_COGNITO_ID, process.env.AUTH_COGNITO_SECRET, process.env.AUTH_COGNITO_ISSUER],
coinbase: [process.env.AUTH_COINBASE_ID, process.env.AUTH_COINBASE_SECRET, process.env.AUTH_COINBASE_ISSUER],
discord: [process.env.AUTH_DISCORD_ID, process.env.AUTH_DISCORD_SECRET, process.env.AUTH_DISCORD_ISSUER],
dropbox: [process.env.AUTH_DROPBOX_ID, process.env.AUTH_DROPBOX_SECRET, process.env.AUTH_DROPBOX_ISSUER],
"duende-identity-server6": [process.env.AUTH_IDS6_ID, process.env.AUTH_IDS6_SECRET, process.env.AUTH_IDS6_ISSUER],
eveonline: [process.env.AUTH_EVEONLINE_ID, process.env.AUTH_EVEONLINE_SECRET, process.env.AUTH_EVEONLINE_ISSUER],
facebook: [process.env.AUTH_FACEBOOK_ID, process.env.AUTH_FACEBOOK_SECRET, process.env.AUTH_FACEBOOK_ISSUER],
faceit: [process.env.AUTH_FACEIT_ID, process.env.AUTH_FACEIT_SECRET, process.env.AUTH_FACEIT_ISSUER],
foursquare: [process.env.AUTH_FOURSQUARE_ID, process.env.AUTH_FOURSQUARE_SECRET, process.env.AUTH_FOURSQUARE_ISSUER],
freshbooks: [process.env.AUTH_FRESHBOOKS_ID, process.env.AUTH_FRESHBOOKS_SECRET, process.env.AUTH_FRESHBOOKS_ISSUER],
fusionauth: [process.env.AUTH_FUSIONAUTH_ID, process.env.AUTH_FUSIONAUTH_SECRET, process.env.AUTH_FUSIONAUTH_ISSUER],
github: [process.env.AUTH_GITHUB_ID, process.env.AUTH_GITHUB_SECRET, process.env.AUTH_GITHUB_ISSUER],
gitlab: [process.env.AUTH_GITLAB_ID, process.env.AUTH_GITLAB_SECRET, process.env.AUTH_GITLAB_ISSUER],
google: [process.env.AUTH_GOOGLE_ID, process.env.AUTH_GOOGLE_SECRET, process.env.AUTH_GOOGLE_ISSUER],
hubspot: [process.env.AUTH_HUBSPOT_ID, process.env.AUTH_HUBSPOT_SECRET, process.env.AUTH_HUBSPOT_ISSUER],
"identity-server4": [process.env.AUTH_IDS4_ID, process.env.AUTH_IDS4_SECRET, process.env.AUTH_IDS4_ISSUER],
instagram: [process.env.AUTH_INSTAGRAM_ID, process.env.AUTH_INSTAGRAM_SECRET, process.env.AUTH_INSTAGRAM_ISSUER],
kakao: [process.env.AUTH_KAKAO_ID, process.env.AUTH_KAKAO_SECRET, process.env.AUTH_KAKAO_ISSUER],
keycloak: [process.env.AUTH_KEYCLOAK_ID, process.env.AUTH_KEYCLOAK_SECRET, process.env.AUTH_KEYCLOAK_ISSUER],
line: [process.env.AUTH_LINE_ID, process.env.AUTH_LINE_SECRET, process.env.AUTH_LINE_ISSUER],
linkedin: [process.env.AUTH_LINKEDIN_ID, process.env.AUTH_LINKEDIN_SECRET, process.env.AUTH_LINKEDIN_ISSUER],
mailchimp: [process.env.AUTH_MAILCHIMP_ID, process.env.AUTH_MAILCHIMP_SECRET, process.env.AUTH_MAILCHIMP_ISSUER],
mailru: [process.env.AUTH_MAILRU_ID, process.env.AUTH_MAILRU_SECRET, process.env.AUTH_MAILRU_ISSUER],
mattermost: [process.env.AUTH_MATTERMOST_ID, process.env.AUTH_MATTERMOST_SECRET, process.env.AUTH_MATTERMOST_ISSUER],
medium: [process.env.AUTH_MEDIUM_ID, process.env.AUTH_MEDIUM_SECRET, process.env.AUTH_MEDIUM_ISSUER],
naver: [process.env.AUTH_NAVER_ID, process.env.AUTH_NAVER_SECRET, process.env.AUTH_NAVER_ISSUER],
netlify: [process.env.AUTH_NETLIFY_ID, process.env.AUTH_NETLIFY_SECRET, process.env.AUTH_NETLIFY_ISSUER],
notion: [process.env.AUTH_NOTION_ID, process.env.AUTH_NOTION_SECRET, process.env.AUTH_NOTION_ISSUER],
okta: [process.env.AUTH_OKTA_ID, process.env.AUTH_OKTA_SECRET, process.env.AUTH_OKTA_ISSUER],
onelogin: [process.env.AUTH_ONELOGIN_ID, process.env.AUTH_ONELOGIN_SECRET, process.env.AUTH_ONELOGIN_ISSUER],
osso: [process.env.AUTH_OSSO_ID, process.env.AUTH_OSSO_SECRET, process.env.AUTH_OSSO_ISSUER],
osu: [process.env.AUTH_OSU_ID, process.env.AUTH_OSU_SECRET, process.env.AUTH_OSU_ISSUER],
patreon: [process.env.AUTH_PATREON_ID, process.env.AUTH_PATREON_SECRET, process.env.AUTH_PATREON_ISSUER],
pinterest: [process.env.AUTH_PINTEREST_ID, process.env.AUTH_PINTEREST_SECRET, process.env.AUTH_PINTEREST_ISSUER],
pipedrive: [process.env.AUTH_PIPEDRIVE_ID, process.env.AUTH_PIPEDRIVE_SECRET, process.env.AUTH_PIPEDRIVE_ISSUER],
reddit: [process.env.AUTH_REDDIT_ID, process.env.AUTH_REDDIT_SECRET, process.env.AUTH_REDDIT_ISSUER],
salesforce: [process.env.AUTH_SALESFORCE_ID, process.env.AUTH_SALESFORCE_SECRET, process.env.AUTH_SALESFORCE_ISSUER],
slack: [process.env.AUTH_SLACK_ID, process.env.AUTH_SLACK_SECRET, process.env.AUTH_SLACK_ISSUER],
spotify: [process.env.AUTH_SPOTIFY_ID, process.env.AUTH_SPOTIFY_SECRET, process.env.AUTH_SPOTIFY_ISSUER],
strava: [process.env.AUTH_STRAVA_ID, process.env.AUTH_STRAVA_SECRET, process.env.AUTH_STRAVA_ISSUER],
todoist: [process.env.AUTH_TODOIST_ID, process.env.AUTH_TODOIST_SECRET, process.env.AUTH_TODOIST_ISSUER],
trakt: [process.env.AUTH_TRAKT_ID, process.env.AUTH_TRAKT_SECRET, process.env.AUTH_TRAKT_ISSUER],
twitch: [process.env.AUTH_TWITCH_ID, process.env.AUTH_TWITCH_SECRET, process.env.AUTH_TWITCH_ISSUER],
twitter: [process.env.AUTH_TWITTER_ID, process.env.AUTH_TWITTER_SECRET, process.env.AUTH_TWITTER_ISSUER],
"united-effects": [process.env.AUTH_UNITED_EFFECTS_ID, process.env.AUTH_UNITED_EFFECTS_SECRET, process.env.AUTH_UNITED_EFFECTS_ISSUER],
vk: [process.env.AUTH_VK_ID, process.env.AUTH_VK_SECRET, process.env.AUTH_VK_ISSUER],
wikimedia: [process.env.AUTH_WIKIMEDIA_ID, process.env.AUTH_WIKIMEDIA_SECRET, process.env.AUTH_WIKIMEDIA_ISSUER],
wordpress: [process.env.AUTH_WORDPRESS_ID, process.env.AUTH_WORDPRESS_SECRET, process.env.AUTH_WORDPRESS_ISSUER],
workos: [process.env.AUTH_WORKOS_ID, process.env.AUTH_WORKOS_SECRET, process.env.AUTH_WORKOS_ISSUER],
yandex: [process.env.AUTH_YANDEX_ID, process.env.AUTH_YANDEX_SECRET, process.env.AUTH_YANDEX_ISSUER],
zitadel: [process.env.AUTH_ZITADEL_ID, process.env.AUTH_ZITADEL_SECRET, process.env.AUTH_ZITADEL_ISSUER],
zoho: [process.env.AUTH_ZOHO_ID, process.env.AUTH_ZOHO_SECRET, process.env.AUTH_ZOHO_ISSUER],
zoom: [process.env.AUTH_ZOOM_ID, process.env.AUTH_ZOOM_SECRET, process.env.AUTH_ZOOM_ISSUER],
}
export function setEnvDefaults(config: AuthConfig) {
@@ -79,9 +79,10 @@ export function setEnvDefaults(config: AuthConfig) {
if (typeof p !== "function") return p
const provider = p()
if (provider.type === "oauth" || provider.type === "oidc") {
const [clientId, clientSecret] = providersEnv[provider.id] ?? []
const [clientId, clientSecret, issuer] = providersEnv[provider.id] ?? []
provider.clientId ??= clientId
provider.clientSecret ??= clientSecret
provider.issuer ??= issuer
}
return provider
})

View File

@@ -1,10 +1,9 @@
import { Auth, type AuthConfig } from "@auth/core"
import { NextResponse } from "next/server"
import type { headers as nextHeaders } from "next/headers"
import type { JWT } from "@auth/core/jwt"
import type { Awaitable, CallbacksOptions, User } from "@auth/core/types"
import type { Awaitable, CallbacksOptions, Session } from "@auth/core/types"
import type { NextFetchEvent, NextMiddleware, NextRequest } from "next/server"
import type { AppRouteHandlerFn } from "next/dist/server/future/route-modules/app-route/module"
export interface NextAuthCallbacks extends Partial<CallbacksOptions> {
/**
@@ -51,25 +50,24 @@ export interface NextAuthConfig extends AuthConfig {
callbacks?: NextAuthCallbacks
}
async function getAuth(
headers: Headers | ReturnType<typeof nextHeaders>,
config: NextAuthConfig
): Promise<AuthData & { expires: string }> {
async function runAuth(headers: Headers, config: NextAuthConfig) {
const host = headers.get("x-forwarded-host") ?? headers.get("host")
const protocol =
headers.get("x-forwarded-proto") === "http" ? "http" : "https"
// TODO: Handle URL correctly (NEXTAUTH_URL, request host, protocol, custom path, etc.)
const req = new Request("http://n/api/auth/session", {
const origin = `${protocol}://${host}`
const req = new Request(`${origin}/api/auth/session`, {
headers: { cookie: headers.get("cookie") ?? "" },
})
config.trustHost = true
config.useSecureCookies ??= headers.get("x-forwarded-proto") === "https"
if (config.callbacks) {
config.callbacks.session ??= ({ session, user, token }) => ({
expires: session.expires,
user,
token,
})
}
const response = await Auth(req, config)
return response.json()
config.useSecureCookies ??= protocol === "https"
// Since we are server-side, we don't need to filter out the session data
config.callbacks = Object.assign(config.callbacks as any, {
session({ session, user, token }) {
return { expires: session.expires, user: user ?? token }
},
})
return Auth(req, config)
}
export interface NextAuthRequest extends NextRequest {
@@ -85,70 +83,72 @@ export type WithAuthArgs =
| [NextAuthRequest, NextFetchEvent]
| [NextAuthMiddleware]
| [Headers]
| [ReturnType<typeof nextHeaders>]
| []
export function initAuth(config: NextAuthConfig) {
return (...args: WithAuthArgs) => {
// TODO: use `next/headers` when it's available in Middleware too
// if (!args.length) return getAuth($headers(), config)
if (!args.length) return getAuth(new Headers(), config)
if (args[0] instanceof Headers) return getAuth(args[0], config)
if (!args.length)
return runAuth(new Headers(), config).then((r) => r.json())
if (args[0] instanceof Headers) {
return runAuth(args[0], config).then((r) => r.json())
}
if (args[0] instanceof Request) {
// middleare.ts
// export { auth as default } from "auth"
const req = args[0]
const ev = args[1]
return handleAuth([req, ev as any], config)
}
// middleare.ts/router.ts
// import { auth } from "auth"
// export default auth((req) => { console.log(req.auth) }})
const userMiddleware = args[0]
return async (...args: Parameters<NextAuthMiddleware>) => {
// @ts-expect-error instanceof Headers higher up should not let this be a problem
return handleAuth(args, config, userMiddleware)
const userMiddlewareOrRoute = args[0]
return async (
...args: Parameters<NextAuthMiddleware | AppRouteHandlerFn>
) => {
return handleAuth(args, config, userMiddlewareOrRoute)
}
}
}
/** TODO: structure token similar to User */
/** TODO: document */
export interface AuthData {
token: JWT | null
user: User | null
user: Session | null
}
async function handleAuth(
args: Parameters<NextMiddleware>,
args: Parameters<NextMiddleware | AppRouteHandlerFn>,
config: NextAuthConfig,
userMiddleware?: NextAuthMiddleware
userMiddlewareOrRoute?: NextAuthMiddleware | AppRouteHandlerFn
) {
const request = args[0]
// TODO: pass `next/headers` when it's available
const {
token = null,
user = null,
expires = null,
} = (await getAuth(request.headers, config)) ?? {}
const authResponse = await runAuth(request.headers, config)
const { user = null, expires = null } = (await authResponse.json()) ?? {}
const authorized = config.callbacks?.authorized
? await config.callbacks.authorized({
request,
auth: { token, user },
expires,
})
: true
const authorized =
(await config.callbacks?.authorized?.({
request,
auth: { user },
expires,
})) ?? true
let response: Response = NextResponse.next()
if (authorized instanceof Response) {
// User returned a custom response, like redirecting to a page or 401, respect it
response = authorized
} else if (userMiddleware) {
// Execute user's middleware with the augmented request
} else if (userMiddlewareOrRoute) {
// Execute user's middleware/handler with the augmented request
const augmentedReq: NextAuthRequest = request as any
augmentedReq.auth = { token, user }
augmentedReq.auth = { user }
response =
(await userMiddleware(augmentedReq, args[1])) ?? NextResponse.next()
// @ts-expect-error
(await userMiddlewareOrRoute(augmentedReq, args[1])) ??
NextResponse.next()
} else if (!authorized) {
// Redirect to signin page by default if not authorized
// TODO: Support custom signin page
@@ -156,14 +156,14 @@ async function handleAuth(
response = NextResponse.redirect(request.nextUrl)
}
// We will update the session cookie if it exists,
// so that the session expiry is extended
// Preserve cookies set by Auth.js Core
const finalResponse = new NextResponse(response?.body, response)
// TODO: respect config/prefix/chunking etc.
const cookiePrefix = request.nextUrl.protocol === "https:" ? "__Secure-" : ""
const name = `${cookiePrefix}next-auth.session-token`
const val = request.cookies.get(name)?.value
// TODO: respect config/prefix/chunking etc.
if (val) finalResponse.cookies.set(name, val, { expires: new Date(expires!) })
if (authResponse.headers.has("set-cookie")) {
finalResponse.headers.set(
"set-cookie",
authResponse.headers.get("set-cookie")!
)
}
return finalResponse
}

View File

@@ -0,0 +1,16 @@
/**
* :::warning Deprecated
* This module is replaced in v5. Read more at: https://nextjs.authjs.dev/v5
* :::
*
* @module middleware
*/
throw new ReferenceError(
[
'"next-auth/middleware" is deprecated. If you are not ready to migrate, keep using "next-auth@4".',
"Read more on https://nextjs.authjs.dev/v5",
].join("\n")
)
export {}

View File

@@ -0,0 +1,59 @@
import { Auth } from "@auth/core"
import type { CallbacksOptions, Session } from "@auth/core/types"
import type {
GetServerSidePropsContext,
NextApiRequest,
NextApiResponse,
} from "next"
import { NextAuthConfig } from "./lib"
type GetServerSessionOptions = Partial<Omit<NextAuthConfig, "callbacks">> & {
callbacks?: Omit<NextAuthConfig["callbacks"], "session"> & {
session?: (...args: Parameters<CallbacksOptions["session"]>) => any
}
}
type GetServerSessionParams =
| [GetServerSidePropsContext["req"], GetServerSidePropsContext["res"]]
| [NextApiRequest, NextApiResponse]
| []
export function getServerSession(config: NextAuthConfig) {
return async <
O extends GetServerSessionOptions,
R = O["callbacks"] extends { session: (...args: any[]) => infer U }
? U
: Session
>(
...args: GetServerSessionParams
): Promise<R | null> => {
let req, res
req = args[0]
res = args[1]
const host = req.headers["x-forwarded-host"] ?? req.headers["host"]
const protocol =
req.headers["x-forwarded-proto"] === "http" ? "http" : "https"
const origin = `${protocol}://${host}`
const request = new Request(`${origin}/api/auth/session`, {
headers: req.headers,
})
const authResponse = await Auth(request, config)
const { status = 200 } = authResponse
const body = await authResponse.json()
authResponse.headers?.forEach((v, k) => {
if (k === "content-type") return
res.setHeader(k, v)
})
if (body && typeof body !== "string" && Object.keys(body).length) {
if (status === 200) return body as R
throw new Error((body as any).message)
}
return null
}
}

View File

@@ -43,6 +43,7 @@ export type {
SignOutParams,
}
export { SessionProviderProps }
// This behaviour mirrors the default behaviour for getting the site name that
// happens server side in server/index.js
// 1. An empty value is legitimate when the code is being invoked client side as
@@ -81,8 +82,12 @@ const logger: LoggerInstance = {
warn: console.warn,
}
type UpdateSession = (data?: any) => Promise<Session | null>
/** @todo Document */
export type UpdateSession = (data?: any) => Promise<Session | null>
/**
* useSession() returns an object containing three things: a method called {@link UpdateSession|update}, `data` and `status`.
*/
export type SessionContextValue<R extends boolean = false> = R extends true
?
| { update: UpdateSession; data: Session; status: "authenticated" }
@@ -100,8 +105,11 @@ export const SessionContext = React.createContext?.<
>(undefined)
/**
* React Hook that gives you access
* to the logged in user's session data.
* React Hook that gives you access to the logged in user's session data and lets you modify it.
*
* :::info
* You will likely not need `useSession` if you are using the [Next.js App Router (`app/`)](https://nextjs.org/blog/next-13-4#nextjs-app-router).
* :::
*/
export function useSession<R extends boolean>(
options?: UseSessionOptions<R>
@@ -317,9 +325,14 @@ export async function signOut<R extends boolean = true>(
}
/**
* Provider to wrap the app in to make session data available globally.
* Can also be used to throttle the number of requests to the endpoint
* `/api/auth/session`.
* [React Context](https://react.dev/learn/passing-data-deeply-with-context) provider to wrap the app (`pages/`) to make session data available anywhere.
*
* When used, the session state is automatically synchronized across all open tabs/windows and they are all updated whenever they gain or lose focus
* or the state changes (e.g. a user signs in or out) when {@link SessionProviderProps.refetchOnWindowFocus} is `true`.
*
* :::info
* You will likely not need `SessionProvider` if you are using the [Next.js App Router (`app/`)](https://nextjs.org/blog/next-13-4#nextjs-app-router).
* :::
*/
export function SessionProvider(props: SessionProviderProps) {
if (!SessionContext) {

View File

@@ -0,0 +1,29 @@
{
"compilerOptions": {
"isolatedModules": true,
"lib": [
"dom",
"esnext"
],
"jsx": "react-jsx",
"allowSyntheticDefaultImports": true,
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "node",
"outDir": ".",
"rootDir": "src",
"skipDefaultLibCheck": true,
"skipLibCheck": true,
"strictNullChecks": true,
"stripInternal": true,
"declarationMap": true,
"declaration": true
},
"include": [
"src/**/*"
],
"exclude": [
"*.js",
"*.d.ts",
]
}

View File

@@ -51,7 +51,7 @@ export async function signIn<
"Content-Type": "application/x-www-form-urlencoded",
"X-Auth-Return-Redirect": "1",
},
// @ts-expect-error -- ignore
// @ts-ignore
body: new URLSearchParams({
...options,
csrfToken,

View File

@@ -52,7 +52,7 @@ export async function signIn<
"Content-Type": "application/x-www-form-urlencoded",
"X-Auth-Return-Redirect": "1",
},
// @ts-expect-error -- ignore
// @ts-ignore
body: new URLSearchParams({
...options,
csrfToken,

View File

@@ -0,0 +1 @@
//registry.npmjs.org/:_authToken=${NPM_TOKEN}

View File

@@ -0,0 +1,254 @@
<p align="center">
<br/>
<a href="https://next-auth.js.org" target="_blank"><img width="150px" src="https://next-auth.js.org/img/logo/logo-sm.png" /></a>
<h3 align="center">NextAuth.js</h3>
<p align="center">Authentication for Next.js</p>
<p align="center">
Open Source. Full Stack. Own Your Data.
</p>
<p align="center" style="align: center;">
<a href="https://github.com/nextauthjs/next-auth/actions/workflows/release.yml?query=workflow%3ARelease">
<img src="https://github.com/nextauthjs/next-auth/actions/workflows/release.yml/badge.svg" alt="Release" />
</a>
<a href="https://packagephobia.com/result?p=next-auth">
<img src="https://packagephobia.com/badge?p=next-auth" alt="Bundle Size"/>
</a>
<a href="https://www.npmtrends.com/next-auth">
<img src="https://img.shields.io/npm/dm/next-auth" alt="Downloads" />
</a>
<a href="https://github.com/nextauthjs/next-auth/stargazers">
<img src="https://img.shields.io/github/stars/nextauthjs/next-auth" alt="Github Stars" />
</a>
<a href="https://www.npmjs.com/package/next-auth">
<img src="https://img.shields.io/github/v/release/nextauthjs/next-auth?label=latest" alt="Github Stable Release" />
</a>
</p>
</p>
## Overview
NextAuth.js is a complete open source authentication solution for [Next.js](http://nextjs.org/) applications.
It is designed from the ground up to support Next.js and Serverless.
This is a monorepo containing the following packages / projects:
1. The primary `next-auth` package
2. A development test application
3. All `@next-auth/*-adapter` packages
4. The documentation site
## Getting Started
```
npm install next-auth
```
The easiest way to continue getting started, is to follow the [getting started](https://next-auth.js.org/getting-started/example) section in our docs.
We also have a section of [tutorials](https://next-auth.js.org/tutorials) for those looking for more specific examples.
See [next-auth.js.org](https://next-auth.js.org) for more information and documentation.
## Features
### Flexible and easy to use
- Designed to work with any OAuth service, it supports OAuth 1.0, 1.0A and 2.0
- Built-in support for [many popular sign-in services](https://next-auth.js.org/providers)
- Supports email / passwordless authentication
- Supports stateless authentication with any backend (Active Directory, LDAP, etc)
- Supports both JSON Web Tokens and database sessions
- Designed for Serverless but runs anywhere (AWS Lambda, Docker, Heroku, etc…)
### Own your own data
NextAuth.js can be used with or without a database.
- An open source solution that allows you to keep control of your data
- Supports Bring Your Own Database (BYOD) and can be used with any database
- Built-in support for [MySQL, MariaDB, Postgres, Microsoft SQL Server, MongoDB and SQLite](https://next-auth.js.org/configuration/databases)
- Works great with databases from popular hosting providers
- Can also be used _without a database_ (e.g. OAuth + JWT)
### Secure by default
- Promotes the use of passwordless sign-in mechanisms
- Designed to be secure by default and encourage best practices for safeguarding user data
- Uses Cross-Site Request Forgery (CSRF) Tokens on POST routes (sign in, sign out)
- Default cookie policy aims for the most restrictive policy appropriate for each cookie
- When JSON Web Tokens are enabled, they are encrypted by default (JWE) with A256GCM
- Auto-generates symmetric signing and encryption keys for developer convenience
- Features tab/window syncing and session polling to support short lived sessions
- Attempts to implement the latest guidance published by [Open Web Application Security Project](https://owasp.org)
Advanced options allow you to define your own routines to handle controlling what accounts are allowed to sign in, for encoding and decoding JSON Web Tokens and to set custom cookie security policies and session properties, so you can control who is able to sign in and how often sessions have to be re-validated.
### TypeScript
NextAuth.js comes with built-in types. For more information and usage, check out
the [TypeScript section](https://next-auth.js.org/getting-started/typescript) in the documentation.
## Example
### Add API Route
```javascript
// pages/api/auth/[...nextauth].js
import NextAuth from "next-auth"
import AppleProvider from "next-auth/providers/apple"
import GoogleProvider from "next-auth/providers/google"
import EmailProvider from "next-auth/providers/email"
export default NextAuth({
secret: process.env.SECRET,
providers: [
// OAuth authentication providers
AppleProvider({
clientId: process.env.APPLE_ID,
clientSecret: process.env.APPLE_SECRET,
}),
GoogleProvider({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
}),
// Sign in with passwordless email link
EmailProvider({
server: process.env.MAIL_SERVER,
from: "<no-reply@example.com>",
}),
],
})
```
### Add React Hook
The `useSession()` React Hook in the NextAuth.js client is the easiest way to check if someone is signed in.
```javascript
import { useSession, signIn, signOut } from "next-auth/react"
export default function Component() {
const { data: session } = useSession()
if (session) {
return (
<>
Signed in as {session.user.email} <br />
<button onClick={() => signOut()}>Sign out</button>
</>
)
}
return (
<>
Not signed in <br />
<button onClick={() => signIn()}>Sign in</button>
</>
)
}
```
### Share/configure session state
Use the `<SessionProvider>` to allow instances of `useSession()` to share the session object across components. It also takes care of keeping the session updated and synced between tabs/windows.
```jsx title="pages/_app.js"
import { SessionProvider } from "next-auth/react"
export default function App({
Component,
pageProps: { session, ...pageProps },
}) {
return (
<SessionProvider session={session}>
<Component {...pageProps} />
</SessionProvider>
)
}
```
## Security
If you think you have found a vulnerability (or not sure) in NextAuth.js or any of the related packages (i.e. Adapters), we ask you to have a read of our [Security Policy](https://github.com/nextauthjs/next-auth/blob/main/SECURITY.md) to reach out responsibly. Please do not open Pull Requests/Issues/Discussions before consulting with us.
## Acknowledgments
[NextAuth.js is made possible thanks to all of its contributors.](https://next-auth.js.org/contributors)
<a href="https://github.com/nextauthjs/next-auth/graphs/contributors">
<img width="500px" src="https://contrib.rocks/image?repo=nextauthjs/next-auth" />
</a>
<div>
<a href="https://vercel.com?utm_source=nextauthjs&utm_campaign=oss"></a>
</div>
### Support
We're happy to announce we've recently created an [OpenCollective](https://opencollective.com/nextauth) for individuals and companies looking to contribute financially to the project!
<!--sponsors start-->
<table>
<tbody>
<tr>
<td align="center" valign="top">
<a href="https://vercel.com" target="_blank">
<img width="128px" src="https://avatars.githubusercontent.com/u/14985020?v=4" alt="Vercel Logo" />
</a><br />
<div>Vercel</div><br />
<sub>🥉 Bronze Financial Sponsor <br /> ☁️ Infrastructure Support</sub>
</td>
<td align="center" valign="top">
<a href="https://prisma.io" target="_blank">
<img width="128px" src="https://avatars.githubusercontent.com/u/17219288?v=4" alt="Prisma Logo" />
</a><br />
<div>Prisma</div><br />
<sub>🥉 Bronze Financial Sponsor</sub>
</td>
<td align="center" valign="top">
<a href="https://clerk.com" target="_blank">
<img width="128px" src="https://avatars.githubusercontent.com/u/49538330?s=200&v=4" alt="Clerk Logo" />
</a><br />
<div>Clerk</div><br />
<sub>🥉 Bronze Financial Sponsor</sub>
</td>
<td align="center" valign="top">
<a href="https://lowdefy.com" target="_blank">
<img width="128px" src="https://avatars.githubusercontent.com/u/47087496?s=200&v=4" alt="Lowdefy Logo" />
</a><br />
<div>Lowdefy</div><br />
<sub>🥉 Bronze Financial Sponsor</sub>
</td>
<td align="center" valign="top">
<a href="https://workos.com" target="_blank">
<img width="128px" src="https://avatars.githubusercontent.com/u/47638084?s=200&v=4" alt="WorkOS Logo" />
</a><br />
<div>WorkOS</div><br />
<sub>🥉 Bronze Financial Sponsor</sub>
</td>
<td align="center" valign="top">
<a href="https://checklyhq.com" target="_blank">
<img width="128px" src="https://avatars.githubusercontent.com/u/25982255?v=4" alt="Checkly Logo" />
</a><br />
<div>Checkly</div><br />
<sub>☁️ Infrastructure Support</sub>
</td>
<td align="center" valign="top">
<a href="https://superblog.ai/" target="_blank">
<img width="128px" src="https://d33wubrfki0l68.cloudfront.net/cdc4a3833bd878933fcc131655878dbf226ac1c5/10cd6/images/logo_bolt_small.png" alt="superblog Logo" />
</a><br />
<div>superblog</div><br />
<sub>☁️ Infrastructure Support</sub>
</td>
</tr><tr></tr>
</tbody>
</table>
<br />
<!--sponsors end-->
## Contributing
We're open to all community contributions! If you'd like to contribute in any way, please first read
our [Contributing Guide](https://github.com/nextauthjs/.github/blob/main/CONTRIBUTING.md).
## License
ISC

View File

@@ -0,0 +1,62 @@
// @ts-check
// We aim to have the same support as Next.js
// https://nextjs.org/docs/getting-started#system-requirements
// https://nextjs.org/docs/basic-features/supported-browsers-features
/** @type {import("@babel/core").ConfigFunction} */
module.exports = (api) => {
const isTest = api.env("test")
if (isTest) {
return {
presets: [
"@babel/preset-env",
["@babel/preset-react", { runtime: "automatic" }],
["@babel/preset-typescript", { isTSX: true, allExtensions: true }],
],
}
}
return {
presets: [
["@babel/preset-env", { targets: { node: 12 } }],
"@babel/preset-typescript",
],
plugins: [
"@babel/plugin-proposal-optional-catch-binding",
"@babel/plugin-transform-runtime",
],
ignore: [
"../src/**/__tests__/**",
"../src/adapters.ts",
"../src/providers/oauth-types.ts",
],
comments: false,
overrides: [
{
test: [
"../src/react/index.tsx",
"../src/utils/logger.ts",
"../src/core/errors.ts",
"../src/client/**",
],
presets: [
["@babel/preset-env", { targets: { ie: 11 } }],
["@babel/preset-react", { runtime: "automatic" }],
],
},
{
test: ["../src/core/pages/*.tsx"],
presets: ["preact"],
plugins: [
[
"jsx-pragmatic",
{
module: "preact",
export: "h",
import: "h",
},
],
],
},
],
}
}

View File

@@ -0,0 +1,18 @@
const path = require("path")
const fs = require("fs")
const providersPath = path.join(process.cwd(), "src/providers")
const files = fs.readdirSync(providersPath, "utf8")
const providers = files.map((file) => {
const strippedProviderName = file.substring(0, file.indexOf("."))
return `"${strippedProviderName}"`
})
const result = `
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
export type OAuthProviderType =
| ${providers.join("\n | ")}`
fs.writeFileSync(path.join(providersPath, "oauth-types.ts"), result)

View File

@@ -0,0 +1,3 @@
import "regenerator-runtime/runtime"
import "@testing-library/jest-dom"
import "whatwg-fetch"

View File

@@ -0,0 +1,43 @@
const swcConfig = require("./swc.config")
/** @type {import('jest').Config} */
module.exports = {
projects: [
{
displayName: "core",
testMatch: ["<rootDir>/tests/**/*.test.ts"],
rootDir: ".",
transform: {
"\\.(js|jsx|ts|tsx)$": ["@swc/jest", swcConfig],
},
coveragePathIgnorePatterns: ["tests"],
testEnvironment: "@edge-runtime/jest-environment",
transformIgnorePatterns: ["node_modules/(?!uuid)/"],
/** @type {import("@edge-runtime/vm").EdgeVMOptions} */
testEnvironmentOptions: {
codeGeneration: {
strings: true,
},
},
},
{
displayName: "client",
testMatch: ["<rootDir>/src/client/**/*.test.js"],
setupFilesAfterEnv: ["./config/jest-setup.js"],
rootDir: ".",
transform: {
"\\.(js|jsx|ts|tsx)$": ["@swc/jest", swcConfig],
},
testEnvironment: "jsdom",
coveragePathIgnorePatterns: ["__tests__"],
},
],
watchPlugins: [
"jest-watch-typeahead/filename",
"jest-watch-typeahead/testname",
],
collectCoverage: true,
coverageDirectory: "../coverage",
coverageReporters: ["html", "text-summary"],
collectCoverageFrom: ["src/**/*.(js|jsx|ts|tsx)"],
}

View File

@@ -0,0 +1,7 @@
module.exports = {
plugins: [
require('autoprefixer'),
require('postcss-nested'),
require('cssnano')({ preset: 'default' })
]
}

View File

@@ -0,0 +1,18 @@
/** @type {import("@swc/core").Config} */
module.exports = {
jsc: {
parser: {
syntax: "typescript",
tsx: true,
},
transform: {
react: {
runtime: "automatic",
pragma: "React.createElement",
pragmaFrag: "React.Fragment",
throwIfNamespace: true,
useBuiltins: true,
},
},
},
}

View File

@@ -0,0 +1,17 @@
// Serverless target in Next.js does not work if you try to read in files at runtime
// that are not JavaScript or JSON (e.g. CSS files).
// https://github.com/nextauthjs/next-auth/issues/281
//
// To work around this issue, this script is a manual step that wraps CSS in a
// JavaScript file that has the compiled CSS embedded in it, and exports only
// a function that returns the CSS as a string.
const fs = require("fs")
const path = require("path")
const pathToCss = path.join(__dirname, "../css/index.css")
const css = fs.readFileSync(pathToCss, "utf8")
const cssWithEscapedQuotes = css.replace(/"/gm, '\\"')
const js = `module.exports = function() { return "${cssWithEscapedQuotes}" }`
const pathToCssJs = path.join(__dirname, "../css/index.js")
fs.writeFileSync(pathToCssJs, js)

View File

@@ -2,7 +2,7 @@
"name": "next-auth",
"version": "4.22.1",
"description": "Authentication for Next.js",
"homepage": "https://nextjs.authjs.dev",
"homepage": "https://next-auth.js.org",
"repository": "https://github.com/nextauthjs/next-auth.git",
"author": "Iain Collins <me@iaincollins.com>",
"contributors": [
@@ -11,6 +11,9 @@
"Lluis Agusti <hi@llu.lu>",
"Thang Huu Vu <thvu@hey.com>"
],
"main": "index.js",
"module": "index.js",
"types": "index.d.ts",
"keywords": [
"react",
"nodejs",
@@ -23,59 +26,104 @@
"oidc",
"nextauth"
],
"type": "module",
"types": "./index.d.ts",
"exports": {
".": {
"types": "./index.d.ts",
"import": "./index.js"
},
"./adapters": {
"types": "./adapters.d.ts"
},
"./jwt": {
"types": "./jwt.d.ts",
"import": "./jwt.js"
},
"./middleware": {
"types": "./middleware.d.ts",
"import": "./middleware.js"
},
"./next": {
"types": "./next.d.ts",
"import": "./next.js"
},
"./providers": {
"types": "./providers.d.ts"
},
"./react": {
"types": "./react.d.ts",
"import": "./react.js"
},
"./package.json": "./package.json"
".": "./index.js",
"./jwt": "./jwt/index.js",
"./react": "./react/index.js",
"./core": "./core/index.js",
"./next": "./next/index.js",
"./middleware": "./middleware.js",
"./client/_utils": "./client/_utils.js",
"./providers/*": "./providers/*.js"
},
"scripts": {
"dev": "tsc -w",
"clean": "rm -rf *.js *.d.ts lib",
"build": "pnpm clean && tsc"
"build": "pnpm clean && pnpm build:js && pnpm build:css",
"build:js": "pnpm clean && pnpm generate-providers && pnpm tsc --project tsconfig.json && babel --config-file ./config/babel.config.js src --out-dir . --extensions \".tsx,.ts,.js,.jsx\"",
"clean": "rm -rf coverage client css utils providers core jwt react next index.d.ts index.js adapters.d.ts middleware.d.ts middleware.js",
"build:css": "postcss --config config/postcss.config.js src/**/*.css --base src --dir . && node config/wrap-css.js",
"dev": "pnpm clean && pnpm generate-providers && concurrently \"pnpm watch:css\" \"pnpm watch:ts\"",
"watch:ts": "pnpm tsc --project tsconfig.dev.json",
"watch:css": "postcss --config config/postcss.config.js --watch src/**/*.css --base src --dir .",
"test": "jest --config ./config/jest.config.js",
"prepublishOnly": "pnpm build",
"generate-providers": "node ./config/generate-providers.js",
"lint": "eslint src config tests"
},
"files": [
"*.js",
"*.d.ts",
"client",
"core",
"css",
"jwt",
"lib",
"src"
"next",
"providers",
"react",
"src",
"utils",
"*.d.ts*",
"*.js"
],
"devDependencies": {
"@types/react": "18.0.37",
"typescript": "^4",
"next": "13.3.3"
},
"license": "ISC",
"dependencies": {
"@auth/core": "workspace:*"
"@babel/runtime": "^7.20.13",
"@panva/hkdf": "^1.0.2",
"cookie": "^0.5.0",
"jose": "^4.11.4",
"oauth": "^0.9.15",
"openid-client": "^5.4.0",
"preact": "^10.6.3",
"preact-render-to-string": "^5.1.19",
"uuid": "^8.3.2"
},
"peerDependencies": {
"next": "^13.3.3",
"react": "^18.2.0"
"next": "^12.2.5 || ^13",
"nodemailer": "^6.6.5",
"react": "^17.0.2 || ^18",
"react-dom": "^17.0.2 || ^18"
},
"peerDependenciesMeta": {
"nodemailer": {
"optional": true
}
},
"devDependencies": {
"@babel/cli": "^7.17.10",
"@babel/core": "^7.18.2",
"@babel/plugin-proposal-optional-catch-binding": "^7.16.7",
"@babel/plugin-transform-runtime": "^7.18.2",
"@babel/preset-env": "^7.18.2",
"@babel/preset-react": "^7.17.12",
"@babel/preset-typescript": "^7.17.12",
"@edge-runtime/jest-environment": "1.1.0-beta.35",
"@next-auth/tsconfig": "workspace:*",
"@swc/core": "^1.2.198",
"@swc/jest": "^0.2.21",
"@testing-library/dom": "^8.13.0",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0",
"@testing-library/react-hooks": "^8.0.0",
"@testing-library/user-event": "^14.2.0",
"@types/jest": "^28.1.3",
"@types/node": "^17.0.42",
"@types/nodemailer": "^6.4.4",
"@types/oauth": "^0.9.1",
"@types/react": "18.0.37",
"@types/react-dom": "^18.0.6",
"autoprefixer": "^10.4.7",
"babel-plugin-jsx-pragmatic": "^1.0.2",
"babel-preset-preact": "^2.0.0",
"concurrently": "^7",
"cssnano": "^5.1.11",
"jest": "^28.1.1",
"jest-environment-jsdom": "^28.1.1",
"jest-watch-typeahead": "^1.1.0",
"msw": "^0.42.3",
"next": "13.3.0",
"postcss": "^8.4.14",
"postcss-cli": "^9.1.0",
"postcss-nested": "^5.0.6",
"react": "^18",
"react-dom": "^18",
"whatwg-fetch": "^3.6.2"
}
}

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="4 32 376.4 449.4" width="32" height="32">
<title>Apple icon</title>
<path fill="#fff" d="M318.7 268.7c-.2-36.7 16.4-64.4 50-84.8-18.8-26.9-47.2-41.7-84.7-44.6-35.5-2.8-74.3 20.7-88.5 20.7-15 0-49.4-19.7-76.4-19.7C63.3 141.2 4 184.8 4 273.5q0 39.3 14.4 81.2c12.8 36.7 59 126.7 107.2 125.2 25.2-.6 43-17.9 75.8-17.9 31.8 0 48.3 17.9 76.4 17.9 48.6-.7 90.4-82.5 102.6-119.3-65.2-30.7-61.7-90-61.7-91.9zm-56.6-164.2c27.3-32.4 24.8-61.9 24-72.5-24.1 1.4-52 16.4-67.9 34.9-17.5 19.8-27.8 44.3-25.6 71.9 26.1 2 49.9-11.4 69.5-34.3z"/>
</svg>

After

Width:  |  Height:  |  Size: 588 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="4 32 376.4 449.4" width="32" height="32">
<title>Apple icon</title>
<path d="M318.7 268.7c-.2-36.7 16.4-64.4 50-84.8-18.8-26.9-47.2-41.7-84.7-44.6-35.5-2.8-74.3 20.7-88.5 20.7-15 0-49.4-19.7-76.4-19.7C63.3 141.2 4 184.8 4 273.5q0 39.3 14.4 81.2c12.8 36.7 59 126.7 107.2 125.2 25.2-.6 43-17.9 75.8-17.9 31.8 0 48.3 17.9 76.4 17.9 48.6-.7 90.4-82.5 102.6-119.3-65.2-30.7-61.7-90-61.7-91.9zm-56.6-164.2c27.3-32.4 24.8-61.9 24-72.5-24.1 1.4-52 16.4-67.9 34.9-17.5 19.8-27.8 44.3-25.6 71.9 26.1 2 49.9-11.4 69.5-34.3z"/>
</svg>

After

Width:  |  Height:  |  Size: 576 B

View File

@@ -0,0 +1,8 @@
<svg viewBox="0.29136862699701993 -41.138268758326056 145.22149045698177 186.73799623391153" xmlns="http://www.w3.org/2000/svg" width="32" height="32">
<linearGradient id="a" gradientTransform="matrix(1 0 0 -1 0 228)" gradientUnits="userSpaceOnUse" x1="62.57" x2="25.03" y1="150.13" y2="85.11">
<stop offset="0" stop-color="#0052cc"/>
<stop offset=".92" stop-color="#2684ff"/>
</linearGradient>
<path d="M43 67a4.14 4.14 0 0 0-5.79-.78A4.29 4.29 0 0 0 36 67.73L.45 138.85a4.25 4.25 0 0 0 1.9 5.7 4.18 4.18 0 0 0 1.9.45h49.53a4.08 4.08 0 0 0 3.8-2.35C68.27 120.57 61.79 87 43 67z" fill="url(#a)"/>
<path d="M69.13 2.28a93.82 93.82 0 0 0-5.48 92.61l23.88 47.76a4.25 4.25 0 0 0 3.8 2.35h49.52a4.24 4.24 0 0 0 4.25-4.25 4.31 4.31 0 0 0-.44-1.9L76.36 2.26a4 4 0 0 0-7.23 0z" fill="#2684ff"/>
</svg>

After

Width:  |  Height:  |  Size: 810 B

View File

@@ -0,0 +1,4 @@
<svg viewBox="0.29136862699701993 -41.138268758326056 145.22149045698177 186.73799623391153" xmlns="http://www.w3.org/2000/svg" width="32" height="32">
<path d="M43 67a4.14 4.14 0 0 0-5.79-.78A4.29 4.29 0 0 0 36 67.73L.45 138.85a4.25 4.25 0 0 0 1.9 5.7 4.18 4.18 0 0 0 1.9.45h49.53a4.08 4.08 0 0 0 3.8-2.35C68.27 120.57 61.79 87 43 67z" fill="#fff"/>
<path d="M69.13 2.28a93.82 93.82 0 0 0-5.48 92.61l23.88 47.76a4.25 4.25 0 0 0 3.8 2.35h49.52a4.24 4.24 0 0 0 4.25-4.25 4.31 4.31 0 0 0-.44-1.9L76.36 2.26a4 4 0 0 0-7.23 0z" fill="#fff"/>
</svg>

After

Width:  |  Height:  |  Size: 549 B

View File

@@ -0,0 +1,12 @@
<svg width="32" height="32" viewBox="0 0 41 45" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0)">
<path d="M35.3018 0H20.5L25.0737 14.076H39.8755L27.9009 22.4701L32.4746 36.6253C40.1827 31.081 42.7027 22.6883 39.8755 14.076L35.3018 0Z" fill="white"/>
<path d="M1.12504 14.076H15.9268L20.5005 0H5.69875L1.12504 14.076C-1.70213 22.6898 0.8178 31.081 8.52592 36.6253L13.0996 22.4701L1.12504 14.076Z" fill="white"/>
<path d="M8.52539 36.6251L20.5 44.9998L32.4746 36.6251L20.5 28.1084L8.52539 36.6251Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0">
<rect width="41" height="45" fill="none"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 677 B

View File

@@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 256 287" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet">
<path d="M203.24 231.531l-28.73-88.434 75.208-54.64h-92.966L128.019.025l-.009-.024h92.98l28.74 88.446.002-.002.024-.013c16.69 51.31-.5 109.67-46.516 143.098zm-150.45 0l-.023.017 75.228 54.655 75.245-54.67-75.221-54.656-75.228 54.654zM6.295 88.434c-17.57 54.088 2.825 111.4 46.481 143.108l.007-.028 28.735-88.429-75.192-54.63h92.944L128.004.024 128.01 0H35.025L6.294 88.434z" fill="#EB5424"/>
</svg>

After

Width:  |  Height:  |  Size: 523 B

View File

@@ -0,0 +1,3 @@
<svg viewBox="0 0 59.242 47.271" width="32" height="32" xmlns="http://www.w3.org/2000/svg">
<path d="m32.368 0-17.468 15.145-14.9 26.75h13.437zm2.323 3.543-7.454 21.008 14.291 17.956-27.728 4.764h45.442z" fill="#fff"/>
</svg>

After

Width:  |  Height:  |  Size: 228 B

View File

@@ -0,0 +1,3 @@
<svg viewBox="0 0 59.242 47.271" width="32" height="32" xmlns="http://www.w3.org/2000/svg">
<path d="m32.368 0-17.468 15.145-14.9 26.75h13.437zm2.323 3.543-7.454 21.008 14.291 17.956-27.728 4.764h45.442z" fill="#0072c6"/>
</svg>

After

Width:  |  Height:  |  Size: 231 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 580.44" width="32" height="32">
<path d="M473.49,197.63c-75.94-35.11-185.08-57.42-287.78-49.11,5.15-34,17.85-57.69,38.7-62.68,28.69-6.88,60,12,89.84,46.35,19.55,2.53,42.73,7,58.86,10.71C318.7,40.56,245.72-16.8,190.21,4.36,148,20.47,126.39,78.59,129,156.69c-55,11.7-97.87,32.49-125.31,62.39-1.39,1.61-4.53,5.67-3.41,7.61.85,1.47,3.65-.18,4.85-1,31.83-22.26,72.58-34.31,125.66-41.89,7.56,83.32,42.81,189,101.36,273.78-32,12.56-58.89,13.39-73.64-2.17-20.29-21.41-19.61-57.95-4.77-101-7.58-18.2-15.31-40.51-20.15-56.34C72.12,396.41,58.93,488.29,105,525.78c35.07,28.52,96.18,18.15,162.54-23.12,37.64,41.79,77.07,68.51,116.69,77.33,2.09.39,7.17,1.09,8.29-.85.85-1.47-2-3.08-3.28-3.71-35.19-16.44-66-45.71-99.1-87.88C358.53,439.34,432.42,356,476.57,262.88c26.9,21.47,41,44.3,34.94,64.85-8.4,28.29-40.38,46-85.06,54.63C414.48,398,399,415.86,387.74,428c115.84,4,202-30.47,211.43-89.12,7.17-44.64-32.37-92.38-101.3-129.21,17.38-53.49,20.8-101,8.63-139.72-.71-2-2.64-6.76-4.88-6.76-1.7,0-1.68,3.26-1.58,4.7C503.41,106.55,493.47,147.88,473.49,197.63ZM260.21,444.33c-49-78.61-77.24-171.21-77.06-264.84h0C275.71,176.39,370,198.2,451,245.17h0c-43.59,81.71-109.64,152.49-190.82,199.15Z" fill="#fff"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 580.44" width="32" height="32">
<path d="M473.49,197.63c-75.94-35.11-185.08-57.42-287.78-49.11,5.15-34,17.85-57.69,38.7-62.68,28.69-6.88,60,12,89.84,46.35,19.55,2.53,42.73,7,58.86,10.71C318.7,40.56,245.72-16.8,190.21,4.36,148,20.47,126.39,78.59,129,156.69c-55,11.7-97.87,32.49-125.31,62.39-1.39,1.61-4.53,5.67-3.41,7.61.85,1.47,3.65-.18,4.85-1,31.83-22.26,72.58-34.31,125.66-41.89,7.56,83.32,42.81,189,101.36,273.78-32,12.56-58.89,13.39-73.64-2.17-20.29-21.41-19.61-57.95-4.77-101-7.58-18.2-15.31-40.51-20.15-56.34C72.12,396.41,58.93,488.29,105,525.78c35.07,28.52,96.18,18.15,162.54-23.12,37.64,41.79,77.07,68.51,116.69,77.33,2.09.39,7.17,1.09,8.29-.85.85-1.47-2-3.08-3.28-3.71-35.19-16.44-66-45.71-99.1-87.88C358.53,439.34,432.42,356,476.57,262.88c26.9,21.47,41,44.3,34.94,64.85-8.4,28.29-40.38,46-85.06,54.63C414.48,398,399,415.86,387.74,428c115.84,4,202-30.47,211.43-89.12,7.17-44.64-32.37-92.38-101.3-129.21,17.38-53.49,20.8-101,8.63-139.72-.71-2-2.64-6.76-4.88-6.76-1.7,0-1.68,3.26-1.58,4.7C503.41,106.55,493.47,147.88,473.49,197.63ZM260.21,444.33c-49-78.61-77.24-171.21-77.06-264.84h0C275.71,176.39,370,198.2,451,245.17h0c-43.59,81.71-109.64,152.49-190.82,199.15Z" fill="#148eff"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 444.893 245.414">
<g fill="#fff">
<path d="M239.038 72.43c-33.081 0-61.806 18.6-76.322 45.904-14.516-27.305-43.24-45.902-76.32-45.902-19.443 0-37.385 6.424-51.821 17.266V16.925h-.008C34.365 7.547 26.713 0 17.286 0 7.858 0 .208 7.547.008 16.925H0v143.333h.036c.768 47.051 39.125 84.967 86.359 84.967 33.08 0 61.805-18.603 76.32-45.908 14.517 27.307 43.241 45.906 76.321 45.906 47.715 0 86.396-38.684 86.396-86.396.001-47.718-38.682-86.397-86.394-86.397zM86.395 210.648c-28.621 0-51.821-23.201-51.821-51.82 0-28.623 23.201-51.823 51.821-51.823 28.621 0 51.822 23.2 51.822 51.823 0 28.619-23.201 51.82-51.822 51.82zm152.643 0c-28.622 0-51.821-23.201-51.821-51.822 0-28.623 23.2-51.821 51.821-51.821 28.619 0 51.822 23.198 51.822 51.821-.001 28.621-23.203 51.822-51.822 51.822z"/>
<path d="M441.651 218.033l-44.246-59.143 44.246-59.144-.008-.007c5.473-7.62 3.887-18.249-3.652-23.913-7.537-5.658-18.187-4.221-23.98 3.157l-.004-.002-38.188 51.047-38.188-51.047-.006.009c-5.793-7.385-16.441-8.822-23.981-3.16-7.539 5.664-9.125 16.293-3.649 23.911l-.008.005 44.245 59.144-44.245 59.143.008.005c-5.477 7.62-3.89 18.247 3.649 23.909 7.54 5.664 18.188 4.225 23.981-3.155l.006.007 38.188-51.049 38.188 51.049.004-.002c5.794 7.377 16.443 8.814 23.98 3.154 7.539-5.662 9.125-16.291 3.652-23.91l.008-.008z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 444.893 245.414">
<g fill="#0075C9">
<path d="M239.038 72.43c-33.081 0-61.806 18.6-76.322 45.904-14.516-27.305-43.24-45.902-76.32-45.902-19.443 0-37.385 6.424-51.821 17.266V16.925h-.008C34.365 7.547 26.713 0 17.286 0 7.858 0 .208 7.547.008 16.925H0v143.333h.036c.768 47.051 39.125 84.967 86.359 84.967 33.08 0 61.805-18.603 76.32-45.908 14.517 27.307 43.241 45.906 76.321 45.906 47.715 0 86.396-38.684 86.396-86.396.001-47.718-38.682-86.397-86.394-86.397zM86.395 210.648c-28.621 0-51.821-23.201-51.821-51.82 0-28.623 23.201-51.823 51.821-51.823 28.621 0 51.822 23.2 51.822 51.823 0 28.619-23.201 51.82-51.822 51.82zm152.643 0c-28.622 0-51.821-23.201-51.821-51.822 0-28.623 23.2-51.821 51.821-51.821 28.619 0 51.822 23.198 51.822 51.821-.001 28.621-23.203 51.822-51.822 51.822z"/>
<path d="M441.651 218.033l-44.246-59.143 44.246-59.144-.008-.007c5.473-7.62 3.887-18.249-3.652-23.913-7.537-5.658-18.187-4.221-23.98 3.157l-.004-.002-38.188 51.047-38.188-51.047-.006.009c-5.793-7.385-16.441-8.822-23.981-3.16-7.539 5.664-9.125 16.293-3.649 23.911l-.008.005 44.245 59.144-44.245 59.143.008.005c-5.477 7.62-3.89 18.247 3.649 23.909 7.54 5.664 18.188 4.225 23.981-3.155l.006.007 38.188-51.049 38.188 51.049.004-.002c5.794 7.377 16.443 8.814 23.98 3.154 7.539-5.662 9.125-16.291 3.652-23.91l.008-.008z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,10 @@
<svg width="32" height="32" viewBox="0 0 256 299" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid">
<path d="M208.752 58.061l25.771-6.636.192.283.651 155.607-.843.846-5.31.227-20.159-3.138-.302-.794V58.061M59.705 218.971l.095.007 68.027 19.767.173.133.296.236-.096 59.232-.2.252-68.295-33.178v-46.449" fill="#7A3E65"/>
<path d="M208.752 204.456l-80.64 19.312-40.488-9.773-27.919 4.976L128 238.878l105.405-28.537 1.118-2.18-25.771-3.705" fill="#CFB2C1"/>
<path d="M196.295 79.626l-.657-.749-66.904-19.44-.734.283-.672-.343L22.052 89.734l-.575.703.845.463 24.075 3.53.851-.289 80.64-19.311 40.488 9.773 27.919-4.977" fill="#512843"/>
<path d="M47.248 240.537l-25.771 6.221-.045-.149-1.015-155.026 1.06-1.146 25.771 3.704v146.396" fill="#C17B9E"/>
<path d="M82.04 180.403l45.96 5.391.345-.515.187-71.887-.532-.589-45.96 5.392v62.208" fill="#7A3E65"/>
<path d="M173.96 180.403L128 185.794v-72.991l45.96 5.392v62.208M196.295 79.626L128 59.72V0l68.295 33.177v46.449" fill="#C17B9E"/>
<path d="M128 0L0 61.793v175.011l21.477 9.954V90.437L128 59.72V0" fill="#7A3E65"/>
<path d="M234.523 51.425v156.736L128 238.878v59.72l128-61.794V61.793l-21.477-10.368" fill="#C17B9E"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 256 293" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid">
<path d="M226.011 0H29.99C13.459 0 0 13.458 0 30.135v197.778c0 16.677 13.458 30.135 29.989 30.135h165.888l-7.754-27.063 18.725 17.408 17.7 16.384L256 292.571V30.135C256 13.458 242.542 0 226.011 0zm-56.466 191.05s-5.266-6.291-9.655-11.85c19.164-5.413 26.478-17.408 26.478-17.408-5.998 3.95-11.703 6.73-16.823 8.63-7.314 3.073-14.336 5.12-21.211 6.291-14.044 2.633-26.917 1.902-37.888-.146-8.339-1.61-15.507-3.95-21.504-6.29-3.365-1.317-7.022-2.926-10.68-4.974-.438-.293-.877-.439-1.316-.732-.292-.146-.439-.292-.585-.438-2.633-1.463-4.096-2.487-4.096-2.487s7.022 11.703 25.6 17.261c-4.388 5.56-9.801 12.142-9.801 12.142-32.33-1.024-44.617-22.235-44.617-22.235 0-47.104 21.065-85.285 21.065-85.285 21.065-15.799 41.106-15.36 41.106-15.36l1.463 1.756C80.75 77.53 68.608 89.088 68.608 89.088s3.218-1.755 8.63-4.242c15.653-6.876 28.088-8.777 33.208-9.216.877-.147 1.609-.293 2.487-.293a123.776 123.776 0 0 1 29.55-.292c13.896 1.609 28.818 5.705 44.031 14.043 0 0-11.556-10.971-36.425-18.578l2.048-2.34s20.041-.44 41.106 15.36c0 0 21.066 38.18 21.066 85.284 0 0-12.435 21.211-44.764 22.235zm-68.023-68.316c-8.338 0-14.92 7.314-14.92 16.237 0 8.924 6.728 16.238 14.92 16.238 8.339 0 14.921-7.314 14.921-16.238.147-8.923-6.582-16.237-14.92-16.237m53.394 0c-8.339 0-14.922 7.314-14.922 16.237 0 8.924 6.73 16.238 14.922 16.238 8.338 0 14.92-7.314 14.92-16.238 0-8.923-6.582-16.237-14.92-16.237" fill="#fff"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 256 293" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid">
<path d="M226.011 0H29.99C13.459 0 0 13.458 0 30.135v197.778c0 16.677 13.458 30.135 29.989 30.135h165.888l-7.754-27.063 18.725 17.408 17.7 16.384L256 292.571V30.135C256 13.458 242.542 0 226.011 0zm-56.466 191.05s-5.266-6.291-9.655-11.85c19.164-5.413 26.478-17.408 26.478-17.408-5.998 3.95-11.703 6.73-16.823 8.63-7.314 3.073-14.336 5.12-21.211 6.291-14.044 2.633-26.917 1.902-37.888-.146-8.339-1.61-15.507-3.95-21.504-6.29-3.365-1.317-7.022-2.926-10.68-4.974-.438-.293-.877-.439-1.316-.732-.292-.146-.439-.292-.585-.438-2.633-1.463-4.096-2.487-4.096-2.487s7.022 11.703 25.6 17.261c-4.388 5.56-9.801 12.142-9.801 12.142-32.33-1.024-44.617-22.235-44.617-22.235 0-47.104 21.065-85.285 21.065-85.285 21.065-15.799 41.106-15.36 41.106-15.36l1.463 1.756C80.75 77.53 68.608 89.088 68.608 89.088s3.218-1.755 8.63-4.242c15.653-6.876 28.088-8.777 33.208-9.216.877-.147 1.609-.293 2.487-.293a123.776 123.776 0 0 1 29.55-.292c13.896 1.609 28.818 5.705 44.031 14.043 0 0-11.556-10.971-36.425-18.578l2.048-2.34s20.041-.44 41.106 15.36c0 0 21.066 38.18 21.066 85.284 0 0-12.435 21.211-44.764 22.235zm-68.023-68.316c-8.338 0-14.92 7.314-14.92 16.237 0 8.924 6.728 16.238 14.92 16.238 8.339 0 14.921-7.314 14.921-16.238.147-8.923-6.582-16.237-14.92-16.237m53.394 0c-8.339 0-14.922 7.314-14.922 16.237 0 8.924 6.73 16.238 14.922 16.238 8.338 0 14.92-7.314 14.92-16.238 0-8.923-6.582-16.237-14.92-16.237" fill="#7289DA"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,4 @@
<svg clip-rule="evenodd" fill-rule="evenodd" width="32" height="32" image-rendering="optimizeQuality" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" viewBox="6702.77 18309.17 6561.66 6561.660000000007" xmlns="http://www.w3.org/2000/svg">
<path d="M9983.6 18309.17c1811.95 0 3280.83 1468.88 3280.83 3280.83s-1468.88 3280.83-3280.83 3280.83S6702.77 23401.95 6702.77 21590s1468.88-3280.83 3280.83-3280.83z" fill="#fff"/>
<path d="M10409.89 24843.29v-2534.17h714.43l94.7-891.91h-809.13l1.2-446.44c0-232.63 22.1-357.22 356.24-357.22h446.68v-892.06h-714.59c-858.35 0-1160.42 432.65-1160.42 1160.34v535.45h-535.07v891.99H9339v2498.09c208.45 41.53 423.95 63.47 644.6 63.47a3310.9 3310.9 0 0 0 426.29-27.54z" fill="#006aff" fill-rule="nonzero"/>
</svg>

After

Width:  |  Height:  |  Size: 774 B

View File

@@ -0,0 +1,8 @@
<svg clip-rule="evenodd" fill-rule="evenodd" width="32" height="32" image-rendering="optimizeQuality" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" viewBox="6702.77 18309.17 6561.66 6561.660000000007" xmlns="http://www.w3.org/2000/svg">
<linearGradient id="a" gradientUnits="userSpaceOnUse" x1="9983.6" x2="9983.6" y1="18249.39" y2="25150.62">
<stop offset="0" stop-color="#00b2ff"/>
<stop offset="1" stop-color="#006aff"/>
</linearGradient>
<path d="M9983.6 18309.17c1811.95 0 3280.83 1468.88 3280.83 3280.83s-1468.88 3280.83-3280.83 3280.83S6702.77 23401.95 6702.77 21590s1468.88-3280.83 3280.83-3280.83z" fill="url(#a)"/>
<path d="M10409.89 24843.29v-2534.17h714.43l94.7-891.91h-809.13l1.2-446.44c0-232.63 22.1-357.22 356.24-357.22h446.68v-892.06h-714.59c-858.35 0-1160.42 432.65-1160.42 1160.34v535.45h-535.07v891.99H9339v2498.09c208.45 41.53 423.95 63.47 644.6 63.47a3310.9 3310.9 0 0 0 426.29-27.54z" fill="#fff" fill-rule="nonzero"/>
</svg>

After

Width:  |  Height:  |  Size: 991 B

View File

@@ -0,0 +1,18 @@
<svg width="32" height="32" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 275.9 275.9" style="enable-background:new 0 0 275.9 275.9;">
<style type="text/css">
.st0{enable-background:new ;}
.st1{fill:#FFFFFF;}
</style>
<rect x="0.1" y="0.3" width="275.4" height="275.6"/>
<g class="st0">
<path class="st1" d="M69.2,50.1H121v8.5H77.8v23.8h38.4v8.5H77.8v34.4h-8.6C69.2,125.2,69.2,50.1,69.2,50.1z"/>
<path class="st1" d="M67.7,198.8l8.4-1.9c1.5,10.4,8.7,16.6,20.4,16.6c10.5,0,18.8-4.9,18.8-12.9c0-5.7-4.2-10.5-20.6-15.4
c-18.6-5.3-24.8-12.1-24.8-22.1c0-12.9,10.4-19.8,25.5-19.8c16.9,0,24.4,8.6,27,20.4l-8.4,1.9c-2.1-9.7-8.7-13.8-18.9-13.8
c-9.6,0-16.4,3.6-16.4,10.5c0,5.6,4.4,9.9,19.6,14.7c18.1,5.6,25.9,11.7,25.9,23.3c0,14.4-12.1,21.9-27.5,21.9
C80.6,222.1,69.6,213.8,67.7,198.8z"/>
<path class="st1" d="M134.5,182.9c0-22.3,14.6-39.7,37-39.7c22.3,0,36.7,17.5,36.7,39.7c0,11.1-3.7,20.7-9.9,27.7
c3,3,5.9,6,8.8,9.2l-6.2,6c-3-3.2-6.1-6.4-9.2-9.5c-5.7,3.7-12.5,5.8-20.2,5.8C149.9,222.1,134.5,205.6,134.5,182.9z M185.2,209.9
c-2.9-2.8-5.8-5.5-8.8-8.1l6-6.1c3.2,2.8,6.4,5.7,9.4,8.6c4.5-5.4,7.2-12.8,7.2-21.5c0-17.6-10.7-31-27.5-31s-27.6,13.4-27.6,31
c0,18.1,11.7,30.8,27.6,30.8C176.6,213.6,181.2,212.4,185.2,209.9z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,18 @@
<svg width="32" height="32" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 275.9 275.9" style="enable-background:new 0 0 275.9 275.9;">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{enable-background:new ;}
</style>
<rect x="0.1" y="0.3" class="st0" width="275.4" height="275.6"/>
<g class="st1">
<path d="M69.2,50.1H121v8.5H77.8v23.8h38.4v8.5H77.8v34.4h-8.6C69.2,125.2,69.2,50.1,69.2,50.1z"/>
<path d="M67.7,198.8l8.4-1.9c1.5,10.4,8.7,16.6,20.4,16.6c10.5,0,18.8-4.9,18.8-12.9c0-5.7-4.2-10.5-20.6-15.4
c-18.6-5.3-24.8-12.1-24.8-22.1c0-12.9,10.4-19.8,25.5-19.8c16.9,0,24.4,8.6,27,20.4l-8.4,1.9c-2.1-9.7-8.7-13.8-18.9-13.8
c-9.6,0-16.4,3.6-16.4,10.5c0,5.6,4.4,9.9,19.6,14.7c18.1,5.6,25.9,11.7,25.9,23.3c0,14.4-12.1,21.9-27.5,21.9
C80.6,222.1,69.6,213.8,67.7,198.8z"/>
<path d="M134.5,182.9c0-22.3,14.6-39.7,37-39.7c22.3,0,36.7,17.5,36.7,39.7c0,11.1-3.7,20.7-9.9,27.7c3,3,5.9,6,8.8,9.2l-6.2,6
c-3-3.2-6.1-6.4-9.2-9.5c-5.7,3.7-12.5,5.8-20.2,5.8C149.9,222.1,134.5,205.6,134.5,182.9z M185.2,209.9c-2.9-2.8-5.8-5.5-8.8-8.1
l6-6.1c3.2,2.8,6.4,5.7,9.4,8.6c4.5-5.4,7.2-12.8,7.2-21.5c0-17.6-10.7-31-27.5-31s-27.6,13.4-27.6,31c0,18.1,11.7,30.8,27.6,30.8
C176.6,213.6,181.2,212.4,185.2,209.9z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox=".99522558 .9999996 253.69877442 253.6940004" xmlns="http://www.w3.org/2000/svg">
<path d="m107.948 1a106.948 106.948 0 0 0 -106.948 106.966v146.728h146.727c59.067 0 106.955-47.88 106.967-106.948v-146.746zm86.724 43.635a34.6 34.6 0 0 1 -10.164 24.51 34.768 34.768 0 0 1 -24.562 10.152h-37.242v29.73h50.314v34.796h-50.065v71.663h-41.233v-179.891h40.983v32.625c1.072-18.32 16.275-32.625 34.668-32.625h37.358z" fill="#fff"/>
</svg>

After

Width:  |  Height:  |  Size: 467 B

View File

@@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox=".99522558 .9999996 253.69877442 253.6940004" xmlns="http://www.w3.org/2000/svg">
<path d="m107.948 1a106.948 106.948 0 0 0 -106.948 106.966v146.728h146.727c59.067 0 106.955-47.88 106.967-106.948v-146.746zm86.724 43.635a34.6 34.6 0 0 1 -10.164 24.51 34.768 34.768 0 0 1 -24.562 10.152h-37.242v29.73h50.314v34.796h-50.065v71.663h-41.233v-179.891h40.983v32.625c1.072-18.32 16.275-32.625 34.668-32.625h37.358z" fill="#0075dd"/>
</svg>

After

Width:  |  Height:  |  Size: 470 B

View File

@@ -0,0 +1,4 @@
<svg width="32" height="32" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>GitHub icon</title>
<path fill="#fff" d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/>
</svg>

After

Width:  |  Height:  |  Size: 859 B

View File

@@ -0,0 +1,4 @@
<svg width="32" height="32" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>GitHub dark icon</title>
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/>
</svg>

After

Width:  |  Height:  |  Size: 852 B

View File

@@ -0,0 +1,8 @@
<svg width="32" height="32" xmlns="http://www.w3.org/2000/svg" viewBox="93.97 97.52 192.05 184.95">
<defs>
<style>.cls-1{fill:#fff;}</style>
</defs>
<g id="LOGO">
<path class="cls-1" d="M282.83,170.73l-.27-.69-26.14-68.22a6.81,6.81,0,0,0-2.69-3.24,7,7,0,0,0-8,.43,7,7,0,0,0-2.32,3.52l-17.65,54H154.29l-17.65-54A6.86,6.86,0,0,0,134.32,99a7,7,0,0,0-8-.43,6.87,6.87,0,0,0-2.69,3.24L97.44,170l-.26.69a48.54,48.54,0,0,0,16.1,56.1l.09.07.24.17,39.82,29.82,19.7,14.91,12,9.06a8.07,8.07,0,0,0,9.76,0l12-9.06,19.7-14.91,40.06-30,.1-.08A48.56,48.56,0,0,0,282.83,170.73Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 591 B

View File

@@ -0,0 +1,11 @@
<svg width="32" height="32" xmlns="http://www.w3.org/2000/svg" viewBox="93.97 97.52 192.05 184.99">
<defs>
<style>.cls-1{fill:#e24329;}.cls-2{fill:#fc6d26;}.cls-3{fill:#fca326;}</style>
</defs>
<g>
<path class="cls-1" d="M282.83,170.73l-.27-.69-26.14-68.22a6.81,6.81,0,0,0-2.69-3.24,7,7,0,0,0-8,.43,7,7,0,0,0-2.32,3.52l-17.65,54H154.29l-17.65-54A6.86,6.86,0,0,0,134.32,99a7,7,0,0,0-8-.43,6.87,6.87,0,0,0-2.69,3.24L97.44,170l-.26.69a48.54,48.54,0,0,0,16.1,56.1l.09.07.24.17,39.82,29.82,19.7,14.91,12,9.06a8.07,8.07,0,0,0,9.76,0l12-9.06,19.7-14.91,40.06-30,.1-.08A48.56,48.56,0,0,0,282.83,170.73Z"/>
<path class="cls-2" d="M282.83,170.73l-.27-.69a88.3,88.3,0,0,0-35.15,15.8L190,229.25c19.55,14.79,36.57,27.64,36.57,27.64l40.06-30,.1-.08A48.56,48.56,0,0,0,282.83,170.73Z"/>
<path class="cls-3" d="M153.43,256.89l19.7,14.91,12,9.06a8.07,8.07,0,0,0,9.76,0l12-9.06,19.7-14.91S209.55,244,190,229.25C170.45,244,153.43,256.89,153.43,256.89Z"/>
<path class="cls-2" d="M132.58,185.84A88.19,88.19,0,0,0,97.44,170l-.26.69a48.54,48.54,0,0,0,16.1,56.1l.09.07.24.17,39.82,29.82s17-12.85,36.57-27.64Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,7 @@
<svg width="32" height="32" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>Google icon</title>
<path fill="#EA4335 " d="M5.26620003,9.76452941 C6.19878754,6.93863203 8.85444915,4.90909091 12,4.90909091 C13.6909091,4.90909091 15.2181818,5.50909091 16.4181818,6.49090909 L19.9090909,3 C17.7818182,1.14545455 15.0545455,0 12,0 C7.27006974,0 3.1977497,2.69829785 1.23999023,6.65002441 L5.26620003,9.76452941 Z"/>
<path fill="#34A853" d="M16.0407269,18.0125889 C14.9509167,18.7163016 13.5660892,19.0909091 12,19.0909091 C8.86648613,19.0909091 6.21911939,17.076871 5.27698177,14.2678769 L1.23746264,17.3349879 C3.19279051,21.2936293 7.26500293,24 12,24 C14.9328362,24 17.7353462,22.9573905 19.834192,20.9995801 L16.0407269,18.0125889 Z"/>
<path fill="#4A90E2" d="M19.834192,20.9995801 C22.0291676,18.9520994 23.4545455,15.903663 23.4545455,12 C23.4545455,11.2909091 23.3454545,10.5272727 23.1818182,9.81818182 L12,9.81818182 L12,14.4545455 L18.4363636,14.4545455 C18.1187732,16.013626 17.2662994,17.2212117 16.0407269,18.0125889 L19.834192,20.9995801 Z"/>
<path fill="#FBBC05" d="M5.27698177,14.2678769 C5.03832634,13.556323 4.90909091,12.7937589 4.90909091,12 C4.90909091,11.2182781 5.03443647,10.4668121 5.26620003,9.76452941 L1.23999023,6.65002441 C0.43658717,8.26043162 0,10.0753848 0,12 C0,13.9195484 0.444780743,15.7301709 1.23746264,17.3349879 L5.27698177,14.2678769 Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Some files were not shown because too many files have changed in this diff Show More