github-actions[bot]
deploy from github actions 2026-06-18
c8ae75d
Raw
History Blame Contribute Delete
3.36 kB
import { NextResponse } from "next/server";
import { createProviderConnection } from "@/models";
import { extractCodexAccountInfo } from "@/lib/oauth/providers";
/**
* POST /api/oauth/codex/import-token
* Import a ChatGPT access token (created from chatgpt.com settings)
* as a provider connection, bypassing OAuth refresh flow.
*
* Body: { accessToken: string, name?: string }
*/
export async function POST(request) {
try {
const { accessToken, name } = await request.json();
if (!accessToken || typeof accessToken !== "string") {
return NextResponse.json(
{ error: "Access token is required" },
{ status: 400 }
);
}
const token = accessToken.trim();
// Extract account info from the JWT (email, workspace, plan)
let email = null;
let providerSpecificData = { authMethod: "access_token" };
// Try decoding as JWT to extract email + workspace
try {
const parts = token.split(".");
if (parts.length === 3) {
const base64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
const missingPadding = (4 - (base64.length % 4)) % 4;
const padded = base64 + "=".repeat(missingPadding);
const payload = JSON.parse(Buffer.from(padded, "base64").toString("utf8"));
// Extract from OpenAI JWT structure
const auth = payload["https://api.openai.com/auth"] || {};
const profile = payload["https://api.openai.com/profile"] || {};
email = profile.email || payload.email || payload.preferred_username || null;
if (auth.chatgpt_account_id) {
providerSpecificData.chatgptAccountId = auth.chatgpt_account_id;
}
if (auth.chatgpt_plan_type) {
providerSpecificData.chatgptPlanType = auth.chatgpt_plan_type;
}
// Store expiry info from JWT if available
if (payload.exp) {
providerSpecificData.jwtExp = payload.exp;
}
}
} catch {
// Not a JWT or malformed — still allow import as raw token
}
// Also try extractCodexAccountInfo via id_token-style extraction
// (the access token itself may contain the same claims)
if (!email) {
const info = extractCodexAccountInfo(token);
if (info.email) email = info.email;
if (info.chatgptAccountId) providerSpecificData.chatgptAccountId = info.chatgptAccountId;
if (info.chatgptPlanType) providerSpecificData.chatgptPlanType = info.chatgptPlanType;
}
const connectionName = name || email || "ChatGPT Access Token";
// Save to database as access_token authType (no refresh token)
const connection = await createProviderConnection({
provider: "codex",
authType: "access_token",
accessToken: token,
name: connectionName,
email: email,
providerSpecificData,
testStatus: "active",
});
return NextResponse.json({
success: true,
connection: {
id: connection.id,
provider: connection.provider,
email: connection.email,
name: connection.name,
workspace: providerSpecificData.chatgptAccountId || null,
plan: providerSpecificData.chatgptPlanType || null,
},
});
} catch (error) {
console.log("Codex access token import error:", error);
return NextResponse.json({ error: error.message }, { status: 500 });
}
}