Skip to content

Commit 69c96a4

Browse files
committed
Update token creation
1 parent 7e15348 commit 69c96a4

File tree

6 files changed

+196
-44
lines changed

6 files changed

+196
-44
lines changed

package-lock.json

Lines changed: 136 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"eslint-import-resolver-typescript": "^3.6.1",
5757
"eslint-plugin-import": "^2.29.1",
5858
"graphql-tag": "^2.12.6",
59+
"jsonwebtoken": "^9.0.2",
5960
"prettier": "^3.0.0",
6061
"react-email": "2.1.1",
6162
"tsx": "^3.13.0",
@@ -85,6 +86,7 @@
8586
"@pothos/plugin-tracing": "^0.5.8",
8687
"@sanity/client": "^6.7.0",
8788
"@tsndr/cloudflare-worker-jwt": "^2.5.3",
89+
"@types/jsonwebtoken": "^9.0.6",
8890
"@types/react": "^18.2.22",
8991
"cookie": "^0.5.0",
9092
"dataloader": "^2.2.2",

src/authn/index.ts

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import {
99
updateUserProfileInfo,
1010
} from "~/datasources/queries/users";
1111
import { getUsername } from "~/datasources/queries/utils/createUsername";
12-
import { unauthorizedError } from "~/errors";
12+
import { applicationError, ServiceErrors, unauthorizedError } from "~/errors";
13+
14+
const preventUserUpdate = new Set<"retool">(["retool"]);
1315

1416
// Obtener el token de autorización de la solicitud, ya sea del encabezado de
1517
// autorización o de la cookie "community-os-access-token"
@@ -25,12 +27,12 @@ const getAuthToken = (request: Request) => {
2527
return null;
2628
};
2729

28-
export const createAuthToken = async (user: USER, SECRET: string) => {
30+
export const createMinimalAuthToken = async (user: USER, SECRET: string) => {
2931
const payload = {
30-
audience: "retool-autenticated",
31-
id: user.id,
32-
email: user.email,
33-
user_metadata: user,
32+
audience: "retool",
33+
user_metadata: {
34+
sub: user.id,
35+
},
3436
exp: Date.now() + 60 * 60 * 24 * 1000 /* 24 hours */,
3537
};
3638

@@ -136,26 +138,39 @@ export const upsertUserFromRequest = async ({
136138
throw unauthorizedError("Token expired", logger);
137139
}
138140

139-
const { avatar_url, name, user_name, email_verified, sub, picture } =
140-
payload.user_metadata;
141-
const profileInfo = insertUsersSchema.safeParse({
142-
email: payload.email.toLowerCase(),
143-
isEmailVerified: email_verified,
144-
imageUrl: avatar_url ? avatar_url : picture ? picture : "",
145-
externalId: sub,
146-
name,
147-
username: user_name ?? getUsername(),
148-
publicMetadata: payload,
149-
});
150-
151-
if (profileInfo.success === false) {
152-
logger.error("Could not parse profile info", profileInfo.error);
153-
throw new Error("Could not parse profile info", profileInfo.error);
154-
}
141+
if (payload.audience && preventUserUpdate.has(payload.audience)) {
142+
const userId = payload.user_metadata.sub;
143+
144+
logger.info(`Preventing update for user ID: ${userId}`);
145+
const user = await findUserByID(DB, userId);
146+
147+
if (!user) {
148+
throw applicationError("User not found", ServiceErrors.FORBIDDEN, logger);
149+
}
155150

156-
logger.info(`Updating profile Info for user ID: ${sub}`);
151+
return user;
152+
} else {
153+
const { avatar_url, name, user_name, email_verified, sub, picture } =
154+
payload.user_metadata;
155+
const profileInfo = insertUsersSchema.safeParse({
156+
email: payload.email.toLowerCase(),
157+
isEmailVerified: email_verified,
158+
imageUrl: avatar_url ? avatar_url : picture ? picture : "",
159+
externalId: sub,
160+
name,
161+
username: user_name ?? getUsername(),
162+
publicMetadata: payload,
163+
});
164+
165+
if (profileInfo.success === false) {
166+
logger.error("Could not parse profile info", profileInfo.error);
167+
throw new Error("Could not parse profile info", profileInfo.error);
168+
}
169+
170+
logger.info(`Updating profile Info for user ID: ${sub}`);
157171

158-
return updateUserProfileInfo(DB, profileInfo.data, logger);
172+
return updateUserProfileInfo(DB, profileInfo.data, logger);
173+
}
159174
};
160175

161176
export const logPossibleUserIdFromJWT = (

src/authn/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export type TokenPayload = {
77
sub: string;
88
email: string;
99
phone: string;
10+
audience?: string;
1011
app_metadata: { provider: string; providers: string[] };
1112
user_metadata: {
1213
avatar_url: string;

src/schema/user/mutations.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { eq } from "drizzle-orm";
22
import { GraphQLError } from "graphql";
33

4-
import { createAuthToken } from "~/authn";
4+
import { createMinimalAuthToken } from "~/authn";
55
import { builder } from "~/builder";
66
import {
77
PronounsEnum,
@@ -199,7 +199,7 @@ builder.mutationField("retoolToken", (t) =>
199199

200200
const selectedUser = selectUsersSchema.parse(user);
201201

202-
const token = await createAuthToken(
202+
const token = await createMinimalAuthToken(
203203
selectedUser,
204204
ctx.SUPABASE_JWT_ENCODER,
205205
);

src/schema/user/tests/token.test.ts

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { faker } from "@faker-js/faker";
2-
import { decode, verify } from "@tsndr/cloudflare-worker-jwt";
2+
import { verify } from "jsonwebtoken";
33
import { it, describe, assert } from "vitest";
44

5-
import { USER } from "~/datasources/db/users";
65
import { executeGraphqlOperation, insertUser } from "~/tests/fixtures";
76

87
import {
@@ -49,26 +48,25 @@ describe("User", () => {
4948
throw new Error("Token not found");
5049
}
5150

52-
const verified = await verify(token, encoder);
51+
const decodedToken = verify(token, encoder, {
52+
complete: true,
53+
}) as unknown as {
54+
payload: {
55+
audience: string;
56+
user_metadata: {
57+
sub: string;
58+
};
59+
};
60+
};
5361

54-
assert.exists(verified);
55-
56-
assert.equal(verified, true);
57-
58-
const decodedToken = decode<{
59-
user_metadata: USER;
60-
}>(token);
61-
62-
const userTokenData = decodedToken?.payload?.user_metadata;
63-
64-
assert.equal(userTokenData?.email, user1.email);
62+
assert.exists(token);
6563

66-
assert.equal(userTokenData?.isSuperAdmin, true);
64+
const { user_metadata: userTokenData, audience } = decodedToken.payload;
6765

68-
assert.equal(userTokenData?.isRetoolEnabled, true);
66+
assert.equal(userTokenData?.sub, user1.id);
6967

70-
assert.equal(userTokenData?.isEmailVerified, true);
68+
assert.equal(Object.keys(userTokenData).length, 1);
7169

72-
assert.equal(userTokenData?.id, user1.id);
70+
assert.equal(audience, "retool");
7371
});
7472
});

0 commit comments

Comments
 (0)