Skip to main content
Glama
org-utils.ts6.26 kB
import { NextResponse } from "next/server"; import { getOrgIdForClientId, getOrgIdForRefreshTokenSliding } from "./redis"; import { REFRESH_TOKEN_ORG_TTL_SECONDS, SHARED_CLIENT_IDS } from "./const"; function createErrorResponse( error: string, errorDescription: string, status: number = 400, ) { return NextResponse.json( { error, error_description: errorDescription, }, { status, headers: { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "POST, OPTIONS", "Access-Control-Allow-Headers": "Content-Type, Authorization", }, }, ); } /** * Resolves organization ID based on client type and grant type * @param grantType - OAuth grant type ("authorization_code" or "refresh_token") * @param clientId - The OAuth client ID * @param directOrgId - The org_id from OAuth state parameter (shared clients only) * @param refreshToken - The refresh token from OAuth state parameter (shared clients only) * @returns { orgId: string | null, error?: NextResponse } - org_id or error response */ export async function resolveOrgId({ grantType, clientId, directOrgId, refreshToken, }: { grantType: string; clientId: string; directOrgId?: string; refreshToken?: string; }): Promise<{ orgId: string | null; error?: NextResponse }> { const isSharedClient = SHARED_CLIENT_IDS.includes(clientId); const clientIdMasked = clientId ? clientId.slice(0, 4) + "..." : ""; console.debug("[org-utils] resolveOrgId", { grantType, isSharedClient, hasDirectOrgId: Boolean(directOrgId), hasRefreshToken: Boolean(refreshToken), }); if (isSharedClient) { // Shared clients (CLI): Use org_id from OAuth state parameter if (directOrgId) { console.debug("[org-utils] shared client: using direct org_id from body"); return { orgId: directOrgId }; } else if (grantType === "refresh_token") { if (!refreshToken) { console.warn( "[org-utils] shared client: missing refresh_token in request body", ); return { orgId: null, error: createErrorResponse( "invalid_request", "Missing required parameter: refresh_token", ), }; } try { const orgIdFromRefresh = await getOrgIdForRefreshTokenSliding({ refreshToken, ttlSeconds: REFRESH_TOKEN_ORG_TTL_SECONDS, }); if (orgIdFromRefresh) { console.debug( "[org-utils] shared client: resolved org via refresh_token mapping", ); return { orgId: orgIdFromRefresh }; } } catch (e) { console.error( "[org-utils] shared client: error reading refresh_token mapping", { error: e }, ); return { orgId: null, error: createErrorResponse( "server_error", "Failed to retrieve organization context for refresh token.", ), }; } console.warn( "[org-utils] shared client: missing org_id and no refresh mapping", ); return { orgId: null, error: createErrorResponse( "invalid_grant", "Missing organization context in refresh request. Please re-authorize.", ), }; } else { console.warn("[org-utils] shared client: missing org_id in request body"); return { orgId: null, error: createErrorResponse( "invalid_grant", "Missing organization context in OAuth request body. Please re-authorize.", ), }; } } // Ephemeral clients (MCP) if (grantType === "authorization_code") { // Use client_id mapping just to bridge the initial hop try { const orgId = await getOrgIdForClientId({ clientId }); if (orgId) { console.debug( "[org-utils] ephemeral client: resolved org via client_id mapping", ); return { orgId }; } else { console.warn( "[org-utils] ephemeral client: missing org context for authorization_code", { clientIdMasked }, ); return { orgId: null, error: createErrorResponse( "invalid_grant", "Organization context expired for client: " + clientId + ". Please re-authorize to select your organization.", ), }; } } catch (error) { console.error( "[org-utils] ephemeral client: error reading client_id mapping", { error, clientIdMasked }, ); return { orgId: null, error: createErrorResponse( "server_error", "Failed to retrieve organization context for client: " + clientId, ), }; } } else if (grantType === "refresh_token") { // Use refresh_token mapping for ongoing refresh flows if (!refreshToken) { console.debug("[org-utils] refresh flow without refresh_token param"); return { orgId: null, error: createErrorResponse( "invalid_request", "Missing required parameter: refresh_token", ), }; } try { const orgId = await getOrgIdForRefreshTokenSliding({ refreshToken, ttlSeconds: REFRESH_TOKEN_ORG_TTL_SECONDS, }); if (orgId) { console.debug("[org-utils] resolved org via refresh_token mapping"); return { orgId }; } console.warn( "[org-utils] no org mapping for refresh_token (expired or unknown)", ); return { orgId: null, error: createErrorResponse( "invalid_grant", "Organization context expired for this refresh token. Please re-authorize.", ), }; } catch (error) { console.error("[org-utils] error reading refresh_token mapping", { error, }); return { orgId: null, error: createErrorResponse( "server_error", "Failed to retrieve organization context for refresh token.", ), }; } } return { orgId: null, error: createErrorResponse( "unsupported_grant_type", `Grant type '${grantType}' is not supported`, ), }; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/onkernel/kernel-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server