Hopp til innhold

Hvilket sikkerhetsnivå krever Altinn-tjenesten, og hvorfor får jeg 403 på nivå 2?

Hver Altinn-økt og hvert token bærer et sikkerhetsnivå som gjenspeiler hvordan kallet ble autentisert, og en tjenestes policy kan angi et minstenivå den godtar. Et avslag på nivå 2 betyr nettopp det: legitimasjonen du la fram, ligger under minstenivået policyen krever, så forespørselen avvises uansett hvor gyldige rettighetene og delegeringene dine er. Maskinkall møter samme vegg i skarpere form, fordi et rått Maskinporten-token ikke bærer noe nivå-claim i det hele tatt; veksles det til et Altinn-token, legges claimet til, med 3 som dokumentert verdi for en organisasjon. Flater på nivå 4 har ingen dokumentert maskinvei, så de stegene tilhører et menneske.

Fire steg på rad: legitimasjonen, som bærer et nivå eller ikke noe; tjenestens policy, som angir minstenivået; avgjørelsen, der nivået sjekkes; og tilgangen til appen eller API-et. Stiplede merknader under viser hvor hver feil dukker opp: et rått Maskinporten-token uten nivå-claim ved legitimasjonssteget, et avslag under minstenivået ved policysteget, og ingen maskinvei for nivå 4 ved avgjørelsessteget.Legitimasjonbærer et nivå, eller ikke noeTjenestens policyangir minstenivåetAvgjørelsenivået sjekkes herTilgangapp eller APIrått Maskinporten-token: uten nivå-claimunder minstenivået: avvistnivå 4: ingen maskinvei
Nivåporten står ved siden av rettighetsporten, ikke bak den. Et kall kan holde en perfekt delegering og likevel avvises her, som er grunnen til at nivåproblemer maskerer seg som rettighetsfeil.

Hva er sikkerhetsnivåene i Altinn?

Et mål på hvor sterkt kalleren beviste hvem den er, festet til økten eller tokenet ved autentisering. De svakeste metodene ligger nederst: en innlogging med brukernavn og passord gir tilgang til tjenester som krever nivå 1. Sterkere e-ID-er autentiserer på høyere nivåer, og et ID-porten-token noterer resultatet i acr-claimet sitt, med Level3 som dokumentert eksempel. Nivået er ikke en rettighet: det sier ingenting om hva kalleren får gjøre, bare hvor sikkert Altinn vet hvem som kaller.

Maskiner har også nivåer, og det er her modellen biter integratører. Autentisering med virksomhetssertifikat gir sikkerhetsnivå 3, og dokumentasjonen er uttrykkelig på at den bare gir det. Altinn-tokenet fra vekslingsendepunktet gjør verdien synlig: det dokumenterte organisasjonstokenet bærer et AuthenticationLevel-claim med verdien 3. Taket for et maskinkall er altså nivå 3, og ingen scope, delegering eller sertifikatstyrke hever det.

Nivå 4 finnes over dette, nådd med de sterkeste e-ID-ene. For det dokumenterte meldingstjeneste-tilfellet kan en tjeneste på nivå 4 bare leses gjennom ordinær innlogging via ID-porten, og maskinintegrasjon er ikke mulig der. Det ene faktumet avgjør et arkitekturspørsmål: enhver arbeidsflyt som berører en flate på nivå 4, trenger et menneskelig steg, og å late som noe annet gir et avslag ingen kodeendring kan fikse.

Hvordan angir tjenesten minstenivået, og hva skjer under det?

I Altinn 3 bor kravet i tjenestens autorisasjonspolicy. Avgjørelsen som returneres for en forespørsel, kan bære en forpliktelse hvis attributtkategori er urn:altinn:minimum-authenticationlevel, med en heltallsverdi; det dokumenterte eksempelet setter den til 2. Formen betyr mer enn tallet: minstenivået er en egenskap ved ressursens policy, valgt av tjenesteeieren, så to tjenester bak samme vert kan kreve ulike nivåer og besvare samme legitimasjon forskjellig.

