Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ignore protected props in createNewSession* #690

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading