Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 18, 2026

Implements server-side Facebook OAuth and webhook integration for multi-tenant commerce platform, enabling stores to connect Facebook Business accounts, pages, and product catalogs.

Core Implementation

Helper Library (src/lib/facebook.ts)

  • OAuth state management with HMAC signatures and timing-safe comparison
  • Two-step token exchange: authorization code → short-lived → long-lived (60 days)
  • Graph API client for asset discovery (businesses, pages, catalogs)
  • AES-256-GCM encryption with random salts per operation
  • Webhook signature verification (X-Hub-Signature-256)

API Routes

  • POST /api/auth/facebook/start?tenant={storeId} - Initiates OAuth with signed state
  • GET /api/auth/facebook/callback - Handles authorization, exchanges tokens, discovers assets, stores encrypted connection
  • GET|POST /api/facebook/webhook - Webhook verification challenge and event delivery

Database Model

model FacebookConnection {
  id                  String   @id @default(cuid())
  storeId             String
  facebookBusinessId  String
  facebookPageId      String?
  facebookCatalogId   String?
  accessToken         String   // Encrypted: iv:salt:authTag:data
  tokenExpiresAt      DateTime?
  isActive            Boolean  @default(true)
  
  @@unique([storeId, facebookBusinessId])
}

Security

  • Multi-tenant isolation: All queries filter by storeId, state includes signed tenant ID
  • CSRF protection: HMAC-signed state with 10-minute expiration
  • Encryption: AES-256-GCM with random salts, throws on missing ENCRYPTION_KEY (no plain text fallback)
  • Webhook verification: Timing-safe signature comparison
  • Business ID validation: Enforces foreign key integrity before storage

Usage

// Initiate OAuth
window.location.href = `/api/auth/facebook/start?tenant=${storeId}`;

// Retrieve connection
import { getFacebookConnection } from "@/lib/facebook";
const connection = await getFacebookConnection(storeId);
const { accessToken } = connection; // Auto-decrypted

Environment Variables

FACEBOOK_APP_ID="..."
FACEBOOK_APP_SECRET="..."
FACEBOOK_WEBHOOK_VERIFY_TOKEN="..."
ENCRYPTION_KEY="..."  # openssl rand -hex 32

Documentation

  • docs/FACEBOOK_INTEGRATION.md - Complete setup guide, webhook configuration, troubleshooting
  • README.md - Integration overview and quick start
  • .env.example - Required variables with generation instructions
Original prompt

Summary

The Meta (Facebook) integration included in PR #155 is failing in production. This pull request will add a robust, multi-tenant-friendly implementation of "Facebook Login for Business" + Commerce Platform integration in the StormCom UI repository so tenants can connect their Facebook Business and Commerce assets. The changes include OAuth start/callback routes, webhook endpoint with signature verification, a helper library for Graph API token exchange & asset discovery, Prisma schema to persist tenant connections, and .env example updates. The code is implemented to integrate with the repository's existing Prisma client at src/lib/prisma.ts and Next.js App Router conventions (route handlers). It validates state, exchanges short-lived tokens for long-lived tokens, persists encrypted tokens (placeholder encryption function), and verifies webhook signatures. The PR is targeted at the main branch.

Goals

  • Provide a working server-side integration for Facebook Login for Business and Commerce Platform.
  • Make the implementation multi-tenant aware (tenantId passed in OAuth state and persisted per tenant).
  • Add webhook verification and sample handlers for commerce events.
  • Add Prisma model to persist connections and a migration note.
  • Add required environment variables to .env.example and README instructions for App Review and webhooks.