Når legitimasjonen ligger under det angitte minstenivået, avvises forespørselen selv om rettighetskontrollen ville passert. Det er svaret på spørsmålet i sidens tittel: en 403 på nivå 2 er policyen som krever større sikkerhet om hvem som kaller, enn legitimasjonen din gir. Løsningen ligger aldri i forespørselens innhold og alltid på legitimasjonssiden, som er det som skiller denne feilen fra hver annen 403 i feilsøkingssiloen.

Hvordan avslaget kommer til syne, er mindre ryddig enn modellen. Integratører observerer at et for lavt nivå ikke alltid kommer som et merket avslag: det kan dukke opp som en generisk feilside, eller som en serverfeil som ikke nevner sikkerhetsnivåer. Ingenting på de offisielle sidene lover disse formene, så behandle dem som observasjoner og ikke kontrakt, og ta til deg feilsøkingsregelen de peker på: på en nivåstyrt flate er en uforklart avvisning et nivåproblem til legitimasjonens claims beviser noe annet.

Nivåfeilene som symptom, sannsynlig årsak og løsning. Rader merket observert beskriver integrasjonserfaring framfor dokumentert atferd.
SymptomSannsynlig årsakLøsning
403 på nivå 2 i en portalnær flytInnloggingsmetoden ligger under minstenivået tjenestens policy angir.Autentiser på nytt med en sterkere e-ID før tjenesten prøves igjen.
Gyldig delegering, maskinkall avvistEt rått Maskinporten-token ble lagt fram; det bærer ikke noe AuthenticationLevel-claim.Veksle tokenet først og legg fram Altinn-tokenet på kallet.
Samme kall passerer som person, feiler som maskinTjenesten krever nivå 4, og maskinveier når bare nivå 3.Legg det steget til et menneske med e-ID på nivå 4; ingen kodeendring fikser det.
Generisk feilside i stedet for avslag (observert)Et for lavt nivå som kommer til syne uten et merket avslag.Sjekk legitimasjonens nivå-claims før noe annet feilsøkes.
500 der et avslag gir mening (observert)En avslagsformet tilstand som dukker opp med serverfeil-status.Uttøm nivå- og myndighetshypotesene før det behandles som driftsavbrudd.

Hvordan feilsøker jeg et nivåproblem og ikke et rettighetsproblem?

Skill de to portene først. En rettighetsfeil handler om hva kalleren får gjøre, og sporet går gjennom delegeringer, pakker og scopes; den klyngen eies av feilsøkingsveiledningen for systembrukere og feilsøkingsveiledningen for delegering. En nivåfeil handler om hvordan kalleren ble autentisert, og sporet er ett punkt langt: hvilket nivå-claim bærer legitimasjonen som faktisk legges fram.

For maskinkall løser det ene punktet nesten alltid saken. Dekod tokenet du faktisk sender, og se etter AuthenticationLevel-claimet: et rått Maskinporten-token har ikke noe, og løsningen er å sende det gjennom vekslingen først, som veiledningen om tokenveksling eier fra ende til ende. Er claimet på plass og tjenesten fortsatt avviser, ligger du enten under et høyere minstenivå eller på en flate på nivå 4, og løsningen er sterkere legitimasjon eller et menneskelig steg, ikke enda et forsøk.

Apier-utgaven av denne disiplinen er å kjøre kontrollene før kallet som ville strøket på dem. Dry-run-modusen på handlingsendepunktet validerer en innsendingshandling mot fem navngitte kontroller, blant dem om en systembruker-delegering er aktiv og om den bærer de nødvendige scopene, uten at noe sendes oppstrøms. Det som ville vært en uforklart avvisning i enden, kommer i stedet som en navngitt feilet kontroll med forklaring, som er forskjellen på en produksjonshendelse og et punkt på en sjekkliste.

Gjør det første kallet

Sandkassekallet svarer på hvem som overhodet kan opptre for et selskap, uten nøkkel. TypeScript-eksempelet kjører dry-run-førflygingen: de navngitte kontrollene for en innsendingshandling, evaluert uten at noe sendes inn, så et gap kommer merket i stedet for som en 403 i enden av kjeden.

