Compare commits

...

6 Commits

Author SHA1 Message Date
yannicktian
71c78e8e24 feat(provider): allow disabling redirection on sign in with email (#1416)
* feat: allow to disable client-side redirect for email provider

* docs(client): mention that redirect can also be disabled for email provider

* feat: only display one email input in email page
2021-03-02 22:38:02 +01:00
dependabot[bot]
d86609a2dc chore(deps): bump prismjs from 1.22.0 to 1.23.0 in /www (#1409)
Bumps [prismjs](https://github.com/PrismJS/prism) from 1.22.0 to 1.23.0.
- [Release notes](https://github.com/PrismJS/prism/releases)
- [Changelog](https://github.com/PrismJS/prism/blob/master/CHANGELOG.md)
- [Commits](https://github.com/PrismJS/prism/compare/v1.22.0...v1.23.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-01 22:52:46 +01:00
Joost van Wollingen
d0c3400d30 docs(page): Remove unnecessary context param (#1406)
...when calling providers in the docs
2021-03-01 21:39:03 +01:00
Praneeth
172e79cb04 fix(page): add character encoding and page titles (#1380)
* added character encoding fix

* changed multi-line to inline and added title param to send fn in src/server/pages/index.js

* modified the return object of renderPage in src/server/pages/index.js
2021-03-01 21:17:51 +01:00
Balázs Orbán
46d5c76605 docs: reword callbacks.md
Explain the `jwt()` callback before the `session()` callback, as it comes first in the flow.
2021-02-28 18:05:10 +01:00
Zach White
438efd8a9b docs: reword pages.md (#1386)
language edits
2021-02-27 23:43:45 +01:00
8 changed files with 164 additions and 61 deletions

View File

@@ -100,6 +100,11 @@ export default function Header () {
<a>Credentials</a>
</Link>
</li>
<li className={styles.navItem}>
<Link href='/email'>
<a>Email</a>
</Link>
</li>
</ul>
</nav>
</header>

66
pages/email.js Normal file
View File

@@ -0,0 +1,66 @@
import * as React from 'react'
import { signIn, signOut, useSession } from 'next-auth/client'
import Layout from 'components/layout'
export default function Page () {
const [response, setResponse] = React.useState(null)
const [email, setEmail] = React.useState('')
const handleChange = (event) => {
setEmail(event.target.value)
}
const handleLogin = (options) => async (event) => {
event.preventDefault()
if (options.redirect) {
return signIn('email', options)
}
const response = await signIn('email', options)
setResponse(response)
}
const handleLogout = (options) => async (event) => {
if (options.redirect) {
return signOut(options)
}
const response = await signOut(options)
setResponse(response)
}
const [session] = useSession()
if (session) {
return (
<Layout>
<h1>Test different flows for Email logout</h1>
<span className='spacing'>Default:</span>
<button onClick={handleLogout({ redirect: true })}>Logout</button><br />
<span className='spacing'>No redirect:</span>
<button onClick={handleLogout({ redirect: false })}>Logout</button><br />
<p>Response:</p>
<pre style={{ background: '#eee', padding: 16 }}>{JSON.stringify(response, null, 2)}</pre>
</Layout>
)
}
return (
<Layout>
<h1>Test different flows for Email login</h1>
<label className='spacing'>
Email address:{' '}
<input type='text' id='email' name='email' value={email} onChange={handleChange} />
</label><br />
<form onSubmit={handleLogin({ redirect: true, email })}>
<span className='spacing'>Default:</span>
<button type='submit'>Sign in with Email</button>
</form>
<form onSubmit={handleLogin({ redirect: false, email })}>
<span className='spacing'>No redirect:</span>
<button type='submit'>Sign in with Email</button>
</form>
<p>Response:</p>
<pre style={{ background: '#eee', padding: 16 }}>{JSON.stringify(response, null, 2)}</pre>
</Layout>
)
}

View File

@@ -258,6 +258,9 @@ export async function signIn (provider, options = {}, authorizationParams = {})
return
}
const isCredentials = providers[provider].type === 'credentials'
const isEmail = providers[provider].type === 'email'
const canRedirectBeDisabled = isCredentials || isEmail
const signInUrl = isCredentials
? `${baseUrl}/callback/${provider}`
: `${baseUrl}/signin/${provider}`
@@ -279,7 +282,7 @@ export async function signIn (provider, options = {}, authorizationParams = {})
const _signInUrl = `${signInUrl}?${new URLSearchParams(authorizationParams)}`
const res = await fetch(_signInUrl, fetchOptions)
const data = await res.json()
if (redirect || !isCredentials) {
if (redirect || !canRedirectBeDisabled) {
const url = data.url ?? callbackUrl
window.location = url
// If url contains a hash, the browser does not reload the page. We reload manually

View File

@@ -9,14 +9,34 @@ export default function renderPage (req, res) {
const { baseUrl, basePath, callbackUrl, csrfToken, providers, theme } = req.options
res.setHeader('Content-Type', 'text/html')
function send (html) {
res.send(`<!DOCTYPE html><head><style type="text/css">${css()}</style><meta name="viewport" content="width=device-width, initial-scale=1"></head><body class="__next-auth-theme-${theme}"><div class="page">${html}</div></body></html>`)
function send ({ html, title }) {
res.send(`<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><style>${css()}</style><title>${title}</title></head><body class="__next-auth-theme-${theme}"><div class="page">${html}</div></body></html>`)
}
return {
signin (props) { send(signin({ csrfToken, providers, callbackUrl, ...req.query, ...props })) },
signout (props) { send(signout({ csrfToken, baseUrl, basePath, ...props })) },
verifyRequest (props) { send(verifyRequest({ baseUrl, ...props })) },
error (props) { send(error({ basePath, baseUrl, res, ...props })) }
signin (props) {
send({
html: signin({ csrfToken, providers, callbackUrl, ...req.query, ...props }),
title: 'Sign In'
})
},
signout (props) {
send({
html: signout({ csrfToken, baseUrl, basePath, ...props }),
title: 'Sign Out'
})
},
verifyRequest (props) {
send({
html: verifyRequest({ baseUrl, ...props }),
title: 'Verify Request'
})
},
error (props) {
send({
html: error({ basePath, baseUrl, res, ...props }),
title: 'Error'
})
}
}
}

View File

@@ -112,52 +112,6 @@ callbacks: {
The redirect callback may be invoked more than once in the same flow.
:::
## Session callback
The session callback is called whenever a session is checked.
e.g. `getSession()`, `useSession()`, `/api/auth/session`
* When using database sessions, the User object is passed as an argument.
* When using JSON Web Tokens for sessions, the JWT payload is provided instead.
```js title="pages/api/auth/[...nextauth].js"
...
callbacks: {
/**
* @param {object} session Session object
* @param {object} token User object (if using database sessions)
* JSON Web Token (if not using database sessions)
* @return {object} Session that will be returned to the client
*/
async session(session, token) {
if(token?.accessToken) {
// Add property to session, like an access_token from a provider
session.accessToken = token.accessToken
}
return session
}
}
...
```
:::tip
When using JSON Web Tokens the `jwt()` callback is invoked before the `session()` callback, so anything you add to the
JSON Web Token will be immediately available in the session callback, like for example an `access_token` from a provider.
:::
:::tip
To better represent its value, when using a JWT session, the second parameter should be called `token` (This is the same thing you return from the `jwt` callback). If you use a database, call it `user`.
:::
:::warning
The session object is not persisted server side, even when using database sessions - only data such as the session token, the user, and the expiry time is stored in the session table.
If you need to persist session data server side, you can use the `accessToken` returned for the session as a key - and connect to the database in the `session()` callback to access it. Session `accessToken` values do not rotate and are valid as long as the session is valid.
If using JSON Web Tokens instead of database sessions, you should use the User ID or a unique key stored in the token (you will need to generate a key for this yourself on sign in, as access tokens for sessions are not generated when using JSON Web Tokens).
:::
## JWT callback
This JSON Web Token callback is called whenever a JSON Web Token is created (i.e. at sign
@@ -206,3 +160,47 @@ NextAuth.js does not limit how much data you can store in a JSON Web Token, howe
If you need to persist a large amount of data, you will need to persist it elsewhere (e.g. in a database). You can store a key that can be used to look up that data in the `session()` callback.
:::
## Session callback
The session callback is called whenever a session is checked. By default, only a subset of the token is returned for increased security. If you want to make something available you added to the token through the `jwt()` callback, you have to explicitely forward it here to make it available to the client.
e.g. `getSession()`, `useSession()`, `/api/auth/session`
* When using database sessions, the User object is passed as an argument.
* When using JSON Web Tokens for sessions, the JWT payload is provided instead.
```js title="pages/api/auth/[...nextauth].js"
...
callbacks: {
/**
* @param {object} session Session object
* @param {object} token User object (if using database sessions)
* JSON Web Token (if not using database sessions)
* @return {object} Session that will be returned to the client
*/
async session(session, token) {
// Add property to session, like an access_token from a provider.
session.accessToken = token.accessToken
return session
}
}
...
```
:::tip
When using JSON Web Tokens the `jwt()` callback is invoked before the `session()` callback, so anything you add to the
JSON Web Token will be immediately available in the session callback, like for example an `access_token` from a provider.
:::
:::tip
To better represent its value, when using a JWT session, the second parameter should be called `token` (This is the same thing you return from the `jwt()` callback). If you use a database, call it `user`.
:::
:::warning
The session object is not persisted server side, even when using database sessions - only data such as the session token, the user, and the expiry time is stored in the session table.
If you need to persist session data server side, you can use the `accessToken` returned for the session as a key - and connect to the database in the `session()` callback to access it. Session `accessToken` values do not rotate and are valid as long as the session is valid.
If using JSON Web Tokens instead of database sessions, you should use the User ID or a unique key stored in the token (you will need to generate a key for this yourself on sign in, as access tokens for sessions are not generated when using JSON Web Tokens).
:::

View File

@@ -7,7 +7,7 @@ NextAuth.js automatically creates simple, unbranded authentication pages for han
The options displayed on the sign up page are automatically generated based on the providers specified in the options passed to NextAuth.js.
To add a custom login page, for example. You can use the `pages` option:
To add a custom login page, you can use the `pages` option:
```javascript title="pages/api/auth/[...nextauth].js"
...
@@ -42,9 +42,9 @@ export default function SignIn({ providers }) {
)
}
SignIn.getInitialProps = async (context) => {
SignIn.getInitialProps = async () => {
return {
providers: await providers(context)
providers: await providers()
}
}
```
@@ -121,4 +121,4 @@ signIn('credentials', { username: 'jsmith', password: '1234' })
:::tip
Remember to put any custom pages in a folder outside **/pages/api** which is reserved for API code. As per the examples above, a location convention suggestion is `pages/auth/...`.
:::
:::

View File

@@ -212,7 +212,18 @@ The URL must be considered valid by the [redirect callback handler](/configurati
#### Using the redirect: false option
When you use the `credentials` provider, you might not want the user to redirect to an error page if an error occurs, so you can handle any errors (like wrong credentials given by the user) on the same page. For that, you can pass `redirect: false` in the second parameter object. `signIn` then will return a Promise, that resolves to the following:
:::note
The redirect option is only available for `credentials` and `email` providers.
:::
In some cases, you might want to deal with the sign in response on the same page and disable the default redirection. For example, if an error occurs (like wrong credentials given by the user), you might want to handle the error on the same page. For that, you can pass `redirect: false` in the second parameter object.
e.g.
- `signIn('credentials', { redirect: false, password: 'password' })`
- `signIn('email', { redirect: false, email: 'bill@fillmurray.com' })`
`signIn` will then return a Promise, that resolves to the following:
```ts
{

6
www/package-lock.json generated
View File

@@ -10900,9 +10900,9 @@
"integrity": "sha512-MgMhSdHuHymNRqD6KM3eGS0PNqgK9q4QF5P0yoQQvpB6jNjeSAi3jcSAz0Sua/t9fa4xDOMar9HJbLa08gl9ug=="
},
"prismjs": {
"version": "1.22.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.22.0.tgz",
"integrity": "sha512-lLJ/Wt9yy0AiSYBf212kK3mM5L8ycwlyTlSxHBAneXLR0nzFMlZ5y7riFPF3E33zXOF2IH95xdY5jIyZbM9z/w==",
"version": "1.23.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.23.0.tgz",
"integrity": "sha512-c29LVsqOaLbBHuIbsTxaKENh1N2EQBOHaWv7gkHN4dgRbxSREqDnDbtFJYdpPauS4YCplMSNCABQ6Eeor69bAA==",
"requires": {
"clipboard": "^2.0.0"
}