mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
Compare commits
3 Commits
@auth/core
...
docs/provi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d815ce593 | ||
|
|
25be00b749 | ||
|
|
f28b58d49d |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -43,7 +43,6 @@ packages/*/*.d.ts.map
|
||||
apps/dev/src/css
|
||||
apps/dev/prisma/migrations
|
||||
apps/dev/typeorm
|
||||
apps/dev/nextjs-2
|
||||
|
||||
# VS
|
||||
/.vs/slnx.sqlite-journal
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
# Rename file to .env.local (or .env) and populate values
|
||||
# to be able to run the dev app
|
||||
|
||||
NEXTAUTH_URL=http://localhost:3000
|
||||
|
||||
# You can use `openssl rand -hex 32` or
|
||||
# https://generate-secret.vercel.app/32 to generate a secret.
|
||||
# Note: Changing a secret may invalidate existing sessions
|
||||
# and/or verification tokens.
|
||||
NEXTAUTH_SECRET=secret
|
||||
|
||||
AUTH0_ID=
|
||||
AUTH0_SECRET=
|
||||
AUTH0_ISSUER=
|
||||
|
||||
KEYCLOAK_ID=
|
||||
KEYCLOAK_SECRET=
|
||||
KEYCLOAK_ISSUER=
|
||||
|
||||
IDS4_ID=
|
||||
IDS4_SECRET=
|
||||
IDS4_ISSUER=
|
||||
|
||||
GITHUB_ID=
|
||||
GITHUB_SECRET=
|
||||
|
||||
TWITCH_ID=
|
||||
TWITCH_SECRET=
|
||||
|
||||
TWITTER_ID=
|
||||
TWITTER_SECRET=
|
||||
|
||||
LINE_ID=
|
||||
LINE_SECRET=
|
||||
|
||||
TRAKT_ID=
|
||||
TRAKT_SECRET=
|
||||
|
||||
# Example configuration for a Gmail account (will need SMTP enabled)
|
||||
EMAIL_SERVER=smtps://user@gmail.com:password@smtp.gmail.com:465
|
||||
EMAIL_FROM=user@gmail.com
|
||||
|
||||
# Note: If using with Prisma adapter, you need to use a `.env`
|
||||
# file rather than a `.env.local` file to configure env vars.
|
||||
# Postgres: DATABASE_URL=postgres://nextauth:password@127.0.0.1:5432/nextauth?synchronize=true
|
||||
# MySQL: DATABASE_URL=mysql://nextauth:password@127.0.0.1:3306/nextauth?synchronize=true
|
||||
# MongoDB: DATABASE_URL=mongodb://nextauth:password@127.0.0.1:27017/nextauth?synchronize=true
|
||||
DATABASE_URL=
|
||||
|
||||
WIKIMEDIA_ID=
|
||||
WIKIMEDIA_SECRET=
|
||||
|
||||
# Supabase Example Configuration
|
||||
# Supabase Example Configuration
|
||||
# NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321
|
||||
# SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSJ9.vI9obAHOGyVVKa3pD--kJlyxp-Z2zV9UUMAhKpNLAcU
|
||||
# SUPABASE_JWT_SECRET=super-secret-jwt-token-with-at-least-32-characters-long
|
||||
# NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24ifQ.625_WdcF3KHqz5amU0x2X5WWHP-OEs_4qj0ssLNHzTs
|
||||
4
apps/dev/nextjs-v4/.vscode/settings.json
vendored
4
apps/dev/nextjs-v4/.vscode/settings.json
vendored
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"typescript.tsdk": "../../node_modules/.pnpm/typescript@4.8.4/node_modules/typescript/lib",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
# NextAuth.js Development App
|
||||
|
||||
This folder contains a Next.js app using NextAuth.js for local development. See the following section on how to start:
|
||||
|
||||
[Setting up local environment
|
||||
](https://github.com/nextauthjs/next-auth/blob/main/CONTRIBUTING.md#setting-up-local-environment)
|
||||
@@ -1,14 +0,0 @@
|
||||
import NextAuth, { type NextAuthOptions } from "next-auth"
|
||||
import GitHub from "next-auth/providers/github"
|
||||
|
||||
export const authOptions: NextAuthOptions = {
|
||||
providers: [
|
||||
GitHub({
|
||||
clientId: process.env.GITHUB_ID,
|
||||
clientSecret: process.env.GITHUB_SECRET,
|
||||
}),
|
||||
],
|
||||
}
|
||||
|
||||
const handler = NextAuth(authOptions)
|
||||
export { handler as GET, handler as POST }
|
||||
@@ -1,12 +0,0 @@
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<html>
|
||||
<head></head>
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
import { getServerSession } from "next-auth/next"
|
||||
|
||||
export default async function Page() {
|
||||
const session = await getServerSession()
|
||||
return <pre>{JSON.stringify(session, null, 2)}</pre>
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import { signIn } from "next-auth/react"
|
||||
|
||||
export default function AccessDenied() {
|
||||
return (
|
||||
<>
|
||||
<h1>Access Denied</h1>
|
||||
<p>
|
||||
<a
|
||||
href="/api/auth/signin"
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
signIn()
|
||||
}}
|
||||
>
|
||||
You must be signed in to view this page
|
||||
</a>
|
||||
</p>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import Link from "next/link"
|
||||
import styles from "./footer.module.css"
|
||||
import packageJSON from "package.json"
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
<footer className={styles.footer}>
|
||||
<hr />
|
||||
<ul className={styles.navItems}>
|
||||
<li className={styles.navItem}>
|
||||
<a href="https://next-auth.js.org">Documentation</a>
|
||||
</li>
|
||||
<li className={styles.navItem}>
|
||||
<a href="https://www.npmjs.com/package/next-auth">NPM</a>
|
||||
</li>
|
||||
<li className={styles.navItem}>
|
||||
<a href="https://github.com/nextauthjs/next-auth-example">GitHub</a>
|
||||
</li>
|
||||
<li className={styles.navItem}>
|
||||
<Link href="/policy">Policy</Link>
|
||||
</li>
|
||||
<li className={styles.navItem}>
|
||||
<em>{packageJSON.version}</em>
|
||||
</li>
|
||||
</ul>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
.footer {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.navItems {
|
||||
margin-bottom: 1rem;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.navItem {
|
||||
display: inline-block;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
import Link from "next/link"
|
||||
import { signIn, signOut, 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}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
signIn()
|
||||
}}
|
||||
>
|
||||
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}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
signOut()
|
||||
}}
|
||||
>
|
||||
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>
|
||||
<li className={styles.navItem}>
|
||||
<Link href="/supabase-client-rls">Supabase RLS</Link>
|
||||
</li>
|
||||
<li className={styles.navItem}>
|
||||
<Link href="/supabase-ssr">Supabase RLS(SSR)</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/* Set min-height to avoid page reflow while session loading */
|
||||
.signedInStatus {
|
||||
display: block;
|
||||
min-height: 4rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.loading,
|
||||
.loaded {
|
||||
position: relative;
|
||||
top: 0;
|
||||
opacity: 1;
|
||||
overflow: hidden;
|
||||
border-radius: 0 0 .6rem .6rem;
|
||||
padding: .6rem 1rem;
|
||||
margin: 0;
|
||||
background-color: rgba(0,0,0,.05);
|
||||
transition: all 0.2s ease-in;
|
||||
}
|
||||
|
||||
.loading {
|
||||
top: -2rem;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.signedInText,
|
||||
.notSignedInText {
|
||||
position: absolute;
|
||||
padding-top: .8rem;
|
||||
left: 1rem;
|
||||
right: 6.5rem;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
display: inherit;
|
||||
z-index: 1;
|
||||
line-height: 1.3rem;
|
||||
}
|
||||
|
||||
.signedInText {
|
||||
padding-top: 0rem;
|
||||
left: 4.6rem;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
border-radius: 2rem;
|
||||
float: left;
|
||||
height: 2.8rem;
|
||||
width: 2.8rem;
|
||||
background-color: white;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.button,
|
||||
.buttonPrimary {
|
||||
float: right;
|
||||
margin-right: -.4rem;
|
||||
font-weight: 500;
|
||||
border-radius: .3rem;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
line-height: 1.4rem;
|
||||
padding: .7rem .8rem;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
background-color: transparent;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.buttonPrimary {
|
||||
background-color: #346df1;
|
||||
border-color: #346df1;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
padding: .7rem 1.4rem;
|
||||
}
|
||||
|
||||
.buttonPrimary:hover {
|
||||
box-shadow: inset 0 0 5rem rgba(0,0,0,0.2)
|
||||
}
|
||||
|
||||
.navItems {
|
||||
margin-bottom: 2rem;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.navItem {
|
||||
display: inline-block;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import Header from 'components/header'
|
||||
import Footer from 'components/footer'
|
||||
|
||||
export default function Layout ({ children }) {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<main>
|
||||
{children}
|
||||
</main>
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
export { default } from "next-auth/middleware"
|
||||
|
||||
export const config = { matcher: ["/middleware-protected"] }
|
||||
|
||||
// Other ways to use this middleware
|
||||
|
||||
// import withAuth from "next-auth/middleware"
|
||||
// import { withAuth } from "next-auth/middleware"
|
||||
|
||||
// export function middleware(req, ev) {
|
||||
// return withAuth(req)
|
||||
// }
|
||||
|
||||
// export function middleware(req, ev) {
|
||||
// return withAuth(req, ev)
|
||||
// }
|
||||
|
||||
// export function middleware(req, ev) {
|
||||
// return withAuth(req, {
|
||||
// callbacks: {
|
||||
// authorized: ({ token }) => !!token,
|
||||
// },
|
||||
// })
|
||||
// }
|
||||
|
||||
// export default withAuth(function middleware(req, ev) {
|
||||
// console.log(req.nextauth.token)
|
||||
// })
|
||||
|
||||
// export default withAuth(
|
||||
// function middleware(req, ev) {
|
||||
// console.log(req, ev)
|
||||
// },
|
||||
// {
|
||||
// callbacks: {
|
||||
// authorized: ({ token }) => token.name === "Balázs Orbán",
|
||||
// },
|
||||
// }
|
||||
// )
|
||||
|
||||
// export default withAuth({
|
||||
// callbacks: {
|
||||
// authorized: ({ token }) => !!token,
|
||||
// },
|
||||
// })
|
||||
6
apps/dev/nextjs-v4/next-env.d.ts
vendored
6
apps/dev/nextjs-v4/next-env.d.ts
vendored
@@ -1,6 +0,0 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
/// <reference types="next/navigation-types/compat/navigation" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
@@ -1,9 +0,0 @@
|
||||
/** @type {import("next").NextConfig} */
|
||||
module.exports = {
|
||||
webpack(config) {
|
||||
config.experiments = { ...config.experiments, topLevelAwait: true }
|
||||
return config
|
||||
},
|
||||
experimental: { appDir: true },
|
||||
typescript: { ignoreBuildErrors: true },
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
{
|
||||
"name": "next-auth-app-v4",
|
||||
"version": "1.0.0",
|
||||
"description": "NextAuth.js Developer app",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"clean": "rm -rf .next",
|
||||
"dev": "next dev",
|
||||
"lint": "next lint",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"email": "fake-smtp-server",
|
||||
"start:email": "pnpm email"
|
||||
},
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@next-auth/fauna-adapter": "workspace:*",
|
||||
"@next-auth/prisma-adapter": "workspace:*",
|
||||
"@next-auth/supabase-adapter": "workspace:*",
|
||||
"@next-auth/typeorm-legacy-adapter": "workspace:*",
|
||||
"@prisma/client": "^3",
|
||||
"@supabase/supabase-js": "^2.0.5",
|
||||
"faunadb": "^4",
|
||||
"next": "13.3.0",
|
||||
"next-auth": "workspace:*",
|
||||
"nodemailer": "^6",
|
||||
"react": "^18",
|
||||
"react-dom": "^18"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jsonwebtoken": "^8.5.5",
|
||||
"@types/react": "^18.0.15",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"fake-smtp-server": "^0.8.0",
|
||||
"pg": "^8.7.3",
|
||||
"prisma": "^3",
|
||||
"sqlite3": "^5.0.8",
|
||||
"typeorm": "0.3.7"
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import Layout from '../components/layout'
|
||||
|
||||
export default function Page () {
|
||||
return (
|
||||
<Layout>
|
||||
<h1>API Example</h1>
|
||||
<p>The examples below show responses from the example API endpoints.</p>
|
||||
<p><em>You must be signed in to see responses.</em></p>
|
||||
<h2>Session</h2>
|
||||
<p>/api/examples/session</p>
|
||||
<iframe src='/api/examples/session' />
|
||||
<h2>JSON Web Token</h2>
|
||||
<p>/api/examples/jwt</p>
|
||||
<iframe src='/api/examples/jwt' />
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
import NextAuth, { NextAuthOptions } from "next-auth"
|
||||
|
||||
// Providers
|
||||
import Apple from "next-auth/providers/apple"
|
||||
import Auth0 from "next-auth/providers/auth0"
|
||||
import AzureAD from "next-auth/providers/azure-ad"
|
||||
import AzureB2C from "next-auth/providers/azure-ad-b2c"
|
||||
import BoxyHQSAML from "next-auth/providers/boxyhq-saml"
|
||||
// import Cognito from "next-auth/providers/cognito"
|
||||
import Credentials from "next-auth/providers/credentials"
|
||||
import Discord from "next-auth/providers/discord"
|
||||
import DuendeIDS6 from "next-auth/providers/duende-identity-server6"
|
||||
// import Email from "next-auth/providers/email"
|
||||
import Facebook from "next-auth/providers/facebook"
|
||||
import Foursquare from "next-auth/providers/foursquare"
|
||||
import Freshbooks from "next-auth/providers/freshbooks"
|
||||
import GitHub from "next-auth/providers/github"
|
||||
import Gitlab from "next-auth/providers/gitlab"
|
||||
import Google from "next-auth/providers/google"
|
||||
// import IDS4 from "next-auth/providers/identity-server4"
|
||||
import Instagram from "next-auth/providers/instagram"
|
||||
// import Keycloak from "next-auth/providers/keycloak"
|
||||
import Line from "next-auth/providers/line"
|
||||
import LinkedIn from "next-auth/providers/linkedin"
|
||||
import Mailchimp from "next-auth/providers/mailchimp"
|
||||
// import Okta from "next-auth/providers/okta"
|
||||
import Osu from "next-auth/providers/osu"
|
||||
import Patreon from "next-auth/providers/patreon"
|
||||
import Slack from "next-auth/providers/slack"
|
||||
import Spotify from "next-auth/providers/spotify"
|
||||
import Trakt from "next-auth/providers/trakt"
|
||||
import Twitch from "next-auth/providers/twitch"
|
||||
import Twitter from "next-auth/providers/twitter"
|
||||
import Vk from "next-auth/providers/vk"
|
||||
import Wikimedia from "next-auth/providers/wikimedia"
|
||||
import WorkOS from "next-auth/providers/workos"
|
||||
|
||||
// // Prisma
|
||||
// import { PrismaClient } from "@prisma/client"
|
||||
// import { PrismaAdapter } from "@next-auth/prisma-adapter"
|
||||
// const client = globalThis.prisma || new PrismaClient()
|
||||
// if (process.env.NODE_ENV !== "production") globalThis.prisma = client
|
||||
// const adapter = PrismaAdapter(client)
|
||||
|
||||
// // Fauna
|
||||
// import { Client as FaunaClient } from "faunadb"
|
||||
// import { FaunaAdapter } from "@next-auth/fauna-adapter"
|
||||
// const opts = { secret: process.env.FAUNA_SECRET, domain: process.env.FAUNA_DOMAIN }
|
||||
// const client = globalThis.fauna || new FaunaClient(opts)
|
||||
// if (process.env.NODE_ENV !== "production") globalThis.fauna = client
|
||||
// const adapter = FaunaAdapter(client)
|
||||
|
||||
// // TypeORM
|
||||
// import { TypeORMLegacyAdapter } from "@next-auth/typeorm-legacy-adapter"
|
||||
// const adapter = TypeORMLegacyAdapter({
|
||||
// type: "sqlite",
|
||||
// name: "next-auth-test-memory",
|
||||
// database: "./typeorm/dev.db",
|
||||
// synchronize: true,
|
||||
// })
|
||||
|
||||
// // Supabase
|
||||
// import { SupabaseAdapter } from "@next-auth/supabase-adapter"
|
||||
// const adapter = SupabaseAdapter({
|
||||
// url: process.env.NEXT_PUBLIC_SUPABASE_URL,
|
||||
// secret: process.env.SUPABASE_SERVICE_ROLE_KEY,
|
||||
// })
|
||||
|
||||
export const authOptions: NextAuthOptions = {
|
||||
// adapter,
|
||||
// debug: process.env.NODE_ENV !== "production",
|
||||
theme: {
|
||||
logo: "https://next-auth.js.org/img/logo/logo-sm.png",
|
||||
brandColor: "#1786fb",
|
||||
},
|
||||
providers: [
|
||||
Credentials({
|
||||
credentials: { password: { label: "Password", type: "password" } },
|
||||
async authorize(credentials) {
|
||||
if (credentials.password !== "pw") return null
|
||||
return { name: "Fill Murray", email: "bill@fillmurray.com", image: "https://www.fillmurray.com/64/64", id: "1", foo: "" }
|
||||
},
|
||||
}),
|
||||
Apple({ clientId: process.env.APPLE_ID, clientSecret: process.env.APPLE_SECRET }),
|
||||
Auth0({ clientId: process.env.AUTH0_ID, clientSecret: process.env.AUTH0_SECRET, issuer: process.env.AUTH0_ISSUER }),
|
||||
AzureAD({
|
||||
clientId: process.env.AZURE_AD_CLIENT_ID,
|
||||
clientSecret: process.env.AZURE_AD_CLIENT_SECRET,
|
||||
tenantId: process.env.AZURE_AD_TENANT_ID,
|
||||
}),
|
||||
AzureB2C({ clientId: process.env.AZURE_B2C_ID, clientSecret: process.env.AZURE_B2C_SECRET, issuer: process.env.AZURE_B2C_ISSUER }),
|
||||
BoxyHQSAML({ issuer: "https://jackson-demo.boxyhq.com", clientId: "tenant=boxyhq.com&product=saml-demo.boxyhq.com", clientSecret: "dummy" }),
|
||||
// Cognito({ clientId: process.env.COGNITO_ID, clientSecret: process.env.COGNITO_SECRET, issuer: process.env.COGNITO_ISSUER }),
|
||||
Discord({ clientId: process.env.DISCORD_ID, clientSecret: process.env.DISCORD_SECRET }),
|
||||
DuendeIDS6({ clientId: "interactive.confidential", clientSecret: "secret", issuer: "https://demo.duendesoftware.com" }),
|
||||
Facebook({ clientId: process.env.FACEBOOK_ID, clientSecret: process.env.FACEBOOK_SECRET }),
|
||||
Foursquare({ clientId: process.env.FOURSQUARE_ID, clientSecret: process.env.FOURSQUARE_SECRET }),
|
||||
Freshbooks({ clientId: process.env.FRESHBOOKS_ID, clientSecret: process.env.FRESHBOOKS_SECRET }),
|
||||
GitHub({ clientId: process.env.GITHUB_ID, clientSecret: process.env.GITHUB_SECRET }),
|
||||
Gitlab({ clientId: process.env.GITLAB_ID, clientSecret: process.env.GITLAB_SECRET }),
|
||||
Google({ clientId: process.env.GOOGLE_ID, clientSecret: process.env.GOOGLE_SECRET }),
|
||||
// IDS4({ clientId: process.env.IDS4_ID, clientSecret: process.env.IDS4_SECRET, issuer: process.env.IDS4_ISSUER }),
|
||||
Instagram({ clientId: process.env.INSTAGRAM_ID, clientSecret: process.env.INSTAGRAM_SECRET }),
|
||||
// Keycloak({ clientId: process.env.KEYCLOAK_ID, clientSecret: process.env.KEYCLOAK_SECRET, issuer: process.env.KEYCLOAK_ISSUER }),
|
||||
Line({ clientId: process.env.LINE_ID, clientSecret: process.env.LINE_SECRET }),
|
||||
LinkedIn({ clientId: process.env.LINKEDIN_ID, clientSecret: process.env.LINKEDIN_SECRET }),
|
||||
Mailchimp({ clientId: process.env.MAILCHIMP_ID, clientSecret: process.env.MAILCHIMP_SECRET }),
|
||||
// Okta({ clientId: process.env.OKTA_ID, clientSecret: process.env.OKTA_SECRET, issuer: process.env.OKTA_ISSUER }),
|
||||
Osu({ clientId: process.env.OSU_CLIENT_ID, clientSecret: process.env.OSU_CLIENT_SECRET }),
|
||||
Patreon({ clientId: process.env.PATREON_ID, clientSecret: process.env.PATREON_SECRET }),
|
||||
Slack({ clientId: process.env.SLACK_ID, clientSecret: process.env.SLACK_SECRET }),
|
||||
Spotify({ clientId: process.env.SPOTIFY_ID, clientSecret: process.env.SPOTIFY_SECRET }),
|
||||
Trakt({ clientId: process.env.TRAKT_ID, clientSecret: process.env.TRAKT_SECRET }),
|
||||
Twitch({ clientId: process.env.TWITCH_ID, clientSecret: process.env.TWITCH_SECRET }),
|
||||
Twitter({ clientId: process.env.TWITTER_ID, clientSecret: process.env.TWITTER_SECRET }),
|
||||
// TwitterLegacy({ clientId: process.env.TWITTER_LEGACY_ID, clientSecret: process.env.TWITTER_LEGACY_SECRET }),
|
||||
Vk({ clientId: process.env.VK_ID, clientSecret: process.env.VK_SECRET }),
|
||||
Wikimedia({ clientId: process.env.WIKIMEDIA_ID, clientSecret: process.env.WIKIMEDIA_SECRET }),
|
||||
WorkOS({ clientId: process.env.WORKOS_ID, clientSecret: process.env.WORKOS_SECRET }),
|
||||
],
|
||||
}
|
||||
|
||||
if (authOptions.adapter) {
|
||||
// TODO:
|
||||
// authOptions.providers.unshift(
|
||||
// // NOTE: You can start a fake e-mail server with `pnpm email`
|
||||
// // and then go to `http://localhost:1080` in the browser
|
||||
// Email({ server: "smtp://127.0.0.1:1025?tls.rejectUnauthorized=false" })
|
||||
// )
|
||||
}
|
||||
|
||||
export default NextAuth(authOptions)
|
||||
@@ -1,7 +0,0 @@
|
||||
// This is an example of how to read a JSON Web Token from an API route
|
||||
import { getToken } from "next-auth/jwt"
|
||||
|
||||
export default async (req, res) => {
|
||||
const token = await getToken({ req })
|
||||
res.send(JSON.stringify(token, null, 2))
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
// This is an example of to protect an API route
|
||||
import { getServerSession } from "next-auth/next"
|
||||
import { authOptions } from "../auth/[...nextauth]"
|
||||
|
||||
export default async (req, res) => {
|
||||
const session = await getServerSession(req, res, authOptions)
|
||||
|
||||
if (session) {
|
||||
res.send({
|
||||
content:
|
||||
"This is protected content. You can access this content because you are signed in.",
|
||||
session,
|
||||
})
|
||||
} else {
|
||||
res.send({
|
||||
error: "You must be sign in to view the protected content on this page.",
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
// This is an example of how to access a session from an API route
|
||||
import { getServerSession } from "next-auth/next"
|
||||
import { authOptions } from "../auth/[...nextauth]"
|
||||
|
||||
export default async (req, res) => {
|
||||
const session = await getServerSession(req, res, authOptions)
|
||||
res.json(session)
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
// This is an example of how to query data from Supabase with RLS.
|
||||
// Learn more about Row Levele Security (RLS): https://supabase.com/docs/guides/auth/row-level-security
|
||||
import { getServerSession } from "next-auth/next"
|
||||
import { authOptions } from "../auth/[...nextauth]"
|
||||
import { createClient } from "@supabase/supabase-js"
|
||||
|
||||
export default async (req, res) => {
|
||||
const session = await getServerSession(req, res, authOptions)
|
||||
|
||||
if (!session)
|
||||
return res.send(JSON.stringify({ error: "No session!" }, null, 2))
|
||||
|
||||
const { supabaseAccessToken } = session
|
||||
|
||||
const supabase = createClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
|
||||
{
|
||||
global: {
|
||||
headers: {
|
||||
Authorization: `Bearer ${supabaseAccessToken}`,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
// Now you can query with RLS enabled.
|
||||
const { data, error } = await supabase.from("users").select("*")
|
||||
|
||||
res.send(JSON.stringify({ supabaseAccessToken, data, error }, null, 2))
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import Layout from '../components/layout'
|
||||
|
||||
export default function Page () {
|
||||
return (
|
||||
<Layout>
|
||||
<h1>Client Side Rendering</h1>
|
||||
<p>
|
||||
This page uses the <strong>useSession()</strong> React Hook in the <strong></Header></strong> component.
|
||||
</p>
|
||||
<p>
|
||||
The <strong>useSession()</strong> React Hook easy to use and allows pages to render very quickly.
|
||||
</p>
|
||||
<p>
|
||||
The advantage of this approach is that session state is shared between pages by using the <strong>Provider</strong> in <strong>_app.js</strong> so
|
||||
that navigation between pages using <strong>useSession()</strong> is very fast.
|
||||
</p>
|
||||
<p>
|
||||
The disadvantage of <strong>useSession()</strong> is that it requires client side JavaScript.
|
||||
</p>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -1,80 +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 [email, setEmail] = React.useState("")
|
||||
|
||||
const handleChange = (event) => {
|
||||
setEmail(event.target.value)
|
||||
}
|
||||
|
||||
const handleLogin = (options) => async (event) => {
|
||||
event.preventDefault()
|
||||
|
||||
if (options.redirect) {
|
||||
return signIn("email", options)
|
||||
}
|
||||
const response = await signIn("email", options)
|
||||
setResponse(response)
|
||||
}
|
||||
|
||||
const handleLogout = (options) => async (event) => {
|
||||
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 Email 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 Email login</h1>
|
||||
<label className="spacing">
|
||||
Email address:{" "}
|
||||
<input
|
||||
type="text"
|
||||
id="email"
|
||||
name="email"
|
||||
value={email}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</label>
|
||||
<br />
|
||||
<form onSubmit={handleLogin({ redirect: true, email })}>
|
||||
<span className="spacing">Default:</span>
|
||||
<button type="submit">Sign in with Email</button>
|
||||
</form>
|
||||
<form onSubmit={handleLogin({ redirect: false, email })}>
|
||||
<span className="spacing">No redirect:</span>
|
||||
<button type="submit">Sign in with Email</button>
|
||||
</form>
|
||||
<p>Response:</p>
|
||||
<pre style={{ background: "#eee", padding: 16 }}>
|
||||
{JSON.stringify(response, null, 2)}
|
||||
</pre>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
@@ -1,12 +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://next-auth.js.org'>NextAuth.js</a> for authentication.
|
||||
</p>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import Layout from "components/layout"
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<Layout>
|
||||
<h1>Page protected by Middleware</h1>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
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://next-auth.js.org'>NextAuth.js</a> for authentication.
|
||||
</p>
|
||||
<h2>Terms of Service</h2>
|
||||
<p>
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE 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>
|
||||
)
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
// This is an example of how to protect content using server rendering
|
||||
import { getServerSession } from "next-auth/next"
|
||||
import { authOptions } from "./api/auth/[...nextauth]"
|
||||
import Layout from "../components/layout"
|
||||
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 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, authOptions)
|
||||
let content = null
|
||||
|
||||
if (session) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
props: {
|
||||
session,
|
||||
content,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import { useState, useEffect } from "react"
|
||||
import { useSession } from "next-auth/react"
|
||||
import Layout from "../components/layout"
|
||||
|
||||
export default function Page() {
|
||||
const { status } = useSession({
|
||||
required: true,
|
||||
})
|
||||
const [content, setContent] = useState()
|
||||
|
||||
// Fetch content from protected route
|
||||
useEffect(() => {
|
||||
if (status === "loading") return
|
||||
const fetchData = async () => {
|
||||
const res = await fetch("/api/examples/protected")
|
||||
const json = await res.json()
|
||||
if (json.content) {
|
||||
setContent(json.content)
|
||||
}
|
||||
}
|
||||
fetchData()
|
||||
}, [status])
|
||||
|
||||
if (status === "loading") return <Layout>Loading...</Layout>
|
||||
|
||||
// If session exists, display content
|
||||
return (
|
||||
<Layout>
|
||||
<h1>Protected Page</h1>
|
||||
<p>
|
||||
<strong>{content}</strong>
|
||||
</p>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import { getServerSession } from "next-auth/next"
|
||||
import Layout from "../components/layout"
|
||||
import { authOptions } from "./api/auth/[...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, authOptions),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
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: .5rem;
|
||||
filter: invert(1);
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import Layout from "../components/layout"
|
||||
import { useState, useEffect } from "react"
|
||||
import { useSession } from "next-auth/react"
|
||||
import { createClient } from "@supabase/supabase-js"
|
||||
|
||||
export default function Page() {
|
||||
const { data: session } = useSession()
|
||||
const [data, setData] = useState(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (session) {
|
||||
console.log(session)
|
||||
// User is logged in, let's fetch their data.
|
||||
const { supabaseAccessToken } = session
|
||||
const supabase = createClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
|
||||
{
|
||||
global: {
|
||||
headers: { Authorization: `Bearer ${supabaseAccessToken}` },
|
||||
},
|
||||
}
|
||||
)
|
||||
// Fetch data with RLS enabled.
|
||||
supabase
|
||||
.from("users")
|
||||
.select("*")
|
||||
.then(({ data }) => setData(data))
|
||||
}
|
||||
}, [session])
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<h1>Fetch Data from Supabase with RLS</h1>
|
||||
<h2>Client-side data fetching with RLS:</h2>
|
||||
<pre>{JSON.stringify(data, null, 2)}</pre>
|
||||
<h2>API Example</h2>
|
||||
<p>
|
||||
You can also use Supabase in API routes. See the code in the
|
||||
`/pages/api/examples/supabase-rls.js` file.
|
||||
</p>
|
||||
<p>
|
||||
<em>You must be signed in to see responses.</em>
|
||||
</p>
|
||||
<p>/api/examples/supabase-rls</p>
|
||||
<iframe src="/api/examples/supabase-rls" />
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
// This is an example of how to protect content using server rendering
|
||||
// and fetching data from Supabase with RLS enabled.
|
||||
import { getServerSession } from "next-auth/next"
|
||||
import { authOptions } from "./api/auth/[...nextauth]"
|
||||
import { createClient } from "@supabase/supabase-js"
|
||||
import Layout from "../components/layout"
|
||||
import AccessDenied from "../components/access-denied"
|
||||
|
||||
export default function Page({ data, session }) {
|
||||
// If no session exists, display access denied message
|
||||
if (!session) {
|
||||
return (
|
||||
<Layout>
|
||||
<AccessDenied />
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
// If session exists, display content
|
||||
return (
|
||||
<Layout>
|
||||
<h1>Protected Page</h1>
|
||||
<p>Data fetched during SSR from Supabase with RSL enabled:</p>
|
||||
<pre>{JSON.stringify(data, null, 2)}</pre>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getServerSideProps(context) {
|
||||
const session = await getServerSession(context.req, context.res, authOptions)
|
||||
|
||||
if (!session)
|
||||
return {
|
||||
props: {
|
||||
session,
|
||||
data: null,
|
||||
error: "No session",
|
||||
},
|
||||
}
|
||||
|
||||
const { supabaseAccessToken } = session
|
||||
|
||||
const supabase = createClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
|
||||
{
|
||||
global: {
|
||||
headers: {
|
||||
Authorization: `Bearer ${supabaseAccessToken}`,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
// Now you can query with RLS enabled.
|
||||
const { data, error } = await supabase.from("users").select("*")
|
||||
|
||||
return {
|
||||
props: {
|
||||
session,
|
||||
data,
|
||||
error,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "Account" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"userId" TEXT NOT NULL,
|
||||
"type" TEXT NOT NULL,
|
||||
"provider" TEXT NOT NULL,
|
||||
"providerAccountId" TEXT NOT NULL,
|
||||
"refresh_token" TEXT,
|
||||
"access_token" TEXT,
|
||||
"expires_at" INTEGER,
|
||||
"token_type" TEXT,
|
||||
"scope" TEXT,
|
||||
"id_token" TEXT,
|
||||
"session_state" TEXT,
|
||||
"oauth_token_secret" TEXT,
|
||||
"oauth_token" TEXT,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL,
|
||||
CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Session" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"sessionToken" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"expires" DATETIME NOT NULL,
|
||||
CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "User" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"name" TEXT,
|
||||
"email" TEXT,
|
||||
"emailVerified" DATETIME,
|
||||
"image" TEXT
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "VerificationToken" (
|
||||
"identifier" TEXT NOT NULL,
|
||||
"token" TEXT NOT NULL,
|
||||
"expires" DATETIME NOT NULL
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Account_provider_providerAccountId_key" ON "Account"("provider", "providerAccountId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Session_sessionToken_key" ON "Session"("sessionToken");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "VerificationToken_token_key" ON "VerificationToken"("token");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "VerificationToken_identifier_token_key" ON "VerificationToken"("identifier", "token");
|
||||
@@ -1,3 +0,0 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (i.e. Git)
|
||||
provider = "sqlite"
|
||||
@@ -1,57 +0,0 @@
|
||||
datasource db {
|
||||
provider = "sqlite"
|
||||
url = "file:./dev.db"
|
||||
}
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
model Account {
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
type String
|
||||
provider String
|
||||
providerAccountId String
|
||||
refresh_token String?
|
||||
access_token String?
|
||||
expires_at Int?
|
||||
token_type String?
|
||||
scope String?
|
||||
id_token String?
|
||||
session_state String?
|
||||
oauth_token_secret String?
|
||||
oauth_token String?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
|
||||
@@unique([provider, providerAccountId])
|
||||
}
|
||||
|
||||
model Session {
|
||||
id String @id @default(cuid())
|
||||
sessionToken String @unique
|
||||
userId String
|
||||
expires DateTime
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
name String?
|
||||
email String? @unique
|
||||
emailVerified DateTime?
|
||||
image String?
|
||||
accounts Account[]
|
||||
sessions Session[]
|
||||
}
|
||||
|
||||
model VerificationToken {
|
||||
identifier String
|
||||
token String @unique
|
||||
expires DateTime
|
||||
|
||||
@@unique([identifier, token])
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"incremental": true,
|
||||
"jsx": "preserve",
|
||||
"baseUrl": ".",
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"strictNullChecks": true
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"jest.config.js"
|
||||
]
|
||||
}
|
||||
20
apps/dev/nextjs-v4/types/nextauth.d.ts
vendored
20
apps/dev/nextjs-v4/types/nextauth.d.ts
vendored
@@ -1,20 +0,0 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import NextAuth from "next-auth"
|
||||
|
||||
declare module "next-auth" {
|
||||
/**
|
||||
* Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
|
||||
*/
|
||||
interface Session {
|
||||
// A JWT which can be used as Authorization header with supabase-js for RLS.
|
||||
supabaseAccessToken?: string
|
||||
user: {
|
||||
/** The user's postal address. */
|
||||
address: string
|
||||
} & User
|
||||
}
|
||||
|
||||
interface User {
|
||||
foo: string
|
||||
}
|
||||
}
|
||||
1
apps/dev/nextjs/next-env.d.ts
vendored
1
apps/dev/nextjs/next-env.d.ts
vendored
@@ -1,6 +1,5 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
/// <reference types="next/navigation-types/compat/navigation" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"@prisma/client": "^3",
|
||||
"@supabase/supabase-js": "^2.0.5",
|
||||
"faunadb": "^4",
|
||||
"next": "13.3.0",
|
||||
"next": "13.1.1",
|
||||
"next-auth": "workspace:*",
|
||||
"nodemailer": "^6",
|
||||
"react": "^18",
|
||||
|
||||
@@ -102,7 +102,7 @@ export const authConfig: AuthConfig = {
|
||||
Facebook({ clientId: process.env.FACEBOOK_ID, clientSecret: process.env.FACEBOOK_SECRET }),
|
||||
Foursquare({ clientId: process.env.FOURSQUARE_ID, clientSecret: process.env.FOURSQUARE_SECRET }),
|
||||
Freshbooks({ clientId: process.env.FRESHBOOKS_ID, clientSecret: process.env.FRESHBOOKS_SECRET }),
|
||||
GitHub({ clientId: process.env.GITHUB_ID, clientSecret: process.env.GITHUB_SECRET, redirectProxy: process.env.AUTH_REDIRECT_PROXY_URL }),
|
||||
GitHub({ clientId: process.env.GITHUB_ID, clientSecret: process.env.GITHUB_SECRET }),
|
||||
Gitlab({ clientId: process.env.GITLAB_ID, clientSecret: process.env.GITLAB_SECRET }),
|
||||
Google({ clientId: process.env.GOOGLE_ID, clientSecret: process.env.GOOGLE_SECRET }),
|
||||
// IDS4({ clientId: process.env.IDS4_ID, clientSecret: process.env.IDS4_SECRET, issuer: process.env.IDS4_ISSUER }),
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
@@ -23,17 +19,8 @@
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"strictNullChecks": true
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"jest.config.js"
|
||||
]
|
||||
}
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules", "jest.config.js"]
|
||||
}
|
||||
|
||||
@@ -4,11 +4,15 @@ title: Databases
|
||||
|
||||
Auth.js offers multiple database adapters. Check our guides on:
|
||||
|
||||
- [Using a database adapter](/guides/adapters/using-a-database-adapter)
|
||||
- [Creating your own](/guides/adapters/creating-a-database-adapter)
|
||||
- [using a database adapter](/guides/adapters/using-a-database-adapter)
|
||||
- [creating your own](/guides/adapters/creating-a-database-adapter)
|
||||
|
||||
> As of **v4** Auth.js no longer ships with an adapter included by default. If you would like to persist any information, you need to install one of the many available adapters yourself. See the individual adapter documentation pages for more details.
|
||||
|
||||
To learn more about databases in Auth.js and how they are used, check out [databases in the FAQ](/concepts/faq#databases).
|
||||
|
||||
---
|
||||
|
||||
## How to use a database
|
||||
|
||||
See the [documentation for adapters](/reference/adapters) for more information on advanced configuration, including how to use Auth.js with other databases using a [custom adapter](/guides/adapters/creating-a-database-adapter).
|
||||
See the [documentation for adapters](/reference/adapters/overview) for more information on advanced configuration, including how to use Auth.js with other databases using a [custom adapter](/guides/adapters/creating-a-database-adapter).
|
||||
|
||||
@@ -18,55 +18,77 @@ See below for more detailed provider settings.
|
||||
|
||||
## Vercel
|
||||
|
||||
1. Make sure to expose the Vercel [System Environment Variables](https://vercel.com/docs/concepts/projects/environment-variables#system-environment-variables) in your project settings. This way, we can detect the environment. (Setting `NEXTAUTH_URL` environment variable on Vercel is **unnecessary**).
|
||||
2. Create a `NEXTAUTH_SECRET` environment variable for both Production and Preview environments.
|
||||
1. Make sure to expose the Vercel [System Environment Variables](https://vercel.com/docs/concepts/projects/environment-variables#system-environment-variables) in your project settings.
|
||||
2. Create a `NEXTAUTH_SECRET` environment variable for all environments.
|
||||
a. You can use `openssl rand -base64 32` or https://generate-secret.vercel.app/32 to generate a random value.
|
||||
b. You **do not** need the `NEXTAUTH_URL` environment variable in Vercel.
|
||||
3. Add your provider's client ID and client secret to environment variables. _(Skip this step if not using an [OAuth Provider](/reference/providers/index))_
|
||||
4. Deploy!
|
||||
|
||||
Example repository: https://github.com/nextauthjs/next-auth-example
|
||||
|
||||
A few notes about deploying to Vercel. The environment variables are read server-side, so you **should not** prefix them with `NEXT_PUBLIC_` to avoid accidentally bundling a secret in the client-side JavaScript code.
|
||||
A few notes about deploying to Vercel. The environment variables are read server-side, so you do not need to prefix them with `NEXT_PUBLIC_`. When deploying here, you do not need to explicitly set the `NEXTAUTH_URL` environment variable. With other providers **you will** need to also set this environment variable.
|
||||
|
||||
### Securing a preview deployment
|
||||
|
||||
Most OAuth providers cannot be configured with multiple callback URLs or using a wildcard.
|
||||
Securing a preview deployment (with an OAuth provider) comes with some critical obstacles. Most OAuth providers only allow a single redirect/callback URL, or at least a set of full static URLs. Meaning you cannot set the value before publishing the site and you cannot use wildcard subdomains in the callback URL settings of your OAuth provider. Here are a few ways you can still use Auth.js to secure your Preview Deployments.
|
||||
|
||||
However, Auth.js **supports Preview deployments**, even **with OAuth providers**:
|
||||
#### Using the Credentials Provider
|
||||
|
||||
1. Determine a stable deployment URL. Eg.: A deployment whose URL does not change between builds, for example. `auth.yourdomain.com`),
|
||||
2. Set `AUTH_REDIRECT_PROXY_URL` to that URL, adding the path up until your `[...nextauth]` route. Eg.: (`https://auth.yourdomain.com/api/auth`)
|
||||
3. For your OAuth provider, set the callback URL using the stable deployment URL. Eg.: For GitHub `https://auth.yourdomain.com/api/auth/callback/github`)
|
||||
You could check in your `/pages/api/auth/[...nextauth].js` API route / configuration file to see if you're currently in a Vercel preview environment, and if so, enable a simple "credential provider", meaning username/password. Vercel offers a few built-in [system environment variables](https://vercel.com/docs/concepts/projects/environment-variables#system-environment-variables) which you could check against, like `VERCEL_ENV`. This would allow you to use this basic, for testing only, authentication strategy in your preview deployments.
|
||||
|
||||
:::info
|
||||
To support preview deployments, the `AUTH_SECRET` value needs to be the same for the stable deployment and deployments that will need OAuth support.
|
||||
:::
|
||||
Some things to be aware of here, include:
|
||||
|
||||
- Do not let this potential testing-only user have access to any critical data
|
||||
- If possible, maybe do not even connect this preview deployment to your production database
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<b>How does this work?</b>
|
||||
</summary>
|
||||
To support preview deployments, Auth.js uses the stable deployment URL as a redirect proxy server.
|
||||
##### Example
|
||||
|
||||
It will redirect the OAuth callback request to the preview deployment URL, but only when the `AUTH_REDIRECT_PROXY_URL` environment variable is set. The stable deployment can still act as a regular app.
|
||||
```js title="/pages/api/auth/[...nextauth].js"
|
||||
import NextAuth from "next-auth"
|
||||
import GoogleProvider from "next-auth/providers/google"
|
||||
import CredentialsProvider from "next-auth/providers/credentials"
|
||||
|
||||
When a user initiates an OAuth sign-in flow on a preview deployment, we save its URL in the `state` query parameter but set the `redirect_uri` to the stable deployment.
|
||||
export default NextAuth({
|
||||
providers: [
|
||||
process.env.VERCEL_ENV === "preview"
|
||||
? CredentialsProvider({
|
||||
name: "Credentials",
|
||||
credentials: {
|
||||
username: {
|
||||
label: "Username",
|
||||
type: "text",
|
||||
placeholder: "jsmith",
|
||||
},
|
||||
password: { label: "Password", type: "password" },
|
||||
},
|
||||
async authorize() {
|
||||
return {
|
||||
id: 1,
|
||||
name: "J Smith",
|
||||
email: "jsmith@example.com",
|
||||
image: "https://i.pravatar.cc/150?u=jsmith@example.com",
|
||||
}
|
||||
},
|
||||
})
|
||||
: GoogleProvider({
|
||||
clientId: process.env.GOOGLE_ID,
|
||||
clientSecret: process.env.GOOGLE_SECRET,
|
||||
}),
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
Then, the OAuth provider will redirect the user to the stable deployment, which then will verify the `state` parameter and redirect the user to the preview deployment URL if the `state` is valid. This is secured by relying on the same server-side `AUTH_SECRET` for the stable deployment and the preview deployment.
|
||||
#### Using the branch based preview URL
|
||||
|
||||
See also:
|
||||
<ul>
|
||||
<li><a href="https://www.ietf.org/rfc/rfc6749.html#section-4.1.1">OAuth 2.0 specification: `state` query parameter</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
Preview deployments at Vercel are often available via multiple URLs. For example, PR's merged to `master` or `main`, will be available the commit and PR specific preview URLs, but also the branch specific preview URLs. This branch specific URL will obviously not change as long as you work with that same branch. Therefore, you could add to your OAuth provider your `{project}-git-main-{user}.vercel.app` preview URL. As this will stay constant for that branch, you can reuse that preview deployment / URL for testing any authentication related deployments.
|
||||
|
||||
## Netlify
|
||||
|
||||
Netlify is very similar to Vercel in that you can deploy a Next.js project without almost any extra work.
|
||||
|
||||
To set up Auth.js correctly here, you will want to make sure you add your `NEXTAUTH_SECRET` environment variable in the project settings. If you are using the [Essential Next.js Build Plugin](https://github.com/netlify/netlify-plugin-nextjs) within your project, you **do not** need to set the `NEXTAUTH_URL` environment variable as it is set automatically as part of the build process.
|
||||
In order to setup Auth.js correctly here, you will want to make sure you add your `NEXTAUTH_SECRET` environment variable in the project settings. If you are using the [Essential Next.js Build Plugin](https://github.com/netlify/netlify-plugin-nextjs) within your project, you **do not** need to set the `NEXTAUTH_URL` environment variable as it is set automatically as part of the build process.
|
||||
|
||||
Netlify also exposes some [system environment variables](https://docs.netlify.com/configure-builds/environment-variables/) from which you can check which `NODE_ENV` you are currently in and much more.
|
||||
|
||||
After this, make sure you either have your OAuth provider set up correctly with `clientId` / `clientSecret`'s and callback URLs.
|
||||
After this, just make sure you either have your OAuth provider setup correctly with `clientId` / `clientSecret`'s and callback URLs.
|
||||
|
||||
@@ -261,36 +261,32 @@ const docusaurusConfig = {
|
||||
},
|
||||
},
|
||||
],
|
||||
...(process.env.TYPEDOC_SKIP_ADAPTERS
|
||||
? []
|
||||
: [
|
||||
typedocAdapter("Dgraph"),
|
||||
typedocAdapter("DynamoDB"),
|
||||
typedocAdapter("Fauna"),
|
||||
typedocAdapter("Firebase"),
|
||||
typedocAdapter("Mikro ORM"),
|
||||
typedocAdapter("MongoDB"),
|
||||
typedocAdapter("Neo4j"),
|
||||
typedocAdapter("PouchDB"),
|
||||
typedocAdapter("Prisma"),
|
||||
[
|
||||
"docusaurus-plugin-typedoc",
|
||||
{
|
||||
...typedocConfig,
|
||||
id: "typeorm",
|
||||
plugin: [require.resolve("./typedoc-mdn-links")],
|
||||
watch: process.env.TYPEDOC_WATCH,
|
||||
entryPoints: [`../packages/adapter-typeorm-legacy/src/index.ts`],
|
||||
tsconfig: `../packages/adapter-typeorm-legacy/tsconfig.json`,
|
||||
out: `reference/adapter/typeorm`,
|
||||
sidebar: { indexLabel: "TypeORM" },
|
||||
},
|
||||
],
|
||||
typedocAdapter("Sequelize"),
|
||||
typedocAdapter("Supabase"),
|
||||
typedocAdapter("Upstash Redis"),
|
||||
typedocAdapter("Xata"),
|
||||
]),
|
||||
typedocAdapter("Dgraph"),
|
||||
typedocAdapter("DynamoDB"),
|
||||
typedocAdapter("Fauna"),
|
||||
typedocAdapter("Firebase"),
|
||||
typedocAdapter("Mikro ORM"),
|
||||
typedocAdapter("MongoDB"),
|
||||
typedocAdapter("Neo4j"),
|
||||
typedocAdapter("PouchDB"),
|
||||
typedocAdapter("Prisma"),
|
||||
[
|
||||
"docusaurus-plugin-typedoc",
|
||||
{
|
||||
...typedocConfig,
|
||||
id: "typeorm",
|
||||
plugin: [require.resolve("./typedoc-mdn-links")],
|
||||
watch: process.env.TYPEDOC_WATCH,
|
||||
entryPoints: [`../packages/adapter-typeorm-legacy/src/index.ts`],
|
||||
tsconfig: `../packages/adapter-typeorm-legacy/tsconfig.json`,
|
||||
out: `reference/adapter/typeorm`,
|
||||
sidebar: { indexLabel: "TypeORM" },
|
||||
},
|
||||
],
|
||||
typedocAdapter("Sequelize"),
|
||||
typedocAdapter("Supabase"),
|
||||
typedocAdapter("Upstash Redis"),
|
||||
typedocAdapter("Xata"),
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@@ -34,9 +34,9 @@
|
||||
"@docusaurus/theme-common": "2.3.1",
|
||||
"@docusaurus/theme-mermaid": "2.3.1",
|
||||
"@docusaurus/types": "2.3.1",
|
||||
"docusaurus-plugin-typedoc": "1.0.0-next.5",
|
||||
"typedoc": "^0.24.4",
|
||||
"typedoc-plugin-markdown": "4.0.0-next.6"
|
||||
"docusaurus-plugin-typedoc": "1.0.0-next.2",
|
||||
"typedoc": "^0.23.28",
|
||||
"typedoc-plugin-markdown": "4.0.0-next.3"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
|
||||
@@ -46,31 +46,27 @@ module.exports = {
|
||||
},
|
||||
],
|
||||
},
|
||||
...(process.env.TYPEDOC_SKIP_ADAPTERS
|
||||
? []
|
||||
: [
|
||||
{
|
||||
type: "category",
|
||||
label: "Database Adapters",
|
||||
link: { type: "doc", id: "reference/adapters/index" },
|
||||
items: [
|
||||
{ type: "doc", id: "reference/adapter/dgraph/index" },
|
||||
{ type: "doc", id: "reference/adapter/dynamodb/index" },
|
||||
{ type: "doc", id: "reference/adapter/fauna/index" },
|
||||
{ type: "doc", id: "reference/adapter/firebase/index" },
|
||||
{ type: "doc", id: "reference/adapter/mikro-orm/index" },
|
||||
{ type: "doc", id: "reference/adapter/mongodb/index" },
|
||||
{ type: "doc", id: "reference/adapter/neo4j/index" },
|
||||
{ type: "doc", id: "reference/adapter/pouchdb/index" },
|
||||
{ type: "doc", id: "reference/adapter/prisma/index" },
|
||||
{ type: "doc", id: "reference/adapter/sequelize/index" },
|
||||
{ type: "doc", id: "reference/adapter/supabase/index" },
|
||||
{ type: "doc", id: "reference/adapter/typeorm/index" },
|
||||
{ type: "doc", id: "reference/adapter/upstash-redis/index" },
|
||||
{ type: "doc", id: "reference/adapter/xata/index" },
|
||||
],
|
||||
},
|
||||
]),
|
||||
{
|
||||
type: "category",
|
||||
label: "Database Adapters",
|
||||
link: { type: "doc", id: "reference/adapters/index" },
|
||||
items: [
|
||||
{ type: "doc", id: "reference/adapter/dgraph/index" },
|
||||
{ type: "doc", id: "reference/adapter/dynamodb/index" },
|
||||
{ type: "doc", id: "reference/adapter/fauna/index" },
|
||||
{ type: "doc", id: "reference/adapter/firebase/index" },
|
||||
{ type: "doc", id: "reference/adapter/mikro-orm/index" },
|
||||
{ type: "doc", id: "reference/adapter/mongodb/index" },
|
||||
{ type: "doc", id: "reference/adapter/neo4j/index" },
|
||||
{ type: "doc", id: "reference/adapter/pouchdb/index" },
|
||||
{ type: "doc", id: "reference/adapter/prisma/index" },
|
||||
{ type: "doc", id: "reference/adapter/sequelize/index" },
|
||||
{ type: "doc", id: "reference/adapter/supabase/index" },
|
||||
{ type: "doc", id: "reference/adapter/typeorm/index" },
|
||||
{ type: "doc", id: "reference/adapter/upstash-redis/index" },
|
||||
{ type: "doc", id: "reference/adapter/xata/index" },
|
||||
],
|
||||
},
|
||||
"reference/warnings",
|
||||
],
|
||||
conceptsSidebar: [
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
"excludeProtected": true,
|
||||
"hideHierarchy": true,
|
||||
"gitRevision": "main",
|
||||
"groupByReflections": false,
|
||||
"hideBreadcrumbs": true,
|
||||
"hideGenerator": true,
|
||||
"kindSortOrder": [
|
||||
@@ -37,11 +36,11 @@
|
||||
"SetSignature"
|
||||
],
|
||||
"readme": "none",
|
||||
"reflectionsWithOwnFile": "none",
|
||||
"sort": [
|
||||
"kind",
|
||||
"static-first",
|
||||
"required-first",
|
||||
"alphabetical"
|
||||
]
|
||||
],
|
||||
"symbolsWithOwnFile": "none"
|
||||
}
|
||||
@@ -11,11 +11,8 @@
|
||||
"clean": "turbo run clean --no-cache",
|
||||
"dev:db": "turbo run dev --parallel --continue --filter=next-auth-app...",
|
||||
"dev": "turbo run dev --parallel --continue --filter=next-auth-app... --filter=!./packages/adapter-*",
|
||||
"dev-v4:db": "turbo run dev --parallel --continue --filter=next-auth-app-v4...",
|
||||
"dev-v4": "turbo run dev --parallel --continue --filter=next-auth-app-v4... --filter=!./packages/adapter-*",
|
||||
"dev:kit": "turbo run dev --parallel --continue --filter=sveltekit-auth-app...",
|
||||
"dev:docs": "TYPEDOC_SKIP_ADAPTERS=1 turbo run dev --filter=docs",
|
||||
"dev:docs:adapters": "turbo run dev --filter=docs",
|
||||
"dev:docs": "turbo run dev --filter=docs",
|
||||
"email": "cd apps/dev/nextjs && pnpm email",
|
||||
"eslint": "eslint --cache .",
|
||||
"lint": "prettier --check .",
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/dgraph-adapter",
|
||||
"version": "1.0.6",
|
||||
"version": "1.0.5",
|
||||
"description": "Dgraph adapter for next-auth.",
|
||||
"homepage": "https://authjs.dev",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@next-auth/dynamodb-adapter",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
"version": "3.0.2",
|
||||
"version": "3.0.1",
|
||||
"description": "AWS DynamoDB adapter for next-auth.",
|
||||
"keywords": [
|
||||
"next-auth",
|
||||
@@ -59,4 +59,4 @@
|
||||
"dependencies": {
|
||||
"uuid": "^9.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/mongodb-adapter",
|
||||
"version": "1.1.3",
|
||||
"version": "1.1.1",
|
||||
"description": "mongoDB adapter for next-auth.",
|
||||
"homepage": "https://authjs.dev",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
@@ -31,7 +31,7 @@
|
||||
"dist"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"mongodb": "^5 || ^4",
|
||||
"mongodb": "^5 | ^4",
|
||||
"next-auth": "^4"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -44,4 +44,4 @@
|
||||
"jest": {
|
||||
"preset": "@next-auth/adapter-test/jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/pouchdb-adapter",
|
||||
"version": "1.0.0",
|
||||
"version": "0.1.6",
|
||||
"description": "PouchDB adapter for next-auth.",
|
||||
"homepage": "https://authjs.dev",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
@@ -58,4 +58,4 @@
|
||||
"jest": {
|
||||
"preset": "@next-auth/adapter-test/jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/prisma-adapter",
|
||||
"version": "1.0.6",
|
||||
"version": "1.0.5",
|
||||
"description": "Prisma adapter for next-auth.",
|
||||
"homepage": "https://authjs.dev",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
@@ -52,4 +52,4 @@
|
||||
"jest": {
|
||||
"preset": "@next-auth/adapter-test/jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/sequelize-adapter",
|
||||
"version": "1.0.8",
|
||||
"version": "1.0.7",
|
||||
"description": "Sequelize adapter for next-auth.",
|
||||
"homepage": "https://authjs.dev",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
@@ -42,4 +42,4 @@
|
||||
"jest": {
|
||||
"preset": "@next-auth/adapter-test/jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/typeorm-legacy-adapter",
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.1",
|
||||
"description": "TypeORM (legacy) adapter for next-auth.",
|
||||
"homepage": "https://authjs.dev",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
@@ -76,4 +76,4 @@
|
||||
"jest": {
|
||||
"preset": "@next-auth/adapter-test/jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@auth/core",
|
||||
"version": "0.7.0",
|
||||
"version": "0.5.1",
|
||||
"description": "Authentication for the Web.",
|
||||
"keywords": [
|
||||
"authentication",
|
||||
|
||||
@@ -337,36 +337,4 @@ export interface AuthConfig {
|
||||
/** @todo */
|
||||
trustHost?: boolean
|
||||
skipCSRFCheck?: typeof skipCSRFCheck
|
||||
/**
|
||||
* When set, during an OAuth sign-in flow,
|
||||
* the `redirect_uri` of the authorization request
|
||||
* will be set based on this value.
|
||||
*
|
||||
* This is useful if your OAuth Provider only supports a single `redirect_uri`
|
||||
* or you want to use OAuth on preview URLs (like Vercel), where you don't know the final deployment URL beforehand.
|
||||
*
|
||||
* The url needs to include the full path up to where Auth.js is initialized.
|
||||
*
|
||||
* @note This will auto-enable the `state` {@link OAuth2Config.checks} on the provider.
|
||||
*
|
||||
* @example
|
||||
* ```
|
||||
* "https://authjs.example.com/api/auth"
|
||||
* ```
|
||||
*
|
||||
* You can also override this individually for each provider.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* GitHub({
|
||||
* ...
|
||||
* redirectProxyUrl: "https://github.example.com/api/auth"
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* @default `AUTH_REDIRECT_PROXY_URL` environment variable
|
||||
*
|
||||
* See also: [Guide: Securing a Preview Deployment](https://authjs.dev/guides/basics/deployment#securing-a-preview-deployment)
|
||||
*/
|
||||
redirectProxyUrl?: string
|
||||
}
|
||||
|
||||
@@ -56,26 +56,10 @@ export async function init({
|
||||
providers: authOptions.providers,
|
||||
url,
|
||||
providerId,
|
||||
options: authOptions,
|
||||
})
|
||||
|
||||
const maxAge = 30 * 24 * 60 * 60 // Sessions expire after 30 days of being idle by default
|
||||
|
||||
let isOnRedirectProxy = false
|
||||
if (
|
||||
(provider?.type === "oauth" || provider?.type === "oidc") &&
|
||||
provider.redirectProxyUrl
|
||||
) {
|
||||
try {
|
||||
isOnRedirectProxy =
|
||||
new URL(provider.redirectProxyUrl).origin === url.origin
|
||||
} catch {
|
||||
throw new TypeError(
|
||||
`redirectProxyUrl must be a valid URL. Received: ${provider.redirectProxyUrl}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// User provided options are overridden by other options,
|
||||
// except for the options with special handling above
|
||||
const options: InternalOptions = {
|
||||
@@ -129,7 +113,6 @@ export async function init({
|
||||
callbacks: { ...defaultCallbacks, ...authOptions.callbacks },
|
||||
logger,
|
||||
callbackUrl: url.origin,
|
||||
isOnRedirectProxy,
|
||||
}
|
||||
|
||||
// Init cookies
|
||||
|
||||
@@ -15,7 +15,7 @@ import type { Cookie } from "../cookie.js"
|
||||
*/
|
||||
export async function getAuthorizationUrl(
|
||||
query: RequestInternal["query"],
|
||||
options: InternalOptions<"oauth" | "oidc">
|
||||
options: InternalOptions<"oauth">
|
||||
): Promise<ResponseInternal> {
|
||||
const { logger, provider } = options
|
||||
|
||||
@@ -41,21 +41,12 @@ export async function getAuthorizationUrl(
|
||||
}
|
||||
|
||||
const authParams = url.searchParams
|
||||
|
||||
let redirect_uri: string = provider.callbackUrl
|
||||
let data: object | undefined
|
||||
if (!options.isOnRedirectProxy && provider.redirectProxyUrl) {
|
||||
redirect_uri = provider.redirectProxyUrl
|
||||
data = { origin: provider.callbackUrl }
|
||||
logger.debug("using redirect proxy", { redirect_uri, data })
|
||||
}
|
||||
|
||||
const params = Object.assign(
|
||||
{
|
||||
response_type: "code",
|
||||
// clientId can technically be undefined, should we check this in assert.ts or rely on the Authorization Server to do it?
|
||||
client_id: provider.clientId,
|
||||
redirect_uri,
|
||||
redirect_uri: provider.callbackUrl,
|
||||
// @ts-expect-error TODO:
|
||||
...provider.authorization?.params,
|
||||
},
|
||||
@@ -67,7 +58,7 @@ export async function getAuthorizationUrl(
|
||||
|
||||
const cookies: Cookie[] = []
|
||||
|
||||
const state = await checks.state.create(options, data)
|
||||
const state = await checks.state.create(options)
|
||||
if (state) {
|
||||
authParams.set("state", state.value)
|
||||
cookies.push(state.cookie)
|
||||
@@ -77,7 +68,7 @@ export async function getAuthorizationUrl(
|
||||
if (as && !as.code_challenge_methods_supported?.includes("S256")) {
|
||||
// We assume S256 PKCE support, if the server does not advertise that,
|
||||
// a random `nonce` must be used for CSRF protection.
|
||||
if (provider.type === "oidc") provider.checks = ["nonce"] as any
|
||||
provider.checks = ["nonce"]
|
||||
} else {
|
||||
const { value, cookie } = await checks.pkce.create(options)
|
||||
authParams.set("code_challenge", value)
|
||||
|
||||
@@ -24,8 +24,7 @@ import type { Cookie } from "../cookie.js"
|
||||
export async function handleOAuth(
|
||||
query: RequestInternal["query"],
|
||||
cookies: RequestInternal["cookies"],
|
||||
options: InternalOptions<"oauth" | "oidc">,
|
||||
randomState?: string
|
||||
options: InternalOptions<"oauth">
|
||||
) {
|
||||
const { logger, provider } = options
|
||||
let as: o.AuthorizationServer
|
||||
@@ -72,12 +71,7 @@ export async function handleOAuth(
|
||||
|
||||
const resCookies: Cookie[] = []
|
||||
|
||||
const state = await checks.state.use(
|
||||
cookies,
|
||||
resCookies,
|
||||
options,
|
||||
randomState
|
||||
)
|
||||
const state = await checks.state.use(cookies, resCookies, options)
|
||||
|
||||
const codeGrantParams = o.validateAuthResponse(
|
||||
as,
|
||||
@@ -97,15 +91,11 @@ export async function handleOAuth(
|
||||
|
||||
const codeVerifier = await checks.pkce.use(cookies, resCookies, options)
|
||||
|
||||
let redirect_uri = provider.callbackUrl
|
||||
if (!options.isOnRedirectProxy && provider.redirectProxyUrl) {
|
||||
redirect_uri = provider.redirectProxyUrl
|
||||
}
|
||||
let codeGrantResponse = await o.authorizationCodeGrantRequest(
|
||||
as,
|
||||
client,
|
||||
codeGrantParams,
|
||||
redirect_uri,
|
||||
provider.callbackUrl,
|
||||
codeVerifier ?? "auth" // TODO: review fallback code verifier
|
||||
)
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as jose from "jose"
|
||||
import * as o from "oauth4webapi"
|
||||
import { InvalidCheck } from "../../errors.js"
|
||||
import { decode, encode } from "../../jwt.js"
|
||||
import { encode, decode } from "../../jwt.js"
|
||||
|
||||
import type {
|
||||
CookiesOptions,
|
||||
@@ -19,8 +18,7 @@ export async function signCookie(
|
||||
type: keyof CookiesOptions,
|
||||
value: string,
|
||||
maxAge: number,
|
||||
options: InternalOptions<"oauth" | "oidc">,
|
||||
data?: any
|
||||
options: InternalOptions<"oauth">
|
||||
): Promise<Cookie> {
|
||||
const { cookies, logger } = options
|
||||
|
||||
@@ -28,11 +26,13 @@ export async function signCookie(
|
||||
|
||||
const expires = new Date()
|
||||
expires.setTime(expires.getTime() + maxAge * 1000)
|
||||
const token: any = { value }
|
||||
if (type === "state" && data) token.data = data
|
||||
return {
|
||||
name: cookies[type].name,
|
||||
value: await encode({ ...options.jwt, maxAge, token }),
|
||||
value: await encode<CheckPayload>({
|
||||
...options.jwt,
|
||||
maxAge,
|
||||
token: { value },
|
||||
}),
|
||||
options: { ...cookies[type].options, expires },
|
||||
}
|
||||
}
|
||||
@@ -92,45 +92,14 @@ export const pkce = {
|
||||
}
|
||||
|
||||
const STATE_MAX_AGE = 60 * 15 // 15 minutes in seconds
|
||||
export function decodeState(value: string):
|
||||
| {
|
||||
/** If defined, a redirect proxy is being used to support multiple OAuth apps with a single callback URL */
|
||||
origin?: string
|
||||
/** Random value for CSRF protection */
|
||||
random: string
|
||||
}
|
||||
| undefined {
|
||||
try {
|
||||
const decoder = new TextDecoder()
|
||||
return JSON.parse(decoder.decode(jose.base64url.decode(value)))
|
||||
} catch {}
|
||||
}
|
||||
|
||||
export const state = {
|
||||
async create(options: InternalOptions<"oauth">, data?: object) {
|
||||
const { provider } = options
|
||||
if (!provider.checks.includes("state")) {
|
||||
if (data) {
|
||||
throw new InvalidCheck(
|
||||
"State data was provided but the provider is not configured to use state."
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const encodedState = jose.base64url.encode(
|
||||
JSON.stringify({ ...data, random: o.generateRandomState() })
|
||||
)
|
||||
|
||||
async create(options: InternalOptions<"oauth">) {
|
||||
if (!options.provider.checks.includes("state")) return
|
||||
// TODO: support customizing the state
|
||||
const value = o.generateRandomState()
|
||||
const maxAge = STATE_MAX_AGE
|
||||
const cookie = await signCookie(
|
||||
"state",
|
||||
encodedState,
|
||||
maxAge,
|
||||
options,
|
||||
data
|
||||
)
|
||||
return { cookie, value: encodedState }
|
||||
const cookie = await signCookie("state", value, maxAge, options)
|
||||
return { cookie, value }
|
||||
},
|
||||
/**
|
||||
* Returns state if the provider is configured to use state,
|
||||
@@ -142,8 +111,7 @@ export const state = {
|
||||
async use(
|
||||
cookies: RequestInternal["cookies"],
|
||||
resCookies: Cookie[],
|
||||
options: InternalOptions<"oauth">,
|
||||
paramRandom?: string
|
||||
options: InternalOptions<"oauth">
|
||||
): Promise<string | undefined> {
|
||||
const { provider } = options
|
||||
if (!provider.checks.includes("state")) return
|
||||
@@ -153,23 +121,10 @@ export const state = {
|
||||
if (!state) throw new InvalidCheck("State cookie was missing.")
|
||||
|
||||
// IDEA: Let the user do something with the returned state
|
||||
const encodedState = await decode<CheckPayload>({
|
||||
...options.jwt,
|
||||
token: state,
|
||||
})
|
||||
const value = await decode<CheckPayload>({ ...options.jwt, token: state })
|
||||
|
||||
if (!encodedState?.value)
|
||||
throw new InvalidCheck("State (cookie) value could not be parsed.")
|
||||
|
||||
const decodedState = decodeState(encodedState.value)
|
||||
|
||||
if (!decodedState)
|
||||
throw new InvalidCheck("State (encoded) value could not be parsed.")
|
||||
|
||||
if (decodedState.random !== paramRandom)
|
||||
throw new InvalidCheck(
|
||||
`Random state values did not match. Expected: ${decodedState.random}. Got: ${paramRandom}`
|
||||
)
|
||||
if (!value?.value)
|
||||
throw new InvalidCheck("State value could not be parsed.")
|
||||
|
||||
// Clear the state cookie after use
|
||||
resCookies.push({
|
||||
@@ -178,13 +133,13 @@ export const state = {
|
||||
options: { ...options.cookies.state.options, maxAge: 0 },
|
||||
})
|
||||
|
||||
return encodedState.value
|
||||
return value.value
|
||||
},
|
||||
}
|
||||
|
||||
const NONCE_MAX_AGE = 60 * 15 // 15 minutes in seconds
|
||||
export const nonce = {
|
||||
async create(options: InternalOptions<"oidc">) {
|
||||
async create(options: InternalOptions<"oauth">) {
|
||||
if (!options.provider.checks.includes("nonce")) return
|
||||
const value = o.generateRandomNonce()
|
||||
const maxAge = NONCE_MAX_AGE
|
||||
@@ -201,7 +156,7 @@ export const nonce = {
|
||||
async use(
|
||||
cookies: RequestInternal["cookies"],
|
||||
resCookies: Cookie[],
|
||||
options: InternalOptions<"oidc">
|
||||
options: InternalOptions<"oauth">
|
||||
): Promise<string | undefined> {
|
||||
const { provider } = options
|
||||
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
import { InvalidCheck } from "../../errors.js"
|
||||
import { decodeState } from "./checks.js"
|
||||
|
||||
import type { OAuthConfigInternal } from "../../providers/oauth.js"
|
||||
import type { InternalOptions, RequestInternal } from "../../types.js"
|
||||
|
||||
/**
|
||||
* When the authorization flow contains a state, we check if it's a redirect proxy
|
||||
* and if so, we return the random state and the original redirect URL.
|
||||
*/
|
||||
export function handleState(
|
||||
query: RequestInternal["query"],
|
||||
provider: OAuthConfigInternal<any>,
|
||||
isOnRedirectProxy: InternalOptions["isOnRedirectProxy"]
|
||||
) {
|
||||
let randomState: string | undefined
|
||||
let proxyRedirect: string | undefined
|
||||
|
||||
if (provider.redirectProxyUrl && !query?.state) {
|
||||
throw new InvalidCheck(
|
||||
"Missing state in query, but required for redirect proxy"
|
||||
)
|
||||
}
|
||||
|
||||
const state = decodeState(query?.state)
|
||||
randomState = state?.random
|
||||
|
||||
if (isOnRedirectProxy) {
|
||||
if (!state?.origin) return { randomState }
|
||||
proxyRedirect = `${state.origin}?${new URLSearchParams(query)}`
|
||||
}
|
||||
|
||||
return { randomState, proxyRedirect }
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { merge } from "./utils/merge.js"
|
||||
|
||||
import type { InternalProvider } from "../types.js"
|
||||
import type {
|
||||
OAuthConfig,
|
||||
OAuthConfigInternal,
|
||||
@@ -7,7 +8,6 @@ import type {
|
||||
OAuthUserConfig,
|
||||
Provider,
|
||||
} from "../providers/index.js"
|
||||
import type { AuthConfig, InternalProvider } from "../types.js"
|
||||
|
||||
/**
|
||||
* Adds `signinUrl` and `callbackUrl` to each provider
|
||||
@@ -17,12 +17,11 @@ export default function parseProviders(params: {
|
||||
providers: Provider[]
|
||||
url: URL
|
||||
providerId?: string
|
||||
options: AuthConfig
|
||||
}): {
|
||||
providers: InternalProvider[]
|
||||
provider?: InternalProvider
|
||||
} {
|
||||
const { url, providerId, options } = params
|
||||
const { url, providerId } = params
|
||||
|
||||
const providers = params.providers.map((p) => {
|
||||
const provider = typeof p === "function" ? p() : p
|
||||
@@ -35,7 +34,6 @@ export default function parseProviders(params: {
|
||||
})
|
||||
|
||||
if (provider.type === "oauth" || provider.type === "oidc") {
|
||||
merged.redirectProxyUrl ??= options.redirectProxyUrl
|
||||
return normalizeOAuth(merged)
|
||||
}
|
||||
|
||||
@@ -64,17 +62,11 @@ function normalizeOAuth(
|
||||
|
||||
const userinfo = normalizeEndpoint(c.userinfo, c.issuer)
|
||||
|
||||
const checks = c.checks ?? ["pkce"]
|
||||
if (c.redirectProxyUrl) {
|
||||
if (!checks.includes("state")) checks.push("state")
|
||||
c.redirectProxyUrl = `${c.redirectProxyUrl}/callback/${c.id}`
|
||||
}
|
||||
|
||||
return {
|
||||
...c,
|
||||
authorization,
|
||||
token,
|
||||
checks,
|
||||
checks: c.checks ?? ["pkce"],
|
||||
userinfo,
|
||||
profile: c.profile ?? defaultProfile,
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { CallbackRouteError, Verification } from "../../errors.js"
|
||||
import { handleLogin } from "../callback-handler.js"
|
||||
import { CallbackRouteError, Verification } from "../../errors.js"
|
||||
import { handleOAuth } from "../oauth/callback.js"
|
||||
import { handleState } from "../oauth/handle-state.js"
|
||||
import { createHash } from "../web.js"
|
||||
import { handleAuthorized } from "./shared.js"
|
||||
|
||||
import type { AdapterSession } from "../../adapters.js"
|
||||
import type {
|
||||
Account,
|
||||
InternalOptions,
|
||||
RequestInternal,
|
||||
ResponseInternal,
|
||||
InternalOptions,
|
||||
Account,
|
||||
} from "../../types.js"
|
||||
import type { Cookie, SessionStore } from "../cookie.js"
|
||||
|
||||
@@ -44,22 +43,10 @@ export async function callback(params: {
|
||||
|
||||
try {
|
||||
if (provider.type === "oauth" || provider.type === "oidc") {
|
||||
const { proxyRedirect, randomState } = handleState(
|
||||
query,
|
||||
provider,
|
||||
options.isOnRedirectProxy
|
||||
)
|
||||
|
||||
if (proxyRedirect) {
|
||||
logger.debug("proxy redirect", { proxyRedirect, randomState })
|
||||
return { redirect: proxyRedirect }
|
||||
}
|
||||
|
||||
const authorizationResult = await handleOAuth(
|
||||
query,
|
||||
params.cookies,
|
||||
options,
|
||||
randomState
|
||||
options
|
||||
)
|
||||
|
||||
if (authorizationResult.cookies.length) {
|
||||
|
||||
@@ -18,7 +18,7 @@ import type {
|
||||
export async function signin(
|
||||
query: RequestInternal["query"],
|
||||
body: RequestInternal["body"],
|
||||
options: InternalOptions<"oauth" | "oidc" | "email">
|
||||
options: InternalOptions<"oauth" | "email">
|
||||
): Promise<ResponseInternal> {
|
||||
const { url, logger, provider } = options
|
||||
try {
|
||||
|
||||
@@ -37,7 +37,7 @@ export async function toInternalRequest(
|
||||
|
||||
const action = actions.find((a) => pathname.includes(a))
|
||||
if (!action) {
|
||||
throw new UnknownAction(`Cannot detect action in pathname (${pathname}).`)
|
||||
throw new UnknownAction("Cannot detect action.")
|
||||
}
|
||||
|
||||
if (req.method !== "GET" && req.method !== "POST") {
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
/**
|
||||
* <div style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
|
||||
* <span>Built-in <b>Apple</b> integration.</span>
|
||||
* <a href="https://apple.com">
|
||||
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/apple-dark.svg" height="48" width="48"/>
|
||||
* <div style={{display: "flex", justifyContent: "space-between", alignItems: "center"}}>
|
||||
* <span style={{fontSize: "1.35rem" }}>
|
||||
* Built-in sign in with <b>Apple</b> integration.
|
||||
* </span>
|
||||
* <a href="https://apple.com" style={{backgroundColor: "black", padding: "12px", borderRadius: "100%" }}>
|
||||
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/apple-dark.svg" width="24"/>
|
||||
* </a>
|
||||
* </div>
|
||||
*
|
||||
* ---
|
||||
* @module providers/apple
|
||||
*/
|
||||
|
||||
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
|
||||
|
||||
/**
|
||||
* See more at:
|
||||
* [Retrieve the User's Information from Apple ID Servers
|
||||
](https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api/authenticating_users_with_sign_in_with_apple#3383773)
|
||||
*/
|
||||
/** The returned user profile from Apple when using the profile callback. */
|
||||
export interface AppleProfile extends Record<string, any> {
|
||||
/**
|
||||
* The issuer registered claim identifies the principal that issued the identity token.
|
||||
@@ -99,6 +96,44 @@ export interface AppleProfile extends Record<string, any> {
|
||||
auth_time: number
|
||||
}
|
||||
|
||||
/**
|
||||
* ## Setup
|
||||
*
|
||||
* Import the provider and configure it in your **Auth.js** initialization file:
|
||||
*
|
||||
* ```ts title="pages/api/auth/[...nextauth].ts"
|
||||
* import NextAuth from "next-auth"
|
||||
* import AppleProvider from "next-auth/providers/apple"
|
||||
*
|
||||
* export default NextAuth({
|
||||
* providers: [
|
||||
* AppleProvider({
|
||||
* clientId: process.env.GITHUB_ID,
|
||||
* clientSecret: process.env.GITHUB_SECRET,
|
||||
* }),
|
||||
* ],
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* ## Resources
|
||||
*
|
||||
* - Sign in with Apple [Overview](https://developer.apple.com/sign-in-with-apple/get-started/)
|
||||
* - Sign in with Apple [REST API](https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api)
|
||||
* - [How to retrieve](https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api/authenticating_users_with_sign_in_with_apple#3383773) the user's information from Apple ID servers
|
||||
* - [Learn more about OAuth](https://authjs.dev/concepts/oauth)
|
||||
|
||||
* ## Notes
|
||||
*
|
||||
* The Apple provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/apple.ts). To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
|
||||
*
|
||||
* ## Help
|
||||
*
|
||||
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
|
||||
*
|
||||
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
|
||||
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
|
||||
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
|
||||
*/
|
||||
export default function Apple<P extends AppleProfile>(
|
||||
options: Omit<OAuthUserConfig<P>, "clientSecret"> & {
|
||||
/**
|
||||
|
||||
@@ -1,95 +1,100 @@
|
||||
/**
|
||||
* <div style={{backgroundColor: "#24292f", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
|
||||
* <span>Built-in <b>Asgardeo</b> integration.</span>
|
||||
* <a href="https://wso2.com/asgardeo/">
|
||||
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/asgardeo-dark.svg" height="48" width="48"/>
|
||||
* <div style={{display: "flex", justifyContent: "space-between", alignItems: "center"}}>
|
||||
* <span style={{fontSize: "1.35rem" }}>
|
||||
* Built-in sign in with <b>Asgardeo</b> integration.
|
||||
* </span>
|
||||
* <a href="https://wso2.com/asgardeo/" style={{backgroundColor: "#ECEFF1", padding: "12px", borderRadius: "100%" }}>
|
||||
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/asgardeo-dark.svg" width="24"/>
|
||||
* </a>
|
||||
* </div>
|
||||
*
|
||||
* ---
|
||||
* @module providers/asgardeo
|
||||
*/
|
||||
|
||||
import type { OIDCConfig, OIDCUserConfig } from "./index.js"
|
||||
|
||||
export interface AsgardeoProfile {
|
||||
/** The returned user profile from Asgardeo when using the profile callback. */
|
||||
export interface AsgardeoProfile extends Record<string, any> {
|
||||
/**
|
||||
* The user Asgardeo account ID
|
||||
*/
|
||||
sub: string
|
||||
/**
|
||||
* The user name
|
||||
*/
|
||||
given_name: string
|
||||
/**
|
||||
* The user email
|
||||
*/
|
||||
email: string
|
||||
/**
|
||||
* The user profile picture
|
||||
*/
|
||||
picture: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Asgardeo login to your page.
|
||||
* ## Documentation
|
||||
*
|
||||
* https://wso2.com/asgardeo/docs/guides/authentication
|
||||
* ## Setup
|
||||
*
|
||||
* Import the provider and configure it in your **Auth.js** initialization file:
|
||||
*
|
||||
* ## Instructions
|
||||
* ```ts title="pages/api/auth/[...nextauth].ts"
|
||||
* import NextAuth from "next-auth"
|
||||
* import AsgardeoProvider from "next-auth/providers/asgardeo";
|
||||
*
|
||||
* - Log into https://console.asgardeo.io.
|
||||
* - Next, go to "Application" tab (More info: https://wso2.com/asgardeo/docs/guides/applications/register-oidc-web-app/).
|
||||
* - Register standard based - Open id connect, application.
|
||||
* - Add callback URL: http://localhost:3000/api/auth/callback/asgardeo and https://your-domain.com/api/auth/callback/asgardeo
|
||||
* - After registering the application, go to protocol tab.
|
||||
* - Check `code` grant type.
|
||||
* - Add Authorized redirect URLs & Allowed origins fields.
|
||||
* - Make Email, First Name, Photo URL user attributes mandatory from the console.
|
||||
*
|
||||
* Create a `.env` file in the project root add the following entries:
|
||||
*
|
||||
* These values can be collected from the application created.
|
||||
*
|
||||
* ```
|
||||
* ASGARDEO_CLIENT_ID=<Copy client ID from protocol tab here>
|
||||
* ASGARDEO_CLIENT_SECRET=<Copy client from protocol tab here>
|
||||
* ASGARDEO_ISSUER=<Copy the issuer url from the info tab here>
|
||||
* export default NextAuth({
|
||||
* providers: [
|
||||
* AsgardeoProvider({
|
||||
* clientId: process.env.ASGARDEO_CLIENT_ID,
|
||||
* clientSecret: process.env.ASGARDEO_CLIENT_SECRET,
|
||||
* issuer: process.env.ASGARDEO_ISSUER
|
||||
* }),
|
||||
* ],
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* In `pages/api/auth/[...nextauth].js` find or add the `Asgardeo` entries:
|
||||
* ### Configuring Asgardeo
|
||||
*
|
||||
* ```js
|
||||
* import Asgardeo from "next-auth/providers/asgardeo";
|
||||
* ...
|
||||
* providers: [
|
||||
* Asgardeo({
|
||||
* clientId: process.env.ASGARDEO_CLIENT_ID,
|
||||
* clientSecret: process.env.ASGARDEO_CLIENT_SECRET,
|
||||
* issuer: process.env.ASGARDEO_ISSUER
|
||||
* }),
|
||||
* ],
|
||||
* Follow these steps:
|
||||
*
|
||||
* ...
|
||||
* 1. Log into the [Asgardeo console](https://console.asgardeo.io)
|
||||
* 2. Next, go to "Application" tab (more info [here](https://wso2.com/asgardeo/docs/guides/applications/register-oidc-web-app/))
|
||||
* 3. Register a standard based, Open ID connect, application
|
||||
* 4. Add the **callback URLs**: `http://localhost:3000/api/auth/callback/asgardeo` (development) and `https://{YOUR_DOMAIN}.com/api/auth/callback/asgardeo` (production)
|
||||
* 5. After registering the application, go to "Protocol" tab.
|
||||
* 6. Check `code` as the grant type.
|
||||
* 7. Add "Authorized redirect URLs" & "Allowed origins fields"
|
||||
* 8. Make Email, First Name, Photo URL user attributes mandatory from the console.
|
||||
*
|
||||
* Then, create a `.env` file in the project root add the following entries:
|
||||
*
|
||||
* ```
|
||||
* ASGARDEO_CLIENT_ID="Copy client ID from protocol tab here"
|
||||
* ASGARDEO_CLIENT_SECRET="Copy client from protocol tab here"
|
||||
* ASGARDEO_ISSUER="Copy the issuer url from the info tab here"
|
||||
* ```
|
||||
*
|
||||
* ## Resources
|
||||
*
|
||||
* @see [Asgardeo - Authentication Guide](https://wso2.com/asgardeo/docs/guides/authentication)
|
||||
* @see [Learn more about OAuth](https://authjs.dev/concepts/oauth)
|
||||
* @see [Source code](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/asgardeo.ts)
|
||||
* - [Asgardeo - Authentication Guide](https://wso2.com/asgardeo/docs/guides/authentication)
|
||||
* - [Learn more about OAuth](https://authjs.dev/concepts/oauth)
|
||||
*
|
||||
* ## Notes
|
||||
*
|
||||
* By default, Auth.js assumes that the Asgardeo provider is
|
||||
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
|
||||
*
|
||||
* :::tip
|
||||
*
|
||||
* The Asgardeo provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/asgardeo.ts).
|
||||
* To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
|
||||
* The Asgardeo provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/asgardeo.ts). To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
|
||||
*
|
||||
* :::info
|
||||
* By default, Auth.js assumes that the Asgardeo provider is based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) spec
|
||||
* :::
|
||||
*
|
||||
* :::info **Disclaimer**
|
||||
* ## Help
|
||||
*
|
||||
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
|
||||
*
|
||||
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
|
||||
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
|
||||
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
|
||||
*
|
||||
* :::
|
||||
*/
|
||||
export default function Asgardeo(
|
||||
config: OIDCUserConfig<AsgardeoProfile>
|
||||
|
||||
@@ -1,12 +1,72 @@
|
||||
/**
|
||||
* <div style={{display: "flex", justifyContent: "space-between", alignItems: "center"}}>
|
||||
* <span style={{fontSize: "1.35rem" }}>
|
||||
* Built-in sign in with <b>Atlassian</b> integration.
|
||||
* </span>
|
||||
* <a href="https://www.atlassian.com/" style={{backgroundColor: "black", padding: "12px", borderRadius: "100%" }}>
|
||||
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/atlassian.svg" width="24" style={{ marginTop: "-3px"}} />
|
||||
* </a>
|
||||
* </div>
|
||||
*
|
||||
* @module providers/atlassian
|
||||
*/
|
||||
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
|
||||
|
||||
interface AtlassianProfile extends Record<string, any> {
|
||||
/** The returned user profile from Atlassian when using the profile callback. */
|
||||
export interface AtlassianProfile extends Record<string, any> {
|
||||
/**
|
||||
* The user's atlassian account ID
|
||||
*/
|
||||
account_id: string
|
||||
/**
|
||||
* The user name
|
||||
*/
|
||||
name: string
|
||||
/**
|
||||
* The user's email
|
||||
*/
|
||||
email: string
|
||||
/**
|
||||
* The user's profile picture
|
||||
*/
|
||||
picture: string
|
||||
}
|
||||
|
||||
/**
|
||||
* ## Setup
|
||||
*
|
||||
* Import the provider and configure it in your **Auth.js** initialization file:
|
||||
*
|
||||
* ```ts title="pages/api/auth/[...nextauth].ts"
|
||||
* import NextAuth from "next-auth"
|
||||
* import AtlassianProvider from "next-auth/providers/atlassian"
|
||||
*
|
||||
* export default NextAuth({
|
||||
* providers: [
|
||||
* AtlassianProvider({
|
||||
* clientId: process.env.ATLASSIAN_ID,
|
||||
* clientSecret: process.env.ATLASSIAN_SECRET,
|
||||
* }),
|
||||
* ],
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* ## Resources
|
||||
*
|
||||
* - [Atlassian docs](https://developer.atlassian.com/server/jira/platform/oauth/)
|
||||
*
|
||||
* ## Notes
|
||||
*
|
||||
* The Atlassian provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/atlassian.ts). To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
|
||||
*
|
||||
* ## Help
|
||||
*
|
||||
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
|
||||
*
|
||||
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
|
||||
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
|
||||
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
|
||||
*/
|
||||
export default function Atlassian<P extends AtlassianProfile>(
|
||||
options: OAuthUserConfig<P>
|
||||
): OAuthConfig<P> {
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
/**
|
||||
* <div style={{backgroundColor: "#EB5424", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
|
||||
* <span>Built-in <b>Auth0</b> integration.</span>
|
||||
* <a href="https://auth0.com">
|
||||
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/auth0-dark.svg" height="48" width="48"/>
|
||||
* <div style={{display: "flex", justifyContent: "space-between", alignItems: "center"}}>
|
||||
* <span style={{fontSize: "1.35rem" }}>
|
||||
* Built-in sign in with <b>Auth0</b> integration.
|
||||
* </span>
|
||||
* <a href="https://auth0.com" style={{backgroundColor: "black", padding: "12px", borderRadius: "100%" }}>
|
||||
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/auth0-dark.svg" width="24"/>
|
||||
* </a>
|
||||
* </div>
|
||||
*
|
||||
* ---
|
||||
* @module providers/auth0
|
||||
*/
|
||||
|
||||
import type { OIDCConfig, OIDCUserConfig } from "./index.js"
|
||||
|
||||
/** @see [User Profile Structure](https://auth0.com/docs/manage-users/user-accounts/user-profiles/user-profile-structure) */
|
||||
export interface Auth0Profile {
|
||||
/** The returned user profile from Auth0 when using the profile callback. [Reference](https://auth0.com/docs/manage-users/user-accounts/user-profiles/user-profile-structure). */
|
||||
export interface Auth0Profile extends Record<string, any> {
|
||||
/** The user's unique identifier. */
|
||||
sub: string
|
||||
/** Custom fields that store info about a user that influences the user's access, such as support plan, security roles (if not using the Authorization Core feature set), or access control groups. To learn more, read Metadata Overview. */
|
||||
@@ -75,51 +75,40 @@ export interface Auth0Profile {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Auth0 login to your page.
|
||||
* ## Setup
|
||||
*
|
||||
* ## Example
|
||||
* Import the provider and configure it in your **Auth.js** initialization file:
|
||||
*
|
||||
* ```ts
|
||||
* import { Auth } from "@auth/core"
|
||||
* import Auth0 from "@auth/core/providers/auth0"
|
||||
* ```ts title="pages/api/auth/[...nextauth].ts"
|
||||
* import NextAuth from "next-auth"
|
||||
* import Auth0Provider from "next-auth/providers/auth0"
|
||||
*
|
||||
* const request = new Request("https://example.com")
|
||||
* const response = await Auth(request, {
|
||||
* providers: [Auth0({ clientId: "", clientSecret: "", issuer: "" })],
|
||||
* export default NextAuth({
|
||||
* providers: [
|
||||
* Auth0Provider({
|
||||
* clientId: process.env.AUTH0_ID,
|
||||
* clientSecret: process.env.AUTH0_SECRET,
|
||||
* }),
|
||||
* ],
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* ## Resources
|
||||
*
|
||||
* - [Authenticate - Auth0 docs](https://auth0.com/docs/authenticate)
|
||||
*
|
||||
* ---
|
||||
* - [Auth0 docs](https://auth0.com/docs/authenticate)
|
||||
*
|
||||
* ## Notes
|
||||
*
|
||||
* By default, Auth.js assumes that the Auth0 provider is
|
||||
* based on the [OIDC](https://openid.net/specs/openid-connect-core-1_0.html) specification.
|
||||
* The Auth0 provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/auth0.ts). To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
|
||||
*
|
||||
* :::tip
|
||||
*
|
||||
* The Auth0 provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/auth0.ts).
|
||||
* To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
|
||||
*
|
||||
* :::
|
||||
*
|
||||
* :::info **Disclaimer**
|
||||
* ## Help
|
||||
*
|
||||
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
|
||||
*
|
||||
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
|
||||
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
|
||||
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
|
||||
*
|
||||
* :::
|
||||
*/
|
||||
|
||||
export default function Auth0(
|
||||
config: OIDCUserConfig<Auth0Profile>
|
||||
): OIDCConfig<Auth0Profile> {
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
import type { Client } from "oauth4webapi"
|
||||
import type { Awaitable, Profile, TokenSet, User } from "../types.js"
|
||||
import type { CommonProviderOptions } from "../providers/index.js"
|
||||
import type {
|
||||
AuthConfig,
|
||||
Awaitable,
|
||||
Profile,
|
||||
TokenSet,
|
||||
User,
|
||||
} from "../types.js"
|
||||
|
||||
// TODO: fix types
|
||||
// TODO:
|
||||
type AuthorizationParameters = any
|
||||
type CallbackParamsType = any
|
||||
type IssuerMetadata = any
|
||||
@@ -101,7 +95,7 @@ export interface OAuthProviderButtonStyles {
|
||||
textDark: string
|
||||
}
|
||||
|
||||
/** TODO: Document */
|
||||
/** TODO: */
|
||||
export interface OAuth2Config<Profile>
|
||||
extends CommonProviderOptions,
|
||||
PartialIssuer {
|
||||
@@ -149,14 +143,11 @@ export interface OAuth2Config<Profile>
|
||||
* The CSRF protection performed on the callback endpoint.
|
||||
* @default ["pkce"]
|
||||
*
|
||||
* @note When `redirectProxyUrl` or {@link AuthConfig.redirectProxyUrl} is set,
|
||||
* `"state"` will be added to checks automatically.
|
||||
*
|
||||
* [RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients (PKCE)](https://www.rfc-editor.org/rfc/rfc7636.html#section-4) |
|
||||
* [RFC 6749 - The OAuth 2.0 Authorization Framework](https://www.rfc-editor.org/rfc/rfc6749.html#section-4.1.1) |
|
||||
* [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html#IDToken) |
|
||||
*/
|
||||
checks?: Array<"pkce" | "state" | "none">
|
||||
checks?: Array<"pkce" | "state" | "none" | "nonce">
|
||||
clientId?: string
|
||||
clientSecret?: string
|
||||
/**
|
||||
@@ -166,20 +157,9 @@ export interface OAuth2Config<Profile>
|
||||
client?: Partial<Client>
|
||||
style?: OAuthProviderButtonStyles
|
||||
/**
|
||||
* Normally, when you sign in with an OAuth provider and another account
|
||||
* with the same email address already exists,
|
||||
* the accounts are not linked automatically.
|
||||
*
|
||||
* Automatic account linking on sign in is not secure
|
||||
* between arbitrary providers and is disabled by default.
|
||||
* Learn more in our [Security FAQ](https://authjs.dev/reference/faq#security).
|
||||
*
|
||||
* However, it may be desirable to allow automatic account linking if you trust that the provider involved has securely verified the email address
|
||||
* associated with the account. Set `allowDangerousEmailAccountLinking: true`
|
||||
* to enable automatic account linking.
|
||||
* [Documentation](https://authjs.dev/reference/providers/oauth#allowdangerousemailaccountlinking-option)
|
||||
*/
|
||||
allowDangerousEmailAccountLinking?: boolean
|
||||
redirectProxyUrl?: AuthConfig["redirectProxyUrl"]
|
||||
/**
|
||||
* The options provided by the user.
|
||||
* We will perform a deep-merge of these values
|
||||
@@ -190,11 +170,10 @@ export interface OAuth2Config<Profile>
|
||||
options?: OAuthUserConfig<Profile>
|
||||
}
|
||||
|
||||
/** TODO: Document */
|
||||
/** TODO: */
|
||||
export interface OIDCConfig<Profile>
|
||||
extends Omit<OAuth2Config<Profile>, "type" | "checks"> {
|
||||
extends Omit<OAuth2Config<Profile>, "type"> {
|
||||
type: "oidc"
|
||||
checks?: OAuth2Config<Profile>["checks"] & Array<"nonce">
|
||||
}
|
||||
|
||||
export type OAuthConfig<Profile> = OIDCConfig<Profile> | OAuth2Config<Profile>
|
||||
@@ -207,7 +186,7 @@ export type OAuthEndpointType = "authorization" | "token" | "userinfo"
|
||||
*/
|
||||
export type OAuthConfigInternal<Profile> = Omit<
|
||||
OAuthConfig<Profile>,
|
||||
OAuthEndpointType | "redirectProxyUrl"
|
||||
OAuthEndpointType
|
||||
> & {
|
||||
authorization?: { url: URL }
|
||||
token?: {
|
||||
@@ -217,24 +196,8 @@ export type OAuthConfigInternal<Profile> = Omit<
|
||||
conform?: TokenEndpointHandler["conform"]
|
||||
}
|
||||
userinfo?: { url: URL; request?: UserinfoEndpointHandler["request"] }
|
||||
/**
|
||||
* Reconstructed from {@link OAuth2Config.redirectProxyUrl},
|
||||
* adding the callback action and provider id onto the URL.
|
||||
*
|
||||
* If defined, it is favoured over {@link OAuthConfigInternal.callbackUrl} in the authorization request.
|
||||
*
|
||||
* When {@link InternalOptions.isOnRedirectProxy} is set, the actual value is saved in the decoded `state.origin` parameter.
|
||||
*
|
||||
* @example `"https://auth.example.com/api/auth/callback/:provider"`
|
||||
*
|
||||
*/
|
||||
redirectProxyUrl?: OAuth2Config<Profile>["redirectProxyUrl"]
|
||||
} & Pick<Required<OAuthConfig<Profile>>, "clientId" | "checks" | "profile">
|
||||
|
||||
export type OIDCConfigInternal<Profile> = OAuthConfigInternal<Profile> & {
|
||||
checks: OIDCConfig<Profile>["checks"]
|
||||
}
|
||||
|
||||
export type OAuthUserConfig<Profile> = Omit<
|
||||
Partial<OAuthConfig<Profile>>,
|
||||
"options" | "type"
|
||||
|
||||
@@ -4,6 +4,7 @@ export default function OneLogin(options) {
|
||||
id: "onelogin",
|
||||
name: "OneLogin",
|
||||
type: "oidc",
|
||||
// TODO: Verify if issuer has "oidc/2" and remove if it does
|
||||
wellKnown: `${options.issuer}/oidc/2/.well-known/openid-configuration`,
|
||||
options,
|
||||
}
|
||||
|
||||
@@ -1,3 +1,48 @@
|
||||
/**
|
||||
* Add Salesforce login to your page.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```ts
|
||||
* import { Auth } from "@auth/core"
|
||||
* import Salesforce from "@auth/core/providers/salesforce"
|
||||
*
|
||||
* const request = new Request("https://example.com")
|
||||
* const response = await AuthHandler(request, {
|
||||
* providers: [Salesforce({ clientId: "", clientSecret: "" })],
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* ## Resources
|
||||
*
|
||||
* - [](https://example.com)
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* ## Notes
|
||||
*
|
||||
* By default, Auth.js assumes that the Salesforce provider is
|
||||
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
|
||||
*
|
||||
* :::tip
|
||||
*
|
||||
* The Salesforce provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/.ts).
|
||||
* To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
|
||||
*
|
||||
* :::
|
||||
*
|
||||
* :::info **Disclaimer**
|
||||
*
|
||||
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
|
||||
*
|
||||
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
|
||||
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
|
||||
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
|
||||
*
|
||||
* :::
|
||||
*/
|
||||
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
|
||||
|
||||
export interface SalesforceProfile extends Record<string, any> {
|
||||
|
||||
@@ -22,6 +22,7 @@ export default function Trakt<P extends TraktUser>(
|
||||
id: "trakt",
|
||||
name: "Trakt",
|
||||
type: "oauth",
|
||||
// when default, trakt returns auth error. TODO: Does it?
|
||||
authorization: "https://trakt.tv/oauth/authorize?scope=",
|
||||
token: "https://api.trakt.tv/oauth/token",
|
||||
userinfo: {
|
||||
|
||||
@@ -169,6 +169,7 @@ export default function Wikimedia<P extends WikimediaProfile>(
|
||||
type: "oauth",
|
||||
token: "https://meta.wikimedia.org/w/rest.php/oauth2/access_token",
|
||||
userinfo: "https://meta.wikimedia.org/w/rest.php/oauth2/resource/profile",
|
||||
// TODO: is empty scope necessary?
|
||||
authorization:
|
||||
"https://meta.wikimedia.org/w/rest.php/oauth2/authorize?scope=",
|
||||
profile(profile) {
|
||||
|
||||
@@ -43,21 +43,20 @@ export interface YandexProfile {
|
||||
is_avatar_empty?: boolean
|
||||
/**
|
||||
* ID of the Yandex user's profile picture.
|
||||
* Format for downloading user avatars: `https://avatars.yandex.net/get-yapic/<default_avatar_id>/<size>`
|
||||
* The profile picture with this ID can be downloaded via a link that looks like this:
|
||||
* @example "https://avatars.yandex.net/get-yapic/31804/BYkogAC6AoB17bN1HKRFAyKiM4-1/islands-200"
|
||||
* Available sizes:
|
||||
* `islands-small`: 28×28 pixels.
|
||||
* `islands-34`: 34×34 pixels.
|
||||
* `islands-middle`: 42×42 pixels.
|
||||
* `islands-50`: 50×50 pixels.
|
||||
* `islands-retina-small`: 56×56 pixels.
|
||||
* `islands-68`: 68×68 pixels.
|
||||
* `islands-75`: 75×75 pixels.
|
||||
* `islands-retina-middle`: 84×84 pixels.
|
||||
* `islands-retina-50`: 100×100 pixels.
|
||||
* `islands-200`: 200×200 pixels.
|
||||
*/
|
||||
default_avatar_id?: string
|
||||
default_avatar_id?:
|
||||
| "islands-small"
|
||||
| "islands-34"
|
||||
| "islands-middle"
|
||||
| "islands-50"
|
||||
| "islands-retina-small"
|
||||
| "islands-68"
|
||||
| "islands-75"
|
||||
| "islands-retina-middle"
|
||||
| "islands-retina-50"
|
||||
| "islands-200"
|
||||
/**
|
||||
* The user's date of birth in YYYY-MM-DD format.
|
||||
* Unknown elements of the date are filled in with zeros, such as: `0000-12-23`.
|
||||
@@ -72,8 +71,8 @@ export interface YandexProfile {
|
||||
* Non-Latin characters of the first and last names are presented in Unicode format.
|
||||
*/
|
||||
real_name?: string
|
||||
/** User's gender. `null` Stands for unknown or unspecified gender. Will be `undefined` if not provided by Yandex. */
|
||||
sex?: "male" | "female" | null
|
||||
/** User's gender. Possible values: Male: `male', Female: `female`, Unknown gender: `null` */
|
||||
sex?: string
|
||||
/**
|
||||
* The default phone number for contacting the user.
|
||||
* The API can exclude the user's phone number from the response at its discretion.
|
||||
|
||||
@@ -61,22 +61,21 @@ import type {
|
||||
OpenIDTokenEndpointResponse,
|
||||
} from "oauth4webapi"
|
||||
import type { Adapter, AdapterUser } from "./adapters.js"
|
||||
import { AuthConfig } from "./index.js"
|
||||
import type { JWT, JWTOptions } from "./jwt.js"
|
||||
import type { Cookie } from "./lib/cookie.js"
|
||||
import type { LoggerInstance } from "./lib/utils/logger.js"
|
||||
import type {
|
||||
CredentialInput,
|
||||
CredentialsConfig,
|
||||
EmailConfig,
|
||||
OAuthConfigInternal,
|
||||
OIDCConfigInternal,
|
||||
ProviderType,
|
||||
} from "./providers/index.js"
|
||||
import type { JWT, JWTOptions } from "./jwt.js"
|
||||
import type { Cookie } from "./lib/cookie.js"
|
||||
import type { LoggerInstance } from "./lib/utils/logger.js"
|
||||
import { AuthConfig } from "./index.js"
|
||||
|
||||
export type { AuthConfig } from "./index.js"
|
||||
export type { LoggerInstance }
|
||||
export type Awaitable<T> = T | PromiseLike<T>
|
||||
export type { LoggerInstance }
|
||||
|
||||
/**
|
||||
* Change the theme of the built-in pages.
|
||||
@@ -373,15 +372,12 @@ export interface User {
|
||||
/** @internal */
|
||||
export type InternalProvider<T = ProviderType> = (T extends "oauth"
|
||||
? OAuthConfigInternal<any>
|
||||
: T extends "oidc"
|
||||
? OIDCConfigInternal<any>
|
||||
: T extends "email"
|
||||
? EmailConfig
|
||||
: T extends "credentials"
|
||||
? CredentialsConfig
|
||||
: never) & {
|
||||
signinUrl: string
|
||||
/** @example `"https://example.com/api/auth/callback/id"` */
|
||||
callbackUrl: string
|
||||
}
|
||||
|
||||
@@ -419,6 +415,7 @@ export interface ResponseInternal<
|
||||
cookies?: Cookie[]
|
||||
}
|
||||
|
||||
// TODO: rename to AuthConfigInternal
|
||||
/** @internal */
|
||||
export interface InternalOptions<TProviderType = ProviderType> {
|
||||
providers: InternalProvider[]
|
||||
@@ -439,9 +436,4 @@ export interface InternalOptions<TProviderType = ProviderType> {
|
||||
callbacks: CallbacksOptions
|
||||
cookies: CookiesOptions
|
||||
callbackUrl: string
|
||||
/**
|
||||
* If true, the OAuth callback is being proxied by the server to the original URL.
|
||||
* See also {@link OAuthConfigInternal.redirectProxyUrl}.
|
||||
*/
|
||||
isOnRedirectProxy: boolean
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@auth/sveltekit",
|
||||
"version": "0.3.1",
|
||||
"version": "0.3.0",
|
||||
"description": "Authentication for SvelteKit.",
|
||||
"keywords": [
|
||||
"authentication",
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
||||
@@ -41,7 +41,7 @@ This is a monorepo containing the following packages / projects:
|
||||
## Getting Started
|
||||
|
||||
```
|
||||
npm install next-auth
|
||||
npm install --save 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.
|
||||
@@ -168,7 +168,7 @@ export default function App({
|
||||
|
||||
## 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.
|
||||
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/.github/blob/main/SECURITY.md) to reach out responsibly. Please do not open Pull Requests/Issues/Discussions before consulting with us.
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
@@ -189,6 +189,27 @@ We're happy to announce we've recently created an [OpenCollective](https://openc
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="top">
|
||||
<a href="https://stytch.com" target="_blank">
|
||||
<img width="128px" src="https://avatars.githubusercontent.com/u/69983493?s=200&v=4" alt="Stytch Logo" />
|
||||
</a><br />
|
||||
<div>Stytch</div><br />
|
||||
<sub>🥈 Silver Financial Sponsor</sub>
|
||||
</td>
|
||||
<td align="center" valign="top">
|
||||
<a href="https://beyondidentity.com" target="_blank">
|
||||
<img width="128px" src="https://avatars.githubusercontent.com/u/69811361?s=200&v=4" alt="Beyond Identity Logo" />
|
||||
</a><br />
|
||||
<div>Beyond Identity</div><br />
|
||||
<sub>🥈 Silver Financial Sponsor</sub>
|
||||
</td>
|
||||
<td align="center" valign="top">
|
||||
<a href="https://fusionauth.io/" target="_blank">
|
||||
<img width="128px" src="https://avatars.githubusercontent.com/u/41974756?s=200&v=4" alt="FusionAuth Logo" />
|
||||
</a><br />
|
||||
<div>FusionAuth</div><br />
|
||||
<sub>🥈 Silver Financial Sponsor</sub>
|
||||
</td>
|
||||
<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" />
|
||||
@@ -203,13 +224,15 @@ We're happy to announce we've recently created an [OpenCollective](https://openc
|
||||
<div>Prisma</div><br />
|
||||
<sub>🥉 Bronze Financial Sponsor</sub>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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 href="https://clerk.dev" target="_blank">
|
||||
<img width="128px" src="https://avatars.githubusercontent.com/u/49538330?s=200&v=4" alt="Prisma Logo" />
|
||||
</a><br />
|
||||
<div>Clerk</div><br />
|
||||
<sub>🥉 Bronze Financial Sponsor</sub>
|
||||
</td>
|
||||
</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" />
|
||||
@@ -238,7 +261,16 @@ We're happy to announce we've recently created an [OpenCollective](https://openc
|
||||
<div>superblog</div><br />
|
||||
<sub>☁️ Infrastructure Support</sub>
|
||||
</td>
|
||||
</tr><tr></tr>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top">
|
||||
<a href="https://www.permit.io?utm_source=github&utm_medium=referral&utm_campaign=authjs" target="_blank">
|
||||
<img width="128px" src="https://github.com/permitio.png" alt="permit.io Logo" />
|
||||
</a><br />
|
||||
<div>Permit.io</div><br />
|
||||
<sub>🥉 Bronze Financial Sponsor</sub>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<br />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module.exports = {
|
||||
plugins: [
|
||||
require('autoprefixer'),
|
||||
require('postcss-nested'),
|
||||
require('cssnano')({ preset: 'default' })
|
||||
]
|
||||
require("autoprefixer"),
|
||||
require("postcss-nested"),
|
||||
require("cssnano")({ preset: "default" }),
|
||||
],
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "next-auth",
|
||||
"version": "4.22.1",
|
||||
"version": "4.18.6",
|
||||
"description": "Authentication for Next.js",
|
||||
"homepage": "https://next-auth.js.org",
|
||||
"repository": "https://github.com/nextauthjs/next-auth.git",
|
||||
@@ -9,7 +9,7 @@
|
||||
"Balázs Orbán <info@balazsorban.com>",
|
||||
"Nico Domino <yo@ndo.dev>",
|
||||
"Lluis Agusti <hi@llu.lu>",
|
||||
"Thang Huu Vu <thvu@hey.com>"
|
||||
"Thang Huu Vu <hi@thvu.dev>"
|
||||
],
|
||||
"main": "index.js",
|
||||
"module": "index.js",
|
||||
@@ -38,39 +38,40 @@
|
||||
},
|
||||
"scripts": {
|
||||
"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\"",
|
||||
"build:js": "pnpm clean && pnpm generate-providers && tsc && 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:ts": "tsc --watch --emitDeclarationOnly false --module CommonJS",
|
||||
"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"
|
||||
"generate-providers": "node ./config/generate-providers.js"
|
||||
},
|
||||
"files": [
|
||||
"client",
|
||||
"core",
|
||||
"lib",
|
||||
"css",
|
||||
"jwt",
|
||||
"lib",
|
||||
"next",
|
||||
"providers",
|
||||
"react",
|
||||
"src",
|
||||
"utils",
|
||||
"*.d.ts*",
|
||||
"*.js"
|
||||
"next",
|
||||
"client",
|
||||
"providers",
|
||||
"core",
|
||||
"index.d.ts",
|
||||
"index.js",
|
||||
"adapters.d.ts",
|
||||
"middleware.d.ts",
|
||||
"middleware.js",
|
||||
"utils"
|
||||
],
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@panva/hkdf": "^1.0.2",
|
||||
"@babel/runtime": "^7.16.3",
|
||||
"@panva/hkdf": "^1.0.1",
|
||||
"cookie": "^0.5.0",
|
||||
"jose": "^4.11.4",
|
||||
"jose": "^4.9.3",
|
||||
"oauth": "^0.9.15",
|
||||
"openid-client": "^5.4.0",
|
||||
"openid-client": "^5.1.0",
|
||||
"preact": "^10.6.3",
|
||||
"preact-render-to-string": "^5.1.19",
|
||||
"uuid": "^8.3.2"
|
||||
@@ -101,7 +102,7 @@
|
||||
"@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/react-hooks": "^8.0.1",
|
||||
"@testing-library/user-event": "^14.2.0",
|
||||
"@types/jest": "^28.1.3",
|
||||
"@types/node": "^17.0.42",
|
||||
@@ -118,12 +119,15 @@
|
||||
"jest-environment-jsdom": "^28.1.1",
|
||||
"jest-watch-typeahead": "^1.1.0",
|
||||
"msw": "^0.42.3",
|
||||
"next": "13.3.0",
|
||||
"next": "13.1.1",
|
||||
"postcss": "^8.4.14",
|
||||
"postcss-cli": "^9.1.0",
|
||||
"postcss-nested": "^5.0.6",
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
"whatwg-fetch": "^3.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.19.0 || ^14.15.0 || ^16.13.0 || ^18.12.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
<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>
|
||||
|
Before Width: | Height: | Size: 588 B |
@@ -1,4 +0,0 @@
|
||||
<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>
|
||||
|
Before Width: | Height: | Size: 576 B |
@@ -1,8 +0,0 @@
|
||||
<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>
|
||||
|
Before Width: | Height: | Size: 810 B |
@@ -1,4 +0,0 @@
|
||||
<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>
|
||||
|
Before Width: | Height: | Size: 549 B |
@@ -1,12 +0,0 @@
|
||||
<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>
|
||||
|
Before Width: | Height: | Size: 677 B |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user