fix(providers): stop requesting unapproved Fortnox scopes that broke every connect (#1549)
PR #1541 added archive and connectfile to the Fortnox DEFAULT_SCOPES for the voucher attachment import, but the registered Fortnox app does not have those scopes approved in the Fortnox Developer Portal. Fortnox rejects the authorize request with invalid_scope before login, which broke every Fortnox connect in production within minutes of the deploy (verified in Vercel runtime logs). Remove the two scopes from the connect request; the attachment import logic from #1541 stays fully intact and already degrades gracefully: a 403 becomes PROVIDER_DOCUMENT_SCOPES_REQUIRED with a reconnect follow-up card. Re-add the scopes once the portal registration has them approved. Also add charset=utf-8 to the OAuth callback HTML responses: without it browsers render the Swedish error text as Latin-1 mojibake. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
b9bf60234d
commit
98612fb0ac
@@ -3,7 +3,12 @@ import { describe, expect, it } from 'vitest';
|
||||
import { buildFortnoxAuthUrl } from '../oauth';
|
||||
|
||||
describe('Fortnox OAuth scopes', () => {
|
||||
it('requests archive and file-connection access by default', () => {
|
||||
// Pins the 2026-08-13 incident fix: the registered Fortnox app does not
|
||||
// have the archive/connectfile scopes approved, and requesting a scope the
|
||||
// app lacks makes Fortnox reject the authorize request with invalid_scope
|
||||
// before the user can even log in. Do not add them back here until the
|
||||
// Fortnox Developer Portal registration includes them.
|
||||
it('does not request archive or connectfile until the Fortnox app has them approved', () => {
|
||||
const url = new URL(
|
||||
buildFortnoxAuthUrl({
|
||||
clientId: 'client-id',
|
||||
@@ -14,7 +19,7 @@ describe('Fortnox OAuth scopes', () => {
|
||||
const scopes = new Set(url.searchParams.get('scope')?.split(' ') ?? []);
|
||||
|
||||
expect(scopes).toContain('bookkeeping');
|
||||
expect(scopes).toContain('archive');
|
||||
expect(scopes).toContain('connectfile');
|
||||
expect(scopes).not.toContain('archive');
|
||||
expect(scopes).not.toContain('connectfile');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,8 +13,14 @@ const DEFAULT_SCOPES = [
|
||||
'customer',
|
||||
'supplier',
|
||||
'bookkeeping',
|
||||
'archive',
|
||||
'connectfile',
|
||||
// 'archive' and 'connectfile' (voucher attachment import) must NOT be
|
||||
// requested until the registered Fortnox app has them approved in the
|
||||
// Fortnox Developer Portal: requesting a scope the app lacks makes the
|
||||
// authorize endpoint reject with invalid_scope BEFORE login, which kills
|
||||
// every Fortnox connect (prod incident 2026-08-13). The document import
|
||||
// detects the missing scopes at runtime (403 becomes
|
||||
// PROVIDER_DOCUMENT_SCOPES_REQUIRED) and surfaces a reconnect follow-up
|
||||
// instead of failing the migration.
|
||||
];
|
||||
|
||||
export function buildFortnoxAuthUrl(
|
||||
|
||||
Reference in New Issue
Block a user