@auth/core
Version:
Authentication for the Web.
128 lines (127 loc) • 5.62 kB
JavaScript
import { JWTSessionError, SessionTokenError } from "../../errors.js";
import { fromDate } from "../utils/date.js";
/** Return a session object filtered via `callbacks.session` */
export async function session(options, sessionStore, cookies, isUpdate, newSession) {
const { adapter, jwt, events, callbacks, logger, session: { strategy: sessionStrategy, maxAge: sessionMaxAge }, } = options;
const response = {
body: null,
headers: {
"Content-Type": "application/json",
...(!isUpdate && {
"Cache-Control": "private, no-cache, no-store",
Expires: "0",
Pragma: "no-cache",
}),
},
cookies,
};
const sessionToken = sessionStore.value;
if (!sessionToken)
return response;
if (sessionStrategy === "jwt") {
try {
const salt = options.cookies.sessionToken.name;
const payload = await jwt.decode({ ...jwt, token: sessionToken, salt });
if (!payload)
throw new Error("Invalid JWT");
// @ts-expect-error
const token = await callbacks.jwt({
token: payload,
...(isUpdate && { trigger: "update" }),
session: newSession,
});
const newExpires = fromDate(sessionMaxAge);
if (token !== null) {
// By default, only exposes a limited subset of information to the client
// as needed for presentation purposes (e.g. "you are logged in as...").
const session = {
user: { name: token.name, email: token.email, image: token.picture },
expires: newExpires.toISOString(),
};
// @ts-expect-error
const newSession = await callbacks.session({ session, token });
// Return session payload as response
response.body = newSession;
// Refresh JWT expiry by re-signing it, with an updated expiry date
const newToken = await jwt.encode({ ...jwt, token, salt });
// Set cookie, to also update expiry date on cookie
const sessionCookies = sessionStore.chunk(newToken, {
expires: newExpires,
});
response.cookies?.push(...sessionCookies);
await events.session?.({ session: newSession, token });
}
else {
response.cookies?.push(...sessionStore.clean());
}
}
catch (e) {
logger.error(new JWTSessionError(e));
// If the JWT is not verifiable remove the broken session cookie(s).
response.cookies?.push(...sessionStore.clean());
}
return response;
}
// Retrieve session from database
try {
const { getSessionAndUser, deleteSession, updateSession } = adapter;
let userAndSession = await getSessionAndUser(sessionToken);
// If session has expired, clean up the database
if (userAndSession &&
userAndSession.session.expires.valueOf() < Date.now()) {
await deleteSession(sessionToken);
userAndSession = null;
}
if (userAndSession) {
const { user, session } = userAndSession;
const sessionUpdateAge = options.session.updateAge;
// Calculate last updated date to throttle write updates to database
// Formula: ({expiry date} - sessionMaxAge) + sessionUpdateAge
// e.g. ({expiry date} - 30 days) + 1 hour
const sessionIsDueToBeUpdatedDate = session.expires.valueOf() -
sessionMaxAge * 1000 +
sessionUpdateAge * 1000;
const newExpires = fromDate(sessionMaxAge);
// Trigger update of session expiry date and write to database, only
// if the session was last updated more than {sessionUpdateAge} ago
if (sessionIsDueToBeUpdatedDate <= Date.now()) {
await updateSession({
sessionToken: sessionToken,
expires: newExpires,
});
}
// Pass Session through to the session callback
const sessionPayload = await callbacks.session({
// TODO: user already passed below,
// remove from session object in https://github.com/nextauthjs/next-auth/pull/9702
// @ts-expect-error
session: { ...session, user },
user,
newSession,
...(isUpdate ? { trigger: "update" } : {}),
});
// Return session payload as response
response.body = sessionPayload;
// Set cookie again to update expiry
response.cookies?.push({
name: options.cookies.sessionToken.name,
value: sessionToken,
options: {
...options.cookies.sessionToken.options,
expires: newExpires,
},
});
// @ts-expect-error
await events.session?.({ session: sessionPayload });
}
else if (sessionToken) {
// If `sessionToken` was found set but it's not valid for a session then
// remove the sessionToken cookie from browser.
response.cookies?.push(...sessionStore.clean());
}
}
catch (e) {
logger.error(new SessionTokenError(e));
}
return response;
}