From f1d95fdc61d28b4a20c398810d81709da46a86e0 Mon Sep 17 00:00:00 2001 From: adamjmcgrath Date: Mon, 10 Oct 2022 15:33:22 +0100 Subject: [PATCH 1/2] Change `updateUser` to `updateSession` --- README.md | 2 +- V2_MIGRATION_GUIDE.md | 20 ++++----- src/index.browser.ts | 4 +- src/index.ts | 12 ++--- src/instance.ts | 4 +- src/session/index.ts | 2 +- src/session/update-session.ts | 45 +++++++++++++++++++ src/session/update-user.ts | 44 ------------------ tests/fixtures/global.d.ts | 2 +- tests/fixtures/setup.ts | 6 +-- .../api/{update-user.ts => update-session.ts} | 4 +- ...te-user.test.ts => update-session.test.ts} | 27 ++++++++--- 12 files changed, 93 insertions(+), 79 deletions(-) create mode 100644 src/session/update-session.ts delete mode 100644 src/session/update-user.ts rename tests/fixtures/test-app/pages/api/{update-user.ts => update-session.ts} (70%) rename tests/session/{update-user.test.ts => update-session.test.ts} (53%) diff --git a/README.md b/README.md index ce76c429f..cff7cb759 100644 --- a/README.md +++ b/README.md @@ -185,7 +185,7 @@ For other comprehensive examples, see the [EXAMPLES.md](./EXAMPLES.md) document. - [withPageAuthRequired](https://auth0.github.io/nextjs-auth0/modules/helpers_with_page_auth_required.html#withpageauthrequired) - [withMiddlewareAuthRequired](https://auth0.github.io/nextjs-auth0/modules/helpers_with_middleware_auth_required.html) - [getSession](https://auth0.github.io/nextjs-auth0/modules/session_get_session.html) -- [updateUser](https://auth0.github.io/nextjs-auth0/modules/session_update_user.html) +- [updateSession](https://auth0.github.io/nextjs-auth0/modules/session_update_session.html) - [getAccessToken](https://auth0.github.io/nextjs-auth0/modules/session_get_access_token.html) - [initAuth0](https://auth0.github.io/nextjs-auth0/modules/instance.html) diff --git a/V2_MIGRATION_GUIDE.md b/V2_MIGRATION_GUIDE.md index 81290b54b..bdcfdac74 100644 --- a/V2_MIGRATION_GUIDE.md +++ b/V2_MIGRATION_GUIDE.md @@ -3,7 +3,7 @@ Guide to migrating from `1.x` to `2.x` - [`getSession` now returns a `Promise`](#getsession-now-returns-a-promise) -- [`updateUser` has been added](#updateuser-has-been-added) +- [`updateSession` has been added](#updatesession-has-been-added) - [`getServerSidePropsWrapper` has been removed](#getserversidepropswrapper-has-been-removed) - [Profile API route no longer returns a 401](#profile-api-route-no-longer-returns-a-401) - [The ID token is no longer stored by default](#the-id-token-is-no-longer-stored-by-default) @@ -37,7 +37,7 @@ async function myApiRoute(req, res) { } ``` -## `updateUser` has been added +## `updateSession` has been added ### Before @@ -48,8 +48,8 @@ Previously your application could make modifications to the session during the l import { getSession } from '@auth0/nextjs-auth0'; function myApiRoute(req, res) { - const { user } = getSession(req, res); - user.foo = 'bar'; + const session = getSession(req, res); + session.foo = 'bar'; res.json({ success: true }); } // The updated session is serialized and the cookie is updated @@ -58,19 +58,19 @@ function myApiRoute(req, res) { ### After -We've introduced a new `updateUser` method which must be explicitly invoked in order to update the session's user. +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`. ```js // /pages/api/update-user -import { getSession, updateUser } from '@auth0/nextjs-auth0'; +import { getSession, updateSession } from '@auth0/nextjs-auth0'; async function myApiRoute(req, res) { - const { user } = await getSession(req, res); + const session = await getSession(req, res); // The session is updated, serialized and the cookie is updated - // everytime you call `updateUser`. - await updateUser(req, res, { ...user, foo: 'bar' }); + // everytime you call `updateSession`. + await updateSession(req, res, { ...session, user: { ...session.user, foo: 'bar' } }); res.json({ success: true }); } ``` @@ -214,7 +214,7 @@ export default handleAuth({ login: async (req, res) => { try { await handleLogin(req, res, { - authorizationParams: { connection: 'github' }, + authorizationParams: { connection: 'github' } }); } catch (error) { // ... diff --git a/src/index.browser.ts b/src/index.browser.ts index 15c5ad25e..d550208a3 100644 --- a/src/index.browser.ts +++ b/src/index.browser.ts @@ -19,8 +19,8 @@ const instance: SignInWithAuth0 = { getSession() { throw new Error(serverSideOnly('getSession')); }, - updateUser() { - throw new Error(serverSideOnly('updateUser')); + updateSession() { + throw new Error(serverSideOnly('updateSession')); }, getAccessToken() { throw new Error(serverSideOnly('getAccessToken')); diff --git a/src/index.ts b/src/index.ts index cb8c81fac..079d5156c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -38,8 +38,8 @@ import { AccessTokenRequest, GetAccessTokenResult, Claims, - updateUserFactory, - UpdateUser + updateSessionFactory, + UpdateSession } from './session/'; import { withPageAuthRequiredFactory, @@ -88,7 +88,7 @@ export const _initAuth = (params?: ConfigParameters): SignInWithAuth0 & { sessio // Init Next layer (with next config) const getSession = sessionFactory(sessionCache); - const updateUser = updateUserFactory(sessionCache); + const updateSession = updateSessionFactory(sessionCache); const getAccessToken = accessTokenFactory(nextConfig, getClient, sessionCache); const withApiAuthRequired = withApiAuthRequiredFactory(sessionCache); const withPageAuthRequired = withPageAuthRequiredFactory(nextConfig.routes.login, () => sessionCache); @@ -101,7 +101,7 @@ export const _initAuth = (params?: ConfigParameters): SignInWithAuth0 & { sessio return { sessionCache, getSession, - updateUser, + updateSession, getAccessToken, withApiAuthRequired, withPageAuthRequired, @@ -116,7 +116,7 @@ export const _initAuth = (params?: ConfigParameters): SignInWithAuth0 & { sessio /* c8 ignore start */ const getSessionCache = () => getInstance().sessionCache; export const getSession: GetSession = (...args) => getInstance().getSession(...args); -export const updateUser: UpdateUser = (...args) => getInstance().updateUser(...args); +export const updateSession: UpdateSession = (...args) => getInstance().updateSession(...args); export const getAccessToken: GetAccessToken = (...args) => getInstance().getAccessToken(...args); export const withApiAuthRequired: WithApiAuthRequired = (...args) => getInstance().withApiAuthRequired(...args); export const withPageAuthRequired: WithPageAuthRequired = withPageAuthRequiredFactory(getLoginUrl(), getSessionCache); @@ -174,7 +174,7 @@ export { WithPageAuthRequired, SessionCache, GetSession, - UpdateUser, + UpdateSession, GetAccessToken, Session, Claims, diff --git a/src/instance.ts b/src/instance.ts index e0f5ce801..5c3892ca0 100644 --- a/src/instance.ts +++ b/src/instance.ts @@ -1,4 +1,4 @@ -import { GetSession, GetAccessToken, UpdateUser } from './session'; +import { GetSession, GetAccessToken, UpdateSession } from './session'; import { WithApiAuthRequired, WithPageAuthRequired } from './helpers'; import { HandleAuth, HandleCallback, HandleLogin, HandleLogout, HandleProfile } from './handlers'; import { ConfigParameters } from './auth0-session'; @@ -21,7 +21,7 @@ export interface SignInWithAuth0 { /** * Append properties to the user. */ - updateUser: UpdateUser; + updateSession: UpdateSession; /** * Access token getter. diff --git a/src/session/index.ts b/src/session/index.ts index 1fc810e01..7c8b2dc10 100644 --- a/src/session/index.ts +++ b/src/session/index.ts @@ -7,4 +7,4 @@ export { GetAccessTokenResult } from './get-access-token'; export { default as SessionCache } from './cache'; -export { default as updateUserFactory, UpdateUser } from './update-user'; +export { default as updateSessionFactory, UpdateSession } from './update-session'; diff --git a/src/session/update-session.ts b/src/session/update-session.ts new file mode 100644 index 000000000..1138a9203 --- /dev/null +++ b/src/session/update-session.ts @@ -0,0 +1,45 @@ +import { IncomingMessage, ServerResponse } from 'http'; +import { NextApiRequest, NextApiResponse } from 'next'; +import { Session, SessionCache } from '../session'; +import { assertReqRes } from '../utils/assert'; + +/** + * Update the session object. The provided `session` object will replace the existing session. + * + * **Note** you can't use this method to login or logout - you should use the login and logout handlers for this. + * If no session is provided, it doesn't contain a user or the user is not authenticated; this is a no-op. + * + * ```js + * // pages/api/update-user.js + * import { getSession, updateSession } from '@auth0/nextjs-auth0'; + * + * export default async function updateSession(req, res) { + * if (req.method === 'PUT') { + * const session = getSession(req, res); + * updateSession(req, res, { ...session, user: { ...user, foo: req.query.foo } }); + * res.json({ success: true }); + * } + * }; + * ``` + * + * @category Server + */ +export type UpdateSession = ( + req: IncomingMessage | NextApiRequest, + res: ServerResponse | NextApiResponse, + user: Session +) => Promise; + +/** + * @ignore + */ +export default function updateSessionFactory(sessionCache: SessionCache): UpdateSession { + return async (req, res, newSession) => { + assertReqRes(req, res); + const session = await sessionCache.get(req, res); + if (!session || !newSession || !newSession.user) { + return; + } + await sessionCache.set(req, res, newSession); + }; +} diff --git a/src/session/update-user.ts b/src/session/update-user.ts deleted file mode 100644 index 37e3b44fb..000000000 --- a/src/session/update-user.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { IncomingMessage, ServerResponse } from 'http'; -import { NextApiRequest, NextApiResponse } from 'next'; -import { Claims, SessionCache } from '../session'; -import { assertReqRes } from '../utils/assert'; - -/** - * Update the session's user object. The provided user object will replace `session.user`. - * - * If no user is provided, or the user is not authenticated, this is a no-op. - * - * ```js - * // pages/api/update-user.js - * import { getSession, updateUser } from '@auth0/nextjs-auth0'; - * - * export default async function UpdateUser(req, res) { - * if (req.method === 'PUT') { - * const { user } = getSession(req, res); - * updateUser(req, res, { ...user, foo: req.query.foo }); - * res.json({ success: true }); - * } - * }; - * ``` - * - * @category Server - */ -export type UpdateUser = ( - req: IncomingMessage | NextApiRequest, - res: ServerResponse | NextApiResponse, - user: Claims -) => Promise; - -/** - * @ignore - */ -export default function updateUserFactory(sessionCache: SessionCache): UpdateUser { - return async (req, res, user) => { - assertReqRes(req, res); - const session = await sessionCache.get(req, res); - if (!session || !user) { - return; - } - await sessionCache.set(req, res, { ...session, user }); - }; -} diff --git a/tests/fixtures/global.d.ts b/tests/fixtures/global.d.ts index e56d1d253..885406e54 100644 --- a/tests/fixtures/global.d.ts +++ b/tests/fixtures/global.d.ts @@ -2,7 +2,7 @@ declare global { namespace NodeJS { interface Global { getSession?: Function; - updateUser?: Function; + updateSession?: Function; handleAuth?: Function; withApiAuthRequired?: Function; withPageAuthRequired?: Function; diff --git a/tests/fixtures/setup.ts b/tests/fixtures/setup.ts index 50b59c116..e773b368a 100644 --- a/tests/fixtures/setup.ts +++ b/tests/fixtures/setup.ts @@ -78,7 +78,7 @@ export const setup = async ( handleLogout, handleProfile, getSession, - updateUser, + updateSession, getAccessToken, withApiAuthRequired, withPageAuthRequired @@ -90,7 +90,7 @@ export const setup = async ( const handlers: Handlers = { onError, callback, login, logout, profile }; global.handleAuth = handleAuth.bind(null, handlers); global.getSession = getSession; - global.updateUser = updateUser; + global.updateSession = updateSession; global.withApiAuthRequired = withApiAuthRequired; global.withPageAuthRequired = (): any => withPageAuthRequired(withPageAuthRequiredOptions); global.withPageAuthRequiredCSR = withPageAuthRequired; @@ -105,7 +105,7 @@ export const teardown = async (): Promise => { nock.cleanAll(); await stop(); delete global.getSession; - delete global.updateUser; + delete global.updateSession; delete global.handleAuth; delete global.withApiAuthRequired; delete global.withPageAuthRequired; diff --git a/tests/fixtures/test-app/pages/api/update-user.ts b/tests/fixtures/test-app/pages/api/update-session.ts similarity index 70% rename from tests/fixtures/test-app/pages/api/update-user.ts rename to tests/fixtures/test-app/pages/api/update-session.ts index d791ea4f0..b44cdbf80 100644 --- a/tests/fixtures/test-app/pages/api/update-user.ts +++ b/tests/fixtures/test-app/pages/api/update-session.ts @@ -2,7 +2,7 @@ import { NextApiRequest, NextApiResponse } from 'next'; export default async function sessionHandler(req: NextApiRequest, res: NextApiResponse): Promise { const session = await global.getSession?.(req, res); - const updated = { ...session?.user, ...req.body?.user }; - await global.updateUser?.(req, res, updated); + const updated = { ...session, ...req.body?.session }; + await global.updateSession?.(req, res, updated); res.status(200).json(updated); } diff --git a/tests/session/update-user.test.ts b/tests/session/update-session.test.ts similarity index 53% rename from tests/session/update-user.test.ts rename to tests/session/update-session.test.ts index 1441129a6..4d8de02d2 100644 --- a/tests/session/update-user.test.ts +++ b/tests/session/update-session.test.ts @@ -6,22 +6,25 @@ import { CookieJar } from 'tough-cookie'; describe('update-user', () => { afterEach(teardown); - test('should update user', async () => { + test('should update session', async () => { const baseUrl = await setup(withoutApi); const cookieJar = await login(baseUrl); const user = await get(baseUrl, '/api/auth/me', { cookieJar }); expect(user).toEqual({ nickname: '__test_nickname__', sub: '__test_sub__' }); - await post(baseUrl, '/api/update-user', { cookieJar, body: { user: { foo: 'bar' } } }); - const updatedUser = await get(baseUrl, '/api/auth/me', { cookieJar }); - expect(updatedUser).toMatchObject({ foo: 'bar' }); + await post(baseUrl, '/api/update-session', { cookieJar, body: { session: { foo: 'bar' } } }); + const updatedSession = await get(baseUrl, '/api/session', { cookieJar }); + expect(updatedSession).toMatchObject({ + foo: 'bar', + user: expect.objectContaining({ nickname: '__test_nickname__', sub: '__test_sub__' }) + }); }); - test('should ignore updates if user is not defined', async () => { + test('should ignore updates if session is not defined', async () => { const baseUrl = await setup(withoutApi); const cookieJar = await login(baseUrl); const user = await get(baseUrl, '/api/auth/me', { cookieJar }); expect(user).toEqual({ nickname: '__test_nickname__', sub: '__test_sub__' }); - await post(baseUrl, '/api/update-user', { cookieJar, body: { user: undefined } }); + await post(baseUrl, '/api/update-session', { cookieJar, body: { session: undefined } }); const updatedUser = await get(baseUrl, '/api/auth/me', { cookieJar }); expect(updatedUser).toEqual({ nickname: '__test_nickname__', sub: '__test_sub__' }); }); @@ -30,7 +33,17 @@ describe('update-user', () => { const baseUrl = await setup(withoutApi); const cookieJar = new CookieJar(); await expect(get(baseUrl, '/api/auth/me', { cookieJar })).resolves.toBe(''); - await post(baseUrl, '/api/update-user', { body: { user: { sub: 'foo' } }, cookieJar }); + await post(baseUrl, '/api/update-session', { body: { session: { sub: 'foo' } }, cookieJar }); await expect(get(baseUrl, '/api/auth/me', { cookieJar })).resolves.toBe(''); }); + + test('should ignore updates if user is not defined in update', async () => { + const baseUrl = await setup(withoutApi); + const cookieJar = await login(baseUrl); + const user = await get(baseUrl, '/api/auth/me', { cookieJar }); + expect(user).toEqual({ nickname: '__test_nickname__', sub: '__test_sub__' }); + await post(baseUrl, '/api/update-session', { cookieJar, body: { session: { user: undefined } } }); + const updatedUser = await get(baseUrl, '/api/auth/me', { cookieJar }); + expect(updatedUser).toEqual({ nickname: '__test_nickname__', sub: '__test_sub__' }); + }); }); From b55fa58bec38b0ac9d9738e7712acd855300dc59 Mon Sep 17 00:00:00 2001 From: adamjmcgrath Date: Tue, 11 Oct 2022 11:07:30 +0100 Subject: [PATCH 2/2] Fix tests --- tests/helpers/with-page-auth-required.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/helpers/with-page-auth-required.test.ts b/tests/helpers/with-page-auth-required.test.ts index c6f8c3a0f..732eb42d8 100644 --- a/tests/helpers/with-page-auth-required.test.ts +++ b/tests/helpers/with-page-auth-required.test.ts @@ -132,8 +132,8 @@ describe('with-page-auth-required ssr', () => { withPageAuthRequiredOptions: { async getServerSideProps(ctx) { await Promise.resolve(); - const session = (global as any).getSession(ctx.req, ctx.res); - await (global as any).updateUser(ctx.req, ctx.res, { ...session.user, test: 'Hello World!' }); + const session = await (global as any).getSession(ctx.req, ctx.res); + await (global as any).updateSession(ctx.req, ctx.res, { ...session, test: 'Hello World!' }); return { props: {} }; } } @@ -145,6 +145,6 @@ describe('with-page-auth-required ssr', () => { } = await get(baseUrl, '/protected', { cookieJar, fullResponse: true }); expect(statusCode).toBe(200); const session = await get(baseUrl, '/api/session', { cookieJar }); - expect(session.user.test).toBe('Hello World!'); + expect(session.test).toBe('Hello World!'); }); });