Guide to migrating from 1.x
to 2.x
- Node 10 is no longer supported
getSession
now returns aPromise
- Client methods and components are now exported under /client
updateSession
has been addedgetServerSidePropsWrapper
has been removed- Profile API route no longer returns a 401
- Override default error handler
- afterCallback can write to the response
- Configure default handlers
Node 12 LTS and newer LTS releases are supported.
// /pages/api/my-api
import { getSession } from '@auth0/nextjs-auth0';
function myApiRoute(req, res) {
const session = getSession(req, res);
// ...
}
// /pages/api/my-api
import { getSession } from '@auth0/nextjs-auth0';
async function myApiRoute(req, res) {
const session = await getSession(req, res);
// ...
}
All methods and components for the browser should now be accessed under /client
.
// pages/_app.js
import React from 'react';
import { UserProvider } from '@auth0/nextjs-auth0';
export default function App({ Component, pageProps }) {
return (
<UserProvider>
<Component {...pageProps} />
</UserProvider>
);
}
// pages/index.js
import { useUser } from '@auth0/nextjs-auth0';
export default function Index() {
const { user, error, isLoading } = useUser();
if (isLoading) return <div>Loading...</div>;
if (error) return <div>{error.message}</div>;
if (user) {
return (
<div>
Welcome {user.name}! <a href="/api/auth/logout">Logout</a>
</div>
);
}
return <a href="/api/auth/login">Login</a>;
}
// pages/_app.js
import React from 'react';
import { UserProvider } from '@auth0/nextjs-auth0/client';
export default function App({ Component, pageProps }) {
return (
<UserProvider>
<Component {...pageProps} />
</UserProvider>
);
}
// pages/index.js
import { useUser, withPageAuthRequired as withPageAuthRequiredCSR } from '@auth0/nextjs-auth0/client';
// The SSR version of withPageAuthRequired is still in the root export
import { withPageAuthRequired as withPageAuthRequiredSSR } from '@auth0/nextjs-auth0';
export default withPageAuthRequiredCSR(function Index() {
const { user, error, isLoading } = useUser();
if (isLoading) return <div>Loading...</div>;
if (error) return <div>{error.message}</div>;
if (user) {
return (
<div>
Welcome {user.name}! <a href="/api/auth/logout">Logout</a>
</div>
);
}
return <a href="/api/auth/login">Login</a>;
});
export const getServerSideProps = withPageAuthRequiredSSR();
Previously your application could make modifications to the session during the lifecycle of the request and those updates would be saved implicitly when the response's headers were written, just before delivering the response to the client.
// /pages/api/update-user
import { getSession } from '@auth0/nextjs-auth0';
function myApiRoute(req, res) {
const session = getSession(req, res);
session.foo = 'bar';
res.json({ success: true });
}
// The updated session is serialized and the cookie is updated
// when the cookie headers are written to the response.
We've introduced a new updateSession
method which must be explicitly invoked in order to update the session.
This will immediately serialise the session, write it to the cookie and return a Promise
.
// /pages/api/update-user
import { getSession, updateSession } from '@auth0/nextjs-auth0';
async function myApiRoute(req, res) {
const session = await getSession(req, res);
// The session is updated, serialized and the cookie is updated
// everytime you call `updateSession`.
await updateSession(req, res, { ...session, user: { ...session.user, foo: 'bar' } });
res.json({ success: true });
}
Because the process of modifying the session is now explicit, you no longer have to wrap getServerSideProps
in getServerSidePropsWrapper
.
export const getServerSideProps = getServerSidePropsWrapper((ctx) => {
const session = getSession(ctx.req, ctx.res);
if (session) {
// User is authenticated
} else {
// User is not authenticated
}
});
export const getServerSideProps = async (ctx) => {
const session = await getSession(ctx.req, ctx.res);
if (session) {
// User is authenticated
} else {
// User is not authenticated
}
};
Previously the profile API route, by default at /api/auth/me
, would return a 401 error when the user was not authenticated. While it was technically the right status code for the situation, it showed up in the browser console as an error. This API route will now return a 204 instead. Since 204 is a successful status code, it will not produce a console error.
You can now set the default error handler for the auth routes in a single place.
export default handleAuth({
async login(req, res) {
try {
await handleLogin(req, res);
} catch (error) {
errorLogger(error);
res.status(error.status || 500).end();
}
},
async callback(req, res) {
try {
await handleLogin(req, res);
} catch (error) {
errorLogger(error);
res.status(error.status || 500).end();
}
}
// ...
});
export default handleAuth({
onError(req, res, error) {
errorLogger(error);
// You can finish the response yourself if you want to customize
// the status code or redirect the user
// res.writeHead(302, {
// Location: '/custom-error-page'
// });
// res.end();
}
});
You can now write your own redirect header or terminate the request in afterCallback
.
const afterCallback = (req, res, session, state) => {
if (session.user.isAdmin) {
return session;
} else {
res.status(401).end('User is not admin');
}
}; // 💥 Fails with ERR_HTTP_HEADERS_SENT
const afterCallback = (req, res, session, state) => {
if (!session.user.isAdmin) {
res.setHeader('Location', '/admin');
}
return session;
}; // 💥 Fails with ERR_HTTP_HEADERS_SENT
const afterCallback = (req, res, session, state) => {
if (session.user.isAdmin) {
return session;
} else {
res.status(401).end('User is not admin');
}
}; // Terminates the request with 401 if user is not admin
const afterCallback = (req, res, session, state) => {
if (!session.user.isAdmin) {
res.setHeader('Location', '/admin');
}
return session;
}; // Redirects to `/admin` if user is admin
Previously it was not possible to configure the default handlers. For example, to pass a connection
parameter to the login handler, you had to override it.
export default handleAuth({
login: async (req, res) => {
try {
await handleLogin(req, res, {
authorizationParams: { connection: 'github' }
});
} catch (error) {
// ...
}
}
});
Now you can configure a default handler by passing an options object to it.
export default handleAuth({
login: handleLogin({
authorizationParams: { connection: 'github' }
})
});
You can also pass a function that receives the request and returns an options object.
export default handleAuth({
login: handleLogin((req) => {
return {
authorizationParams: { connection: 'github' }
};
})
});
You can even create new handlers by configuring the default ones.
export default handleAuth({
// Creates /api/auth/signup
signup: handleLogin({
authorizationParams: { screen_hint: 'signup' }
})
});
It is still possible to override the default handlers if needed.