From 0909f7185ac9a23b869c2cd32de6be5cb1cec422 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Tue, 24 Oct 2023 02:53:00 +0200 Subject: [PATCH] docs: cleanup getting started guides (#8927) * fix gitignore * auto-generate providers docs, move pages around, rewrite some docs * move deployment to getting started * remove sidebar position configs * simplify landing page code examples * add framework logos * add cards of frameworks on getting started intro * reword adapters and deployments docs * update typescript guide --- .gitignore | 2 +- .prettierignore | 2 +- docs/docs/getting-started/_category_.json | 5 - docs/docs/getting-started/adapters.mdx | 166 +++++++++++++ docs/docs/getting-started/databases.md | 14 -- docs/docs/getting-started/deployment.md | 70 ++++++ docs/docs/getting-started/introduction.md | 44 ---- docs/docs/getting-started/introduction.mdx | 58 +++++ .../getting-started/providers/_category.json | 3 + .../{ => providers}/credentials-tutorial.mdx | 0 .../{ => providers}/email-tutorial.mdx | 95 ++++---- docs/docs/getting-started/providers/index.mdx | 36 +++ .../{ => providers}/oauth-tutorial.mdx | 14 +- docs/docs/getting-started/typescript.md | 193 ++++++--------- .../adapters/using-a-database-adapter.md | 13 - docs/docs/guides/basics/deployment.md | 75 ------ docs/docs/reference/adapters/index.md | 223 ------------------ docs/package.json | 6 +- docs/scripts/generate-manifest.mjs | 74 ++++++ docs/scripts/generate-providers.mjs | 28 --- docs/sidebars.js | 2 +- docs/src/css/{adapters.css => cards.css} | 30 +-- docs/src/css/index.css | 2 +- docs/src/pages/index.js | 76 ++---- docs/static/img/frameworks/nextjs.svg | 25 ++ docs/static/img/frameworks/solidstart.svg | 176 ++++++++++++++ docs/static/img/frameworks/sveltekit.svg | 1 + docs/static/img/nextjs-logo.svg | 1 - docs/vercel.json | 18 +- 29 files changed, 787 insertions(+), 665 deletions(-) delete mode 100644 docs/docs/getting-started/_category_.json create mode 100644 docs/docs/getting-started/adapters.mdx delete mode 100644 docs/docs/getting-started/databases.md create mode 100644 docs/docs/getting-started/deployment.md delete mode 100644 docs/docs/getting-started/introduction.md create mode 100644 docs/docs/getting-started/introduction.mdx create mode 100644 docs/docs/getting-started/providers/_category.json rename docs/docs/getting-started/{ => providers}/credentials-tutorial.mdx (100%) rename docs/docs/getting-started/{ => providers}/email-tutorial.mdx (83%) create mode 100644 docs/docs/getting-started/providers/index.mdx rename docs/docs/getting-started/{ => providers}/oauth-tutorial.mdx (97%) delete mode 100644 docs/docs/guides/adapters/using-a-database-adapter.md delete mode 100644 docs/docs/guides/basics/deployment.md delete mode 100644 docs/docs/reference/adapters/index.md create mode 100644 docs/scripts/generate-manifest.mjs delete mode 100644 docs/scripts/generate-providers.mjs rename docs/src/css/{adapters.css => cards.css} (61%) create mode 100644 docs/static/img/frameworks/nextjs.svg create mode 100644 docs/static/img/frameworks/solidstart.svg create mode 100644 docs/static/img/frameworks/sveltekit.svg delete mode 100644 docs/static/img/nextjs-logo.svg diff --git a/.gitignore b/.gitignore index 5800a764..81975e52 100644 --- a/.gitignore +++ b/.gitignore @@ -74,7 +74,7 @@ test.schema.gql # docusaurus docs/.docusaurus -docs/providers.json +docs/manifest.json # Core packages/core/src/providers/oauth-types.ts diff --git a/.prettierignore b/.prettierignore index ffd87297..f869789a 100644 --- a/.prettierignore +++ b/.prettierignore @@ -23,7 +23,7 @@ build docs/docs/reference/core docs/docs/reference/sveltekit static -docs/providers.json +docs/manifest.json # --------------- Packages --------------- diff --git a/docs/docs/getting-started/_category_.json b/docs/docs/getting-started/_category_.json deleted file mode 100644 index 3f2f1ca5..00000000 --- a/docs/docs/getting-started/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Getting Started", - "collapsible": true, - "collapsed": true -} diff --git a/docs/docs/getting-started/adapters.mdx b/docs/docs/getting-started/adapters.mdx new file mode 100644 index 00000000..570aec79 --- /dev/null +++ b/docs/docs/getting-started/adapters.mdx @@ -0,0 +1,166 @@ +--- +title: Database Adapters +sidebar_position: 2 +--- + +import { adapters } from "../../manifest.json" + +An **Adapter** in Auth.js connects your application to whatever database or backend system you want to use to store data for users, their accounts, sessions, etc. Adapters are optional, unless you need to persist user information in your own database, or you want to implement certain flows. The [Email Provider](/getting-started/email-tutorial) requires an adapter to be able to save [Verification Tokens](#verification-token). + +On how to use Auth.js with other databases, check out the [creating a database adapter](/guides/adapters/creating-a-database-adapter) guide. + +## Official adapters + +Below you can see a list of official adapters that are distributed as their own packages under the `@auth/{name}-adapter` namespace. Their source code is available in their various adapters package directories at [`nextauthjs/next-auth`](https://github.com/nextauthjs/next-auth/tree/main/packages) + +:::info +If you don't find an adapter for the database or service you use, you can always create one yourself (and optionally open a PR so anyone can make use of it). Have a look at our guide on [how to create a database adapter](/guides/adapters/creating-a-database-adapter). +::: + +
+ {Object.entries(adapters).map(([id, [name, img]]) => ( + + +

{name} Adapter

+
+ ))} +
+ +## Models + +Auth.js can be used with any database. Models tell you what structures Auth.js expects from your database. Models will vary slightly depending on which adapter you use, but in general, will have a similar structure to the graph below. Each model can be extended with additional fields. + +:::note +Auth.js / NextAuth.js uses `camelCase` for its database rows while respecting the conventional `snake_case` formatting for OAuth-related values. If the mixed casing is an issue for you, most adapters have a dedicated documentation section on how to force a casing convention. +::: + +```mermaid +erDiagram + User ||--|{ Account : "" + User { + string id + string name + string email + timestamp emailVerified + string image + } + User ||--|{ Session : "" + Session { + string id + timestamp expires + string sessionToken + string userId + } + Account { + string id + string userId + string type + string provider + string providerAccountId + string refresh_token + string access_token + int expires_at + string token_type + string scope + string id_token + string session_state + } + User ||--|{ VerificationToken : "" + VerificationToken { + string identifier + string token + timestamp expires + } +``` + +--- + +### User + +The User model is for information such as the user's name and email address. + +Email address is optional, but if one is specified for a User, then it must be unique. + +:::note +If a user first signs in with an OAuth provider, then their email address is automatically populated using the one from their OAuth profile if the OAuth provider returns one. + +This provides a way to contact users and for users to maintain access to their account and sign in using email in the event they are unable to sign in with the OAuth provider in the future (if the [Email Provider](/reference/core/providers_email) is configured). +::: + +User creation in the database is automatic and happens when the user is logging in for the first time with a provider. +If the first sign-in is via the [OAuth Provider](/reference/core/providers_oauth), the default data saved is `id`, `name`, `email` and `image`. You can add more profile data by returning extra fields in your [OAuth provider](/guides/providers/custom-provider)'s [`profile()`](/reference/core/providers#profile) callback. + +If the first sign-in is via the [Email Provider](/reference/core/providers_email), then the saved user will have `id`, `email`, `emailVerified`, where `emailVerified` is the timestamp of when the user was created. + +### Account + +The Account model is for information about OAuth accounts associated with a User + +A single User can have multiple Accounts, but each Account can only have one User. + +Account creation in the database is automatic and happens when the user is logging in for the first time with a provider, or the [`Adapter.linkAccount`](/reference/core/adapters#linkaccount) method is invoked. The default data saved is `access_token`, `expires_at`, `refresh_token`, `id_token`, `token_type`, `scope` and `session_state`. You can save other fields or remove the ones you don't need by returning them in the [OAuth provider](/guides/providers/custom-provider)'s [`account()`](/reference/core/providers#account) callback. + +Linking Accounts to Users happen automatically, only when they have the same e-mail address, and the user is currently signed in. Check the [FAQ](/concepts/faq#security) for more information on why this is a requirement. + +:::tip +You can manually unlink accounts if your adapter implements the `unlinkAccount` method. Make sure to take all the necessary security steps to avoid data loss. +::: + +### Session + +:::info +Even if you are using a database, you can still use JWT for session handling for fast access, in which case, this model can be opted out in your database. Learn more about [`session strategies`](/concepts/session-strategies) and their trade-offs. +::: + +The Session model is used for database sessions and it can store arbitrary data for an active user session. A single User can have multiple Sessions, each Session can only have one User. + +When a Session is read, its `expires` field is checked to see if the session is still valid. If it has expired, the session is deleted from the database. + +:::tip +You can also do this clean-up periodically in the background to avoid our extra delete call to the database during an active session retrieval. This might result in a slight performance increase in a few cases. +::: + +### Verification Token + +The Verification Token model is used to store tokens for passwordless sign in. + +A single User can have multiple open Verification Tokens (e.g. to sign in to different devices). + +It has been designed to be extendable for other verification purposes in the future (e.g. 2FA / magic codes, etc.). + +Auth.js makes sure that every token is usable only once, and by default has a short (1 day, can be configured by [`maxAge`](/guides/providers/email)) lifetime. If your user did not manage to finish the sign-in flow in time, they will have to start the sign-in process again. + +:::tip +Due to users forgetting or failing at the sign-in flow, you might end up with unwanted rows in your database, that you might have to periodically clean up to avoid filling the database up with unnecessary data. +::: + +## TypeScript + +Official adapters are all written in TypeScript. If you're writing your own custom Adapter, you can take advantage of the types that comes with `@auth/core` to make sure your implementation conforms to what's expected: + +```ts +import type { Adapter } from "@auth/core/adapters" + +function MyAdapter(): Adapter { + return { + // your adapter methods here + } +} +``` + +When writing your own custom Adapter in plain JavaScript, note that you can use **JSDoc** to get helpful editor hints and auto-completion like so: + +```js +/** @return { import("@auth/core/adapters").Adapter } */ +function MyAdapter() { + return { + // your adapter methods here + } +} +``` + +:::note +This will work in code editors with a strong TypeScript integration like VSCode or WebStorm. It might not work if you're using more lightweight editors like VIM or Atom. +::: + +For more information, check out the [`@auth/core/adapters` API Reference](/reference/core/adapters). diff --git a/docs/docs/getting-started/databases.md b/docs/docs/getting-started/databases.md deleted file mode 100644 index 5d368036..00000000 --- a/docs/docs/getting-started/databases.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -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) - -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). diff --git a/docs/docs/getting-started/deployment.md b/docs/docs/getting-started/deployment.md new file mode 100644 index 00000000..1d5cf98e --- /dev/null +++ b/docs/docs/getting-started/deployment.md @@ -0,0 +1,70 @@ +--- +title: Deployment +--- + +Auth.js relies strictly on standard Web APIs, so it can be deployed anywhere you can deploy a JavaScript application. Auth.js is fully compatible with Edge runtimes too. + +By default, it uses the [JWT session strategy](/concepts/session-strategies#jwt) so it does not require a database to be configured. + +## Environment Variables + +:::tip +For consistency, we recommend prefixing all Auth.js environment variables with `AUTH_`. This way we can autodetect them, and it's they can also be distinguished from other environment variables. +::: + +Auth.js libraries require you to set a `AUTH_SECRET` environment variable. This is used to encrypt cookies and tokens. It should be a random string of at least 32 characters. On Linux systems, you can generate a suitable string using the command `openssl rand -base64 32`. You can also use a tool like [generate-secret.vercel.app](https://generate-secret.vercel.app/32) to generate a random value. + +If you are using an [OAuth Provider](/getting-started/providers), your provider will have a `clientId` and `clientSecret` that you will need to set as environment variables as well. In case of OIDC, a third `issuer` option is required. + +:::info +Some Auth.js libraries can infer environment variables without passing them explicitly. For example, the Next.js library can infer the `AUTH_GITHUB_ID` and `AUTH_GITHUB_SECRET` environment variables as the `clientId` and `clientSecret` options for the GitHub provider. See the API reference for your framework to learn more about this feature. +::: + +## Serverless + +Hosting services like Vercel and Netlify are great for deploying Auth.js apps. The following steps should help you get started: + +1. Create the required [environment variebles](#environment-variables) for the desired deploy environments. +2. In case of an OAuth provider, set the callback URL for the provider to `https://yourdomain.com/api/auth/callback/provider` (replace `yourdomain.com` with your domain and `provider` with the provider name, eg.: `github`). +3. Deploy! + +## Self-hosted + +Auth.js can also be deployed anywhere you can deploy your framework of your choice. Check out the framework's documentation on self-hosting. + +## Securing a preview deployment + +Most OAuth providers cannot be configured with multiple callback URLs or using a wildcard. + +However, Auth.js **supports Preview deployments**, even **with OAuth providers**: + +1. Determine a stable deployment URL. Eg.: 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. Set `AUTH_REDIRECT_PROXY_URL` to that URL, with the base path up to 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`) + +:::note +To support preview deployments, the `AUTH_SECRET` value needs to be the same for the stable deployment and deployments that will need OAuth support. +::: + +:::note +If you are storing users in a [database](reference/adapters), we recommend using a different OAuth app for development/production so that you don't mix your test and production user base. +::: + +
+ +How does this work? + +To support preview deployments, Auth.js uses the stable deployment URL as a redirect proxy server. + +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. + +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. + +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. + +See also: + + +
diff --git a/docs/docs/getting-started/introduction.md b/docs/docs/getting-started/introduction.md deleted file mode 100644 index 069d6033..00000000 --- a/docs/docs/getting-started/introduction.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: Introduction -sidebar_position: 0 ---- - -## About Auth.js - -Auth.js is a complete open-source authentication solution for web applications. Check out the live demos of Auth.js in action: - -- [Next.js](https://next-auth-example.vercel.app/) -- [SvelteKit](https://sveltekit-auth-example.vercel.app/) -- [SolidStart](https://auth-solid.vercel.app/) - -Continue to our tutorials to see how to use Auth.js for authentication: - -- [Setup with OAuth](/getting-started/oauth-tutorial) -- [Setup with magic links](/getting-started/email-tutorial) -- [Integrating with external auth](/getting-started/credentials-tutorial) - -### Features - -- Built in support for 60+ popular services (Google, Facebook, Auth0, Apple…) -- Built-in email/password-less/magic link -- Use with any OAuth 2 or OpenID Connect provider -- Use with any username/password store - -### Flexible -- Runtime agnostic - run anywhere! Vercel Edge Functions, Node.js, Serverless, etc. -- Use with any modern framework! Next.js, SolidStart, SvelteKit, etc. -- [Bring Your Own Database](/getting-started/databases) - or none! MySQL, Postgres, MSSQL, MongoDB, etc. Choose database sessions or JWT. - -_Note: Email sign-in requires a database to store single-use verification tokens._ - -### Secure by default -- Signed, prefixed, server-only cookies -- Built-in CSRF protection -- Doesn't rely on client-side JavaScript -- JWT with JWS / JWE / JWK. - -## Credits - -Auth.js is an open-source project that is only possible [thanks to contributors](/contributors). - -To financially support the development of Auth.js, you can check our [OpenCollective](https://opencollective.com/nextauth) page. We appreciate your support 💚. diff --git a/docs/docs/getting-started/introduction.mdx b/docs/docs/getting-started/introduction.mdx new file mode 100644 index 00000000..e1b9b1fa --- /dev/null +++ b/docs/docs/getting-started/introduction.mdx @@ -0,0 +1,58 @@ +--- +title: Introduction +sidebar_position: 0 +--- + +import { providers, adapters, frameworks } from "../../manifest.json" + +## About Auth.js + +Auth.js is a complete open-source authentication solution for web applications. Check out the live demos of Auth.js in action: + +
+ {frameworks.map(({ id, name, url, logo }) => ( + + +

{name}

+
+ ))} +
+ +support for [more frameworks](/reference#roadmap) are on the way... + +Continue to our guides to see how to use Auth.js for authentication: + +- [Setup with OAuth](/getting-started/providers/oauth-tutorial) +- [Setup with magic links](/getting-started/providers/email-tutorial) +- [Integrating with external auth](/getting-started/providers/credentials-tutorial) + +### Features + +- Built in support for + {Object.keys(providers).length}+ + popular services (Google, Facebook, Auth0, Apple…) +- Built-in support for + {Object.keys(adapters).length}+ + databases/ORMs (MySQL, Postgres, Prisma, Drizzle…) +- Built-in email/passwordless/magic link authentication +- Use with _any_ [OAuth 2](https://www.ietf.org/rfc/rfc6749.html) or [OpenID Connect](https://openid.net/specs/openid-connect-core-1_0.html) provider +- Use with _any_ username/password store + +### Flexible + +- Runtime agnostic - run anywhere! Vercel Edge Functions, Node.js, Serverless, etc. +- Use with any modern framework! Next.js, SolidStart, SvelteKit, etc. +- [Bring Your Own Database](/getting-started/databases) - or none! MySQL, Postgres, MSSQL, MongoDB, etc. Choose database sessions or JWT. + +### Secure by default + +- Signed, prefixed, server-only cookies +- Built-in CSRF protection +- Doesn't rely on client-side JavaScript +- Encrypted JWT sessions + +## Credits + +Auth.js is an open-source project that is only possible [thanks to contributors](/contributors). + +To financially support the development of Auth.js, you can check our [OpenCollective](https://opencollective.com/nextauth) page. We appreciate your support 💚. diff --git a/docs/docs/getting-started/providers/_category.json b/docs/docs/getting-started/providers/_category.json new file mode 100644 index 00000000..0d447582 --- /dev/null +++ b/docs/docs/getting-started/providers/_category.json @@ -0,0 +1,3 @@ +{ + "collapsed": false +} diff --git a/docs/docs/getting-started/credentials-tutorial.mdx b/docs/docs/getting-started/providers/credentials-tutorial.mdx similarity index 100% rename from docs/docs/getting-started/credentials-tutorial.mdx rename to docs/docs/getting-started/providers/credentials-tutorial.mdx diff --git a/docs/docs/getting-started/email-tutorial.mdx b/docs/docs/getting-started/providers/email-tutorial.mdx similarity index 83% rename from docs/docs/getting-started/email-tutorial.mdx rename to docs/docs/getting-started/providers/email-tutorial.mdx index f4ee2a93..fff59895 100644 --- a/docs/docs/getting-started/email-tutorial.mdx +++ b/docs/docs/getting-started/providers/email-tutorial.mdx @@ -2,11 +2,11 @@ title: Email authentication --- -import smtpConfig from "./img/dashboard-smtp.png" -import startPageImg from "./img/email-tutorial-start.png" -import checkPageImg from "./img/email-tutorial-check.png" -import mailboxImg from "./img/email-tutorial-mailbox.png" -import loggedInImg from "./img/email-tutorial-logged.png" +import smtpConfig from "../img/dashboard-smtp.png" +import startPageImg from "../img/email-tutorial-start.png" +import checkPageImg from "../img/email-tutorial-check.png" +import mailboxImg from "../img/email-tutorial-mailbox.png" +import loggedInImg from "../img/email-tutorial-logged.png" import Tabs from "@theme/Tabs" import TabItem from "@theme/TabItem" @@ -69,27 +69,26 @@ Nice! We're getting there. Now we need to read supply this values as the configu - - ```ts title="pages/api/auth/[...nextauth].ts" - import NextAuth from "next-auth" - import Email from "next-auth/providers/email" +```ts title="pages/api/auth/[...nextauth].ts" +import NextAuth from "next-auth" +import Email from "next-auth/providers/email" - export default NextAuth({ - providers: [ - Email({ - server: { - host: process.env.SMTP_HOST, - port: Number(process.env.SMTP_PORT), - auth: { - user: process.env.SMTP_USER, - pass: process.env.SMTP_PASSWORD, - }, +export default NextAuth({ + providers: [ + Email({ + server: { + host: process.env.SMTP_HOST, + port: Number(process.env.SMTP_PORT), + auth: { + user: process.env.SMTP_USER, + pass: process.env.SMTP_PASSWORD, }, - from: process.env.EMAIL_FROM, - }), - ], - }) - ``` + }, + from: process.env.EMAIL_FROM, + }), + ], +}) +``` @@ -103,7 +102,6 @@ Nice! We're getting there. Now we need to read supply this values as the configu - ## 3. Setting up an adapter Finally, we'll need to set up a database adapter to store verification tokens the Email Provider will emit when verifying users. @@ -161,30 +159,30 @@ And now let's reference this new adapter from our Auth.js configuration file: - ```diff title="pages/api/auth/[...nextauth].ts" - import NextAuth from "next-auth" - import EmailProvider from "next-auth/providers/email" - + import { MongoDBAdapter } from "@auth/mongodb-adapter" - + import clientPromise from "../../../lib/mongodb/client" +```diff title="pages/api/auth/[...nextauth].ts" +import NextAuth from "next-auth" +import EmailProvider from "next-auth/providers/email" ++ import { MongoDBAdapter } from "@auth/mongodb-adapter" ++ import clientPromise from "../../../lib/mongodb/client" - export default NextAuth({ - secret: process.env.NEXTAUTH_SECRET, - + adapter: MongoDBAdapter(clientPromise), - providers: [ - EmailProvider({ - server: { - host: process.env.EMAIL_SERVER_HOST, - port: process.env.EMAIL_SERVER_PORT, - auth: { - user: process.env.EMAIL_SERVER_USER, - pass: process.env.EMAIL_SERVER_PASSWORD - } - }, - from: process.env.EMAIL_FROM - }), - ], - }) - ``` +export default NextAuth({ + secret: process.env.NEXTAUTH_SECRET, ++ adapter: MongoDBAdapter(clientPromise), + providers: [ + EmailProvider({ + server: { + host: process.env.EMAIL_SERVER_HOST, + port: process.env.EMAIL_SERVER_PORT, + auth: { + user: process.env.EMAIL_SERVER_USER, + pass: process.env.EMAIL_SERVER_PASSWORD + } + }, + from: process.env.EMAIL_FROM + }), + ], +}) +``` @@ -198,7 +196,6 @@ And now let's reference this new adapter from our Auth.js configuration file: - ## 4. Wiring all together Now that everything is properly configured, let's try to sign in via email on our application. diff --git a/docs/docs/getting-started/providers/index.mdx b/docs/docs/getting-started/providers/index.mdx new file mode 100644 index 00000000..cc2c33cb --- /dev/null +++ b/docs/docs/getting-started/providers/index.mdx @@ -0,0 +1,36 @@ +--- +title: Login Providers +sidebar_position: 1 +--- + +import { providers } from "../../../manifest.json" + +Using an Auth.js / NextAuth.js provider.... + +- [Using a database adapter](/guides/adapters/using-a-database-adapter) +- [Creating your own database adapter](/guides/adapters/creating-a-database-adapter) +- [Learn: Database session strategy](/concepts/session-strategies#database) + +## How to use an adapter + +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). + +## Official providers + +The following listed official providers are created and maintained by the community: + +
+ {Object.entries(providers).map(([id, name]) => ( + + +

{name}

+
+ ))} +
+ +## TypeScript + +Check out the [`@auth/core/providers` API Reference](/reference/core/providers) documentation. diff --git a/docs/docs/getting-started/oauth-tutorial.mdx b/docs/docs/getting-started/providers/oauth-tutorial.mdx similarity index 97% rename from docs/docs/getting-started/oauth-tutorial.mdx rename to docs/docs/getting-started/providers/oauth-tutorial.mdx index 9eb413bd..7a856159 100644 --- a/docs/docs/getting-started/oauth-tutorial.mdx +++ b/docs/docs/getting-started/providers/oauth-tutorial.mdx @@ -2,12 +2,12 @@ title: OAuth authentication --- -import creatingOauthAppImg from "./img/getting-started-creating-oauth-app.png" -import addingCallbackUrlImg from "./img/getting-started-oauth-callback-url.png" -import gettingClientIdSecretImg from "./img/getting-started-oauth-clientid-secret.png" -import startAppAndSignInImg from "./img/getting-started-app-start.png" -import githubAuthCredentials from "./img/getting-started-github-auth.png" -import nextAuthUserLoggedIn from "./img/getting-started-nextauth-success.png" +import creatingOauthAppImg from "../img/getting-started-creating-oauth-app.png" +import addingCallbackUrlImg from "../img/getting-started-oauth-callback-url.png" +import gettingClientIdSecretImg from "../img/getting-started-oauth-clientid-secret.png" +import startAppAndSignInImg from "../img/getting-started-app-start.png" +import githubAuthCredentials from "../img/getting-started-github-auth.png" +import nextAuthUserLoggedIn from "../img/getting-started-nextauth-success.png" import Tabs from "@theme/Tabs" import TabItem from "@theme/TabItem" @@ -391,7 +391,7 @@ Note that, for each provider, the configuration process will be similar to what 2. Create create your OAuth application within it 3. Set the callback URL 4. Get the Client ID and Generate a Client Secret -::: + ::: ## 3. Wiring all together diff --git a/docs/docs/getting-started/typescript.md b/docs/docs/getting-started/typescript.md index 35b35b19..7c3dd75a 100644 --- a/docs/docs/getting-started/typescript.md +++ b/docs/docs/getting-started/typescript.md @@ -2,10 +2,7 @@ title: TypeScript --- -Auth.js has its own type definitions to use in your TypeScript projects safely. Even if you don't use TypeScript, IDEs like VSCode will pick this up to provide you with a better developer experience. While you are typing, you will get suggestions about what certain objects/functions look like, and sometimes links to documentation, examples, and other valuable resources. - -Check out the example repository showcasing how to use `next-auth` on a Next.js application with TypeScript: -https://github.com/nextauthjs/next-auth-example +Auth.js is committed to type-safety, so it's written in TypeScript and comes with its own type definitions to use in projects. Even if you don't use TypeScript, IDEs like VSCode will pick this up to provide you with a better developer experience. While you are typing, you will get suggestions about what certain objects/functions look like, and sometimes links to documentation, examples, and other valuable resources. --- @@ -14,68 +11,58 @@ import TabItem from "@theme/TabItem" ## Adapters -If you're writing your own custom Adapter, you can take advantage of the types to make sure your implementation conforms to what's expected: - -```ts -import type { Adapter } from "next-auth/adapters" - -function MyAdapter(): Adapter { - return { - // your adapter methods here - } -} -``` - -When writing your own custom Adapter in plain JavaScript, note that you can use **JSDoc** to get helpful editor hints and auto-completion like so: - -```js -/** @return { import("next-auth/adapters").Adapter } */ -function MyAdapter() { - return { - // your adapter methods here - } -} -``` - -:::note -This will work in code editors with a strong TypeScript integration like VSCode or WebStorm. It might not work if you're using more lightweight editors like VIM or Atom. -::: +Check out the [Database Adapters: TypeScript](/getting-started/adapters#typescript) section. ## Module Augmentation -`next-auth` comes with certain types/interfaces that are shared across submodules. Good examples are `Session` and `JWT`. Ideally, you should only need to create these types at a single place, and TS should pick them up in every location where they are referenced. Luckily, Module Augmentation is exactly that, which can do this for us. Define your shared interfaces in a single place, and get type-safety across your application when using `next-auth` (or one of its submodules). +Auth.js libraries come with certain interfaces that are shared across submodules and different Auth.js libraries (For example: `next-auth` and `@auth/prisma-adapter` will rely on types from `@auth/core`). -### Main module +Good examples of such interfaces are `Session` or `User`. You can use TypeScript's [Module Augmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation) to extend these types to add your own properties. -Let's look at `Session`: +
+ +Why not use generics? + +The interfaces that are shared across submodules are not passed to Auth.js library functions as generics. + +Whenever these types are used, the functions always expect to return these formats. With generics, one might be able to override the type in one place, but not the other, which would cause the types to be out of sync with the implementation. + +With module augmentation, you defined the types once, and you can be sure that they are always the same where it's expected. + +
+ +Let's look at `Session` for example: - ```ts title="pages/api/auth/[...nextauth].ts" - import NextAuth from "next-auth" +```ts +// auth.ts +import NextAuth, { type DefaultSession } from "next-auth" - export default NextAuth({ - callbacks: { - session({ session, token, user }) { - return session // The return type will match the one returned in `useSession()` - }, - }, - }) - ``` - - ```ts title="pages/index.ts" - import { useSession } from "next-auth/react" - - export default function IndexPage() { - // `session` will match the returned value of `callbacks.session()` from `NextAuth()` - const { data: session } = useSession() - - return ( - // Your component - ) +declare module "@auth/core" { + /** + * Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context + */ + interface Session { + user: { + /** The user's postal address. */ + address: string + // By default, TypeScript merges new interface properties and overwrite existing ones. In this case, the default session user properties will be overwritten, with the new one defined above. To keep the default session user properties, you need to add them back into the newly declared interface + } & DefaultSession["user"] // To keep the default types } - ``` +} + +export const { auth } = NextAuth({ + callbacks: { + session({ session, token, user }) { + // session.user.address is now a valid property, and will be type-checked + // in places like `useSession().data.user` or `auth().user` + return session + }, + }, +}) +``` @@ -89,76 +76,34 @@ Let's look at `Session`: -To extend/augment this type, create a `types/next-auth.d.ts` file in your project: - -```ts title="types/next-auth.d.ts" -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 { - user: { - /** The user's postal address. */ - address: string - } - } -} -``` - -#### Extend default interface properties - -By default, TypeScript will merge new interface properties and overwrite existing ones. In this case, the default session user properties will be overwritten, with the new one defined above. - -If you want to keep the default session user properties, you need to add them back into the newly declared interface: - -```ts title="types/next-auth.d.ts" -import NextAuth, { DefaultSession } from "next-auth" - -declare module "next-auth" { - /** - * Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context - */ - interface Session { - user: { - /** The user's postal address. */ - address: string - } & DefaultSession["user"] - } -} -``` - #### Popular interfaces to augment -Although you can augment almost anything, here are some of the more common interfaces that you might want to override in the `next-auth` module: +Module augmentation is not limitied to specific interfaces. You can augment almost anything, but here are some of the more common interfaces that you might need to override in based on your use-case: ```ts -/** - * The shape of the user object returned in the OAuth providers' `profile` callback, - * or the second parameter of the `session` callback, when using a database. - */ -interface User {} -/** - * Usually contains information about the provider being used - * and also extends `TokenSet`, which is different tokens returned by OAuth Providers. - */ -interface Account {} -/** The OAuth profile returned from your provider */ -interface Profile {} -``` +declare module "@auth/core" { + /** + * The shape of the user object returned in the OAuth providers' `profile` callback, + * or the second parameter of the `session` callback, when using a database. + */ + interface User {} + /** + * The shape of the account object returned in the OAuth providers' `account` callback, + * Usually contains information about the provider being used, like OAuth tokens (`access_token`, etc). + */ + interface Account {} -Make sure that the `types` folder is added to [`typeRoots`](https://www.typescriptlang.org/tsconfig/#typeRoots) in your project's `tsconfig.json` file. + /** + * Returned by `useSession`, `auth`, contains information about the active session. + */ + interface Session {} +} -### Submodules +// The `JWT` interface can be found in the `next-auth/jwt` submodule +import { JWT } from "@auth/core/jwt" -The `JWT` interface can be found in the `next-auth/jwt` submodule: - -```ts title="types/next-auth.d.ts" -import { JWT } from "next-auth/jwt" - -declare module "next-auth/jwt" { - /** Returned by the `jwt` callback and `getToken`, when using JWT sessions */ +declare module "@auth/core/jwt" { + /** Returned by the `jwt` callback and `auth`, when using JWT sessions */ interface JWT { /** OpenID ID Token */ idToken?: string @@ -166,15 +111,9 @@ declare module "next-auth/jwt" { } ``` -### Useful links +The module declaration can be added to any file that is [included](https://www.typescriptlang.org/tsconfig#include) in your project. + +## Useful links 1. [TypeScript documentation: Module Augmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation) 2. [Digital Ocean: Module Augmentation in TypeScript](https://www.digitalocean.com/community/tutorials/typescript-module-augmentation) - -## Contributing - -Contributions of any kind are always welcome, especially for TypeScript. Please keep in mind that we are a small team working on this project in our free time. We will try our best to give support, but if you think you have a solution for a problem, please open a PR! - -:::note -When contributing to TypeScript, if the actual JavaScript user API does not change in a breaking manner, we reserve the right to push any TypeScript change in a minor release. This ensures that we can keep on a faster release cycle. -::: diff --git a/docs/docs/guides/adapters/using-a-database-adapter.md b/docs/docs/guides/adapters/using-a-database-adapter.md deleted file mode 100644 index f52d7181..00000000 --- a/docs/docs/guides/adapters/using-a-database-adapter.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -title: Using a database adapter ---- - -An **Adapter** in Auth.js connects your application to whatever database or backend system you want to use to store data for users, their accounts, sessions, etc. Adapters are optional, unless you need to persist user information in your own database, or you want to implement certain flows. The [Email Provider](/getting-started/email-tutorial) requires an adapter to be able to save [Verification Tokens](/reference/adapters#verification-token). - -:::tip -When using a database, you can still use JWT for session handling for fast access. Learn more about [`session strategies`](/concepts/session-strategies) and their trade-offs. -::: - -We have a list of official adapters that are distributed as their own packages under the `@auth/{name}-adapter` namespace. Their source code is available in their various adapters package directories at [`nextauthjs/next-auth`](https://github.com/nextauthjs/next-auth/tree/main/packages): - -- [All available adapters](/reference/adapters) diff --git a/docs/docs/guides/basics/deployment.md b/docs/docs/guides/basics/deployment.md deleted file mode 100644 index 96c4f099..00000000 --- a/docs/docs/guides/basics/deployment.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -title: Deployment ---- - -Deploying Auth.js only requires a few steps. It can be run anywhere a Next.js application can. Therefore, in a default configuration using only JWT session strategy, i.e. without a database, you will only need these few things in addition to your application: - -1. Auth.js environment variables - - - `NEXTAUTH_SECRET` - - `NEXTAUTH_URL` - -2. Auth.js API Route and its configuration (`/pages/api/auth/[...nextauth].js`). - - OAuth Provider `clientId` / `clientSecret` - -Deploying a modern JavaScript application using Auth.js consists of making sure your environment variables are set correctly as well as the configuration in the Auth.js API route is setup, as well as any configuration (like Callback URLs, etc.) are correctly done in your OAuth provider(s) themselves. - -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. - a. You can use `openssl rand -base64 32` or https://generate-secret.vercel.app/32 to generate a random value. -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. - -### Securing a preview deployment - -Most OAuth providers cannot be configured with multiple callback URLs or using a wildcard. - -However, Auth.js **supports Preview deployments**, even **with OAuth providers**: - -1. Determine a stable deployment URL. Eg.: A deployment whose URL does not change between builds, for example. `auth.yourdomain.com` (using a subdomain is not a requirement, this can simply be the main site's URL too.), -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`) - -:::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. -::: - -:::note -If you are storing users in a [database](reference/adapters), we recommend using a different OAuth app for development/production so that you don't mix your test and production user base. -::: - -
- -How does this work? - -To support preview deployments, Auth.js uses the stable deployment URL as a redirect proxy server. - -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. - -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. - -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. - -See also: - -
- -## 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. - -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. diff --git a/docs/docs/reference/adapters/index.md b/docs/docs/reference/adapters/index.md deleted file mode 100644 index f7d1a1eb..00000000 --- a/docs/docs/reference/adapters/index.md +++ /dev/null @@ -1,223 +0,0 @@ ---- -title: Overview ---- - -Using an Auth.js / NextAuth.js adapter you can connect to any database service or even several different services at the same time. The following listed official adapters are created and maintained by the community: - -
- - -

Azure Table Storage Adapter

-
- - -

D1 Adapter

-
- - -

EdgeDB Adapter

-
- - -

Dgraph Adapter

-
- - -

Drizzle Adapter

-
- - -

DynamoDB Adapter

-
- - -

Fauna Adapter

-
- - -

Firebase Adapter

-
- - -

Hasura Adapter

-
- - -

Kysely Adapter

-
- - -

Mikro ORM Adapter

-
- - -

MongoDB Adapter

-
- - -

Neo4j Adapter

-
- - -

Postgres Adapter

-
- - -

PouchDB Adapter

-
- - -

Prisma Adapter

-
- - -

Sequelize Adapter

-
- - -

Supabase Adapter

-
- - -

SurrealDB Adapter

-
- - -

TypeORM Adapter

-
- - -

Upstash Adapter

-
- - -

Xata Adapter

-
-
- -:::info -If you don't find an adapter for the database or service you use, you can always create one yourself. Have a look at our guide on [how to create a database adapter](/guides/adapters/creating-a-database-adapter). -::: - -## Models - -Auth.js can be used with any database. Models tell you what structures Auth.js expects from your database. Models will vary slightly depending on which adapter you use, but in general, will look something like this: - -```mermaid -erDiagram - User ||--|{ Account : "" - User { - string id - string name - string email - timestamp emailVerified - string image - } - User ||--|{ Session : "" - Session { - string id - timestamp expires - string sessionToken - string userId - } - Account { - string id - string userId - string type - string provider - string providerAccountId - string refresh_token - string access_token - int expires_at - string token_type - string scope - string id_token - string session_state - } - VerificationToken { - string identifier - string token - timestamp expires - } -``` - -More information about each Model/Table can be found below. - -:::note -You can [create your adapter](/guides/adapters/creating-a-database-adapter) if you want to use Auth.js with a database that is not supported out of the box, or you have to change fields on any of the models. -::: - ---- - -### User - -The User model is for information such as the user's name and email address. - -Email address is optional, but if one is specified for a User, then it must be unique. - -:::note -If a user first signs in with an OAuth provider, then their email address is automatically populated using the one from their OAuth profile if the OAuth provider returns one. - -This provides a way to contact users and for users to maintain access to their account and sign in using email in the event they are unable to sign in with the OAuth provider in the future (if the [Email Provider](/reference/core/providers_email) is configured). -::: - -User creation in the database is automatic and happens when the user is logging in for the first time with a provider. -If the first sign-in is via the [OAuth Provider](/reference/core/providers_oauth), the default data saved is `id`, `name`, `email` and `image`. You can add more profile data by returning extra fields in your [OAuth provider](/guides/providers/custom-provider)'s [`profile()`](/reference/core/providers#profile) callback. - -If the first sign-in is via the [Email Provider](/reference/core/providers_email), then the saved user will have `id`, `email`, `emailVerified`, where `emailVerified` is the timestamp of when the user was created. - -### Account - -The Account model is for information about OAuth accounts associated with a User - -A single User can have multiple Accounts, but each Account can only have one User. - -Account creation in the database is automatic and happens when the user is logging in for the first time with a provider, or the [`Adapter.linkAccount`](/reference/core/adapters#linkaccount) method is invoked. The default data saved is `access_token`, `expires_at`, `refresh_token`, `id_token`, `token_type`, `scope` and `session_state`. You can save other fields or remove the ones you don't need by returning them in the [OAuth provider](/guides/providers/custom-provider)'s [`account()`](/reference/core/providers#account) callback. - -Linking Accounts to Users happen automatically, only when they have the same e-mail address, and the user is currently signed in. Check the [FAQ](/concepts/faq#security) for more information on why this is a requirement. - -:::tip -You can manually unlink accounts if your adapter implements the `unlinkAccount` method. Make sure to take all the necessary security steps to avoid data loss. -::: - -:::note -Linking and unlinking accounts through an API is a planned feature: https://github.com/nextauthjs/next-auth/issues/230 -::: - -### Session - -The Session model is used for database sessions. It is not used if JSON Web Tokens are enabled. Keep in mind, that you can use a database to persist Users and Accounts, and still use JWT for sessions. See the [`session.strategy`](/reference/configuration/auth-config) option. - -A single User can have multiple Sessions, each Session can only have one User. - -:::tip -When a Session is read, we check if its `expires` field indicates an invalid session, and delete it from the database. You can also do this clean-up periodically in the background to avoid our extra delete call to the database during an active session retrieval. This might result in a slight performance increase in a few cases. -::: - -### Verification Token - -The Verification Token model is used to store tokens for passwordless sign in. - -A single User can have multiple open Verification Tokens (e.g. to sign in to different devices). - -It has been designed to be extendable for other verification purposes in the future (e.g. 2FA / magic codes, etc.). - -:::note -Auth.js makes sure that every token is usable only once, and by default has a short (1 day, can be configured by [`maxAge`](/guides/providers/email)) lifetime. If your user did not manage to finish the sign-in flow in time, they will have to start the sign-in process again. -::: - -:::tip -Due to users forgetting or failing at the sign-in flow, you might end up with unwanted rows in your database, that you might have to periodically clean up to avoid filling the database up with unnecessary data. -::: - -## RDBMS Naming Convention - -Auth.js / NextAuth.js uses `camelCase` for its database rows while respecting the conventional `snake_case` formatting for OAuth-related values. If the mixed casing is an issue for you, most adapters have a dedicated documentation section on how to force a casing convention. - -## TypeScript - -Check out the [`@auth/core/adapters` API Reference](/reference/core/adapters) documentation. - -## Create a custom adapter - -If you are using a database that we don't have an official adapter for, you can check out the [Creating a database adapter](/guides/adapters/creating-a-database-adapter) guide. \ No newline at end of file diff --git a/docs/package.json b/docs/package.json index 9e51e32b..9f412b31 100644 --- a/docs/package.json +++ b/docs/package.json @@ -4,14 +4,14 @@ "name": "docs", "scripts": { "start": "TYPEDOC_WATCH=true docusaurus start --no-open", - "dev": "pnpm providers && pnpm snippets && pnpm start", - "build": "pnpm providers && docusaurus build", + "dev": "pnpm manifest && pnpm snippets && pnpm start", + "build": "pnpm manifest && docusaurus build", "docusaurus": "docusaurus", "swizzle": "docusaurus swizzle", "deploy": "docusaurus deploy", "serve": "docusaurus serve", "clear": "docusaurus clear", - "providers": "node scripts/generate-providers.mjs", + "manifest": "node scripts/generate-manifest.mjs", "snippets": "node scripts/generate-snippets.mjs" }, "dependencies": { diff --git a/docs/scripts/generate-manifest.mjs b/docs/scripts/generate-manifest.mjs new file mode 100644 index 00000000..d465ba6c --- /dev/null +++ b/docs/scripts/generate-manifest.mjs @@ -0,0 +1,74 @@ +import { join } from "path" +import { readdir, readFile, writeFile } from "fs/promises" + +// TODO: Autogenerate +const frameworks = [ + { id: "nextjs", name: "NextAuth.js", url: "next-auth-example" }, + { id: "sveltekit", name: "SvelteKit Auth", url: "sveltekit-auth-example" }, + { id: "solidstart", name: "SolidStart Auth", url: "auth-solid" }, +] + +// TODO: Autogenerate +// const packagesPath = join(process.cwd(), "../packages") +// const adaptersPaths = await readdir(packagesPath).then((d) => +// d.filter((dirent) => dirent.startsWith("adapter-")) +// ) +// const adapters = await adaptersPaths.reduce((acc, path) => { +// const id = path.replace("adapter-", "") +// const name = id +// .split("-") +// .map((w) => w[0].toUpperCase() + w.slice(1)) +// .join(" ") +// acc[id] = name +// return acc +// }, {}) + +const adapters = { + "azure-tables": ["Azure Tables Storage"], + d1: ["D1"], + dgraph: ["Dgraph", "dgraph.png"], + drizzle: ["Drizzle ORM", "drizzle-orm.png"], + dynamodb: ["DynamoDB", "dynamodb.png"], + edgedb: ["EdgeDB"], + fauna: ["Fauna", "fauna.png"], + firebase: ["Firebase"], + hasura: ["Hasura"], + kysely: ["Kysely"], + "mikro-orm": ["Mikro ORM", "mikro-orm.png"], + mongodb: ["MongoDB"], + neo4j: ["Neo4j"], + pg: ["pg", "pg.png"], + pouchdb: ["PouchDB"], + prisma: ["Prisma"], + sequelize: ["Sequelize"], + supabase: ["Supabase"], + surrealdb: ["SurrealDB", "surreal.png"], + typeorm: ["TypeORM", "typeorm.png"], + "upstash-redis": ["Upstash Redis"], + xata: ["Xata"], +} + +const providersPath = join(process.cwd(), "../packages/core/src/providers") +const providerFiles = await readdir(providersPath, "utf8") +const notOAuth = [ + "index.ts", + "oauth-types.ts", + "email.ts", + "credentials.ts", + "oauth.ts", +] + +const providers = {} + +for (const file of providerFiles) { + if (notOAuth.includes(file)) continue + const provider = await readFile(join(providersPath, file), "utf8") + const { id } = provider.match(/id: "(?.+)",/).groups + const { title } = provider.match(/name: "(?.+)",/).groups + providers[id] = title +} + +await writeFile( + join(process.cwd(), "manifest.json"), + JSON.stringify({ frameworks, adapters, providers }, null, 2) +) diff --git a/docs/scripts/generate-providers.mjs b/docs/scripts/generate-providers.mjs deleted file mode 100644 index c816a8c8..00000000 --- a/docs/scripts/generate-providers.mjs +++ /dev/null @@ -1,28 +0,0 @@ -import { join } from "path" -import { readdirSync, readFileSync, writeFileSync } from "fs" - -const providersPath = join(process.cwd(), "../packages/core/src/providers") - -const files = readdirSync(providersPath, "utf8") - -const notOAuth = [ - "index.ts", - "oauth-types.ts", - "email.ts", - "credentials.ts", - "oauth.ts", -] - -const result = files.reduce((acc, file) => { - if (notOAuth.includes(file)) return acc - const provider = readFileSync(join(providersPath, file), "utf8") - const { id } = provider.match(/id: "(?<id>.+)",/).groups - const { title } = provider.match(/name: "(?<title>.+)",/).groups - acc[id] = title - return acc -}, {}) - -writeFileSync( - join(process.cwd(), "providers.json"), - JSON.stringify(result, null, 2) -) diff --git a/docs/sidebars.js b/docs/sidebars.js index 4ca519a8..1f59cc96 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -50,7 +50,7 @@ module.exports = { { type: "category", label: "Database Adapters", - link: { type: "doc", id: "reference/adapters/index" }, + collapsed: false, items: [ { type: "doc", id: "reference/adapter/azure-tables/index" }, { type: "doc", id: "reference/adapter/d1/index" }, diff --git a/docs/src/css/adapters.css b/docs/src/css/cards.css similarity index 61% rename from docs/src/css/adapters.css rename to docs/src/css/cards.css index 140dd129..e923c1a1 100644 --- a/docs/src/css/adapters.css +++ b/docs/src/css/cards.css @@ -1,44 +1,40 @@ -.adapter-card-list { +.card-list { display: flex; - flex-direction: row; - align-items: center; - flex-wrap: wrap; width: 100%; margin-bottom: 40px; + gap: 8px; + flex-wrap: wrap; } -.adapter-card { +.card { border: 1px solid #dbdde1; border-radius: 8px; - padding: 16px; - width: 255px; height: 80px; + width: calc(33.33% - 16px); + padding: 16px; display: flex; - flex-direction: row; - align-items: center; justify-content: center; - margin: 0; - margin-right: 8px; - margin-bottom: 8px; + align-items: center; + flex-direction: row; color: black; + gap: 8px; text-decoration: none; transition: 0.2s background-color ease-in-out; } -html[data-theme="dark"] .adapter-card { +html[data-theme="dark"] .card { color: #f5f5f5; } -html[data-theme="dark"] .adapter-card:hover, -.adapter-card:hover { +html[data-theme="dark"] .card:hover, +.card:hover { text-decoration: none; color: black; background-color: #f5f5f5; } -.adapter-card__title { +.card__title { font-size: 18px; font-weight: bold; margin: 0; - margin-left: 8px; } diff --git a/docs/src/css/index.css b/docs/src/css/index.css index bb6c9e09..87433cf3 100644 --- a/docs/src/css/index.css +++ b/docs/src/css/index.css @@ -63,7 +63,7 @@ html[data-theme="dark"] svg[id^="mermaid-svg"] text[id*="-attr"] { @import "providers.css"; @import "navbar.css"; @import "search.css"; -@import "adapters.css"; +@import "cards.css"; a { font-weight: 600; diff --git a/docs/src/pages/index.js b/docs/src/pages/index.js index c71656ee..c19601b7 100644 --- a/docs/src/pages/index.js +++ b/docs/src/pages/index.js @@ -8,7 +8,7 @@ import classnames from "classnames" import { useEffect } from "react" import ProviderMarquee from "../components/ProviderMarquee" import styles from "./index.module.css" -import providers from "../../providers.json" +import { providers } from "../../manifest.json" import { Clerk } from "../components/clerk" const providersCount = Object.keys(providers).length + 2 // email, credentials @@ -219,9 +219,7 @@ export default function Home() { <div className="row"> <div className="col col--6"> <div className="code"> - <div className="code-heading"> - Next.js <span>/pages/api/auth/[...nextauth].ts</span> - </div> + <div className="code-heading">Next.js</div> <CodeBlock className="prism-code language-js"> {nextJsCode} </CodeBlock> @@ -274,63 +272,39 @@ export default function Home() { const svelteKitCode = ` import { SvelteKitAuth } from "@auth/sveltekit" import GitHub from '@auth/core/providers/github' -import Facebook from '@auth/core/providers/facebook' -import Google from '@auth/core/providers/google' - -import { - GITHUB_ID, - GITHUB_SECRET, - FACEBOOK_ID, - FACEBOOK_SECRET, - GOOGLE_ID, - GOOGLE_SECRET -} from "$env/static/private" - +import { GITHUB_ID, GITHUB_SECRET } from "$env/static/private" export const handle = SvelteKitAuth({ providers: [ - GitHub({ clientId: GITHUB_ID, clientSecret: GITHUB_SECRET }), - Facebook({ clientId: FACEBOOK_ID, clientSecret: FACEBOOK_SECRET }), - Google({ clientId: GOOGLE_ID, clientSecret: GOOGLE_SECRET }) + GitHub({ + clientId: GITHUB_ID, + clientSecret: GITHUB_SECRET + }) ], }) `.trim() -const solidStartCode = - `import { SolidAuth, type SolidAuthConfig } from "@auth/solid-start"; -import GitHub from "@auth/core/providers/github"; - -export const authOpts: SolidAuthConfig = { - providers: [ - GitHub({ - clientId: process.env.GITHUB_ID, - clientSecret: process.env.GITHUB_SECRET, - }), - ], - debug: false, -}; - -export const { GET, POST } = SolidAuth(authOpts);`.trim() - -const nextJsCode = ` -import NextAuth from 'next-auth' -import GitHub from 'next-auth/providers/github' -import Facebook from 'next-auth/providers/facebook' -import Google from 'next-auth/providers/google' - -export default NextAuth({ +const solidStartCode = `import { SolidAuth } from "@auth/solid-start" +import GitHub from "@auth/core/providers/github" +export const { GET, POST } = SolidAuth({ providers: [ GitHub({ clientId: process.env.GITHUB_ID, clientSecret: process.env.GITHUB_SECRET - }), - Facebook({ - clientId: process.env.FACEBOOK_ID, - clientSecret: process.env.FACEBOOK_SECRET - }), - Google({ - clientId: process.env.GOOGLE_ID, - clientSecret: process.env.GOOGLE_SECRET }) ] -}) +})`.trim() + +const nextJsCode = ` +// auth.ts +import NextAuth from "next-auth" +import GitHub from "next-auth/providers/github" +export const { auth, handlers } = NextAuth({ providers: [ GitHub ] }) + +// middleware.ts +export { auth as default } from "auth" + +// app/api/auth/[...nextauth].ts +import { handlers } from "auth" +export const { GET, POST } = handlers +export const runtime = "edge" `.trim() diff --git a/docs/static/img/frameworks/nextjs.svg b/docs/static/img/frameworks/nextjs.svg new file mode 100644 index 00000000..f9df46ab --- /dev/null +++ b/docs/static/img/frameworks/nextjs.svg @@ -0,0 +1,25 @@ +<svg aria-label="Next.js logomark" class="next-mark_root__iLw9v" height="80" role="img" viewBox="0 0 180 180" + width="80" xmlns="http://www.w3.org/2000/svg"> + <mask height="180" id=":R0:mask0_408_134" maskUnits="userSpaceOnUse" style="mask-type:alpha" width="180" x="0" y="0"> + <circle cx="90" cy="90" fill="black" r="90"></circle> + </mask> + <g mask="url(#:R0:mask0_408_134)"> + <circle cx="90" cy="90" data-circle="true" fill="black" r="90"></circle> + <path + d="M149.508 157.52L69.142 54H54V125.97H66.1136V69.3836L139.999 164.845C143.333 162.614 146.509 160.165 149.508 157.52Z" + fill="url(#:R0:paint0_linear_408_134)"></path> + <rect fill="url(#:R0:paint1_linear_408_134)" height="72" width="12" x="115" y="54"></rect> + </g> + <defs> + <linearGradient gradientUnits="userSpaceOnUse" id=":R0:paint0_linear_408_134" x1="109" x2="144.5" y1="116.5" + y2="160.5"> + <stop stop-color="white"></stop> + <stop offset="1" stop-color="white" stop-opacity="0"></stop> + </linearGradient> + <linearGradient gradientUnits="userSpaceOnUse" id=":R0:paint1_linear_408_134" x1="121" x2="120.799" y1="54" + y2="106.875"> + <stop stop-color="white"></stop> + <stop offset="1" stop-color="white" stop-opacity="0"></stop> + </linearGradient> + </defs> +</svg> diff --git a/docs/static/img/frameworks/solidstart.svg b/docs/static/img/frameworks/solidstart.svg new file mode 100644 index 00000000..b6dbba6b --- /dev/null +++ b/docs/static/img/frameworks/solidstart.svg @@ -0,0 +1,176 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 189.3 162" style="enable-background:new 0 0 189.3 162;" xml:space="preserve"> + <style type="text/css"> + + .st0{clip-path:url(#SVGID_00000013879980047908369760000014333962450926967714_);fill:url(#SVGID_00000078733767835006856550000006406972695347467450_);} + + .st1{clip-path:url(#SVGID_00000166657476033872553300000009290018689514942103_);fill:url(#SVGID_00000098211131999124439290000005169890874362699711_);} + + .st2{clip-path:url(#SVGID_00000146479414203068755060000016979134224437623728_);fill:url(#SVGID_00000059285692623184630360000001939970175973229744_);} + + .st3{clip-path:url(#SVGID_00000126316706323320366270000003443020999953567882_);fill:url(#SVGID_00000057146489597920305920000000095531551485622941_);} + + .st4{clip-path:url(#SVGID_00000026133452627006666250000000783177764216147076_);fill:url(#SVGID_00000139986013050778127310000015816529348737596301_);} + + .st5{clip-path:url(#SVGID_00000059296638833140507400000008861971299043677096_);fill:url(#SVGID_00000068637401408036592480000016595487892732985731_);} + + .st6{clip-path:url(#SVGID_00000172429222656778552100000016729080874745752493_);fill:url(#SVGID_00000055696528104929319780000017787341016400387243_);} + + .st7{clip-path:url(#SVGID_00000138543928091543779610000002511334883899467966_);fill:url(#SVGID_00000116194030819667900180000011798705412116101279_);} + </style> + <g> + <g> + <defs> + <path id="SVGID_1_" d="M87.8,149.1l26.9-2.1c0,0,9.2-1.5,14.6-10.4l-19-4.6L87.8,149.1z"/> + </defs> + <clipPath id="SVGID_00000147182750622660896660000006407372419834521264_"> + <use xlink:href="#SVGID_1_" style="overflow:visible;"/> + </clipPath> + + <linearGradient id="SVGID_00000043455066869680698080000005568379131735620027_" gradientUnits="userSpaceOnUse" x1="-19.413" y1="-133.61" x2="-18.413" y2="-133.61" gradientTransform="matrix(-35.4765 174.8486 -174.8486 -35.4765 -23914.8516 -1337.4067)"> + <stop offset="0" style="stop-color:#1593F5"/> + <stop offset="1" style="stop-color:#0084CE"/> + </linearGradient> + + <polygon style="clip-path:url(#SVGID_00000147182750622660896660000006407372419834521264_);fill:url(#SVGID_00000043455066869680698080000005568379131735620027_);" points=" + 132.6,132.7 127.7,157.2 84.4,148.4 89.4,123.9 "/> + </g> + </g> + <g> + <g> + <defs> + <path id="SVGID_00000117666701953909478440000000358665142799502254_" d="M50.6,86c-8,0.4-22.8,1.8-22.8,1.8l64.5,46.9l14.7,3.7 + l22.3-1.7l-64.9-47c0,0-5.2-3.7-12.7-3.7C51.3,86,51,86,50.6,86"/> + </defs> + <clipPath id="SVGID_00000021826999385872549080000006033995051984800693_"> + <use xlink:href="#SVGID_00000117666701953909478440000000358665142799502254_" style="overflow:visible;"/> + </clipPath> + + <linearGradient id="SVGID_00000178165318439686096770000016761567998857907644_" gradientUnits="userSpaceOnUse" x1="-20.1016" y1="-134.4351" x2="-19.1016" y2="-134.4351" gradientTransform="matrix(-148.6635 -238.1995 238.1995 -148.6635 29165.9043 -24576.1035)"> + <stop offset="0" style="stop-color:#1593F5"/> + <stop offset="1" style="stop-color:#0084CE"/> + </linearGradient> + + <polygon style="clip-path:url(#SVGID_00000021826999385872549080000006033995051984800693_);fill:url(#SVGID_00000178165318439686096770000016761567998857907644_);" points=" + 56.2,184 4.2,100.7 100.8,40.4 152.9,123.7 "/> + </g> + </g> + <g> + <g> + <defs> + <path id="SVGID_00000156577277383517703870000010068914798521039488_" d="M133.8,76.7l27.7-1.7c0,0,9.5-1.4,14.9-10.2l-19.7-4.9 + L133.8,76.7z"/> + </defs> + <clipPath id="SVGID_00000142889961474463198250000008302825665989277093_"> + <use xlink:href="#SVGID_00000156577277383517703870000010068914798521039488_" style="overflow:visible;"/> + </clipPath> + + <linearGradient id="SVGID_00000132086538092097319310000013227422625556950408_" gradientUnits="userSpaceOnUse" x1="-20.1877" y1="-134.65" x2="-19.1877" y2="-134.65" gradientTransform="matrix(-89.536 -160.489 160.489 -89.536 20031.8984 -15094.2344)"> + <stop offset="0" style="stop-color:#FFFFFF"/> + <stop offset="1" style="stop-color:#15ABFF"/> + </linearGradient> + + <polygon style="clip-path:url(#SVGID_00000142889961474463198250000008302825665989277093_);fill:url(#SVGID_00000132086538092097319310000013227422625556950408_);" points=" + 143.9,94.8 126.6,63.8 166.3,41.7 183.6,72.7 "/> + </g> + </g> + <g> + <g> + <defs> + <path id="SVGID_00000098205560235091293490000000421317058630212272_" d="M94.6,12.7c-8.3,0.3-23.5,1.5-23.5,1.5l67.2,48 + l15.2,3.9l23-1.4l-67.6-48.1c0,0-5.6-3.9-13.5-3.9C95.2,12.7,94.9,12.7,94.6,12.7"/> + </defs> + <clipPath id="SVGID_00000093861389545452675440000006562489652059062448_"> + <use xlink:href="#SVGID_00000098205560235091293490000000421317058630212272_" style="overflow:visible;"/> + </clipPath> + + <linearGradient id="SVGID_00000028286881209211410930000013657892739144660655_" gradientUnits="userSpaceOnUse" x1="-19.7576" y1="-133.3852" x2="-18.7576" y2="-133.3852" gradientTransform="matrix(76.0211 174.0039 -174.0039 76.0211 -21636.4453 13496.8467)"> + <stop offset="0" style="stop-color:#FFFFFF"/> + <stop offset="1" style="stop-color:#79CFFF"/> + </linearGradient> + + <polygon style="clip-path:url(#SVGID_00000093861389545452675440000006562489652059062448_);fill:url(#SVGID_00000028286881209211410930000013657892739144660655_);" points=" + 159.6,-25.9 196.1,57.6 88,104.8 51.5,21.3 "/> + </g> + </g> + <g> + <g> + <defs> + <path id="SVGID_00000124852738755951175750000014112089622672183742_" d="M11.8,98.2c-0.1,0.1-0.1,0.2-0.1,0.3l17.7,12.8 + L46.9,124l29.4,21.3c9.9,7.1,23.3,4.1,30.1-6.9l-17.9-12.9l-17.9-12.9L41.5,91.4c-3.6-2.6-7.6-3.8-11.6-3.8 + C22.9,87.6,16,91.3,11.8,98.2"/> + </defs> + <clipPath id="SVGID_00000173141673324521593940000008495516377158554543_"> + <use xlink:href="#SVGID_00000124852738755951175750000014112089622672183742_" style="overflow:visible;"/> + </clipPath> + + <linearGradient id="SVGID_00000041979392027505662920000001185191142966959546_" gradientUnits="userSpaceOnUse" x1="-19.3994" y1="-133.9089" x2="-18.3994" y2="-133.9089" gradientTransform="matrix(-120.7891 167.2465 -167.2465 -120.7891 -24582.5547 -12945.1768)"> + <stop offset="0" style="stop-color:#0057E5"/> + <stop offset="1" style="stop-color:#0084CE"/> + </linearGradient> + + <polygon style="clip-path:url(#SVGID_00000173141673324521593940000008495516377158554543_);fill:url(#SVGID_00000041979392027505662920000001185191142966959546_);" points=" + 137.2,109.8 73.9,197.4 -19.1,130.2 44.1,42.6 "/> + </g> + </g> + <g> + <g> + <defs> + <path id="SVGID_00000149381950616545464640000006795646713605551536_" d="M55.4,24.5c0,0.1-0.1,0.2-0.2,0.3l18.4,13.1L92,50.9 + l30.6,21.8c10.3,7.3,24.1,4.4,30.9-6.5l-18.6-13.3l-18.6-13.2L85.9,18c-3.8-2.7-8.1-4-12.3-4C66.5,14.1,59.6,17.7,55.4,24.5"/> + </defs> + <clipPath id="SVGID_00000033332812682631022190000015239929743782804902_"> + <use xlink:href="#SVGID_00000149381950616545464640000006795646713605551536_" style="overflow:visible;"/> + </clipPath> + + <linearGradient id="SVGID_00000127026399759025924800000014634641933690009743_" gradientUnits="userSpaceOnUse" x1="-18.9789" y1="-133.53" x2="-17.9789" y2="-133.53" gradientTransform="matrix(-38.0106 104.7402 -104.7402 -38.0106 -14578.2188 -3108.949)"> + <stop offset="0" style="stop-color:#FFFFFF"/> + <stop offset="1" style="stop-color:#15ABFF"/> + </linearGradient> + + <polygon style="clip-path:url(#SVGID_00000033332812682631022190000015239929743782804902_);fill:url(#SVGID_00000127026399759025924800000014634641933690009743_);" points=" + 174.6,21.7 142.1,111.5 34.1,72.3 66.7,-17.5 "/> + </g> + </g> + <g> + <g> + <defs> + <path id="SVGID_00000107570939368200823020000005836291322447728054_" d="M42.4,39.3l23.7,26c1.1,1.5,2.3,2.9,3.7,4.1l46.2,50.8 + l23-1.4c6.8-10.9,4-25.7-6.3-33L102.1,64L83.8,51L65.4,37.9L42.4,39.3z"/> + </defs> + <clipPath id="SVGID_00000060738096654201270220000007292607683568861614_"> + <use xlink:href="#SVGID_00000107570939368200823020000005836291322447728054_" style="overflow:visible;"/> + </clipPath> + + <linearGradient id="SVGID_00000056428342560333279100000016968355024903630220_" gradientUnits="userSpaceOnUse" x1="-20.0549" y1="-134.41" x2="-19.0549" y2="-134.41" gradientTransform="matrix(-190.8975 -251.7144 251.7144 -190.8975 30181.0039 -30518.9199)"> + <stop offset="0" style="stop-color:#FFFFFF"/> + <stop offset="1" style="stop-color:#79CFFF"/> + </linearGradient> + + <polygon style="clip-path:url(#SVGID_00000060738096654201270220000007292607683568861614_);fill:url(#SVGID_00000056428342560333279100000016968355024903630220_);" points=" + 80.2,170.1 2.7,68 108.1,-11.9 185.5,90.2 "/> + </g> + </g> + <g> + <g> + <defs> + <path id="SVGID_00000122720151179349157490000018212233550025661058_" d="M42.2,39.6c-6.7,10.8-3.9,25.4,6.2,32.6l30.3,21.6 + L97.4,107l18.6,13.2c6.8-10.9,4-25.7-6.3-33L79.2,65.5L60.8,52.4L42.4,39.3C42.4,39.4,42.3,39.5,42.2,39.6"/> + </defs> + <clipPath id="SVGID_00000055708945144556612230000014551721053078643382_"> + <use xlink:href="#SVGID_00000122720151179349157490000018212233550025661058_" style="overflow:visible;"/> + </clipPath> + + <linearGradient id="SVGID_00000127756858579337829260000014186552904843483300_" gradientUnits="userSpaceOnUse" x1="-19.4497" y1="-133.8208" x2="-18.4497" y2="-133.8208" gradientTransform="matrix(-97.9828 193.4315 -193.4315 -97.9828 -27647.6406 -9396.6328)"> + <stop offset="0" style="stop-color:#FFFFFF"/> + <stop offset="1" style="stop-color:#79CFFF"/> + </linearGradient> + + <polygon style="clip-path:url(#SVGID_00000055708945144556612230000014551721053078643382_);fill:url(#SVGID_00000127756858579337829260000014186552904843483300_);" points=" + 155.5,55.9 105,155.5 2.9,103.8 53.4,4.1 "/> + </g> + </g> +</svg> diff --git a/docs/static/img/frameworks/sveltekit.svg b/docs/static/img/frameworks/sveltekit.svg new file mode 100644 index 00000000..49492a83 --- /dev/null +++ b/docs/static/img/frameworks/sveltekit.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="107" height="128" viewBox="0 0 107 128"><title>svelte-logo \ No newline at end of file diff --git a/docs/static/img/nextjs-logo.svg b/docs/static/img/nextjs-logo.svg deleted file mode 100644 index 63c9500f..00000000 --- a/docs/static/img/nextjs-logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/docs/vercel.json b/docs/vercel.json index 5a8d16e8..2208290a 100644 --- a/docs/vercel.json +++ b/docs/vercel.json @@ -39,7 +39,7 @@ }, { "source": "/schemas/adapters", - "destination": "/adapters/overview", + "destination": "/getting-started/adapters", "permanent": true }, { @@ -90,7 +90,7 @@ { "source": "/", "has": [{ "type": "host", "value": "adapters.authjs.dev" }], - "destination": "https://authjs.dev/reference/adapters" + "destination": "https://authjs.dev/getting-started/adapters" }, { "source": "/:path(.*)", @@ -98,9 +98,19 @@ "destination": "https://authjs.dev/reference/adapter/:path*" }, { - "source": "/:path", + "source": "/", "has": [{ "type": "host", "value": "providers.authjs.dev" }], - "destination": "https://authjs.dev/reference/core/providers_:path.default" + "destination": "https://authjs.dev/getting-started/providers" + }, + { + "source": "/:path(.*)", + "has": [{ "type": "host", "value": "providers.authjs.dev" }], + "destination": "https://authjs.dev/reference/core/providers_:path" + }, + { + "source": "/getting-started/upgrade-to-v4", + "destination": "https://next-auth.js.org/getting-started/upgrade-v4", + "permanent": true } ] }