What I will change (files to add / modify)

  1. Add src/lib/facebook.ts — helper library implementing:

    • encodeState / verifyState (HMAC-signed state blob)
    • exchangeCodeForToken, exchangeForLongLivedToken
    • getBusinessAssets (basic Graph API discovery)
    • verifyFacebookWebhookSignature
    • simple encrypt/decrypt placeholders (uses Node crypto with repository-level ENCRYPTION_KEY)
    • interacts with prisma client for tenant lookup when necessary
  2. Add app/router route handlers (Next.js 16 App Router):

    • src/app/api/auth/facebook/start/route.ts (redirects merchant to FB OAuth)
    • src/app/api/auth/facebook/callback/route.ts (handles code->token, asset discovery, store connection)
    • src/app/api/facebook/webhook/route.ts (handles verify challenge + POST events, verifies signature)

    These routes require the tenantId query param when starting (e.g. /api/auth/facebook/start?tenant=) and will use signed state to persist tenant context.

  3. Modify prisma/schema.prisma — add a FacebookConnection model:

    • id, tenantId -> relation to existing Tenant model (tenant model assumed present), fbAppId, accessTokenEncrypted, expiresAt, assets json, rawResponse json, createdAt, updatedAt
    • Note in PR that developer must run prisma migrate after merging
  4. Update .env.example and README.md (or docs/CONTRIBUTING.md) to include:

    • FB_APP_ID
    • FB_APP_SECRET
    • FB_WEBHOOK_VERIFY_TOKEN
    • FB_STATE_SECRET
    • ENCRYPTION_KEY (for token encryption at rest)
  5. Add a short note in .github/copilot-instructions.md about the new integration and environment variable requirements.

Implementation details & assumptions

  • This PR assumes the repo already exposes a Prisma client at src/lib/prisma.ts (per repository overview). The code uses import prisma from '@/lib/prisma'.
  • For encryption the PR includes a simple AES-256-GCM helper using ENCRYPTION_KEY from env. This is a placeholder: in production you should use a proper KMS or secret manager. The PR protects tokens at rest but still requires secure secret storage.
  • The OAuth redirect URI used is ${process.env.NEXT_PUBLIC_APP_ORIGIN || process.env.NEXTAUTH_URL}/api/auth/facebook/callback — ensure that value is registered in Facebook App settings.
  • The route handlers are minimal and intentionally synchronous (no background queue). In production you may want to enqueue heavier Graph queries and use a background worker to fetch catalogs/orders.
  • The PR adds Prisma schema changes — after merge, run: npm run prisma:generate && npm run prisma:migrate:dev with env vars sourced.

Security / App Review notes

  • New environment variables must be added to deployment: FB_APP_ID, FB_APP_SECRET, FB_WEBHOOK_VERIFY_TOKEN, FB_STATE_SECRET, ENCRYPTION_KEY.
  • App Review will be required for business_management, commerce and catalog permissions. The PR includes a README checklist to prepare screenshots and screencast.

Files to be added / modified in the PR (short list)

  • Add: src/lib/facebook.ts
  • Add: src/app/api/auth/facebook/start/route.ts
  • Add: src/app/api/auth/facebook/callback/route.ts
  • Add: src/app/api/facebook/webhook/route.ts
  • Modify: prisma/schema.prisma (add model FacebookConnection)
  • Modify: .env.example (add FB_ and ENCRYPTION variables)
  • Modify: .github/copilot-instructions.md (note integration added)
  • Modify: README.md (integration instructions & App Review checklist)

Additional notes for reviewers

  • After merge, follow the repository's Build & Validation Workflow: install deps, create .env.local with required FB variables and run prisma generate/migrate, type-check, lint, and build.
  • Tes...

This pull request was created from Copilot chat.


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@vercel
Copy link

vercel bot commented Jan 18, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
stormcomui Ready Ready Preview, Comment Jan 18, 2026 9:48pm

- Add FacebookConnection Prisma model with encrypted token storage
- Implement src/lib/facebook.ts helper library with:
  - OAuth state encoding/verification (HMAC-signed)
  - Token exchange (short-lived to long-lived)
  - Graph API asset discovery
  - Webhook signature verification
  - AES-256-GCM token encryption/decryption
- Add OAuth routes:
  - /api/auth/facebook/start (initiate OAuth flow)
  - /api/auth/facebook/callback (handle authorization)
- Add webhook endpoint:
  - /api/facebook/webhook (GET verification, POST events)
- Update .env.example with required Facebook variables
- Add comprehensive Facebook integration docs to README
- Create database migration for FacebookConnection model
- All type checks and builds pass successfully

Co-authored-by: syed-reza98 <71028588+syed-reza98@users.noreply.github.com>

