Skip to content

Commit 434c7ca

Browse files
committed
Enabling retool generation
1 parent a6992c8 commit 434c7ca

7 files changed

Lines changed: 105 additions & 1 deletion

File tree

.dev.vars.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ STRIPE_KEY=""
99
MERCADOPAGO_KEY=""
1010
RESEND_API_KEY=""
1111
SUPABASE_JWT_DECODER""
12+
SUPABASE_JWT_ENCODER=""
1213

1314
ENFORCED_JWT_TOKEN=""

src/authn/index.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { decode, verify } from "@tsndr/cloudflare-worker-jwt";
1+
import { sign, decode, verify } from "@tsndr/cloudflare-worker-jwt";
22
import { Logger } from "pino";
33

44
import { TokenPayload } from "~/authn/types";
@@ -25,6 +25,20 @@ const getAuthToken = (request: Request) => {
2525
return null;
2626
};
2727

28+
export const createAuthToken = async (user: USER, SECRET: string) => {
29+
const payload = {
30+
audience: "retool-autenticated",
31+
id: user.id,
32+
email: user.email,
33+
user_metadata: user,
34+
exp: Date.now() + 60 * 60 * 24 * 1000 /* 24 hours */,
35+
};
36+
37+
const token = await sign(payload, SECRET);
38+
39+
return token;
40+
};
41+
2842
// Obtener el token de autorización de la solicitud, ya sea del encabezado de
2943
// autorización o de la cookie "community-os-access-token"
3044
const getImpersonatedUserFromRequest = async (

src/context.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ export const createGraphqlContext = async ({
2525
SANITY_API_VERSION,
2626
SANITY_SECRET_TOKEN,
2727
SUPABASE_JWT_DECODER,
28+
SUPABASE_JWT_ENCODER,
29+
RETOOL_AUTHENTICATION_TOKEN,
2830
STRIPE_KEY,
2931
HYPERDRIVE,
3032
MERCADOPAGO_KEY,
@@ -38,6 +40,14 @@ export const createGraphqlContext = async ({
3840
throw new Error("Missing MAIL_QUEUE");
3941
}
4042

43+
if (!RETOOL_AUTHENTICATION_TOKEN) {
44+
throw new Error("Missing RETOOL_AUTHENTICATION_TOKEN");
45+
}
46+
47+
if (!SUPABASE_JWT_ENCODER) {
48+
throw new Error("Missing SUPABASE_JWT_ENCODER");
49+
}
50+
4151
if (!RPC_SERVICE_EMAIL) {
4252
throw new Error("RPC_SERVICE_EMAIL is not defined");
4353
}
@@ -134,5 +144,7 @@ export const createGraphqlContext = async ({
134144
GET_MERCADOPAGO_CLIENT,
135145
logger,
136146
RPC_SERVICE_EMAIL,
147+
RETOOL_AUTHENTICATION_TOKEN,
148+
SUPABASE_JWT_ENCODER,
137149
} satisfies Context;
138150
};

src/schema/user/mutations.ts

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

4+
import { createAuthToken } from "~/authn";
45
import { builder } from "~/builder";
56
import {
67
PronounsEnum,
@@ -9,6 +10,7 @@ import {
910
usersSchema,
1011
usersToCommunitiesSchema,
1112
} from "~/datasources/db/schema";
13+
import { applicationError, ServiceErrors } from "~/errors";
1214
import { UserRef } from "~/schema/shared/refs";
1315
import { pronounsEnum } from "~/schema/user/types";
1416
import {
@@ -156,3 +158,65 @@ builder.mutationField("updateUserRoleInCommunity", (t) =>
156158
},
157159
}),
158160
);
161+
162+
const retoolToken = builder.inputType("retoolToken", {
163+
fields: (t) => ({
164+
userEmail: t.string({ required: true }),
165+
authToken: t.string({ required: true }),
166+
}),
167+
});
168+
169+
export const TokenRef = builder.objectRef<{
170+
token: string;
171+
}>("TokenRef");
172+
173+
builder.mutationField("retoolToken", (t) =>
174+
t.field({
175+
description: "Update a user role",
176+
type: TokenRef,
177+
deprecationReason: "Not enabled",
178+
nullable: false,
179+
args: {
180+
input: t.arg({ type: retoolToken, required: true }),
181+
},
182+
resolve: async (root, { input }, ctx) => {
183+
try {
184+
const { userEmail, authToken } = input;
185+
186+
if (authToken !== ctx.RETOOL_AUTHENTICATION_TOKEN) {
187+
throw new Error("Not authorized");
188+
}
189+
190+
const user = await ctx.DB.query.usersSchema.findFirst({
191+
where: (u, { eq, and }) =>
192+
and(
193+
eq(u.email, userEmail.trim().toLocaleLowerCase()),
194+
eq(u.isRetoolEnabled, true),
195+
eq(u.isSuperAdmin, true),
196+
),
197+
});
198+
199+
if (!user) {
200+
throw new Error("Not authorized");
201+
}
202+
203+
const selectedUser = selectUsersSchema.parse(user);
204+
205+
const token = await createAuthToken(
206+
selectedUser,
207+
ctx.SUPABASE_JWT_ENCODER,
208+
);
209+
210+
return {
211+
token,
212+
};
213+
} catch (e) {
214+
throw applicationError(
215+
"Not authorized",
216+
ServiceErrors.FORBIDDEN,
217+
ctx.logger,
218+
);
219+
}
220+
},
221+
}),
222+
);

src/schema/user/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
} from "~/datasources/db/schema";
99
import { CommunityRef, UserRef } from "~/schema/shared/refs";
1010
import { TeamRef } from "~/schema/teams/types";
11+
import { TokenRef } from "~/schema/user/mutations";
1112

1213
export const pronounsEnum = builder.enumType(PronounsEnum, {
1314
name: "PronounsEnum",
@@ -108,3 +109,10 @@ builder.objectType(UserRef, {
108109
export const SearchableUserTags = builder.enumType("SearchableUserTags", {
109110
values: Object.values(AllowedUserTags),
110111
});
112+
113+
builder.objectType(TokenRef, {
114+
description: "Representation of a token",
115+
fields: (t) => ({
116+
token: t.exposeString("token", { nullable: false }),
117+
}),
118+
});

src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ export type Context = {
2121
GOOGLE_PHOTOS_IMPORT_QUEUE: Queue;
2222
PURCHASE_CALLBACK_URL: string;
2323
RPC_SERVICE_EMAIL: Env["RPC_SERVICE_EMAIL"];
24+
RETOOL_AUTHENTICATION_TOKEN: string;
25+
SUPABASE_JWT_ENCODER: string;
2426
};
2527

2628
export type GraphqlContext = Context &

worker-configuration.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Generated by Wrangler on Fri Jul 07 2023 08:10:34 GMT-0700 (Pacific Daylight Time)
22
import type WorkerEntrypoint from "./workers/transactional_email_service";
3+
34
export interface Env {
45
GRAPHQL_BASE_ENDPOINT: "/";
56
NEON_URL: string | undefined;
@@ -18,6 +19,8 @@ export interface Env {
1819
HYPERDRIVE: Hyperdrive;
1920
PURCHASE_CALLBACK_URL: string;
2021
RPC_SERVICE_EMAIL: Service<WorkerEntrypoint>;
22+
RETOOL_AUTHENTICATION_TOKEN: string;
23+
SUPABASE_JWT_ENCODER: string;
2124
}
2225

2326
declare global {

0 commit comments

Comments
 (0)