Brand Studio docs
Engineering manualAPI route source

/api/auth/google/exchange

Exact current route handler: hosted-app/app/api/auth/google/exchange/route.js

Methods: POST. Input signals: JSON body, query parameters. Literal response-status signals: 400, 401, 503.

Source: hosted-app/app/api/auth/google/exchange/route.js. Imports: ../../../../../lib/supabase-rest.mjs, next/server. Exact source is shown for inspection; its presence does not prove authentication, validation or live behavior.

All API routes · API and execution contract

import { NextResponse } from "next/server";
import { supabaseConfigFromEnv } from "../../../../../lib/supabase-rest.mjs";

export const runtime = "nodejs";
export const dynamic = "force-dynamic";

const PKCE_COOKIE = "brand-studio.oauth_pkce";
const validAuthCode = (value) => /^[0-9a-f-]{36}$/i.test(String(value || ""));

const clearVerifier = (response, request) => {
  response.headers.set("cache-control", "no-store");
  response.cookies.set(PKCE_COOKIE, "", {
    httpOnly: true,
    secure: new URL(request.url).protocol === "https:",
    sameSite: "lax",
    path: "/api/auth/google/exchange",
    maxAge: 0
  });
  return response;
};

export async function POST(request) {
  const { supabaseUrl, supabaseAnonKey } = supabaseConfigFromEnv(process.env);
  if (!supabaseUrl || !supabaseAnonKey) {
    return NextResponse.json({ status: "refused", reason: "supabase_not_configured" }, { status: 503 });
  }
  const verifier = request.cookies.get(PKCE_COOKIE)?.value || "";
  const body = await request.json().catch(() => ({}));
  const authCode = String(body.auth_code || "");
  if (!verifier || !validAuthCode(authCode)) {
    return clearVerifier(
      NextResponse.json({ status: "refused", reason: "invalid_or_unsolicited_oauth_callback" }, { status: 400 }),
      request
    );
  }

  const exchanged = await fetch(`${supabaseUrl}/auth/v1/token?grant_type=pkce`, {
    method: "POST",
    headers: { apikey: supabaseAnonKey, "content-type": "application/json" },
    body: JSON.stringify({ auth_code: authCode, code_verifier: verifier })
  });
  const payload = await exchanged.json().catch(() => ({}));
  if (!exchanged.ok || !payload?.access_token) {
    return clearVerifier(
      NextResponse.json({ status: "refused", reason: "oauth_code_exchange_failed" }, { status: 401 }),
      request
    );
  }
  return clearVerifier(NextResponse.json({
    status: "passed",
    access_token: payload.access_token,
    refresh_token: payload.refresh_token ?? null,
    expires_at: payload.expires_at ?? null,
    user: { id: payload.user?.id || null, email: payload.user?.email || null }
  }), request);
}