// 2. Get and verify state
const state = searchParams.get("state");
if (!state) {

Check failure

Code scanning / CodeQL

User-controlled bypass of security check High

This condition guards a sensitive
action
, but a
user-provided value
controls it.
const searchParams = request.nextUrl.searchParams;
const tenantParam = searchParams.get("tenant");

if (!tenantParam) {

Check failure

Code scanning / CodeQL

User-controlled bypass of security check High

This condition guards a sensitive
action
, but a
user-provided value
controls it.
- Add docs/FACEBOOK_INTEGRATION.md with complete setup guide
- Document OAuth flow, webhook configuration, and security features
- Include testing procedures and troubleshooting guide
- Provide multi-tenant isolation patterns and best practices
- Add App Review requirements and maintenance procedures

Co-authored-by: syed-reza98 <71028588+syed-reza98@users.noreply.github.com>
- Use timing-safe comparison for OAuth state signatures
- Generate random salt per encryption operation (instead of static salt)
- Store salt with encrypted data (format: iv:salt:authTag:encrypted)
- Throw errors instead of fallback to plain text when ENCRYPTION_KEY missing
- Validate Facebook Business ID exists before storing connection
- All type checks pass successfully

Co-authored-by: syed-reza98 <71028588+syed-reza98@users.noreply.github.com>
],
});

console.log("[facebook/callback] Connection stored successfully for tenant:", tenantId);

Check warning

Code scanning / CodeQL

Log injection Medium

Log entry depends on a
user-provided value
.

Copilot Autofix

AI 4 days ago

In general, to fix log injection you should sanitize any user-controlled value before logging, typically by stripping \r and \n characters (and optionally other control characters), and/or clearly delimiting user input. In this case, the only problematic use is the logging of tenantId, not its use as an identifier for DB operations, so we must avoid changing the value used in business logic and only sanitize what is passed to console.log.

The best targeted fix here is:

  • Introduce a local, sanitized variable (e.g., safeTenantIdForLog) that is derived from tenantId using String(tenantId).replace(/[\r\n]/g, "").
  • Use this sanitized value exclusively in the log statement on line 132.
  • Keep tenantId itself unchanged for all other uses (e.g., storeFacebookConnection).

This requires only a minimal change in src/app/api/auth/facebook/callback/route.ts, near the existing log call, and no new imports or helper functions are strictly needed.

Suggested changeset 1
src/app/api/auth/facebook/callback/route.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/app/api/auth/facebook/callback/route.ts b/src/app/api/auth/facebook/callback/route.ts
--- a/src/app/api/auth/facebook/callback/route.ts
+++ b/src/app/api/auth/facebook/callback/route.ts
@@ -129,7 +129,11 @@
       ],
     });
 
-    console.log("[facebook/callback] Connection stored successfully for tenant:", tenantId);
+    const safeTenantIdForLog = String(tenantId).replace(/[\r\n]/g, "");
+    console.log(
+      "[facebook/callback] Connection stored successfully for tenant:",
+      safeTenantIdForLog
+    );
 
     // 10. Redirect to success page
     const successUrl = new URL("/settings/integrations", request.nextUrl.origin);
EOF
@@ -129,7 +129,11 @@
],
});

console.log("[facebook/callback] Connection stored successfully for tenant:", tenantId);
const safeTenantIdForLog = String(tenantId).replace(/[\r\n]/g, "");
console.log(
"[facebook/callback] Connection stored successfully for tenant:",
safeTenantIdForLog
);

// 10. Redirect to success page
const successUrl = new URL("/settings/integrations", request.nextUrl.origin);
Copilot is powered by AI and may make mistakes. Always verify output.
Copilot AI changed the title [WIP] Add Facebook Login for Business integration in StormCom UI Add Facebook Login for Business + Commerce Platform integration with multi-tenant support Jan 18, 2026
Copilot AI requested a review from syed-reza98 January 18, 2026 21:36
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

// 1. Check for errors
const error = searchParams.get("error");
if (error) {

Check failure

Code scanning / CodeQL

User-controlled bypass of security check High

This condition guards a sensitive
action
, but a
user-provided value
controls it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

2 participants