diff --git a/.env b/.env index 8e3473b7e0..8e115d0fe5 100644 --- a/.env +++ b/.env @@ -1,5 +1,7 @@ BILLING_PLAN_ENV=dev +VSCODE_EXTENSION_BASE_URL=vscode://RooVeterinaryInc.roo-cline + # Clerk # https://clerk.com/docs/deployments/clerk-environment-variables#sign-in-and-sign-up-redirects NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in diff --git a/src/app/(authenticated)/extension/sign-in/route.ts b/src/app/(authenticated)/extension/sign-in/route.ts new file mode 100644 index 0000000000..bf5a2565f1 --- /dev/null +++ b/src/app/(authenticated)/extension/sign-in/route.ts @@ -0,0 +1,25 @@ +import { getSignInToken } from '@/lib/server/clerk'; +import { Env } from '@/lib/server/env'; +import { auth } from '@clerk/nextjs/server'; +import { redirect } from 'next/navigation'; +import { type NextRequest } from 'next/server'; + +export async function GET(request: NextRequest) { + const authObj = await auth(); + const userId = authObj.userId; + const state = request.nextUrl.searchParams.get('state') || ''; + + if (!userId) { + throw new Error('not logged in?'); + } + + const signInToken = await getSignInToken(authObj.userId); + if (!signInToken) { + throw new Error("couldn't sign in"); + } + + const url = new URL(`${Env.VSCODE_EXTENSION_BASE_URL}/auth/clerk/callback`); + url.searchParams.append('state', state); + url.searchParams.append('code', signInToken); + redirect(url.href); +} diff --git a/src/lib/server/clerk.ts b/src/lib/server/clerk.ts new file mode 100644 index 0000000000..fe2b3049a7 --- /dev/null +++ b/src/lib/server/clerk.ts @@ -0,0 +1,39 @@ +import { Env } from './env'; +import { logger } from './logger'; + +export async function getSignInToken( + userId: string, +): Promise { + const response = await fetch('https://api.clerk.com/v1/sign_in_tokens', { + method: 'POST', + headers: { + Authorization: `Bearer ${Env.CLERK_SECRET_KEY}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + user_id: userId, + // Default expiration is 30 days (2592000 seconds) + }), + }); + + if (!response.ok) { + const errorData = await response.json(); + logger.error({ + event: 'sign_in_token_creation_failed', + error: errorData, + userId, + }); + + return undefined; // probably just throw + } + + const data = await response.json(); + + logger.info({ + event: 'sign_in_token_created', + userId, + data, // TODO: none of this, probably + }); + + return data.token; +} diff --git a/src/lib/server/env.ts b/src/lib/server/env.ts index 8749ac6308..6912a58da3 100644 --- a/src/lib/server/env.ts +++ b/src/lib/server/env.ts @@ -9,6 +9,7 @@ export const Env = createEnv({ STRIPE_SECRET_KEY: z.string().min(1), STRIPE_WEBHOOK_SECRET: z.string().min(1), BILLING_PLAN_ENV: z.enum(['dev', 'test', 'prod']), + VSCODE_EXTENSION_BASE_URL: z.string().min(1), }, client: { NEXT_PUBLIC_APP_URL: z.string().optional(), @@ -32,6 +33,7 @@ export const Env = createEnv({ STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET: process.env.STRIPE_WEBHOOK_SECRET, BILLING_PLAN_ENV: process.env.BILLING_PLAN_ENV, + VSCODE_EXTENSION_BASE_URL: process.env.VSCODE_EXTENSION_BASE_URL, NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL, NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,