Skip to content

Commit

Permalink
feat(provider): update session when signIn/signOut successful (#1267)
Browse files Browse the repository at this point in the history
* feat(provider): update session when login/logout successful

* chore: remove manual page reload from dev app

* docs(client): document redirect: false
  • Loading branch information
balazsorban44 authored Feb 5, 2021
1 parent 15e9d9e commit f4269f7
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 22 deletions.
2 changes: 1 addition & 1 deletion components/header.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export default function Header () {
className={styles.button}
onClick={(e) => {
e.preventDefault()
signOut()
signOut({ redirect: false })
}}
>
Sign out
Expand Down
8 changes: 0 additions & 8 deletions pages/credentials.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ export default function Page () {
}
const response = await signIn('credentials', options)
setResponse(response)
if (response.ok) {
window.alert('Manually refreshing to update session, if login was successful')
window.location.reload()
}
}

const handleLogout = (options) => async () => {
Expand All @@ -22,10 +18,6 @@ export default function Page () {
}
const response = await signOut(options)
setResponse(response)
if (response.ok) {
window.alert('Manually refreshing to update session, if logout was successful')
window.location.reload()
}
}

const [session] = useSession()
Expand Down
30 changes: 17 additions & 13 deletions src/client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,10 @@ const setOptions = ({
}

// Universal method (client + server)
export const getSession = async ({ req, ctx, triggerEvent = true } = {}) => {
// If passed 'appContext' via getInitialProps() in _app.js then get the req
// object from ctx and use that for the req value to allow getSession() to
// work seemlessly in getInitialProps() on server side pages *and* in _app.js.
if (!req && ctx && ctx.req) { req = ctx.req }

// If passed 'appContext' via getInitialProps() in _app.js then get the req
// object from ctx and use that for the req value to allow getSession() to
// work seemlessly in getInitialProps() on server side pages *and* in _app.js.
export async function getSession ({ ctx, req = ctx?.req, triggerEvent = true } = {}) {
const baseUrl = _apiBaseUrl()
const fetchOptions = req ? { headers: { cookie: req.headers.cookie } } : {}
const session = await _fetchData(`${baseUrl}/session`, fetchOptions)
Expand All @@ -130,12 +128,10 @@ export const getSession = async ({ req, ctx, triggerEvent = true } = {}) => {
}

// Universal method (client + server)
const getCsrfToken = async ({ req, ctx } = {}) => {
// If passed 'appContext' via getInitialProps() in _app.js then get the req
// object from ctx and use that for the req value to allow getCsrfToken() to
// work seemlessly in getInitialProps() on server side pages *and* in _app.js.
if (!req && ctx && ctx.req) { req = ctx.req }

// If passed 'appContext' via getInitialProps() in _app.js then get the req
// object from ctx and use that for the req value to allow getCsrfToken() to
// work seemlessly in getInitialProps() on server side pages *and* in _app.js.
async function getCsrfToken ({ ctx, req = ctx?.req } = {}) {
const baseUrl = _apiBaseUrl()
const fetchOptions = req ? { headers: { cookie: req.headers.cookie } } : {}
const data = await _fetchData(`${baseUrl}/csrf`, fetchOptions)
Expand Down Expand Up @@ -287,10 +283,16 @@ export async function signIn (provider, options = {}, authorizationParams = {})
}

const error = new URL(data.url).searchParams.get('error')

if (res.ok) {
await __NEXTAUTH._getSession({ event: 'storage' })
}

return {
error,
status: res.status,
ok: res.ok
ok: res.ok,
url: error ? null : data.url
}
}

Expand Down Expand Up @@ -326,6 +328,8 @@ export async function signOut (options = {}) {
return
}

await __NEXTAUTH._getSession({ event: 'storage' })

return data
}

Expand Down
39 changes: 39 additions & 0 deletions www/docs/getting-started/client.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,35 @@ e.g.

The URL must be considered valid by the [redirect callback handler](/configuration/callbacks#redirect). By default it requires the URL to be an absolute URL at the same hostname, or else it will redirect to the homepage. You can define your own redirect callback to allow other URLs, including supporting relative URLs.

#### 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:

```ts
{
/**
* Will be different error codes,
* depending on the type of error.
*/
error: string | undefined
/**
* HTTP status code,
* hints the kind of error that happened.
*/
status: number
/**
* `true` if the signin was successful
*/
ok: boolean
/**
* `null` if there was an error,
* otherwise the url the user
* should have been redirected to.
*/
url: string | null
}
```

#### Additional params

It is also possible to pass additional parameters to the `/authorize` endpoint through the third argument of `signIn()`.
Expand Down Expand Up @@ -256,6 +285,16 @@ e.g. `signOut({ callbackUrl: 'http://localhost:3000/foo' })`

The URL must be considered valid by the [redirect callback handler](/configuration/callbacks#redirect). By default this means it must be an absolute URL at the same hostname (or else it will default to the homepage); you can define your own custom redirect callback to allow other URLs, including supporting relative URLs.

#### Using the redirect: false option

If you pass `redirect: false` to `signOut`, the page will not reload. The session will be deleted, and the `useSession` hook is notified, so any indication about the user will be shown as logged out automatically. It can give a very nice experience for the user.

:::tip
If you need to redirect to another page but you want to avoid a page reload, you can try:
`const data = await signOut({redirect: false, callbackUrl: "/foo"})`
where `data.url` is the validated url you can redirect the user to without any flicker by using Next.js's `useRouter().push(data.url)`
:::

---

## Provider
Expand Down

0 comments on commit f4269f7

Please sign in to comment.