Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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
6 changes: 5 additions & 1 deletion api-mock/fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const API_PORT = Number(process.env.API_PORT || 3030);
let apiStatus: "NOT_READY" | "OK" = "NOT_READY";
let stConfig: string;

const fdiVersion = "4.0";
export const fdiVersion = "4.1";

type Callback = (
error: any | null,
Expand Down Expand Up @@ -186,6 +186,10 @@ export function setMockStatus(newStatus: "NOT_READY" | "OK") {
apiStatus = newStatus;
}

export function getMockStatus() {
return apiStatus;
}

export function setSTConfig(config) {
stConfig = config;
}
Expand Down
2 changes: 1 addition & 1 deletion api-mock/mocks/SessionMock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ class RemoteSessionObject implements SessionContainerInterface {
}
}

function deserializeSession(session) {
export function deserializeSession(session) {
if (!session) return session;
return new RemoteSessionObject(session);
}
Expand Down
148 changes: 148 additions & 0 deletions test/session/session.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
*
* This software is licensed under the Apache License, Version 2.0 (the
* "License") as published by the Apache Software Foundation.
*
* You may not use this file except in compliance with the License. You may
* obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
const { printPath, createCoreApplication } = require("../utils");
const assert = require("assert");
const { recipesMock, request } = require("../../api-mock");
const { EmailPassword, Session, supertokens } = recipesMock;
const SuperTokens = require("supertokens-node");
const setCookieParser = require("set-cookie-parser");
const { User: UserClass } = require("supertokens-node/lib/build/user");

const signUp = async (tenantId, email, password, session, userContext) => {
let response = await new Promise((resolve) =>
request()
.post("/auth/signup")
.send({
formFields: [
{
id: "email",
value: email,
},
{
id: "password",
value: password,
},
],
tenantId,
session,
userContext,
})
.expect(200)
.end((err, res) => {
if (err) {
reject(undefined);
} else {
resolve(res);
}
})
);

const responseBody = response.body;

return {
headers: response.headers,
response: {
...responseBody,
...("user" in responseBody
? {
user: new UserClass(responseBody.user),
}
: {}),
...("recipeUserId" in responseBody
? {
recipeUserId: SuperTokens.convertToRecipeUserId(responseBody.recipeUserId),
}
: {}),
}
};
};

describe(`sessionTests: ${printPath("[test/session/session.test.js]")}`, function () {
describe("Cookie checks", function () {
it("access and refresh tokens set correctly on new session", async function () {
const connectionURI = await createCoreApplication();
supertokens.init({
supertokens: {
connectionURI,
},
appInfo: {
apiDomain: "api.supertokens.io",
appName: "SuperTokens",
websiteDomain: "supertokens.io",
},
recipeList: [EmailPassword.init(), Session.init({ getTokenTransferMethod: "cookie" })],
});

const { response, headers } = await signUp("public", "[email protected]", "password123");
const epUser = response.user;

// Create a new session for the user, get headers from the response
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not used.

const session = await Session.createNewSessionWithoutRequestResponse("public", epUser.loginMethods[0].recipeUserId);

let cookies = headers?.["set-cookie"];
assert(cookies, "No cookies found in response headers");

if (!Array.isArray(cookies)) {
cookies = [cookies];
}

// Parse cookies from the response. Fastapi set-cookie responses are a large string.
cookies = cookies
.flat() // Ensure we have a flat array of cookies
// Split cookie strings into arrays
.map((cookieStr) => setCookieParser.splitCookiesString(cookieStr))
.flat(); // Since we have an array of arrays now

console.log(cookies);

cookies.forEach((cookieStr) => {
if (cookieStr.startsWith("sAccessToken=") || cookieStr.startsWith("sRefreshToken=")) {
cookieStr.split("; ").forEach((part) => {
if (part.startsWith("Expires=")) {
assert(part.endsWith("GMT"), "Cookie expiry is not in GMT format");
}
});
}
});

const parsedCookies = cookies
.map(setCookieParser.parseString);

const accessTokenCookie = parsedCookies.find((info) => (info?.key ?? info?.name) == "sAccessToken");
const refreshTokenCookie = parsedCookies.find((info) => (info?.key ?? info?.name) === "sRefreshToken");

console.log(new Date(accessTokenCookie.expires).getTimezoneOffset())

// Ensure cookies are set and with GMT timezones
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Date - Date headers are always GMT
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie#expiresdate - Invalid cookies become session cookies
assert(accessTokenCookie, "Access token cookie not found");
assert(accessTokenCookie.expires, "Access token cookie expiry not set");
assert(
new Date(accessTokenCookie.expires).getTimezoneOffset() === 0,
"Access token cookie expiry is not in GMT"
);

assert(refreshTokenCookie, "Refresh token cookie not found");
assert(refreshTokenCookie.expires, "Refresh token cookie expiry not set");
assert(
new Date(refreshTokenCookie.expires).getTimezoneOffset() === 0,
"Refresh token cookie expiry is not in GMT"
);

assert(session.getUserId() === session.getRecipeUserId().getAsString());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this check is relevant. What was this meant for?

});
});
});