diff --git a/docs/components/OAuthProviderInstructions/OAuthProviderSelect.tsx b/docs/components/OAuthProviderInstructions/OAuthProviderSelect.tsx
index c505580d85..aeff3bef6c 100644
--- a/docs/components/OAuthProviderInstructions/OAuthProviderSelect.tsx
+++ b/docs/components/OAuthProviderInstructions/OAuthProviderSelect.tsx
@@ -4,44 +4,68 @@ import {
ComboboxPopover,
ComboboxProvider,
} from "@ariakit/react"
-import { useOAuthProviderSelect } from "./useOAuthProviderSelect"
import dynamic from "next/dynamic"
-import type { ChangeEvent } from "react"
-
import { Link } from "@/components/Link"
import manifest from "@/data/manifest.json"
+import {
+ PreviewProviders,
+ type Provider,
+} from "@/components/SearchBarProviders/PreviewProviders"
+import { useSelectCombobox } from "@/hooks/use-select-combobox"
const OAuthProviderInstructions = dynamic(() =>
import("./content").then((mod) => mod.OAuthInstructions)
)
+const previewProviders: Provider[] = [
+ { id: "google", name: "Google" },
+ { id: "github", name: "GitHub" },
+ { id: "twitter", name: "Twitter" },
+ { id: "keycloak", name: "Keycloak" },
+ { id: "okta", name: "Okta" },
+]
+
+const items = Object.entries(manifest.providersOAuth).map(([id, name]) => ({
+ id,
+ name,
+}))
+
export function OAuthProviderSelect() {
- const { items, term, selected, handleSearchItem, handleSelectOption } =
- useOAuthProviderSelect()
+ const {
+ selectedItem,
+ filteredItems,
+ hasMatchItem,
+ handleChange,
+ handleSelect,
+ } = useSelectCombobox({
+ items,
+ })
return (
-
+
) =>
- handleSearchItem(e.target.value)
- }
+ value={selectedItem.name}
+ onChange={handleChange}
/>
- {items.map((item) => (
+ {filteredItems.map((item) => (
handleSelectOption(item)}
+ onClick={() => handleSelect(item)}
>
))}
- {!term ? (
+ {!selectedItem.name && (
<>
Or jump directly to one of the popular ones below.
-
-
- handleSelectOption({ id: "google", name: "Google" })
- }
- className="flex h-32 w-32 flex-col items-center justify-between rounded-lg border border-solid border-neutral-200 p-4 shadow-sm transition-colors duration-300 hover:bg-neutral-50 dark:border-neutral-800 dark:hover:bg-neutral-950"
- >
-
-
Google
-
-
- handleSelectOption({ id: "github", name: "GitHub" })
- }
- >
-
-
GitHub
-
-
- handleSelectOption({ id: "twitter", name: "Twitter" })
- }
- className="flex h-32 w-32 flex-col items-center justify-between rounded-lg border border-solid border-neutral-200 p-4 shadow-sm transition-colors duration-300 hover:bg-neutral-50 dark:border-neutral-800 dark:hover:bg-neutral-950"
- >
-
-
Twitter
-
-
- handleSelectOption({ id: "keycloak", name: "keycloak" })
- }
- className="flex h-32 w-32 flex-col items-center justify-between rounded-lg border border-solid border-neutral-200 p-4 shadow-sm transition-colors duration-300 hover:bg-neutral-50 dark:border-neutral-800 dark:hover:bg-neutral-950"
- >
-
-
Keycloak
-
-
handleSelectOption({ id: "okta", name: "okta" })}
- className="flex h-32 w-32 flex-col items-center justify-between rounded-lg border border-solid border-neutral-200 p-4 shadow-sm transition-colors duration-300 hover:bg-neutral-50 dark:border-neutral-800 dark:hover:bg-neutral-950"
- >
-
-
Okta
-
-
+
>
- ) : null}
- {term && items.length === 0 ? (
+ )}
+ {!hasMatchItem && filteredItems.length === 0 && (
Can't find the OAuth provider you're looking for? You can always{" "}
@@ -119,17 +95,11 @@ export function OAuthProviderSelect() {
.
- ) : null}
+ )}
- {selected && term && items.length !== 0 ? (
-
- ) : null}
+ {hasMatchItem && (
+
+ )}
)
}
diff --git a/docs/components/OAuthProviderInstructions/content/components/SetupCode.tsx b/docs/components/OAuthProviderInstructions/content/components/SetupCode.tsx
index a28c8bfe3d..cbbb25ed91 100644
--- a/docs/components/OAuthProviderInstructions/content/components/SetupCode.tsx
+++ b/docs/components/OAuthProviderInstructions/content/components/SetupCode.tsx
@@ -21,6 +21,7 @@ export function SetupCode({ providerId, providerName, highlight }: Props) {
data-copy=""
data-language="tsx"
icon={TSIcon}
+ className="px-4"
dangerouslySetInnerHTML={{
__html: highlight(`
import NextAuth from "next-auth"
@@ -40,6 +41,7 @@ export const { handlers, signIn, signOut, auth } = NextAuth({
data-theme="default"
data-copy=""
data-language="tsx"
+ className="px-4"
icon={TSIcon}
dangerouslySetInnerHTML={{
__html: highlight(`
@@ -57,13 +59,14 @@ export const { GET, POST } = handlers
data-theme="default"
data-copy=""
data-language="tsx"
+ className="px-4"
icon={TSIcon}
dangerouslySetInnerHTML={{
__html: highlight(`
-import { SvelteKitAuth } from "@auth/sveltekit"
-import ${providerName} from "@auth/sveltekit/providers/${providerId}"
+import { QwikAuth$ } from "@auth/qwik"
+import ${providerName} from "@auth/qwik/providers/${providerId}"
-export const { handle, signIn, signOut } = SvelteKitAuth({
+export const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$({
providers: [${providerName}],
}) `),
}}
@@ -78,6 +81,7 @@ export const { handle, signIn, signOut } = SvelteKitAuth({
data-theme="default"
data-copy=""
data-language="tsx"
+ className="px-4"
icon={TSIcon}
dangerouslySetInnerHTML={{
__html: highlight(`
@@ -98,6 +102,7 @@ export const { handle, signIn } = SvelteKitAuth({
data-theme="default"
data-copy=""
data-language="tsx"
+ className="px-4"
icon={TSIcon}
dangerouslySetInnerHTML={{
__html: highlight(`export { handle } from "./auth"`),
@@ -113,6 +118,7 @@ export const { handle, signIn } = SvelteKitAuth({
data-theme="default"
data-copy=""
data-language="tsx"
+ className="px-4"
icon={TSIcon}
dangerouslySetInnerHTML={{
__html: highlight(`
@@ -134,6 +140,7 @@ export const load: LayoutServerLoad = async (event) => {
data-theme="default"
data-copy=""
data-language="tsx"
+ className="px-4"
icon={TSIcon}
dangerouslySetInnerHTML={{
__html: highlight(`
diff --git a/docs/components/OAuthProviderInstructions/content/components/SignInCode.tsx b/docs/components/OAuthProviderInstructions/content/components/SignInCode.tsx
index ee38c49767..eb0147c6f2 100644
--- a/docs/components/OAuthProviderInstructions/content/components/SignInCode.tsx
+++ b/docs/components/OAuthProviderInstructions/content/components/SignInCode.tsx
@@ -17,6 +17,7 @@ export function SignInCode({ providerId, providerName, highlight }: Props) {
data-theme="default"
data-copy=""
data-language="tsx"
+ className="px-4"
icon={TSIcon}
dangerouslySetInnerHTML={{
__html: highlight(`
@@ -37,6 +38,27 @@ export default function SignIn() {
}}
/>
+
+ signIn("${providerId}")}>
+}
+`),
+ }}
+ />
+
With Qwik we can do a server-side login with Form action, or a more
simple client-side login via submit method.
@@ -45,6 +67,7 @@ export default function SignIn() {
data-theme="default"
data-copy=""
data-language="tsx"
+ className="px-4"
icon={TSIcon}
dangerouslySetInnerHTML={{
__html: highlight(`
@@ -88,6 +111,7 @@ export default component$(() => {
data-theme="default"
data-copy=""
data-language="tsx"
+ className="px-4"
icon={TSIcon}
data-filename="src/routes/+page.svelte"
dangerouslySetInnerHTML={{
@@ -112,6 +136,7 @@ export default component$(() => {
data-theme="default"
data-copy=""
data-language="tsx"
+ className="px-4"
icon={TSIcon}
data-filename="src/routes/signin/+page.server.ts"
dangerouslySetInnerHTML={{
@@ -129,6 +154,7 @@ export const actions: Actions = { default: signIn }
data-theme="default"
data-copy=""
data-language="tsx"
+ className="px-4"
icon={TSIcon}
data-filename="src/routes/+page.svelte"
dangerouslySetInnerHTML={{
diff --git a/docs/components/OAuthProviderInstructions/content/components/StepTitle.tsx b/docs/components/OAuthProviderInstructions/content/components/StepTitle.tsx
index 6843098d21..1c4137fb29 100644
--- a/docs/components/OAuthProviderInstructions/content/components/StepTitle.tsx
+++ b/docs/components/OAuthProviderInstructions/content/components/StepTitle.tsx
@@ -1,12 +1,29 @@
interface Props {
children: React.ReactNode
+ count: number
}
-export function StepTitle({ children }: Props) {
+export function StepTitle({ children, count }: Props) {
return (
+
+ {count}
+
{children}
)
diff --git a/docs/components/OAuthProviderInstructions/content/index.tsx b/docs/components/OAuthProviderInstructions/content/index.tsx
index 8ed2537956..3d53c6a19b 100644
--- a/docs/components/OAuthProviderInstructions/content/index.tsx
+++ b/docs/components/OAuthProviderInstructions/content/index.tsx
@@ -1,8 +1,7 @@
import { useEffect, useState } from "react"
-import { type Highlighter, getHighlighter } from "shiki"
+import { type Highlighter, createHighlighter } from "shiki"
import cx from "classnames"
import { Callout, Pre, Code as NXCode } from "nextra/components"
-
import { StepTitle } from "./components/StepTitle"
import { SetupCode } from "./components/SetupCode"
import { SignInCode } from "./components/SignInCode"
@@ -19,7 +18,7 @@ export function OAuthInstructions({ providerId, disabled = false }: Props) {
const [highlighter, setHighlighter] = useState(null)
useEffect(() => {
;(async () => {
- const hl = await getHighlighter({
+ const hl = await createHighlighter({
themes: ["github-light", "github-dark"],
langs: ["ts", "tsx", "bash"],
})
@@ -39,16 +38,27 @@ export function OAuthInstructions({ providerId, disabled = false }: Props) {
}
const providerName = manifest.providersOAuth[providerId]
+ const envVars = [
+ `AUTH_${providerId.toUpperCase().replace(/-/gi, "_")}_ID={CLIENT_ID}`,
+ `AUTH_${providerId.toUpperCase().replace(/-/gi, "_")}_SECRET={CLIENT_SECRET}`,
+ ]
+ if (manifest.requiresIssuer.includes(providerId)) {
+ envVars.push(
+ `AUTH_${providerId.toUpperCase().replace(/-/gi, "_")}_ISSUER={ISSUER_URL}`
+ )
+ }
+ const envString = `\n${envVars.join("\n")}\n`
return (
{/* Step 1 */}
-
Register OAuth App in {providerName}'s dashboard
+
+ Register OAuth App in {providerName}'s dashboard
+
First you have to setup an OAuth application on the {providerName}{" "}
developers dashboard.
@@ -105,68 +115,65 @@ export function OAuthInstructions({ providerId, disabled = false }: Props) {
environment(s).
{/* Step 2 */}
- Setup Environment Variables
+ Setup Environment Variables
- Once registered, you should get a Client ID and{" "}
- Client Secret. Add those in your application
- environment file:
+ Once registered, you should receive a{" "}
+ {manifest.requiresIssuer.includes(providerId) ? (
+ <>
+ Client ID, Client Secret and{" "}
+ Issuer URL
+ >
+ ) : (
+ <>
+ Client ID and Client Secret
+ >
+ )}
+ . Add those in your application environment file:
-
+
+
+
-
+
+
+
-
+
+
+
-
+
+
+
Assuming{" "}
@@ -189,7 +196,7 @@ AUTH_${providerId.toUpperCase().replace(/-/gi, "_")}_SECRET={CLIENT_SECRET}
if needed, but then you’ll need to pass them to the provider manually.
{/* Step 3 */}
- Setup Provider
+ Setup Provider
Let’s enable {providerName} as a sign in option in our Auth.js
configuration. You’ll have to import the {providerName}{" "}
@@ -202,7 +209,7 @@ AUTH_${providerId.toUpperCase().replace(/-/gi, "_")}_SECRET={CLIENT_SECRET}
highlight={highlight}
/>
{/* Step 4 */}
- Add Signin Button
+ Add Signin Button
Next, we can add a signin button somewhere in your application like the
Navbar. It will trigger Auth.js sign in when clicked.
@@ -213,7 +220,7 @@ AUTH_${providerId.toUpperCase().replace(/-/gi, "_")}_SECRET={CLIENT_SECRET}
highlight={highlight}
/>
{/* Step 5 */}
- Ship it!
+ Ship it!
Click the “Sign in with {providerName}" button and if all went well, you
should be redirected to {providerName} and once authenticated,
diff --git a/docs/components/OAuthProviderInstructions/useOAuthProviderSelect.ts b/docs/components/OAuthProviderInstructions/useOAuthProviderSelect.ts
deleted file mode 100644
index 97d5072861..0000000000
--- a/docs/components/OAuthProviderInstructions/useOAuthProviderSelect.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { useState } from "react"
-import manifest from "@/data/manifest.json"
-
-const providerList = Object.entries(manifest.providersOAuth).map(
- ([id, name]) => {
- return { id, name }
- }
-)
-
-export function useOAuthProviderSelect() {
- const [term, setTerm] = useState("")
- const [selected, setSelected] = useState("")
-
- function handleSearchItem(term: string) {
- setTerm(term)
- }
-
- function handleSelectOption(item: { id: string; name: string }) {
- setTerm(item.name)
- setSelected(item.id)
- }
-
- return {
- items: providerList.filter((item) =>
- item.name.toLowerCase().includes(term?.toLowerCase())
- ),
- term,
- selected,
- handleSearchItem,
- handleSelectOption,
- }
-}
diff --git a/docs/components/SearchBarProviders/PreviewProviders.tsx b/docs/components/SearchBarProviders/PreviewProviders.tsx
new file mode 100644
index 0000000000..d1fdfdb0f2
--- /dev/null
+++ b/docs/components/SearchBarProviders/PreviewProviders.tsx
@@ -0,0 +1,35 @@
+export interface Provider {
+ id: string
+ name: string
+}
+
+export interface PreviewProvidersProps {
+ className?: string
+ providers: Provider[]
+ onSelected: (provider: Provider) => void
+}
+
+export function PreviewProviders({
+ className,
+ providers,
+ onSelected,
+}: PreviewProvidersProps) {
+ return (
+
+ {providers.map((provider) => (
+ onSelected(provider)}
+ >
+
+
{provider.name}
+
+ ))}
+
+ )
+}
diff --git a/docs/components/Tooltip/index.tsx b/docs/components/Tooltip/index.tsx
deleted file mode 100644
index 12e8497f62..0000000000
--- a/docs/components/Tooltip/index.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import { useRef } from "react"
-import polyfill from "@oddbird/css-anchor-positioning/fn"
-
-interface Props {
- label: string
- framework: string
- children: React.ReactNode
-}
-
-export function Tooltip({ label, framework, children }: Props) {
- // CSS Anchor Positioning Polyfill
- // https://github.com/oddbird/css-anchor-positioning
- polyfill()
-
- const popoverTargetRef = useRef()
- const slug = label.replace(/[^a-zA-Z0-9]/g, "-").toLowerCase()
-
- return (
-
-
-
- {label}
-
-
- )
-}
diff --git a/docs/hooks/use-select-combobox.ts b/docs/hooks/use-select-combobox.ts
new file mode 100644
index 0000000000..b8ee43a0dc
--- /dev/null
+++ b/docs/hooks/use-select-combobox.ts
@@ -0,0 +1,48 @@
+import { ChangeEvent, useState } from "react"
+
+interface SelectComboboxValue {
+ id: string
+ name: string
+}
+
+interface SelectComboboxProps {
+ defaultValue?: SelectComboboxValue
+ items: SelectComboboxValue[]
+}
+
+export const useSelectCombobox = ({
+ defaultValue = { id: "", name: "" },
+ items,
+}: SelectComboboxProps) => {
+ const [selectedItem, setSelectedItem] =
+ useState(defaultValue)
+ const [filteredItems, setFilteredItems] = useState(items)
+ const [hasMatchItem, setHasMatchItem] = useState(false)
+
+ const handleSelect = (value: SelectComboboxValue) => {
+ let hasMatchItem = false
+ setFilteredItems(
+ items.filter((item) => {
+ if (item.id === value.id) {
+ hasMatchItem = true
+ }
+ return item.name.toLowerCase().includes(value.name.toLowerCase())
+ })
+ )
+ setSelectedItem(value)
+ setHasMatchItem(hasMatchItem)
+ }
+
+ const handleChange = (event: ChangeEvent) => {
+ const { value } = event.target
+ handleSelect({ id: value, name: value })
+ }
+
+ return {
+ selectedItem,
+ filteredItems,
+ handleSelect,
+ handleChange,
+ hasMatchItem,
+ }
+}
diff --git a/docs/next-env.d.ts b/docs/next-env.d.ts
index fd36f9494e..725dd6f245 100644
--- a/docs/next-env.d.ts
+++ b/docs/next-env.d.ts
@@ -3,4 +3,4 @@
///
// NOTE: This file should not be edited
-// see https://nextjs.org/docs/basic-features/typescript for more information.
+// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
diff --git a/docs/package.json b/docs/package.json
index 32eb8ca551..75b2da8a8b 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -24,35 +24,34 @@
},
"homepage": "https://authjs.dev",
"dependencies": {
- "@ariakit/react": "^0.4.5",
- "@inkeep/widgets": "^0.2.272",
- "@next/third-parties": "^14.2.1",
- "@oddbird/css-anchor-positioning": "^0.0.5",
- "@radix-ui/react-accordion": "^1.1.2",
- "@radix-ui/react-tabs": "^1.0.4",
- "@vercel/analytics": "^1.2.2",
+ "@ariakit/react": "^0.4.13",
+ "@inkeep/widgets": "^0.2.289",
+ "@next/third-parties": "^14.2.15",
+ "@radix-ui/react-accordion": "^1.2.1",
+ "@radix-ui/react-tabs": "^1.1.1",
+ "@vercel/analytics": "^1.3.1",
"@vercel/kv": "^1.0.1",
- "algoliasearch": "^4.23.3",
+ "algoliasearch": "^4.24.0",
"classnames": "^2.5.1",
- "framer-motion": "^11.2.6",
- "next": "14.2.3",
+ "framer-motion": "^11.11.8",
+ "next": "14.2.15",
"next-sitemap": "^4.2.3",
- "nextra": "3.0.0-alpha.24",
- "nextra-theme-docs": "3.0.0-alpha.24",
+ "nextra": "3.0.15",
+ "nextra-theme-docs": "3.0.15",
"react": "18.3.1",
"react-dom": "18.3.1",
- "react-instantsearch": "^7.7.2",
- "react-instantsearch-nextjs": "^0.2.2"
+ "react-instantsearch": "^7.13.3",
+ "react-instantsearch-nextjs": "^0.2.5"
},
"devDependencies": {
"@types/node": "20.12.7",
"@types/react": "18.2.78",
- "autoprefixer": "^10.4.19",
- "postcss": "^8.4.38",
- "shiki": "^1.3.0",
- "tailwindcss": "^3.4.3",
+ "autoprefixer": "^10.4.20",
+ "postcss": "^8.4.47",
+ "shiki": "^1.22.0",
+ "tailwindcss": "^3.4.13",
"typedoc": "^0.25.13",
"typedoc-plugin-markdown": "4.0.0-next.54",
- "typedoc-plugin-mdn-links": "^3.2.7"
+ "typedoc-plugin-mdn-links": "^3.3.2"
}
}
diff --git a/docs/pages/data/manifest.json b/docs/pages/data/manifest.json
index e727763653..b9bba6f916 100644
--- a/docs/pages/data/manifest.json
+++ b/docs/pages/data/manifest.json
@@ -62,7 +62,7 @@
"box": "Box",
"boxyhq-saml": "BoxyHQ SAML",
"bungie": "Bungie",
- "clickup": "ClickUp",
+ "click-up": "ClickUp",
"cognito": "Cognito",
"coinbase": "Coinbase",
"descope": "Descope",
@@ -119,8 +119,33 @@
"zoom": "Zoom"
},
"providersEmail": {
+ "forwardemail": "Forward Email",
"resend": "Resend",
"sendgrid": "Sendgrid",
"nodemailer": "Nodemailer"
- }
+ },
+ "requiresIssuer": [
+ "asgardeo",
+ "auth0",
+ "authentik",
+ "azure-ad-b2c",
+ "battlenet",
+ "beyondidentity",
+ "boxyhq-saml",
+ "cognito",
+ "descope",
+ "duende-identityserver-6",
+ "fusionauth",
+ "identity-server4",
+ "keycloak",
+ "kinde",
+ "mastodon",
+ "mattermost",
+ "nextcloud",
+ "okta",
+ "ory-hydra",
+ "osso",
+ "passage",
+ "ping-id"
+ ]
}
diff --git a/docs/pages/getting-started/adapters/drizzle.mdx b/docs/pages/getting-started/adapters/drizzle.mdx
index 84ed30eeda..09c9be0428 100644
--- a/docs/pages/getting-started/adapters/drizzle.mdx
+++ b/docs/pages/getting-started/adapters/drizzle.mdx
@@ -34,8 +34,8 @@ To use this adapter, you must have setup Drizzle ORM and Drizzle Kit in your pro
1. Create your schema file, based off of one of the ones below.
2. Install a supported database driver to your project, like `@libsql/client`, `mysql2` or `postgres`.
3. Create a `drizzle.config.ts` [file](https://orm.drizzle.team/kit-docs/conf).
-4. Generate the initial migration from your schema file with a command like, `drizzle-kit generate:pg`.
-5. Apply migrations by using `migrate()` function or push changes directly to your database with a command like, `drizzle-kit push:pg`.
+4. Generate the initial migration from your schema file with a command like, `drizzle-kit generate`.
+5. Apply migrations by using `migrate()` function or push changes directly to your database with a command like, `drizzle-kit push`.
6. If your schemas differ from the default ones, pass them as the second parameter to the adapter.
#### Schemas
diff --git a/docs/pages/getting-started/adapters/firebase.mdx b/docs/pages/getting-started/adapters/firebase.mdx
index f48dbbcfaf..eb17e28dc9 100644
--- a/docs/pages/getting-started/adapters/firebase.mdx
+++ b/docs/pages/getting-started/adapters/firebase.mdx
@@ -40,7 +40,8 @@ AUTH_FIREBASE_PRIVATE_KEY
import NextAuth from "next-auth"
import { FirestoreAdapter } from "@auth/firebase-adapter"
-export { handlers, auth, signIn, signOut } = NextAuth({
+export const { handlers, auth, signIn, signOut } = NextAuth({
+ providers: [],
adapter: FirestoreAdapter(),
})
```
@@ -67,7 +68,8 @@ export const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(
import { SvelteKitAuth } from "@auth/sveltekit"
import { FirestoreAdapter } from "@auth/firebase-adapter"
-export { handle, signIn, signOut } = SvelteKitAuth({
+export const { handle, signIn, signOut } = SvelteKitAuth({
+ providers: [],
adapter: FirestoreAdapter(),
})
```
diff --git a/docs/pages/getting-started/adapters/prisma.mdx b/docs/pages/getting-started/adapters/prisma.mdx
index cddfc7e168..2c79e7bf9e 100644
--- a/docs/pages/getting-started/adapters/prisma.mdx
+++ b/docs/pages/getting-started/adapters/prisma.mdx
@@ -523,7 +523,7 @@ This will create an SQL migration file and execute it:
npm exec prisma migrate dev
```
-Note that you will need to specify your database connection string in the environment variable `DATABASE_URL`. You can do this by setting it in a `.env` file at the root of your project. Note Prisma doesn't support `.env.local` syntax, it must be named `.env`. For more information, check out their [environment variables docs](https://www.prisma.io/docs/orm/more/development-environment/environment-variables/using-multiple-env-files).
+Note that you will need to specify your database connection string in the environment variable `DATABASE_URL`. You can do this by setting it in a `.env` file at the root of your project.
### Generate Prisma Client
@@ -597,6 +597,6 @@ model VerificationToken {
expires DateTime
@@unique([identifier, token])
- @@map("verificationtokens")
+ @@map("verification_tokens")
}
```
diff --git a/docs/pages/getting-started/adapters/surrealdb.mdx b/docs/pages/getting-started/adapters/surrealdb.mdx
index 4411e84206..a70d335635 100644
--- a/docs/pages/getting-started/adapters/surrealdb.mdx
+++ b/docs/pages/getting-started/adapters/surrealdb.mdx
@@ -105,18 +105,26 @@ The SurrealDB adapter does not handle connections automatically, so you will hav
import { Surreal } from "surrealdb.js"
const connectionString = process.env.AUTH_SURREALDB_CONNECTION
-const user = process.env.AUTH_SURREALDB_USERNAME
-const pass = process.env.AUTH_SURREALDB_PASSWORD
-const ns = process.env.AUTH_SURREALDB_NS
-const db = process.env.AUTH_SURREALDB_DB
+const username = process.env.AUTH_SURREALDB_USERNAME
+const password = process.env.AUTH_SURREALDB_PASSWORD
+const namespace = process.env.AUTH_SURREALDB_NAMESPACE
+const database = process.env.AUTH_SURREALDB_DATABASE
+if (!connectionString || !username || !password || !namespace || !database) {
+ throw new Error(
+ "SurrealDB connection string, username, password, namespace, and database are required"
+ )
+}
const clientPromise = new Promise(async (resolve, reject) => {
const db = new Surreal()
try {
await db.connect(`${connectionString}/rpc`, {
- ns,
- db,
- auth: { user, pass },
+ namespace,
+ database,
+ auth: {
+ username,
+ password,
+ },
})
resolve(db)
} catch (e) {
@@ -124,7 +132,7 @@ const clientPromise = new Promise(async (resolve, reject) => {
}
})
-// Export a module-scoped MongoClient promise. By doing this in a
+// Export a module-scoped Promise. By doing this in a
// separate module, the client can be shared across functions.
export default clientPromise
```
@@ -137,19 +145,27 @@ Useful in serverless environments like Vercel.
import { ExperimentalSurrealHTTP } from "surrealdb.js"
const connectionString = process.env.AUTH_SURREALDB_CONNECTION
-const user = process.env.AUTH_SURREALDB_USERNAME
-const pass = process.env.AUTH_SURREALDB_PASSWORD
-const ns = process.env.AUTH_SURREALDB_NS
-const db = process.env.AUTH_SURREALDB_DB
+const username = process.env.AUTH_SURREALDB_USERNAME
+const password = process.env.AUTH_SURREALDB_PASSWORD
+const namespace = process.env.AUTH_SURREALDB_NAMESPACE
+const database = process.env.AUTH_SURREALDB_DATABASE
+if (!connectionString || !username || !password || !namespace || !database) {
+ throw new Error(
+ "SurrealDB connection string, username, password, namespace, and database are required"
+ )
+}
const clientPromise = new Promise>(
async (resolve, reject) => {
try {
const db = new ExperimentalSurrealHTTP(connectionString, {
fetch,
- ns,
- db,
- auth: { user, pass },
+ namespace,
+ database,
+ auth: {
+ username,
+ password,
+ },
})
resolve(db)
} catch (e) {
@@ -158,7 +174,7 @@ const clientPromise = new Promise>(
}
)
-// Export a module-scoped MongoClient promise. By doing this in a
+// Export a module-scoped Promise. By doing this in a
// separate module, the client can be shared across functions.
export default clientPromise
```
diff --git a/docs/pages/getting-started/authentication/credentials.mdx b/docs/pages/getting-started/authentication/credentials.mdx
index f3128403a2..e7af253de6 100644
--- a/docs/pages/getting-started/authentication/credentials.mdx
+++ b/docs/pages/getting-started/authentication/credentials.mdx
@@ -7,7 +7,29 @@ import { Code } from "@/components/Code"
# Credentials
-To setup Auth.js with external authentication mechanisms or simply use username and password, we need to use the `Credentials` provider. This provider is designed to forward any credentials inserted into the login form (.i.e username/password) to your authentication service via the `authorize` callback on the provider configuration.
+To setup Auth.js with any external authentication mechanisms or use a traditional username/email and password flow, we can use the `Credentials` provider. This provider is designed to forward any credentials inserted into the login form (i.e. username/password, but not limited to) to your authentication service.
+
+
+ The industry has come a long way since usernames and passwords
+ as the go-to mechanism for authenticating and authorizing users to
+ web applications. Therefore, if possible, we recommend a more modern and
+ secure authentication mechanism such as any of the [OAuth
+ providers](/getting-started/authentication/oauth), [Email Magic
+ Links](/getting-started/authentication/email), or [WebAuthn
+ (Passkeys)](/getting-started/authentication/webauthn) options instead.
+
+However, we also want to be flexible and support anything
+you deem appropriate for your application and use case,
+so there are no plans to remove this provider.
+
+
+
+
+ By default, the Credentials provider does not persist data in the database.
+ However, you can still create and save any data in your database, you just
+ have to provide the necessary logic, eg. to encrypt passwords, add
+ rate-limiting, add password reset functionality, etc.
+
@@ -44,8 +66,8 @@ export const { handlers, signIn, signOut, auth } = NextAuth({
if (!user) {
// No user found, so this is their first attempt to login
- // meaning this is also the place you could do registration
- throw new Error("User not found.")
+ // Optionally, this is also the place you could do a user registration
+ throw new Error("Invalid credentials.")
}
// return user object with their profile data
@@ -110,7 +132,9 @@ export const { signIn, signOut, handle } = SvelteKitAuth({
user = await getUserFromDb(credentials.email, pwHash)
if (!user) {
- throw new Error("User not found.")
+ // No user found, so this is their first attempt to login
+ // Optionally, this is also the place you could do a user registration
+ throw new Error("Invalid credentials.")
}
// return JSON object with the user data
@@ -161,8 +185,8 @@ app.use(
if (!user) {
// No user found, so this is their first attempt to login
- // meaning this is also the place you could do registration
- throw new Error("User not found.")
+ // Optionally, this is also the place you could do a user registration
+ throw new Error("Invalid credentials.")
}
// return user object with the their profile data
@@ -217,6 +241,34 @@ export function SignIn() {
```
+
+
+```tsx filename="./components/sign-in.tsx"
+"use client"
+import { signIn } from "next-auth/react"
+
+export function SignIn() {
+ const credentialsAction = (formData: FormData) => {
+ signIn("credentials", formData)
+ }
+
+ return (
+
+ )
+}
+```
+
+
```ts filename="/src/routes/index.tsx"
@@ -277,15 +329,15 @@ export default component$(() => {
-## Verifying Data with Zod
+## Validating credentials
-To improve the security of your `Credentials` provider use, we can leverage a run-time schema validation library like [Zod](https://zod.dev) to validate that the inputs match what we expect.
+Always validate the credentials server-side, i.e. by leveraging a schema validation library like [Zod](https://zod.dev).
```bash npm2yarn
npm install zod
```
-Next, we'll setup the schema and parsing in our `auth.ts` configuration file, using the `authorize` callback on the `Credentials` provider.
+Next, we'll set up the schema and parsing in our `auth.ts` configuration file, using the `authorize` callback on the `Credentials` provider.
@@ -335,7 +387,7 @@ export const { handlers, auth } = NextAuth({
user = await getUserFromDb(email, pwHash)
if (!user) {
- throw new Error("User not found.")
+ throw new Error("Invalid credentials.")
}
// return JSON object with the user data
@@ -442,7 +494,7 @@ export const { handle } = SvelteKitAuth({
user = await getUserFromDb(email, pwHash)
if (!user) {
- throw new Error("User not found.")
+ throw new Error("Invalid credentials.")
}
// return JSON object with the user data
@@ -461,24 +513,3 @@ export const { handle } = SvelteKitAuth({
-
-
- The industry has come a long way since usernames and passwords were first
- introduced as the go-to mechanism for authenticating and authorizing users to
- web applications. Therefore, if possible, we recommend a more modern and
- secure authentication mechanism such as any of the [OAuth
- providers](/getting-started/authentication/oauth), [Email Magic
- Links](/getting-started/authentication/email), or [WebAuthn
- (Passkeys)](/getting-started/authentication/webauthn) options instead of
- username / password.
-
-However, we also want to be flexible and support anything
-you deem appropriate for your application and use-case.
-
-
-
-
- The Credentials provider only supports the JWT session strategy. You can still
- create and save a database session and reference it from the JWT via an id,
- but you'll need to provide that logic yourself.
-
diff --git a/docs/pages/getting-started/authentication/email.mdx b/docs/pages/getting-started/authentication/email.mdx
index 3e514bee85..ce51246686 100644
--- a/docs/pages/getting-started/authentication/email.mdx
+++ b/docs/pages/getting-started/authentication/email.mdx
@@ -32,7 +32,15 @@ This login mechanism starts by the user providing their email address at the log
defaultValue="resend"
className="w-full flex flex-col"
>
-
+
+
+
+ Forward Email
+
Postmark
+
+
+
+ Loops
+
+
+
+ Mailgun
+### Forward Email Setup
+
+
+
+### Database Adapter
+
+Please make sure you've [setup a database adapter](/getting-started/database), as mentioned earlier,
+a database is required for passwordless login to work as verification tokens need to be stored.
+
+### Setup Environment Variables
+
+Auth.js will automatically pick up these if formatted like the example above.
+You can [also use a different name for the environment variables](/guides/environment-variables#oauth-variables) if needed, but then you’ll need to pass them to the provider manually.
+
+```bash filename=".env"
+AUTH_FORWARDEMAIL_KEY=abc123
+```
+
+### Setup Provider
+
+Let’s enable `ForwardEmail` as a sign in option in our Auth.js configuration. You’ll have to import the `ForwardEmail` provider from the package and pass it to the providers array we setup earlier in the Auth.js config file:
+
+
+
+
+```ts filename="./auth.ts"
+import NextAuth from "next-auth"
+import ForwardEmail from "next-auth/providers/forwardemail"
+
+export const { handlers, auth, signIn, signOut } = NextAuth({
+ providers: [ForwardEmail],
+})
+```
+
+
+
+
+```ts filename="/src/routes/plugin@auth.ts"
+import { QwikAuth$ } from "@auth/qwik"
+import ForwardEmail from "@auth/qwik/providers/forwardemail"
+
+export const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(
+ () => ({
+ providers: [ForwardEmail],
+ })
+)
+```
+
+
+
+
+```ts filename="./src/auth.ts"
+import SvelteKitAuth from "@auth/sveltekit"
+import ForwardEmail from "@auth/sveltekit/providers/forwardemail"
+
+export const { handle, signIn, signOut } = SvelteKitAuth({
+ providers: [ForwardEmail],
+})
+```
+
+```ts filename="./src/hooks.server.ts"
+export { handle } from "./auth"
+```
+
+
+
+
+### Add Signin Button
+
+Next, we can add a signin button somewhere in your application like the Navbar. This will send an email to the user containing the magic link to sign in.
+
+
+
+
+```tsx filename="./components/sign-in.tsx"
+import { signIn } from "../../auth.ts"
+
+export function SignIn() {
+ return (
+
+ )
+}
+```
+
+
+
+
+```ts filename="./components/sign-in.tsx"
+import { component$ } from "@builder.io/qwik"
+import { useSignIn } from "./plugin@auth"
+
+export default component$(() => {
+ const signInSig = useSignIn()
+
+ return (
+
+ )
+})
+```
+
+
+
+
+```html filename="src/routes/+page.svelte"
+
+
+
+
+
+```
+
+
+
+
+### Signin
+
+Start your application, once the user enters their Email and clicks on the signin button, they'll be redirected to a page that asks them to check their email. When they click on the link in their email, they will be signed in.
+
+
+
+
+ Check our [Customising magic links
+ emails](/getting-started/providers/forwardemail#customization) to learn how to
+ change the look and feel of the emails the user receives to sign in.
+
+
+For more information on this provider go to the [Forward Email docs page](/getting-started/providers/forwardemail).
+
+
+
+
+
### Resend Setup
@@ -109,7 +287,7 @@ export const { handlers, auth, signIn, signOut } = NextAuth({
-
+
```ts filename="/src/routes/plugin@auth.ts"
import { QwikAuth$ } from "@auth/qwik"
import Resend from "@auth/qwik/providers/resend"
@@ -166,6 +344,30 @@ export function SignIn() {
```
+
+
+```tsx filename="./components/sign-in.tsx"
+"use client"
+import { signIn } from "next-auth/react"
+
+export function SignIn() {
+ const resendAction = (formData: FormData) => {
+ signIn("resend", formData)
+ }
+
+ return (
+
+ )
+}
+```
+
+
```ts filename="./components/sign-in.tsx"
@@ -263,7 +465,7 @@ export const { handlers, auth, signIn, signOut } = NextAuth({
-
+
```ts filename="/src/routes/plugin@auth.ts"
import { QwikAuth$ } from "@auth/qwik"
import Sendgrid from "@auth/qwik/providers/sendgrid"
@@ -320,6 +522,30 @@ export function SignIn() {
```
+
+
+```tsx filename="./components/sign-in.tsx"
+"use client"
+import { signIn } from "next-auth/react"
+
+export function SignIn() {
+ const sendgridAction = (formData: FormData) => {
+ signIn("sendgrid", formData)
+ }
+
+ return (
+
+ )
+}
+```
+
+
```ts filename="./components/sign-in.tsx"
@@ -431,7 +657,7 @@ export const { handlers, auth, signIn, signOut } = NextAuth({
-
+
```ts filename="/src/routes/plugin@auth.ts"
import { QwikAuth$ } from "@auth/qwik"
import Nodemailer from "@auth/qwik/providers/nodemailer"
@@ -510,7 +736,7 @@ export const { handlers, auth, signIn, signOut } = NextAuth({
-
+
```ts filename="/src/routes/plugin@auth.ts"
import { QwikAuth$ } from "@auth/qwik"
import Nodemailer from "@auth/qwik/providers/nodemailer"
@@ -688,7 +914,7 @@ export const { handlers, auth, signIn, signOut } = NextAuth({
-
+
```ts filename="/src/routes/plugin@auth.ts"
import { QwikAuth$ } from "@auth/qwik"
import Postmark from "@auth/qwik/providers/postmark"
@@ -745,6 +971,30 @@ export function SignIn() {
```
+
+
+```tsx filename="./components/sign-in.tsx"
+"use client"
+import { signIn } from "next-auth/react"
+
+export function SignIn() {
+ const postmarkAction = (formData: FormData) => {
+ signIn("postmark", formData)
+ }
+
+ return (
+
+ )
+}
+```
+
+
```ts filename="./components/sign-in.tsx"
@@ -798,6 +1048,298 @@ Start your application, once the user enters their Email and clicks on the signi
For more information on this provider go to the [Postmark docs page](/getting-started/providers/postmark).
+
+
+### Loops Setup
+
+
+
+### Database Adapter
+
+Please make sure you've [setup a database adapter](/getting-started/database), as mentioned earlier,
+a database is required for passwordless login to work as verification tokens need to be stored.
+
+### Create your Transactional Email Template on Loops
+
+Loops have provided a super handy [guide](https://loops.so/docs/transactional/guide) to help you get started with creating your transactional email template.
+This provider only passes one data varaiable into the template, `url` which is the magic link to sign in. This is case sensitive, so make sure you use `url` in your template.
+On the last page of Template creation, you'll need to copy the `TRANSACTIONAL ID`. If you skipped this step, don't worry, you can get this at any from the Template edit page.
+
+### Create an API Key on Loops
+
+You'll need to create an API key to authenticate with Loops. This key should be kept secret and not shared with anyone.
+You can Generate a key by going to the [API Settings Page](https://app.loops.so/settings?page=api) and clicking Generate.
+You should name the key something that makes sense to you, like "Auth.js".
+
+### Setup Environment Variables
+
+To implement Loops, you need to set up the following environment variables. You should have these from the previous steps.
+
+```bash filename=".env"
+AUTH_LOOPS_KEY=abc123
+AUTH_LOOPS_TRANSACTIONAL_ID=def456
+```
+
+### Setup Provider
+
+Let's enable `Loops` as a sign-in option for our Auth.js configuration. You'll have to import the `Loops` provider from the package and pass it to the providers array we set up earlier in the Auth.js config file:
+
+
+
+
+```ts filename="./auth.ts"
+import NextAuth from "next-auth"
+import Loops from "next-auth/providers/loops"
+
+export const { handlers, auth, signIn, signOut } = NextAuth({
+ providers: [
+ Loops({
+ apiKey: process.env.AUTH_LOOPS_KEY,
+ transactionalId: process.env.AUTH_LOOPS_TRANSACTIONAL_ID,
+ }),
+ ],
+})
+```
+
+
+
+
+```ts filename="./src/auth.ts"
+import SvelteKitAuth from "@auth/sveltekit"
+import Loops from "@auth/sveltekit/providers/loops"
+import {
+ AUTH_LOOPS_KEY,
+ AUTH_LOOPS_TRANSACTIONAL_ID,
+} from "$env/static/private"
+
+export const { handle, signIn, signOut } = SvelteKitAuth({
+ providers: [
+ Loops({
+ apiKey: AUTH_LOOPS_KEY,
+ transactionalId: AUTH_LOOPS_TRANSACTIONAL_ID,
+ }),
+ ],
+})
+```
+
+```ts filename="./src/hooks.server.ts"
+export { handle } from "./auth"
+```
+
+
+
+
+### Add Signin Button
+
+Next, we add a signin button somewhere in your application like the Navbar. This will send an email to the user containing the magic link to sign in.
+
+
+
+
+```tsx filename="./components/sign-in.tsx"
+import { signIn } from "../../auth.ts"
+
+export function SignIn() {
+ return (
+
+ )
+}
+```
+
+
+
+
+```ts filename="src/routes/+page.svelte"
+
+
+
+
+
+
+
+```
+
+
+
+
+### Signin
+
+Start your application, click on the signin button we just added, and you should see Auth.js built-in sign in page with the option to sign in with your email.
+A user can enter their email, click "Sign in with Loops", and receive their beautifully formatted signin email.
+Clicking on the link in the email will redirect the user to your application, landing already authenticated!
+
+
+
+
+
+### Mailgun Setup
+
+
+
+### Database Adapter
+
+Please make sure you've [setup a database adapter](/getting-started/database), as mentioned earlier,
+a database is required for passwordless login to work as verification tokens need to be stored.
+
+### Setup Environment Variables
+
+Auth.js will automatically pick up these if formatted like the example above.
+You can [also use a different name for the environment variables](/guides/environment-variables#oauth-variables) if needed, but then you’ll need to pass them to the provider manually.
+
+```bash filename=".env"
+AUTH_MAILGUN_KEY=abc123
+```
+
+### Setup Provider
+
+Let’s enable `Mailgun` as a sign in option in our Auth.js configuration.
+You’ll have to import the `Mailgun` provider from the package and pass it to the providers array we setup earlier in the Auth.js config file:
+
+
+
+
+```ts filename="./auth.ts"
+import NextAuth from "next-auth"
+import Mailgun from "next-auth/providers/mailgun"
+
+export const { handlers, auth, signIn, signOut } = NextAuth({
+ providers: [Mailgun],
+})
+```
+
+
+
+
+```ts filename="/src/routes/plugin@auth.ts"
+import { QwikAuth$ } from "@auth/qwik"
+import Mailgun from "@auth/qwik/providers/mailgun"
+
+export const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(
+ () => ({
+ providers: [Mailgun],
+ })
+)
+```
+
+
+
+
+```ts filename="./src/auth.ts"
+import SvelteKitAuth from "@auth/sveltekit"
+import Mailgun from "@auth/sveltekit/providers/mailgun"
+
+export const { handle, signIn, signOut } = SvelteKitAuth({
+ providers: [Mailgun],
+})
+```
-
+```ts filename="./src/hooks.server.ts"
+export { handle } from "./auth"
+```
+
+
+
+
+### Add Signin Button
+
+Next, we can add a signin button somewhere in your application like the Navbar. This will send an email to the user containing the magic link to sign in.
+
+
+
+
+```tsx filename="./components/sign-in.tsx"
+import { signIn } from "../../auth.ts"
+
+export function SignIn() {
+ return (
+
+ )
+}
+```
+
+
+
+
+```ts filename="./components/sign-in.tsx"
+import { component$ } from "@builder.io/qwik"
+import { useSignIn } from "./plugin@auth"
+
+export default component$(() => {
+ const signInSig = useSignIn()
+
+ return (
+
+ )
+})
+```
+
+
+
+
+```html filename="src/routes/+page.svelte"
+
+
+
+
+
+```
+
+
+
+
+### Signin
+
+Start your application, once the user enters their Email and clicks on the signin button, they'll be redirected to a page that asks them to check their email. When they click on the link in their email, they will be signed in.
+
+
+
+
+ Check our [Customising magic links
+ emails](/getting-started/providers/mailgun#customization) to learn how to
+ change the look and feel of the emails the user receives to sign in.
+
+
+For more information on this provider go to the [Mailgun docs page](/getting-started/providers/mailgun).
+
+
+
diff --git a/docs/pages/getting-started/database.mdx b/docs/pages/getting-started/database.mdx
index af9d69a8da..0e455ccdfa 100644
--- a/docs/pages/getting-started/database.mdx
+++ b/docs/pages/getting-started/database.mdx
@@ -43,7 +43,7 @@ monorepo](https://github.com/nextauthjs/next-auth/tree/main/packages). If you're
## Models
-This is a generic ER Diagram of what the full database schema should look like. Your database adapter of choice will include a template schema with more details for applying this schema to the underlying database. For more details, check out our [database models](/concepts/database-models) documentation. Please note, that the entire schema is not required for every use-case, for more details check out out our [database adapters guide](/guides/creating-a-database-adapter).
+This is a generic ER Diagram of what the full database schema should look like. Your database adapter of choice will include a template schema with more details for applying this schema to the underlying database. For more details, check out our [database models](/concepts/database-models) documentation. Please note, that the entire schema is not required for every use-case, for more details check out our [database adapters guide](/guides/creating-a-database-adapter).
```mermaid
%%{init: {'theme':'neutral'}}%%
diff --git a/docs/pages/getting-started/deployment.mdx b/docs/pages/getting-started/deployment.mdx
index 96ff2c5dfa..13a4c7684e 100644
--- a/docs/pages/getting-started/deployment.mdx
+++ b/docs/pages/getting-started/deployment.mdx
@@ -11,18 +11,12 @@ import { Accordion, Accordions } from "@/components/Accordion"
distinguished from other environment variables more easily.
-Auth.js libraries require you to set an `AUTH_SECRET` environment variable. This is used to encrypt cookies and tokens. It should be a random string of at least 32 characters. On UNIX based systems you can use this command:
+Auth.js libraries require you to set an `AUTH_SECRET` environment variable. This is used to encrypt cookies and tokens. It should be a cryptographically secure random string of at least 32 characters:
```bash npm2yarn
npm exec auth secret
```
-Alternatively, you can use the following `openssl` command, which should be available on all Linux / Mac OS X systems.
-
-```bash
-openssl rand -base64 33
-```
-
If you are using an [OAuth Provider](/concepts/oauth), your provider will provide you with a **Client ID** and **Client Secret** that you will need to set as environment variables as well (in the case of an OIDC provider, like Auth0, a third `issuer` value might be also required, refer to the provider's specific documentation).
@@ -174,7 +168,7 @@ Most OAuth providers cannot be configured with multiple callback URLs or using a
However, Auth.js **supports Preview deployments**, even **with OAuth providers**. The idea is to have one deployment which proxies authentication requests to the dynamic URLs of your main application. So you could have 1 stable deployment, like at `auth.company.com` where you would point all your OAuth provider's `callbackUrl`s, and this application would then, upon successful authentication, redirect the user back to the preview deploy URL, like `https://git-abc123-myapp.vercel.app`. Follow these steps to get started with securing preview deploys with Auth.js.
1. Determine a stable deployment URL. For example, a deployment whose URL does not change between builds, for example. `auth.yourdomain.com` (using a subdomain is not a requirement, this can be the main site's URL too, for example.)
-2. In your preview deploy application, set `AUTH_REDIRECT_PROXY_URL` to that stable deployment URL, including the path from where Auth.js handles the routes. Eg.: (`https://auth.yourdomain.com/api/auth`)
+2. In both the preview and stable environment, set `AUTH_REDIRECT_PROXY_URL` to that stable deployment URL, including the path from where Auth.js handles the routes. Eg.: (`https://auth.yourdomain.com/api/auth`)
3. Update the `callbackUrl` in your OAuth provider's configuration to use the stable deployment URL. For example, for GitHub it would be `https://auth.yourdomain.com/api/auth/callback/github`.
Fun fact: all of our example apps are using the proxy functionality!
diff --git a/docs/pages/getting-started/index.mdx b/docs/pages/getting-started/index.mdx
index f03fad3e30..ceee02642a 100644
--- a/docs/pages/getting-started/index.mdx
+++ b/docs/pages/getting-started/index.mdx
@@ -51,7 +51,7 @@ Select your framework of choice to get started, or view the example application
-Check the [integrations page](/getting-started/integrations) for all supported packages. We are working on supporting more frameworks, but you can create own or
+Check the [integrations page](/getting-started/integrations) for all supported packages. We are working on supporting more frameworks, but you can create your own or
help us create one for your favorite framework.