Brand Studio docs
Engineering manualAPI route source

/api/auth/google

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

Methods: GET. Input signals: query parameters. Literal response-status signals: 503.

Source: hosted-app/app/api/auth/google/route.js. Imports: ../../../../lib/public-origin.mjs, ../../../../lib/supabase-rest.mjs, next/server, node:crypto. 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 { createHash, randomBytes } from "node:crypto";
import { supabaseConfigFromEnv } from "../../../../lib/supabase-rest.mjs";
import { publicOriginFor } from "../../../../lib/public-origin.mjs";

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

const safeNextPath = (value) => /^\/[^/\\]/.test(String(value || "")) ? String(value) : "/";
const PKCE_COOKIE = "brand-studio.oauth_pkce";

const base64Url = (value) => Buffer.from(value).toString("base64url");

export async function GET(request) {
  const { supabaseUrl } = supabaseConfigFromEnv(process.env);
  if (!supabaseUrl) {
    return NextResponse.json({ status: "refused", reason: "supabase_not_configured" }, { status: 503 });
  }
  const requestUrl = new URL(request.url);
  const callback = new URL("/auth/callback", publicOriginFor(request));
  callback.searchParams.set("next", safeNextPath(requestUrl.searchParams.get("next")));
  const verifier = base64Url(randomBytes(48));
  const challenge = base64Url(createHash("sha256").update(verifier).digest());
  const authorize = new URL(`${supabaseUrl}/auth/v1/authorize`);
  authorize.searchParams.set("provider", "google");
  authorize.searchParams.set("redirect_to", callback.toString());
  authorize.searchParams.set("code_challenge", challenge);
  authorize.searchParams.set("code_challenge_method", "s256");
  const response = NextResponse.redirect(authorize);
  response.cookies.set(PKCE_COOKIE, verifier, {
    httpOnly: true,
    secure: requestUrl.protocol === "https:",
    sameSite: "lax",
    path: "/api/auth/google/exchange",
    maxAge: 10 * 60
  });
  return response;
}