# Nøkkelløs sandkasse: hvem som kan opptre for et selskap, fra registeret.
curl -s https://www.apier.no/api/v1/sandbox/public/company/999999999/authority
// Førflyging: kjør de navngitte valideringskontrollene for en
// innsendingshandling mot apier.no med én Apier-nøkkel, før noe møter veggen.
const res = await fetch(
  "https://www.apier.no/api/v1/actions/execute?dry_run=true",
  {
    method: "POST",
    headers: {
      "content-type": "application/json",
      Authorization: `Bearer ${process.env.APIER_API_KEY}`,
    },
    body: JSON.stringify({
      org_number: "999999999",
      action_type: "mva_melding",
      period: "2026-T1",
      payload: {},
    }),
  },
);

if (!res.ok) {
  // Alle ikke-2xx-svar bruker den samme strukturerte konvolutten.
  const { error_code, explanation } = await res.json();
  throw new Error(`${error_code}: ${explanation.summary}`);
}

const { data } = await res.json();
// Hver kontroll rapporterer med navn: company_exists,
// system_user_authorised, scopes_delegated og resten, så et gap kommer merket.
for (const check of data.outcome.checks) {
  console.log(check.check, check.passed);
}

Ofte stilte spørsmål

Hvor kommer sikkerhetsnivået mitt egentlig fra?
Fra hvordan kallet ble autentisert, ikke fra hva det har lov til å gjøre. En innlogging med brukernavn og passord ligger på nivå 1, og sterkere e-ID-er autentiserer på høyere nivåer. På maskinsiden gir autentisering med virksomhetssertifikat nivå 3, og Altinn-tokenet fra vekslingsendepunktet bærer et AuthenticationLevel-claim, med 3 som dokumentert verdi for et organisasjonstoken. Nivået følger økten eller tokenet; rettigheter og delegeringer sjekkes for seg.
Delegeringen min er gyldig. Hvorfor avvises kallet likevel?
Fordi nivåkontrollen og rettighetskontrollen er to forskjellige porter, og å passere den ene sier ingenting om den andre. Den vanlige maskinformen er et rått Maskinporten-token sendt til en flate hvis policy krever et minstenivå: det rå tokenet bærer ikke noe AuthenticationLevel-claim i det hele tatt, så policyen har ingenting å godta. Veksles tokenet først, legges claimet til, og den samme delegeringen passerer.
Kan en maskin nå en tjeneste som krever nivå 4?
Ikke i det dokumenterte meldingstjeneste-tilfellet. Dokumentasjonen sier at meldingstjenester på sikkerhetsnivå 4 bare kan leses gjennom ordinær innlogging via ID-porten, og at maskinintegrasjon ikke er mulig der, siden sertifikatautentisering bare gir nivå 3. Når en flyt treffer en slik tjeneste, er den ærlige designen en overlevering: et menneske autentiserer seg med en e-ID på nivå 4 og utfører det ene steget.
Hvorfor får jeg noen ganger en generisk feilside eller 500 i stedet for et tydelig avslag?
Integratører observerer at et for lavt nivå ikke alltid dukker opp som et merket avslag: svaret kan komme som en generisk feilside eller en serverfeil som ikke nevner sikkerhetsnivåer. Ingenting av dette er dokumentert atferd å bygge på. Den praktiske regelen er å behandle en uforklart avvisning på en nivåstyrt flate som et nivåproblem først, og sjekke hva legitimasjonen bærer før du behandler den som et driftsavbrudd.
Hvordan unngår jeg å treffe nivåveggen i blinde med Apier?
Ved å kjøre kontrollene som en førflyging med navngitte utfall. POST /api/v1/actions/execute med dry_run=true validerer en innsendingshandling mot fem kontroller, blant dem om selskapet finnes, om en systembruker-delegering er aktiv, og om delegeringen bærer de nødvendige scopene, uten at noe sendes oppstrøms. Et gap kommer som en navngitt feilet kontroll med forklaring, ikke som en uforklart avvisning i enden av kjeden.