Skip to content

Commit f8cf6e1

Browse files
:bug fix(super-tokens-auth) customize supertokens auth (#1234)
1 parent 1bcf2ee commit f8cf6e1

File tree

73 files changed

+1874
-1781
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+1874
-1781
lines changed

packages/backend/src/__tests__/drivers/user.driver.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1+
import { TokenPayload } from "google-auth-library";
12
import { ObjectId, WithId } from "mongodb";
23
import { faker } from "@faker-js/faker";
3-
import { UserInfo_Google } from "@core/types/auth.types";
44
import { Schema_User } from "@core/types/user.types";
55
import userService from "../../user/services/user.service";
66

77
export class UserDriver {
88
static generateGoogleUser(
9-
overrides: Partial<UserInfo_Google["gUser"]> = {},
10-
): UserInfo_Google["gUser"] {
9+
overrides: Partial<TokenPayload> = {},
10+
): TokenPayload {
1111
const firstName = faker.person.firstName();
1212
const lastName = faker.person.lastName();
1313

packages/backend/src/__tests__/helpers/mock.setup.ts

Lines changed: 99 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
import { UserContext } from "supertokens-node/lib/build/types";
1616
import { createMockCalendarListEntry as mockCalendarListCreate } from "@core/__tests__/helpers/gcal.factory";
1717
import { gSchema$CalendarListEntry } from "@core/types/gcal";
18+
import { StringV4Schema, zObjectId } from "@core/types/type.utils";
1819
import { UserMetadata } from "@core/types/user.types";
1920
import { mockAndCategorizeGcalEvents } from "@backend/__tests__/mocks.gcal/factories/gcal.event.batch";
2021
import { mockGcal } from "@backend/__tests__/mocks.gcal/factories/gcal.factory";
@@ -85,29 +86,39 @@ function mockSuperToken() {
8586
? JSON.parse(sessionString)
8687
: undefined;
8788

88-
if (typeof session?.userId === "string") {
89-
req.session = {
90-
getUserId() {
91-
return session.userId;
92-
},
93-
getAccessTokenPayload(): SupertokensAccessTokenPayload {
94-
return {
95-
iat: now.getMilliseconds(),
96-
exp: now.getMilliseconds() + 5000,
97-
iss: req.headers.origin ?? "http://localhost",
98-
sub: session.userId,
99-
rsub: session.userId,
100-
tId,
101-
sessionHandle: session.sessionId ?? sessionHandle,
102-
refreshTokenHash1: null,
103-
parentRefreshTokenHash1: null,
104-
antiCsrfToken: null,
105-
};
106-
},
107-
} as SessionContainerInterface;
108-
109-
return next?.();
110-
}
89+
const userId = zObjectId.parse(session?.userId, {
90+
error: () => "invalid superToken session",
91+
});
92+
93+
const sessionId = StringV4Schema.parse(
94+
session?.sessionId ?? sessionHandle,
95+
{ error: () => "invalid superToken session" },
96+
);
97+
98+
req.session = {
99+
getUserId() {
100+
return userId.toString();
101+
},
102+
getHandle() {
103+
return sessionId;
104+
},
105+
getAccessTokenPayload(): SupertokensAccessTokenPayload {
106+
return {
107+
iat: now.getMilliseconds(),
108+
exp: now.getMilliseconds() + 5000,
109+
iss: req.headers.origin ?? "http://localhost",
110+
sub: userId.toString(),
111+
rsub: userId.toString(),
112+
tId,
113+
sessionHandle: sessionId,
114+
refreshTokenHash1: null,
115+
parentRefreshTokenHash1: null,
116+
antiCsrfToken: null,
117+
};
118+
},
119+
} as SessionContainerInterface;
120+
121+
return next?.();
111122

112123
if (input?.verifySessionOptions?.sessionRequired) {
113124
throw new Error("invalid superToken session");
@@ -145,6 +156,71 @@ function mockSuperToken() {
145156
});
146157
}
147158

159+
const mappings = new Map<string, string>();
160+
161+
async function getUserIdMapping(input: {
162+
userId: string;
163+
userIdType?: "SUPERTOKENS" | "EXTERNAL" | "ANY";
164+
userContext?: Record<string, unknown>;
165+
}): Promise<
166+
| {
167+
status: "OK";
168+
superTokensUserId: string;
169+
externalUserId: string;
170+
}
171+
| { status: "UNKNOWN_MAPPING_ERROR" }
172+
> {
173+
const superTokensUserId = mappings.get(input.userId);
174+
175+
if (!superTokensUserId) {
176+
return { status: "UNKNOWN_MAPPING_ERROR" };
177+
}
178+
179+
return { status: "OK", superTokensUserId, externalUserId: input.userId };
180+
}
181+
182+
async function createUserIdMapping(input: {
183+
superTokensUserId: string;
184+
externalUserId: string;
185+
externalUserIdInfo?: string;
186+
userContext?: Record<string, unknown>;
187+
force?: boolean;
188+
}): Promise<
189+
| { status: "OK" | "UNKNOWN_SUPERTOKENS_USER_ID_ERROR" }
190+
| {
191+
status: "USER_ID_MAPPING_ALREADY_EXISTS_ERROR";
192+
doesSuperTokensUserIdExist: boolean;
193+
doesExternalUserIdExist: boolean;
194+
}
195+
> {
196+
const superTokensUserId = mappings.get(input.externalUserId);
197+
const exists = superTokensUserId === input.superTokensUserId;
198+
199+
if (superTokensUserId && !input.force) {
200+
return {
201+
status: "USER_ID_MAPPING_ALREADY_EXISTS_ERROR",
202+
doesSuperTokensUserIdExist: exists,
203+
doesExternalUserIdExist: true,
204+
};
205+
}
206+
207+
mappings.set(input.externalUserId, input.superTokensUserId);
208+
209+
return { status: "OK" };
210+
}
211+
212+
mockModule(
213+
"supertokens-node",
214+
(superTokens: typeof import("supertokens-node")) => {
215+
const superTokensModule = mergeWith(superTokens, {
216+
getUserIdMapping: jest.fn(getUserIdMapping),
217+
createUserIdMapping: jest.fn(createUserIdMapping),
218+
});
219+
220+
return mergeWith(superTokensModule, { default: superTokensModule });
221+
},
222+
);
223+
148224
mockModule(
149225
"supertokens-node/recipe/session/framework/express",
150226
(

packages/backend/src/auth/auth.routes.config.ts

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,35 +15,13 @@ export class AuthRoutes extends CommonRoutesConfig {
1515
}
1616

1717
configureRoutes(): express.Application {
18-
/**
19-
* Checks whether user's google access token is still valid
20-
*/
21-
this.app
22-
.route(`/api/auth/google`)
23-
.get([verifySession(), authController.verifyGToken]);
24-
2518
this.app
2619
.route(`/api/auth/session`)
2720
.all(authMiddleware.verifyIsDev)
2821

2922
.post(authController.createSession)
3023
.get([verifySession(), authController.getUserIdFromSession]);
3124

32-
this.app
33-
.route(`/api/auth/session/revoke`)
34-
.all(authMiddleware.verifyIsDev)
35-
.post([verifySession(), authController.revokeSessionsByUser]);
36-
37-
/**
38-
* Google calls this route after successful oauth
39-
*/
40-
this.app
41-
.route(`/api/oauth/google`)
42-
.post([
43-
authMiddleware.verifyGoogleOauthCode,
44-
authController.loginOrSignup,
45-
]);
46-
4725
return this.app;
4826
}
4927
}

packages/backend/src/auth/controllers/auth.controller.test.ts

Lines changed: 0 additions & 64 deletions
This file was deleted.

0 commit comments

Comments
 (0)