Skip to content

Commit

Permalink
Allow getting a session without updating the session (#1116)
Browse files Browse the repository at this point in the history
  • Loading branch information
adamjmcgrath committed Mar 27, 2023
2 parents ebba676 + ad73278 commit e49e8be
Show file tree
Hide file tree
Showing 13 changed files with 130 additions and 2 deletions.
7 changes: 7 additions & 0 deletions src/auth0-session/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,13 @@ export interface SessionConfig {
*/
absoluteDuration: boolean | number;

/**
* Boolean value to enable automatic session saving when using rolling sessions.
* If this is `false`, you must call `touchSession(req, res)` to update the session.
* Defaults to `true`.
*/
autoSave?: boolean;

/**
* Boolean value to store the ID token in the session. Storing it can make the session cookie too
* large.
Expand Down
1 change: 1 addition & 0 deletions src/auth0-session/get-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const paramsSchema = Joi.object({
})
.optional()
.default(7 * 24 * 60 * 60), // 7 days,
autoSave: Joi.boolean().optional().default(true),
name: Joi.string().token().optional().default('appSession'),
store: Joi.object().optional(),
genId: Joi.function().maxArity(1).when(Joi.ref('store'), { then: Joi.required() }),
Expand Down
11 changes: 11 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,14 @@ export interface SessionConfig {
*/
absoluteDuration: boolean | number;

/**
* Boolean value to enable automatic session saving when using rolling sessions.
* If this is `false`, you must call `touchSession(req, res)` to update the session.
* Defaults to `true`.
* You can also use the `AUTH0_SESSION_AUTO_SAVE` environment variable.
*/
autoSave?: boolean;

/**
* Boolean value to store the ID token in the session. Storing it can make the session cookie too
* large.
Expand Down Expand Up @@ -395,6 +403,7 @@ export interface NextConfig extends Pick<BaseConfig, 'identityClaimFilter'> {
* - `AUTH0_SESSION_ROLLING`: See {@link SessionConfig.rolling}.
* - `AUTH0_SESSION_ROLLING_DURATION`: See {@link SessionConfig.rollingDuration}.
* - `AUTH0_SESSION_ABSOLUTE_DURATION`: See {@link SessionConfig.absoluteDuration}.
* - `AUTH0_SESSION_AUTO_SAVE`: See {@link SessionConfig.autoSave}.
* - `AUTH0_COOKIE_DOMAIN`: See {@link CookieConfig.domain}.
* - `AUTH0_COOKIE_PATH`: See {@link CookieConfig.path}.
* - `AUTH0_COOKIE_TRANSIENT`: See {@link CookieConfig.transient}.
Expand Down Expand Up @@ -495,6 +504,7 @@ export const getConfig = (params: ConfigParameters = {}): { baseConfig: BaseConf
const AUTH0_SESSION_ROLLING = process.env.AUTH0_SESSION_ROLLING;
const AUTH0_SESSION_ROLLING_DURATION = process.env.AUTH0_SESSION_ROLLING_DURATION;
const AUTH0_SESSION_ABSOLUTE_DURATION = process.env.AUTH0_SESSION_ABSOLUTE_DURATION;
const AUTH0_SESSION_AUTO_SAVE = process.env.AUTH0_SESSION_AUTO_SAVE;
const AUTH0_SESSION_STORE_ID_TOKEN = process.env.AUTH0_SESSION_STORE_ID_TOKEN;
const AUTH0_COOKIE_DOMAIN = process.env.AUTH0_COOKIE_DOMAIN;
const AUTH0_COOKIE_PATH = process.env.AUTH0_COOKIE_PATH;
Expand Down Expand Up @@ -542,6 +552,7 @@ export const getConfig = (params: ConfigParameters = {}): { baseConfig: BaseConf
AUTH0_SESSION_ABSOLUTE_DURATION && isNaN(Number(AUTH0_SESSION_ABSOLUTE_DURATION))
? bool(AUTH0_SESSION_ABSOLUTE_DURATION)
: num(AUTH0_SESSION_ABSOLUTE_DURATION),
autoSave: bool(AUTH0_SESSION_AUTO_SAVE, true),
storeIDToken: bool(AUTH0_SESSION_STORE_ID_TOKEN),
...baseParams.session,
cookie: {
Expand Down
10 changes: 10 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ import {
AccessTokenRequest,
GetAccessTokenResult,
Claims,
touchSessionFactory,
TouchSession,
updateSessionFactory,
UpdateSession
} from './session/';
Expand Down Expand Up @@ -75,6 +77,11 @@ export interface Auth0Server {
*/
getSession: GetSession;

/**
* Update the expiry of a rolling session when autoSave is disabled.
*/
touchSession: TouchSession;

/**
* Append properties to the user.
*/
Expand Down Expand Up @@ -174,6 +181,7 @@ export const _initAuth = (params?: ConfigParameters): Auth0Server & { sessionCac

// Init Next layer (with next config)
const getSession = sessionFactory(sessionCache);
const touchSession = touchSessionFactory(sessionCache);
const updateSession = updateSessionFactory(sessionCache);
const getAccessToken = accessTokenFactory(nextConfig, getClient, sessionCache);
const withApiAuthRequired = withApiAuthRequiredFactory(sessionCache);
Expand All @@ -187,6 +195,7 @@ export const _initAuth = (params?: ConfigParameters): Auth0Server & { sessionCac
return {
sessionCache,
getSession,
touchSession,
updateSession,
getAccessToken,
withApiAuthRequired,
Expand Down Expand Up @@ -250,6 +259,7 @@ export {
WithPageAuthRequired,
SessionCache,
GetSession,
TouchSession,
UpdateSession,
GetAccessToken,
Session,
Expand Down
2 changes: 1 addition & 1 deletion src/session/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default class SessionCache<
const [json, iat] = await this.sessionStore.read(req);
this.iatCache.set(req, iat);
this.cache.set(req, fromJson(json));
if (this.config.session.rolling && autoSave) {
if (this.config.session.rolling && this.config.session.autoSave && autoSave) {
await this.save(req, res);
}
}
Expand Down
1 change: 1 addition & 0 deletions src/session/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ export {
GetAccessTokenResult
} from './get-access-token';
export { default as SessionCache } from './cache';
export { default as touchSessionFactory, TouchSession } from './touch-session';
export { default as updateSessionFactory, UpdateSession } from './update-session';
40 changes: 40 additions & 0 deletions src/session/touch-session.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { IncomingMessage, ServerResponse } from 'http';
import { NextApiRequest, NextApiResponse } from 'next';
import { SessionCache } from '../session';
import { assertReqRes } from '../utils/assert';

/**
* Touch the session object. If rolling sessions are enabled and autoSave is disabled, you will need
* to call this method to update the session expiry.
*
* ```js
* // pages/api/graphql.js
* import { touchSession } from '@auth0/nextjs-auth0';
*
* export default async function graphql(req, res) {
* await touchSession(req, res);
*
* // ...
* };
* ```
*
* @category Server
*/
export type TouchSession = (
req: IncomingMessage | NextApiRequest,
res: ServerResponse | NextApiResponse
) => Promise<void>;

/**
* @ignore
*/
export default function touchSessionFactory(sessionCache: SessionCache): TouchSession {
return async (req, res) => {
assertReqRes(req, res);
const session = await sessionCache.get(req, res);
if (!session) {
return;
}
await sessionCache.save(req, res);
};
}
1 change: 1 addition & 0 deletions tests/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ describe('config params', () => {
rolling: true,
rollingDuration: 86400,
absoluteDuration: 604800,
autoSave: true,
storeIDToken: true,
cookie: {
domain: undefined,
Expand Down
1 change: 1 addition & 0 deletions tests/fixtures/global.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
declare global {
var getSession: Function | undefined;
var touchSession: Function | undefined;
var updateSession: Function | undefined;
var handleAuth: Function | undefined;
var withApiAuthRequired: Function | undefined;
Expand Down
3 changes: 3 additions & 0 deletions tests/fixtures/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export const setup = async (
handleLogout,
handleProfile,
getSession,
touchSession,
updateSession,
getAccessToken,
withApiAuthRequired,
Expand All @@ -90,6 +91,7 @@ export const setup = async (
const handlers: Handlers = { onError, callback, login, logout, profile };
global.handleAuth = handleAuth.bind(null, handlers);
global.getSession = getSession;
global.touchSession = touchSession;
global.updateSession = updateSession;
global.withApiAuthRequired = withApiAuthRequired;
global.withPageAuthRequired = (): any => withPageAuthRequired(withPageAuthRequiredOptions);
Expand All @@ -105,6 +107,7 @@ export const teardown = async (): Promise<void> => {
nock.cleanAll();
await stop();
delete global.getSession;
delete global.touchSession;
delete global.updateSession;
delete global.handleAuth;
delete global.withApiAuthRequired;
Expand Down
7 changes: 7 additions & 0 deletions tests/fixtures/test-app/pages/api/touch-session.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { NextApiRequest, NextApiResponse } from 'next';

export default async function sessionHandler(req: NextApiRequest, res: NextApiResponse): Promise<void> {
await global.touchSession?.(req, res);
const json = await global.getSession?.(req, res);
res.status(200).json(json);
}
3 changes: 2 additions & 1 deletion tests/fixtures/test-app/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve"
"jsx": "preserve",
"incremental": true
},
"include": [
"next-env.d.ts",
Expand Down
45 changes: 45 additions & 0 deletions tests/session/touch-session.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { login, setup, teardown } from '../fixtures/setup';
import { withoutApi } from '../fixtures/default-settings';
import { get } from '../auth0-session/fixtures/helpers';

describe('touch-session', () => {
afterEach(teardown);

test('should not update the session when getting the session', async () => {
const baseUrl = await setup({
...withoutApi,
session: {
autoSave: false
}
});
const cookieJar = await login(baseUrl);
const [authCookie] = await cookieJar.getCookies(baseUrl);
await get(baseUrl, '/api/auth/me', { cookieJar });
const [updatedAuthCookie] = await cookieJar.getCookies(baseUrl);
expect(updatedAuthCookie).toEqual(authCookie);
});

test('should update the session when calling touchSession', async () => {
const baseUrl = await setup({
...withoutApi,
session: {
autoSave: false
}
});
const cookieJar = await login(baseUrl);
const [authCookie] = await cookieJar.getCookies(baseUrl);
await get(baseUrl, '/api/touch-session', { cookieJar });
const [updatedAuthCookie] = await cookieJar.getCookies(baseUrl);
expect(updatedAuthCookie).not.toEqual(authCookie);
});

test('should not throw when there is no session', async () => {
const baseUrl = await setup({
...withoutApi,
session: {
autoSave: false
}
});
await expect(get(baseUrl, '/api/touch-session')).resolves.not.toThrow();
});
});

0 comments on commit e49e8be

Please sign in to comment.