feat(providers): request the Fortnox attachment scopes now that the portal grants them (#1776)

Arkivplats and Koppla filer are enabled for integration 39254 in the Fortnox
Developer Portal, so FORTNOX_DOCUMENT_SCOPES_APPROVED goes true and the opt-in
underlag reconnect starts asking for archive and connectfile. The ordinary
connect is untouched, so no customer is put in front of an Arkivplats licence
to connect at all.

This is the half that actually delivers the files. #1761 only stopped the loop
that told people to grant a permission we never requested; until now no Fortnox
token in production has ever carried these scopes, and no voucher attachment has
ever imported (166 companies, 24 consents since the feature shipped).

Set the flag back to false if the portal ever loses the scopes: authorize then
rejects with invalid_scope before login, which now costs the underlag flow
rather than every Fortnox connection.

Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-08-21 09:20:28 +02:00
committed by GitHub
co-authored by Jakob Wennberg Claude Opus 5
parent f3e4fdcf32
commit f11a78ec50
3 changed files with 22 additions and 8 deletions
+12 -2
View File
@@ -32,11 +32,21 @@ describe('Fortnox OAuth scopes', () => {
// user is never told to reconnect for a permission the connect request does
// not ask for. Flipping it here without enabling the scopes in the Fortnox
// Developer Portal reintroduces the 2026-08-13 invalid_scope outage.
it('keeps the document scopes flagged as not approved, and names both of them', () => {
expect(FORTNOX_DOCUMENT_SCOPES_APPROVED).toBe(false);
it('has the document scopes approved in the portal, and names both of them', () => {
expect(FORTNOX_DOCUMENT_SCOPES_APPROVED).toBe(true);
expect(FORTNOX_DOCUMENT_SCOPES).toEqual(['archive', 'connectfile']);
});
// The whole point of the opt-in consent: this is the only scope list that
// carries the attachment permissions, and the ordinary connect above still
// must not, so no customer is asked for an Arkivplats licence to connect.
it('puts the attachment scopes in the document consent only', () => {
expect(fortnoxConsentScopes({ documents: true })).toContain('archive');
expect(fortnoxConsentScopes({ documents: true })).toContain('connectfile');
expect(fortnoxConsentScopes()).not.toContain('archive');
expect(fortnoxConsentScopes()).not.toContain('connectfile');
});
// Even once the portal registration lands, opting in must never cost the
// consent its ledger access: the callback overwrites its tokens in place.
it('keeps a document consent a superset of an ordinary one', () => {
+9 -6
View File
@@ -20,17 +20,20 @@ export const FORTNOX_DOCUMENT_SCOPES = ['archive', 'connectfile'];
/**
* Whether the registered Fortnox app has Arkivplats and Koppla filer enabled in
* the Fortnox Developer Portal (integration 39254). 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), so this stays
* false until the portal registration has both. Flip it in the same change that
* enables them there.
* the Fortnox Developer Portal (integration 39254). True since 2026-08-21, when
* the portal registration was confirmed to carry both.
*
* Requesting a scope the app lacks makes the authorize endpoint reject with
* invalid_scope BEFORE login, so set this back to false the moment the portal
* loses them, rather than leaving the underlag reconnect pointed at a scope
* Fortnox will refuse (prod incident 2026-08-13, when the ordinary connect
* still carried these scopes and every Fortnox connection died).
*
* It gates the opt-in document consent below and the document-import error
* message, never the ordinary connect: a user is never told to reconnect for a
* permission we don't ask for (support case Klura AB, 2026-08-20).
*/
export const FORTNOX_DOCUMENT_SCOPES_APPROVED: boolean = false;
export const FORTNOX_DOCUMENT_SCOPES_APPROVED: boolean = true;
/**
* The scopes a Fortnox consent is minted with. The document scopes are opt-in