Compare commits

...

3 Commits

Author SHA1 Message Date
Marshall Bowers
d30da0170f fix(provider): make WorkOS domain configurable from signIn (#2038)
* Don't pass `domain` to the WorkOS provider

* Update docs

* Change `apiUrl` to `domain`
2021-05-22 13:40:48 +02:00
Nico Domino
887b2985fc docs(adapters): update copy regarding adapters (#2026)
* docs(adapters): update copy regarding adapters

* docs(adapters): add prisma schema page

* docs(adapters): add fauna schema/setup page

* docs(adapters): address PR comments

* Update www/docs/schemas/adapters.md

Co-authored-by: Lluis Agusti <hi@llu.lu>

* docs(adapters): update adapters.md

* docs(adapters): update adapters.md

Co-authored-by: Lluis Agusti <hi@llu.lu>
2021-05-22 12:10:26 +02:00
Nico Domino
d2bbac1164 docs: explain where pageProps come from in Provider docs (#2016)
* docs: explain where pageProps come from in Provider docs

* chore: formatting

* docs(getting-started): add alternative client session handling methods

* docs(getting-started): update alternative client api docs
2021-05-22 11:30:38 +02:00
7 changed files with 406 additions and 35 deletions

View File

@@ -1,4 +1,6 @@
export default function WorkOS(options) {
const domain = options.domain || 'api.workos.com';
return {
id: 'workos',
name: 'WorkOS',
@@ -10,9 +12,9 @@ export default function WorkOS(options) {
client_id: options.clientId,
client_secret: options.clientSecret
},
accessTokenUrl: 'https://api.workos.com/sso/token/',
authorizationUrl: `https://api.workos.com/sso/authorize/?response_type=code&domain=${options.domain}`,
profileUrl: 'https://api.workos.com/sso/profile/',
accessTokenUrl: `https://${domain}/sso/token`,
authorizationUrl: `https://${domain}/sso/authorize?response_type=code`,
profileUrl: `https://${domain}/sso/profile`,
profile: (profile) => {
return {
...profile,

View File

@@ -5,13 +5,17 @@ title: Databases
NextAuth.js comes with multiple ways of connecting to a database:
* **TypeORM** (default)<br/>
_The TypeORM adapter supports MySQL, Postgres, MsSql, SQLite and MongoDB databases._
* **Prisma**<br/>
_The Prisma 2 adapter supports MySQL, Postgres and SQLite databases._
* **Custom Adapter**<br/>
- **TypeORM** (default)<br/>
_The TypeORM adapter supports MySQL, PostgreSQL, MSSQL, SQLite and MongoDB databases._
- **Prisma**<br/>
_The Prisma 2 adapter supports MySQL, PostgreSQL and SQLite databases._
- **Fauna**<br/>
_The FaunaDB adapter only supports FaunaDB._
- **Custom Adapter**<br/>
_A custom Adapter can be used to connect to any database._
> There are currently efforts in the [`nextauthjs/adapters`](https://github.com/nextauthjs/adapters) repository to get community-based DynamoDB, Sanity, PouchDB and Sequelize Adapters merged. If you are interested in any of the above, feel free to check out the PRs in the `nextauthjs/adapters` repository!
**This document covers the default adapter (TypeORM).**
See the [documentation for adapters](/schemas/adapters) to learn more about using Prisma adapter or using a custom adapter.
@@ -27,7 +31,7 @@ You can specify database credentials as as a connection string or a [TypeORM con
The following approaches are exactly equivalent:
```js
database: 'mysql://nextauth:password@127.0.0.1:3306/database_name'
database: "mysql://nextauth:password@127.0.0.1:3306/database_name"
```
```js
@@ -44,13 +48,14 @@ database: {
:::tip
You can pass in any valid [TypeORM configuration option](https://github.com/typeorm/typeorm/blob/master/docs/using-ormconfig.md).
*e.g. To set a prefix for all table names you can use the **entityPrefix** option as connection string parameter:*
_e.g. To set a prefix for all table names you can use the **entityPrefix** option as connection string parameter:_
```js
'mysql://nextauth:password@127.0.0.1:3306/database_name?entityPrefix=nextauth_'
"mysql://nextauth:password@127.0.0.1:3306/database_name?entityPrefix=nextauth_"
```
*…or as a database configuration object:*
_…or as a database configuration object:_
```js
database: {
@@ -63,6 +68,7 @@ database: {
entityPrefix: 'nextauth_'
}
```
:::
---
@@ -73,15 +79,15 @@ Using SQL to create tables and columns is the recommended way to set up an SQL d
Check out the links below for SQL you can run to set up a database for NextAuth.js.
* [MySQL Schema](/schemas/mysql)
* [Postgres Schema](/schemas/postgres)
- [MySQL Schema](/schemas/mysql)
- [Postgres Schema](/schemas/postgres)
_If you are running SQLite, MongoDB or a Document database you can skip this step._
Alternatively, you can also have your database configured automatically using the `synchronize: true` option:
```js
database: 'mysql://nextauth:password@127.0.0.1:3306/database_name?synchronize=true'
database: "mysql://nextauth:password@127.0.0.1:3306/database_name?synchronize=true"
```
```js
@@ -122,7 +128,7 @@ Install module:
#### Example
```js
database: 'mysql://username:password@127.0.0.1:3306/database_name'
database: "mysql://username:password@127.0.0.1:3306/database_name"
```
### MariaDB
@@ -133,7 +139,7 @@ Install module:
#### Example
```js
database: 'mariadb://username:password@127.0.0.1:3306/database_name'
database: "mariadb://username:password@127.0.0.1:3306/database_name"
```
### Postgres / CockroachDB
@@ -144,13 +150,15 @@ Install module:
#### Example
PostgresDB
```js
database: 'postgres://username:password@127.0.0.1:5432/database_name'
database: "postgres://username:password@127.0.0.1:5432/database_name"
```
CockroachDB
```js
database: 'postgres://username:password@127.0.0.1:26257/database_name'
database: "postgres://username:password@127.0.0.1:26257/database_name"
```
If the node is using Self-signed cert
@@ -182,7 +190,7 @@ Install module:
#### Example
```js
database: 'mssql://sa:password@localhost:1433/database_name'
database: "mssql://sa:password@localhost:1433/database_name"
```
### MongoDB
@@ -193,12 +201,12 @@ Install module:
#### Example
```js
database: 'mongodb://username:password@127.0.0.1:3306/database_name'
database: "mongodb://username:password@127.0.0.1:3306/database_name"
```
### SQLite
*SQLite is intended only for development / testing and not for production use.*
_SQLite is intended only for development / testing and not for production use._
Install module:
`npm i sqlite3`
@@ -206,7 +214,7 @@ Install module:
#### Example
```js
database: 'sqlite://localhost/:memory:'
database: "sqlite://localhost/:memory:"
```
## Other databases

View File

@@ -328,6 +328,24 @@ export default function App ({ Component, pageProps }) {
If you pass the `session` page prop to the `<Provider>` as in the example above you can avoid checking the session twice on pages that support both server and client side rendering.
This only works on pages where you provide the correct `pageProps`, however. This is normally done in `getInitialProps` or `getServerSideProps` like so:
```js title="pages/index.js"
import { getSession } from "next-auth/client"
...
export async function getServerSideProps(ctx) {
return {
props: {
session: await getSession(ctx)
}
}
}
```
If every one of your pages needs to be protected, you can do this in `_app`, otherwise you can do it on a page-by-page basis. Alternatively, there is you can do per page authentication checks client side, instead of having each auth check be blocking (SSR) by using the method described below in [alternative client session handling](#custom-client-session-handling).
### Options
The session state is automatically synchronized across all open tabs/windows and they are all updated whenever they gain or lose focus or the state changes in any of them (e.g. a user signs in or out).
@@ -386,3 +404,72 @@ The value for `keepAlive` should always be lower than the value of the session `
:::note
See [**the Next.js documentation**](https://nextjs.org/docs/advanced-features/custom-app) for more information on **_app.js** in Next.js applications.
:::
## Alternatives
### Custom Client Session Handling
Due to the way Next.js handles `getServerSideProps` / `getInitialProps`, every protected page load has to make a server-side query to check if the session is valid and then generate the requested page. This alternative solution allows for showing a loading state on the initial check and every page transition afterward will be client-side, without having to check with the server and regenerate pages.
```js title="pages/admin.jsx"
export default function AdminDashboard () {
const [session] = useSession()
// session is always non-null inside this page, all the way down the React tree.
return "Some super secret dashboard"
}
AdminDashboard.auth = true
```
```jsx title="pages/_app.jsx"
export default function App({ Component, pageProps }) {
return (
<SessionProvider session={pageProps.session}>
{Component.auth
? <Auth><Component {...pageProps} /></Auth>
: <Component {...pageProps} />
}
</SessionProvider>
)
}
function Auth({ children }) {
const [session, loading] = useSession()
const isUser = !!session?.user
React.useEffect(() => {
if (loading) return // Do nothing while loading
if (!isUser) signIn() // If not authenticated, force log in
}, [isUser, loading])
if (isUser) {
return children
}
// Session is being fetched, or no user.
// If no user, useEffect() will redirect.
return <div>Loading...</div>
}
```
It can be easily be extended/modified to support something like an options object for role based authentication on pages. An example:
```jsx title="pages/admin.jsx"
AdminDashboard.auth = {
role: "admin",
loading: <AdminLoadingSkeleton/>,
unauthorized: "/login-with-different-user" // redirect to this url
}
```
Because of how _app is done, it won't unnecessarily contant the /api/auth/session endpoint for pages that do not require auth.
More information can be found in the following [Github Issue](https://github.com/nextauthjs/next-auth/issues/1210).
### NextAuth.js + React-Query
There is also an alternative client-side API library based upon [`react-query`](https://www.npmjs.com/package/react-query) available under [`nextauthjs/react-query`](https://github.com/nextauthjs/react-query).
If you use `react-query` in your project already, you can leverage it with NextAuth.js to handle the client-side session management for you as well. This replaces NextAuth.js's native `useSession` and `Provider` from `next-auth/client`.
See repository [`README`](https://github.com/nextauthjs/react-query) for more details.

View File

@@ -7,6 +7,10 @@ title: WorkOS
https://workos.com/docs/sso/guide
## Configuration
https://dashboard.workos.com
## Options
The **WorkOS Provider** comes with a set of default options:
@@ -22,10 +26,87 @@ import Providers from `next-auth/providers`
...
providers: [
Providers.WorkOS({
clientId: process.env.WORKOS_ID,
clientSecret: process.env.WORKOS_SECRET,
domain: process.env.WORKOS_DOMAIN
clientId: process.env.WORKOS_CLIENT_ID,
clientSecret: process.env.WORKOS_API_KEY,
}),
],
...
```
WorkOS is not an identity provider itself, but, rather, a bridge to multiple single sign-on (SSO) providers. As a result, we need to make some additional changes to authenticate users using WorkOS.
In order to sign a user in using WorkOS, we need to specify which WorkOS Connection to use. A common way to do this is to collect the user's email address and extract the domain.
This can be done using a custom login page.
To add a custom login page, you can use the `pages` option:
```javascript title="pages/api/auth/[...nextauth].js"
...
pages: {
signIn: '/auth/signin',
}
```
We can then add a custom login page that displays an input where the user can enter their email address. We then extract the domain from the user's email address and pass it to the `authorizationParams` parameter on the `signIn` function:
```jsx title="pages/auth/signin.js"
import { getProviders, signIn } from 'next-auth/client'
export default function SignIn({ providers }) {
const [email, setEmail] = useState('')
return (
<>
{Object.values(providers).map((provider) => {
if (provider.id === 'workos') {
return (
<div key={provider.id}>
<input
type="email"
value={email}
placeholder="Email"
onChange={(event) => setEmail(event.target.value)}
/>
<button
onClick={() =>
signIn(provider.id, undefined, {
domain: email.split('@')[1],
})
}
>
Sign in with SSO
</button>
</div>
);
}
return (
<div key={provider.id}>
<button onClick={() => signIn(provider.id)}>
Sign in with {provider.name}
</button>
</div>
);
})}
</>
);
}
// This is the recommended way for Next.js 9.3 or newer
export async function getServerSideProps(context){
const providers = await getProviders()
return {
props: { providers }
}
}
/*
// If older than Next.js 9.3
SignIn.getInitialProps = async () => {
return {
providers: await getProviders()
}
}
*/
```

View File

@@ -5,7 +5,19 @@ title: Database Adapters
An **Adapter** in NextAuth.js connects your application to whatever database or backend system you want to use to store data for user accounts, sessions, etc.
You do not need to specify an Adapter explicitly unless you want to use advanced options such as custom models or schemas, if you want to use the Prisma Adapter instead of the default TypeORM Adapter, or if you are creating a custom Adapter to connect to a database that is not one of the supported databases.
The adapters can be found in their own repository under [`nextauthjs/adapters`](https://github.com/nextauthjs/adapters).
There you can find the following adapters:
- typeorm-legacy
- prisma
- prisma-legacy
- fauna
- dynamodb
## TypeORM Adapter
NextAuth.js comes with a default Adapter that uses [TypeORM](https://typeorm.io/) so that it can be used with many different databases without any further configuration, you simply add the node module for the database driver you want to use in your project and pass a database connection string to NextAuth.js.
### Database Schemas
@@ -15,11 +27,7 @@ Configure your database by creating the tables and columns to match the schema e
- [Postgres Schema](/schemas/postgres)
- [Microsoft SQL Server Schema](/schemas/mssql)
## TypeORM Adapter
NextAuth.js comes with a default Adapter that uses [TypeORM](https://typeorm.io/) so that it can be used with many different databases without any further configuration, you simply add the node module for the database driver you want to use to your project and pass a database connection string to NextAuth.js.
The default Adapter is the TypeORM Adapter, the following configuration options are exactly equivalent.
The default Adapter is the TypeORM Adapter and the default database type for TypeORM is SQLite, the following configuration options are exactly equivalent.
```javascript
database: {
@@ -48,9 +56,7 @@ adapter: Adapters.TypeORM.Adapter({
The tutorial [Custom models with TypeORM](/tutorials/typeorm-custom-models) explains how to extend the built in models and schemas used by the TypeORM Adapter. You can use these models in your own code.
:::tip
The `synchronize` option in TypeORM will generate SQL that exactly matches the documented schemas for MySQL and Postgres.
However, it should not be enabled against production databases as it may cause data loss if the configured schema does not match the expected schema!
The `synchronize` option in TypeORM will generate SQL that exactly matches the documented schemas for MySQL and Postgres. This will automatically apply any changes it finds in the entity model, therefore it **should not be enabled against production databases** as it may cause data loss if the configured schema does not match the expected schema!
:::
## Prisma Adapter

43
www/docs/schemas/fauna.md Normal file
View File

@@ -0,0 +1,43 @@
# FaunaDB
FaunaDB Schema (`@next-auth/fauna-adapter`)
## Setup
```javascript
CreateCollection({ name: 'accounts' })
CreateCollection({ name: 'sessions' })
CreateCollection({ name: 'users' })
CreateCollection({ name: 'verification_requests' })
CreateIndex({
name: 'account_by_provider_account_id',
source: Collection('accounts'),
unique: true,
terms: [
{ field: ['data', 'providerId'] },
{ field: ['data', 'providerAccountId'] },
],
})
CreateIndex({
name: 'session_by_token',
source: Collection('sessions'),
unique: true,
terms: [{ field: ['data', 'sessionToken'] }],
})
CreateIndex({
name: 'user_by_email',
source: Collection('users'),
unique: true,
terms: [{ field: ['data', 'email'] }],
})
CreateIndex({
name: 'verification_request_by_token',
source: Collection('verification_requests'),
unique: true,
terms: [
{ field: ['data', 'token'] },
{ field: ['data', 'identifier'] }
],
})
```

144
www/docs/schemas/prisma.md Normal file
View File

@@ -0,0 +1,144 @@
# Prisma
Schema for the Prisma2 Adapter (`@next-auth/prisma-adapter`)
## Schema
```prisma filename="schema.prisma"
model Account {
id String @id @default(cuid())
userId String
providerType String
providerId String
providerAccountId String
refreshToken String?
accessToken String?
accessTokenExpires DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id])
@@unique([providerId, providerAccountId])
}
model Session {
id String @id @default(cuid())
userId String
expires DateTime
sessionToken String @unique
accessToken String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id])
}
model User {
id String @id @default(cuid())
name String?
email String? @unique
emailVerified DateTime?
image String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
accounts Account[]
sessions Session[]
}
model VerificationRequest {
id String @id @default(cuid())
identifier String
token String @unique
expires DateTime
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([identifier, token])
}
```
Changes from the original Prisma Adapter
```diff
model Account {
- id Int @default(autoincrement()) @id
+ id String @id @default(cuid())
- compoundId String @unique @map(name: "compound_id")
- userId Int @map(name: "user_id")
+ userId String
+ user User @relation(fields: [userId], references: [id])
- providerType String @map(name: "provider_type")
+ providerType String
- providerId String @map(name: "provider_id")
+ providerId String
- providerAccountId String @map(name: "provider_account_id")
+ providerAccountId String
- refreshToken String? @map(name: "refresh_token")
+ refreshToken String?
- accessToken String? @map(name: "access_token")
+ accessToken String?
- accessTokenExpires DateTime? @map(name: "access_token_expires")
+ accessTokenExpires DateTime?
- createdAt DateTime @default(now()) @map(name: "created_at")
+ createdAt DateTime @default(now())
- updatedAt DateTime @default(now()) @map(name: "updated_at")
+ updatedAt DateTime @updatedAt
- @@index([providerAccountId], name: "providerAccountId")
- @@index([providerId], name: "providerId")
- @@index([userId], name: "userId")
- @@map(name: "accounts")
+ @@unique([providerId, providerAccountId])
}
model Session {
- id Int @default(autoincrement()) @id
+ id String @id @default(cuid())
- userId Int @map(name: "user_id")
+ userId String
+ user User @relation(fields: [userId], references: [id])
expires DateTime
- sessionToken String @unique @map(name: "session_token")
+ sessionToken String @unique
- accessToken String @unique @map(name: "access_token")
+ accessToken String @unique
- createdAt DateTime @default(now()) @map(name: "created_at")
+ createdAt DateTime @default(now())
- updatedAt DateTime @default(now()) @map(name: "updated_at")
+ updatedAt DateTime @updatedAt
-
- @@map(name: "sessions")
}
model User {
- id Int @default(autoincrement()) @id
+ id String @id @default(cuid())
name String?
email String? @unique
- emailVerified DateTime? @map(name: "email_verified")
+ emailVerified DateTime?
image String?
+ accounts Account[]
+ sessions Session[]
- createdAt DateTime @default(now()) @map(name: "created_at")
+ createdAt DateTime @default(now())
- updatedAt DateTime @default(now()) @map(name: "updated_at")
+ updatedAt DateTime @updatedAt
- @@map(name: "users")
}
model VerificationRequest {
- id Int @default(autoincrement()) @id
+ id String @id @default(cuid())
identifier String
token String @unique
expires DateTime
- createdAt DateTime @default(now()) @map(name: "created_at")
+ createdAt DateTime @default(now())
- updatedAt DateTime @default(now()) @map(name: "updated_at")
+ updatedAt DateTime @updatedAt
- @@map(name: "verification_requests")
+ @@unique([identifier, token])
}