Skip to content

Commit

Permalink
Limit subscriber row fields in session object
Browse files Browse the repository at this point in the history
This limits the subscriber fields available in the session object
to an allowlist of properties, rather than using a denylist.
  • Loading branch information
Vinnl committed Oct 30, 2024
1 parent 3446c47 commit ce95d67
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 32 deletions.
59 changes: 29 additions & 30 deletions src/app/api/utils/auth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import { NextRequest } from "next/server";
import { AuthOptions, Profile as FxaProfile, User } from "next-auth";
import { SubscriberRow } from "knex/types/tables";
import { logger } from "../../functions/server/logging";

import {
Expand All @@ -26,6 +25,7 @@ import { record } from "../../functions/server/glean";
import { renderEmail } from "../../../emails/renderEmail";
import { SignupReportEmail } from "../../../emails/templates/signupReport/SignupReportEmail";
import { getEnvVarsOrThrow } from "../../../envVars";
import { sanitizeSubscriberRow } from "../../functions/server/sanitize";

const envVars = getEnvVarsOrThrow([
"OAUTH_AUTHORIZATION_URI",
Expand Down Expand Up @@ -117,14 +117,11 @@ export const authOptions: AuthOptions = {
);

if (subscriberFromDb) {
profile = subscriberFromDb.fxa_profile_json as FxaProfile;
const sanitizedSubscriber = sanitizeSubscriberRow(subscriberFromDb);
profile = sanitizedSubscriber.fxa_profile_json as FxaProfile;

// MNTOR-2599 The breach_resolution object can get pretty big,
// causing the session token cookie to balloon in size,
// eventually resulting in a 400 Bad Request due to headers being too large.
delete (subscriberFromDb as Partial<SubscriberRow>).breach_resolution;
token.subscriber =
subscriberFromDb as unknown as SerializedSubscriber;
sanitizedSubscriber as unknown as SerializedSubscriber;
}
}
if (profile) {
Expand Down Expand Up @@ -153,11 +150,9 @@ export const authOptions: AuthOptions = {
const existingUser = await getSubscriberByFxaUid(profile.uid);

if (existingUser) {
// MNTOR-2599 The breach_resolution object can get pretty big,
// causing the session token cookie to balloon in size,
// eventually resulting in a 400 Bad Request due to headers being too large.
delete (existingUser as Partial<SubscriberRow>).breach_resolution;
token.subscriber = existingUser as unknown as SerializedSubscriber;
const sanitizedSubscriber = sanitizeSubscriberRow(existingUser);
token.subscriber =
sanitizedSubscriber as unknown as SerializedSubscriber;
if (account.access_token && account.refresh_token) {
const updatedUser = await updateFxAData(
existingUser,
Expand All @@ -166,13 +161,13 @@ export const authOptions: AuthOptions = {
account.expires_at ?? 0,
profile,
);
// MNTOR-2599 The breach_resolution object can get pretty big,
// causing the session token cookie to balloon in size,
// eventually resulting in a 400 Bad Request due to headers being too large.
delete (updatedUser as Partial<SubscriberRow>).breach_resolution;
// Next-Auth implicitly converts `updatedUser` to a SerializedSubscriber,
// hence the type assertion:
token.subscriber = updatedUser as unknown as SerializedSubscriber;
if (updatedUser) {
const sanitizedUpdatedUser = sanitizeSubscriberRow(updatedUser);
// Next-Auth implicitly converts `updatedUser` to a SerializedSubscriber,
// hence the type assertion:
token.subscriber =
sanitizedUpdatedUser as unknown as SerializedSubscriber;
}
}
} else if (!existingUser && profile.email) {
const verifiedSubscriber = await addSubscriber(
Expand All @@ -183,10 +178,14 @@ export const authOptions: AuthOptions = {
account.expires_at,
profile,
);
// The date fields of `verifiedSubscriber` get converted to an ISO 8601
// date string when serialised in the token, hence the type assertion:
token.subscriber =
verifiedSubscriber as unknown as SerializedSubscriber;
if (verifiedSubscriber) {
const sanitizedSubscriber =
sanitizeSubscriberRow(verifiedSubscriber);
// The date fields of `verifiedSubscriber` get converted to an ISO 8601
// date string when serialised in the token, hence the type assertion:
token.subscriber =
sanitizedSubscriber as unknown as SerializedSubscriber;
}

const allBreaches = await getBreaches();
const unsafeBreachesForEmail = await getBreachesForEmail(
Expand Down Expand Up @@ -276,13 +275,13 @@ export const authOptions: AuthOptions = {
Date.now() + responseTokens.expires_in * 1000,
);

// MNTOR-2599 The breach_resolution object can get pretty big,
// causing the session token cookie to balloon in size,
// eventually resulting in a 400 Bad Request due to headers being too large.
delete (updatedUser as Partial<SubscriberRow>).breach_resolution;
// Next-Auth implicitly converts `updatedUser` to a SerializedSubscriber,
// hence the type assertion:
token.subscriber = updatedUser as unknown as SerializedSubscriber;
if (updatedUser) {
const sanitizedUpdatedUser = sanitizeSubscriberRow(updatedUser);
// Next-Auth implicitly converts `updatedUser` to a SerializedSubscriber,
// hence the type assertion:
token.subscriber =
sanitizedUpdatedUser as unknown as SerializedSubscriber;
}
} catch (error) {
logger.error("refresh_access_token", error);
// The error property can be used client-side to handle the refresh token error
Expand Down
7 changes: 5 additions & 2 deletions src/next-auth.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import { DefaultSession } from "next-auth";
import { SubscriberRow } from "knex/types/tables";
import { ISO8601DateString } from "./utils/parse";
import { SanitizedSubscriberRow } from "./app/functions/server/sanitize";

export type SerializedSubscriber = Omit<SubscriberRow, "created_at"> & {
export type SerializedSubscriber = Omit<
SanitizedSubscriberRow,
"created_at"
> & {
created_at: ISO8601DateString;
};

Expand Down

0 comments on commit ce95d67

Please sign in to comment.