Skip to content

fix(instagram,facebook): prevent broken page-grant channels and guide reconnection#1679

Open
giladresisi wants to merge 2 commits into
mainfrom
fix/facebook-instagram-page-grant
Open

fix(instagram,facebook): prevent broken page-grant channels and guide reconnection#1679
giladresisi wants to merge 2 commits into
mainfrom
fix/facebook-instagram-page-grant

Conversation

@giladresisi

@giladresisi giladresisi commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

Problem

Connecting an Instagram (Facebook Business) or Facebook Page channel while granting the wrong Facebook page silently stores a broken token — undefined___<user-token> for Instagram — and every publish then fails with Graph code 190 disguised as a misleading "missing permissions" error. There is no reconnect prompt and no way out short of support.

This is easy to hit for two reasons:

  1. The account picker also offered pages discovered via the Business Manager API that were never granted to the app in the OAuth dialog.
  2. Facebook's granular consent lets users grant an Instagram account without its linked page (they're independent asset lists).

Reproduction

Connect an Instagram (Facebook Business) channel and, in the Facebook dialog, grant a page that is not the one linked to the Instagram account. Postiz still offered the IG account in the picker; selecting it connected a channel whose token is undefined___…; every post then failed with "The account is missing some permissions…" — with no indication the channel needed reconnecting.

Fix

Prevention (silent, no new UI in the picker):

  • pages() (Instagram) now verifies every candidate page with the same page-node access_token call the save path uses, and only offers pages the app can actually publish through. (The Business-Manager edge can report a token the page node refuses, so the node call is the only check that keeps the picker and the save consistent.) The Facebook provider filters on its edge tokens, which its own save path reads.
  • auth_type=rerequest on the OAuth URL, so declined permissions/assets are re-prompted and a bad grant can actually be repaired on reconnect.

Recovery for already-broken channels:

  • Graph code 190 is now classified as refresh-token: the next post from a broken channel marks it refreshNeeded and emails the user; subsequent posts keep failing with a reconnect email until fixed.
  • The refresh-error email now explains that no posts can be published until the channel is reconnected, and that re-adding the same account replaces the broken connection and keeps all scheduled posts (no deletion needed).
  • updateIntegration() clears refreshNeeded when a channel is re-added over an existing one, so "Add Channel with the same account" fully heals a flagged channel.

Guidance UI (Instagram/Facebook only):

  • A "Before you continue" popup before every redirect to Facebook — on Add Channel, on reconnect, and on half-connected channels — explaining to select the Facebook page the Instagram account is connected to, and that changing a previous selection goes through Facebook's "Edit settings".
  • Half-connected IG/FB channels restart the OAuth flow when clicked (the in-app picker is a dead end when the grant is wrong); other providers keep the in-app continue flow.
  • "Back to Postiz" button on the full-page empty selection state (previously a dead end with no navigation).
  • Connect errors are surfaced as a toast in the page-selection modal instead of silently closing it.
  • New i18n keys translated across all 16 locales.

Shared-grant hazard (second commit)

Facebook page grants are per Meta app and per user, not per channel: re-authorizing any Instagram/Facebook channel with a narrowed page selection revokes the unchecked pages' grants and invalidates their page tokens — silently breaking every other Postiz channel that depends on them. Verified experimentally in both directions:

  • Reconnecting Instagram while granting only the Instagram-linked page → the existing Facebook page channel started failing with an unmapped code-190 error (surfaced as "Unknown Error").
  • Reconnecting the Facebook page while granting only that page → the Instagram channel started failing with the code-190 permissions error.
  • Re-granting both pages fixed both channels, with no changes to the channels themselves.

The code-190 → refresh-token classification in this PR already routes such casualties into the reconnect flow at their next post. The second commit additionally adds a line to the "Before you continue" popup warning users that their page selection applies to all their Meta channels and that unchecking pages used by other channels will break them (translated across all locales).

Post-fix validation (manual, real Facebook/Instagram accounts)

  • With the wrong page granted: the IG account is no longer offered — empty selection state with a working "Back to Postiz" button.
  • Posting from a previously-broken channel: post fails once, the channel is flagged (badge + "Reconnect channel" menu), and a single clear email is sent.
  • Reconnecting via the popup → Facebook screens → correct page: the channel heals in place, refreshNeeded clears, and all scheduled posts survive.

🤖 Generated with Claude Code

… reconnection

Selecting a Facebook page the app was never granted stored a broken
token ("undefined___..." for Instagram), and every publish then failed
with Graph code 190 dressed up as a misleading "missing permissions"
error, with no reconnect prompt and no way out. This was easy to hit:
the picker also offered pages discovered via the Business Manager API
that were never granted in the OAuth dialog, and Facebook's granular
consent lets users grant an Instagram account without its linked page.

