Skip to content

Commit

Permalink
feat: ignore protected props in createNewSession* (#690)
Browse files Browse the repository at this point in the history
  • Loading branch information
porcellus authored Sep 11, 2023
1 parent 244a225 commit f3fcaf8
Show file tree
Hide file tree
Showing 17 changed files with 73 additions and 49 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Now supporting FDI 1.18
- removed the recipe specific `User` type, now all functions are using the new generic `User` type
- The `fetchValue` callback of claims now take a new `recipeUserId` param
- Now ignoring protected props in the payload in `createNewSession` and `createNewSessionWithoutRequestResponse`

- EmailPassword:
- removed `getUserById`, `getUserByEmail`
Expand Down
3 changes: 3 additions & 0 deletions lib/build/recipe/session/constants.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ export declare const REFRESH_API_PATH = "/session/refresh";
export declare const SIGNOUT_API_PATH = "/signout";
export declare const availableTokenTransferMethods: TokenTransferMethod[];
export declare const hundredYearsInMs = 3153600000000;
export declare const JWKCacheCooldownInMs = 500;
export declare const JWKCacheMaxAgeInMs = 60000;
export declare const protectedProps: string[];
15 changes: 14 additions & 1 deletion lib/build/recipe/session/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,21 @@
* under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.hundredYearsInMs = exports.availableTokenTransferMethods = exports.SIGNOUT_API_PATH = exports.REFRESH_API_PATH = void 0;
exports.protectedProps = exports.JWKCacheMaxAgeInMs = exports.JWKCacheCooldownInMs = exports.hundredYearsInMs = exports.availableTokenTransferMethods = exports.SIGNOUT_API_PATH = exports.REFRESH_API_PATH = void 0;
exports.REFRESH_API_PATH = "/session/refresh";
exports.SIGNOUT_API_PATH = "/signout";
exports.availableTokenTransferMethods = ["cookie", "header"];
exports.hundredYearsInMs = 3153600000000;
exports.JWKCacheCooldownInMs = 500;
exports.JWKCacheMaxAgeInMs = 60000;
exports.protectedProps = [
"sub",
"iat",
"exp",
"sessionHandle",
"parentRefreshTokenHash1",
"refreshTokenHash1",
"antiCsrfToken",
"rsub",
"tId",
];
4 changes: 4 additions & 0 deletions lib/build/recipe/session/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const utils_1 = require("./utils");
const sessionRequestFunctions_1 = require("./sessionRequestFunctions");
const __1 = require("../..");
const constants_1 = require("../multitenancy/constants");
const constants_2 = require("./constants");
class SessionWrapper {
static async createNewSession(
req,
Expand Down Expand Up @@ -71,6 +72,9 @@ class SessionWrapper {
const appInfo = recipeInstance.getAppInfo();
const issuer = appInfo.apiDomain.getAsStringDangerous() + appInfo.apiBasePath.getAsStringDangerous();
let finalAccessTokenPayload = Object.assign(Object.assign({}, accessTokenPayload), { iss: issuer });
for (const prop of constants_2.protectedProps) {
delete finalAccessTokenPayload[prop];
}
let user = await __1.getUser(recipeUserId.getAsString(), userContext);
let userId = recipeUserId.getAsString();
if (user !== undefined) {
Expand Down
2 changes: 0 additions & 2 deletions lib/build/recipe/session/recipeImplementation.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ export declare type Helpers = {
appInfo: NormalisedAppinfo;
getRecipeImpl: () => RecipeInterface;
};
export declare const JWKCacheMaxAgeInMs = 60000;
export declare const protectedProps: string[];
export default function getRecipeInterface(
querier: Querier,
config: TypeNormalisedInput,
Expand Down
21 changes: 4 additions & 17 deletions lib/build/recipe/session/recipeImplementation.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ var __importDefault =
return mod && mod.__esModule ? mod : { default: mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.protectedProps = exports.JWKCacheMaxAgeInMs = void 0;
const jose_1 = require("jose");
const SessionFunctions = __importStar(require("./sessionFunctions"));
const cookieAndHeaders_1 = require("./cookieAndHeaders");
Expand All @@ -54,24 +53,12 @@ const accessToken_1 = require("./accessToken");
const error_1 = __importDefault(require("./error"));
const recipeUserId_1 = __importDefault(require("../../recipeUserId"));
const constants_1 = require("../multitenancy/constants");
const JWKCacheCooldownInMs = 500;
exports.JWKCacheMaxAgeInMs = 60000;
exports.protectedProps = [
"sub",
"iat",
"exp",
"sessionHandle",
"parentRefreshTokenHash1",
"refreshTokenHash1",
"antiCsrfToken",
"rsub",
"tId",
];
const constants_2 = require("./constants");
function getRecipeInterface(querier, config, appInfo, getRecipeImplAfterOverrides) {
const JWKS = querier.getAllCoreUrlsForPath("/.well-known/jwks.json").map((url) =>
jose_1.createRemoteJWKSet(new URL(url), {
cooldownDuration: JWKCacheCooldownInMs,
cacheMaxAge: exports.JWKCacheMaxAgeInMs,
cooldownDuration: constants_2.JWKCacheCooldownInMs,
cacheMaxAge: constants_2.JWKCacheMaxAgeInMs,
})
);
/**
Expand Down Expand Up @@ -362,7 +349,7 @@ function getRecipeInterface(querier, config, appInfo, getRecipeImplAfterOverride
return false;
}
let newAccessTokenPayload = Object.assign({}, sessionInfo.customClaimsInAccessTokenPayload);
for (const key of exports.protectedProps) {
for (const key of constants_2.protectedProps) {
delete newAccessTokenPayload[key];
}
newAccessTokenPayload = Object.assign(Object.assign({}, newAccessTokenPayload), accessTokenPayloadUpdate);
Expand Down
6 changes: 3 additions & 3 deletions lib/build/recipe/session/sessionClass.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
*/
const cookieAndHeaders_1 = require("./cookieAndHeaders");
const error_1 = __importDefault(require("./error"));
const recipeImplementation_1 = require("./recipeImplementation");
const utils_1 = require("./utils");
const jwt_1 = require("./jwt");
const logger_1 = require("../../logger");
const constants_1 = require("./constants");
class Session {
constructor(
helpers,
Expand Down Expand Up @@ -132,7 +132,7 @@ class Session {
// Any update to this function should also be reflected in the respective JWT version
async mergeIntoAccessTokenPayload(accessTokenPayloadUpdate, userContext) {
let newAccessTokenPayload = Object.assign({}, this.getAccessTokenPayload(userContext));
for (const key of recipeImplementation_1.protectedProps) {
for (const key of constants_1.protectedProps) {
delete newAccessTokenPayload[key];
}
newAccessTokenPayload = Object.assign(Object.assign({}, newAccessTokenPayload), accessTokenPayloadUpdate);
Expand Down Expand Up @@ -220,7 +220,7 @@ class Session {
userContext,
});
if (validateClaimResponse.accessTokenPayloadUpdate !== undefined) {
for (const key of recipeImplementation_1.protectedProps) {
for (const key of constants_1.protectedProps) {
delete validateClaimResponse.accessTokenPayloadUpdate[key];
}
await this.mergeIntoAccessTokenPayload(validateClaimResponse.accessTokenPayloadUpdate, userContext);
Expand Down
4 changes: 2 additions & 2 deletions lib/build/recipe/session/sessionFunctions.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ const accessToken_1 = require("./accessToken");
const error_1 = __importDefault(require("./error"));
const processState_1 = require("../../processState");
const normalisedURLPath_1 = __importDefault(require("../../normalisedURLPath"));
const recipeImplementation_1 = require("./recipeImplementation");
const utils_1 = require("../../utils");
const logger_1 = require("../../logger");
const recipeUserId_1 = __importDefault(require("../../recipeUserId"));
const constants_1 = require("../multitenancy/constants");
const constants_2 = require("./constants");
/**
* @description call this to "login" a user.
*/
Expand Down Expand Up @@ -129,7 +129,7 @@ async function getSession(helpers, parsedAccessToken, antiCsrfToken, doAntiCsrfC
}
// We check if the token was created since the last time we refreshed the keys from the core
// Since we do not know the exact timing of the last refresh, we check against the max age
if (timeCreated <= Date.now() - recipeImplementation_1.JWKCacheMaxAgeInMs) {
if (timeCreated <= Date.now() - constants_2.JWKCacheMaxAgeInMs) {
throw err;
}
} else {
Expand Down
3 changes: 3 additions & 0 deletions lib/build/recipe/session/sessionRequestFunctions.js
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,9 @@ async function createNewSessionInRequest({
const claimsAddedByOtherRecipes = recipeInstance.getClaimsAddedByOtherRecipes();
const issuer = appInfo.apiDomain.getAsStringDangerous() + appInfo.apiBasePath.getAsStringDangerous();
let finalAccessTokenPayload = Object.assign(Object.assign({}, accessTokenPayload), { iss: issuer });
for (const prop of constants_1.protectedProps) {
delete finalAccessTokenPayload[prop];
}
for (const claim of claimsAddedByOtherRecipes) {
const update = await claim.build(userId, recipeUserId, tenantId, userContext);
finalAccessTokenPayload = Object.assign(Object.assign({}, finalAccessTokenPayload), update);
Expand Down
15 changes: 15 additions & 0 deletions lib/ts/recipe/session/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,18 @@ export const SIGNOUT_API_PATH = "/signout";
export const availableTokenTransferMethods: TokenTransferMethod[] = ["cookie", "header"];

export const hundredYearsInMs = 3153600000000;

export const JWKCacheCooldownInMs = 500;
export const JWKCacheMaxAgeInMs = 60000;

export const protectedProps = [
"sub",
"iat",
"exp",
"sessionHandle",
"parentRefreshTokenHash1",
"refreshTokenHash1",
"antiCsrfToken",
"rsub",
"tId",
];
5 changes: 5 additions & 0 deletions lib/ts/recipe/session/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { createNewSessionInRequest, getSessionFromRequest, refreshSessionInReque
import RecipeUserId from "../../recipeUserId";
import { getUser } from "../..";
import { DEFAULT_TENANT_ID } from "../multitenancy/constants";
import { protectedProps } from "./constants";

export default class SessionWrapper {
static init = Recipe.init;
Expand Down Expand Up @@ -90,6 +91,10 @@ export default class SessionWrapper {
iss: issuer,
};

for (const prop of protectedProps) {
delete finalAccessTokenPayload[prop];
}

let user = await getUser(recipeUserId.getAsString(), userContext);
let userId = recipeUserId.getAsString();
if (user !== undefined) {
Expand Down
16 changes: 1 addition & 15 deletions lib/ts/recipe/session/recipeImplementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { validateAccessTokenStructure } from "./accessToken";
import SessionError from "./error";
import RecipeUserId from "../../recipeUserId";
import { DEFAULT_TENANT_ID } from "../multitenancy/constants";
import { JWKCacheCooldownInMs, JWKCacheMaxAgeInMs, protectedProps } from "./constants";

export type Helpers = {
querier: Querier;
Expand All @@ -31,21 +32,6 @@ export type Helpers = {
getRecipeImpl: () => RecipeInterface;
};

const JWKCacheCooldownInMs = 500;
export const JWKCacheMaxAgeInMs = 60000;

export const protectedProps = [
"sub",
"iat",
"exp",
"sessionHandle",
"parentRefreshTokenHash1",
"refreshTokenHash1",
"antiCsrfToken",
"rsub",
"tId",
];

export default function getRecipeInterface(
querier: Querier,
config: TypeNormalisedInput,
Expand Down
3 changes: 2 additions & 1 deletion lib/ts/recipe/session/sessionClass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@
import { buildFrontToken, clearSession, setAntiCsrfTokenInHeaders, setToken } from "./cookieAndHeaders";
import STError from "./error";
import { SessionClaim, SessionClaimValidator, SessionContainerInterface, ReqResInfo, TokenInfo } from "./types";
import { Helpers, protectedProps } from "./recipeImplementation";
import { Helpers } from "./recipeImplementation";
import { setAccessTokenInResponse } from "./utils";
import { parseJWTWithoutSignatureVerification } from "./jwt";
import { logDebugMessage } from "../../logger";
import RecipeUserId from "../../recipeUserId";
import { protectedProps } from "./constants";

export default class Session implements SessionContainerInterface {
constructor(
Expand Down
3 changes: 2 additions & 1 deletion lib/ts/recipe/session/sessionFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ import STError from "./error";
import { PROCESS_STATE, ProcessState } from "../../processState";
import { CreateOrRefreshAPIResponse, SessionInformation } from "./types";
import NormalisedURLPath from "../../normalisedURLPath";
import { Helpers, JWKCacheMaxAgeInMs } from "./recipeImplementation";
import { Helpers } from "./recipeImplementation";
import { maxVersion } from "../../utils";
import { logDebugMessage } from "../../logger";
import RecipeUserId from "../../recipeUserId";
import { DEFAULT_TENANT_ID } from "../multitenancy/constants";
import { JWKCacheMaxAgeInMs } from "./constants";

/**
* @description call this to "login" a user.
Expand Down
6 changes: 5 additions & 1 deletion lib/ts/recipe/session/sessionRequestFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import SuperTokens from "../../supertokens";
import { getRequiredClaimValidators } from "./utils";
import { getRidFromHeader, isAnIpAddress, normaliseHttpMethod, setRequestInUserContextIfNotDefined } from "../../utils";
import { logDebugMessage } from "../../logger";
import { availableTokenTransferMethods } from "./constants";
import { availableTokenTransferMethods, protectedProps } from "./constants";
import { clearSession, getAntiCsrfTokenFromHeaders, getToken, setCookie } from "./cookieAndHeaders";
import { ParsedJWTInfo, parseJWTWithoutSignatureVerification } from "./jwt";
import { validateAccessTokenStructure } from "./accessToken";
Expand Down Expand Up @@ -359,6 +359,10 @@ export async function createNewSessionInRequest({
iss: issuer,
};

for (const prop of protectedProps) {
delete finalAccessTokenPayload[prop];
}

for (const claim of claimsAddedByOtherRecipes) {
const update = await claim.build(userId, recipeUserId, tenantId, userContext);
finalAccessTokenPayload = {
Expand Down
2 changes: 1 addition & 1 deletion test/accountlinking/session.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ let EmailVerification = require("../../recipe/emailverification");
const express = require("express");
const request = require("supertest");
let { middleware, errorHandler } = require("../../framework/express");
let { protectedProps } = require("../../lib/build/recipe/session/recipeImplementation");
let { protectedProps } = require("../../lib/build/recipe/session/constants");
let { PrimitiveClaim } = require("../../lib/build/recipe/session/claimBaseClasses/primitiveClaim");

describe(`sessionTests: ${printPath("[test/accountlinking/session.test.js]")}`, function () {
Expand Down
13 changes: 8 additions & 5 deletions test/session/accessTokenVersions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ describe(`AccessToken versions: ${printPath("[test/session/accessTokenVersions.t
assert(parsedHeader.kid.startsWith("s-"));
});

it("should throw an error when adding protected props", async function () {
it("should ignore protected props", async function () {
const connectionURI = await startST();
SuperTokens.init({
supertokens: {
Expand All @@ -156,7 +156,7 @@ describe(`AccessToken versions: ${printPath("[test/session/accessTokenVersions.t
},
})
)
.expect(400)
.expect(200)
.end((err, resp) => {
if (err) {
rej(err);
Expand All @@ -167,9 +167,12 @@ describe(`AccessToken versions: ${printPath("[test/session/accessTokenVersions.t
);

let cookies = extractInfoFromResponse(res);
assert.strictEqual(cookies.accessTokenFromAny, undefined);
assert.strictEqual(cookies.refreshTokenFromAny, undefined);
assert.strictEqual(cookies.frontToken, undefined);
assert.notEqual(cookies.accessTokenFromAny, undefined);
assert.notEqual(cookies.refreshTokenFromAny, undefined);
assert.notEqual(cookies.frontToken, undefined);

const parsedToken = parseJWTWithoutSignatureVerification(cookies.accessTokenFromAny);
assert.notEqual(parsedToken.payload.sub, "asdf");
});

it("should make sign in/up return a 500 when adding protected props", async function () {
Expand Down

0 comments on commit f3fcaf8

Please sign in to comment.