Reproduction: connect an Instagram (Facebook Business) channel and
grant a Facebook page that is not the one linked to the Instagram
account. The account could still be selected in Postiz, the channel
connected, and every post then failed with the permissions error.

- pages(): verify every candidate page with the same page-node
  access_token call used when the selection is saved, and only offer
  pages the app can actually publish through (the Business-Manager page
  list can report tokens the page node refuses, so the node call is the
  only reliable check); Facebook filters on its edge tokens, which its
  own save path uses
- handleErrors(): classify Graph code 190 as refresh-token, so a post
  from an already-broken channel marks it refreshNeeded and emails the
  user; subsequent posts keep failing with a reconnect email until fixed
- reword the refresh-error email: no posts can be published until the
  channel is reconnected; re-adding the same account replaces the broken
  connection and keeps scheduled posts (no deletion needed)
- updateIntegration(): clear refreshNeeded when a channel is re-added
  over an existing one, so re-adding fully heals a flagged channel
- generateAuthUrl(): auth_type=rerequest so declined permissions/assets
  are re-prompted on reconnect and a bad grant can be repaired
- frontend: "Before you continue" guidance popup on the Add Channel,
  reconnect and half-connected paths for Instagram/Facebook (select the
  page the Instagram account is connected to; use Facebook's "Edit
  settings" to change a previous selection); half-connected IG/FB
  channels restart the OAuth flow instead of opening the in-app picker;
  "Back to Postiz" button on the full-page empty selection state;
  connect errors surfaced in the page-selection modal; new i18n keys
  translated across all locales

Validated end to end: with the wrong page granted, the Instagram
account is no longer offered (empty selection + Back to Postiz);
posting from a previously-broken channel triggers the refresh-needed
flow (email, channel badge, Reconnect channel menu); reconnecting
through the guidance popup and the Facebook screens with the correct
page heals the channel in place with all scheduled posts intact.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@postiz-contribution postiz-contribution Bot added the contribution:approved Approved contributor label Jul 6, 2026
@postiz-contribution

Copy link
Copy Markdown

Contribution-checker quality warning
Heuristic score: 0/100 (low). This is a non-blocking warning surfaced by the project's quality settings.

Heuristics that flagged:

  • Wall-of-text PR body: 3680 chars (>2500)
  • Excessive inline code references: 10 inline refs (>3)
  • PR doesn't use the repo's PR template: 5 required checkbox items missing (max 1, match ≥80%)
  • Body adds too many extra headers beyond the template: 4 extra headers (>1)
  • AI watermark phrase: Matched: "Generated with Claude Code"
  • Commit message too long: Longest: 2866 chars
  • Excessive added comments: 30 added comment lines (ratio 0.09)
  • Missing final newline

If this is a genuine contribution, please add detail to your PR description and tighten the diff scope before reviewers look at it.

@postiz-agent

postiz-agent Bot commented Jul 6, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

Comment on lines +565 to +573
const { access_token } = await (
await fetch(
`https://graph.facebook.com/v20.0/${p.instagram_business_account.id}?fields=name,profile_picture_url&access_token=${accessToken}`
`https://graph.facebook.com/v20.0/${p.id}?fields=access_token&access_token=${accessToken}`
)
).json()),
id: p.instagram_business_account.id,
};
})
);
).json();

if (!access_token) {
return null;
}

This comment was marked as outdated.

…Meta channels

Facebook page grants are per Meta app and per user, not per channel:
re-authorizing any Instagram/Facebook channel with a narrowed page
selection revokes the unchecked pages' grants and invalidates their
page tokens — silently breaking every other Postiz channel that uses
them (verified experimentally in both directions: granting only the
Instagram-linked page broke the Facebook page channel with an unmapped
code-190 "Unknown Error", and granting only the Facebook page broke the
Instagram channel with the code-190 permissions error; re-granting both
pages fixed both channels).

Add a line to the "Before you continue" guidance popup telling users
their page selection applies to all their Facebook/Instagram channels
and that unchecking pages used by other channels will break them.
Translated across all locales.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@postiz-contribution

Copy link
Copy Markdown

Contribution-checker quality warning
Heuristic score: 0/100 (low). This is a non-blocking warning surfaced by the project's quality settings.

Heuristics that flagged:

  • Wall-of-text PR body: 3680 chars (>2500)
  • Excessive inline code references: 10 inline refs (>3)
  • PR doesn't use the repo's PR template: 5 required checkbox items missing (max 1, match ≥80%)
  • Body adds too many extra headers beyond the template: 4 extra headers (>1)
  • AI watermark phrase: Matched: "Generated with Claude Code"
  • Commit message too long: Longest: 2866 chars
  • Excessive added comments: 30 added comment lines (ratio 0.08)
  • Missing final newline

If this is a genuine contribution, please add detail to your PR description and tighten the diff scope before reviewers look at it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contribution:approved Approved contributor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant