Hvordan kan AI-agenter trygt få tilgang til norske offentlige tjenester?
Gjennom delegert, avgrenset fullmakt og strukturert JSON, aldri ved å legge offentlig legitimasjon inn i modellen. Agenten holder en API-nøkkel som gir navngitte lesescopes; virksomhetssertifikatet og token-utvekslingen mot Maskinporten blir liggende bak API-et. Å opptre for et bestemt selskap krever i tillegg at selskapet har delegert fullmakt til systemet som kaller, gjennom Altinn, og det er en beslutning bare kunden kan ta. Hvert kall bærer en correlation-id, slik at det agenten gjorde forblir mulig å rekonstruere i ettertid.
Hvordan autentiserer en AI-agent seg mot Altinn?
Indirekte, og det er hele designet. Altinn 3 utsteder ikke API-nøkler. Tjenesten forventer et Maskinporten access token, og nøkkelen bak det tokenet er forankret i et virksomhetssertifikat utstedt til en enhet registrert i Brønnøysund. Å gi det sertifikatet til en agent er en dårlig idé av grunner som ikke har noe med tillit til modellen å gjøre: ledetekster og verktøyargumenter blir logget, spilt av på nytt, mellomlagret og noen ganger vist til sluttbrukere, og en langlevd organisasjonsidentitet bør ikke reise gjennom noe av dette.
Så agenten autentiserer seg mot Apier i stedet, med én enkelt Authorization: Bearer-nøkkel, og Apier holder sertifikatet, signerer JWT-assertionene og roterer tokenene på den andre siden av kallet. Agenten ser aldri et offentlig token og har ingen kodesti som kunne lekke ett. Hele mekanikken i den utvekslingen er dekket i veiledningen om autentisering mot norske offentlige API-er.
Det andre leddet handler om fullmakt, ikke identitet. Å bevise hvilken virksomhet som kaller sier ingenting om hvorvidt den virksomheten kan opptre for selskapet det gjelder. Den tillatelsen kommer fra en delegering til Altinn systembruker, gitt av kunden til ditt registrerte system. Ingen mengde infrastruktur fjerner den, fordi den koder en beslutning bare kunden har rett til å ta, og en agent som behandler en manglende delegering som en feil den kan prøve på nytt, går i løkke mot en vegg som fungerer helt riktig.
Hva hindrer en agent i å opptre utover sin delegerte fullmakt?
To grenser som svikter uavhengig av hverandre, og det betyr mer enn at hver enkelt grense er streng. Den første er nøkkelen selv. Hver API-nøkkel bærer en eksplisitt scope-liste på formen domain:action, og hvert beskyttet endepunkt oppgir hvilket scope det krever, kontrollert før noen handlerlogikk kjører. Vokabularet som kan tildeles er lesesidig og smalt: read:brreg, read:altinn, read:changes, subscribe:webhooks og noen få søsken. Jokertegn utvider bare handlingsleddet, så read:* gir enhver lesing og ingenting annet.
Scope-prefiksene som ville gitt fullmakt til en bindende offentlig skriving er reservert og kan ikke utstedes i det hele tatt. En nøkkel som holder ett av dem gir ingenting, og endepunktene som ville brukt det avviser kallet direkte i stedet for å falle tilbake på en ettergivende standard. Det er et bevisst tak, ikke et hull som venter på å bli fylt: det betyr at ingen nøkkel i omløp i dag kan snakkes til å utføre en bindende innsending, uansett hva en agent blir overtalt til å forsøke.
Den andre grensen er kundens delegering, og ingen scope hos deg kan erstatte den. Det praktiske rådet til agentutviklere er å slå opp fullmakt før en handling planlegges, i stedet for å oppdage den i en feil: spør /api/v1/company/{org}/authority om hvem som kan signere og hvordan, og forgren deg på svaret. En joint-klassifisering betyr at to signaturer kreves, og riktig agentoppførsel er å overlate saken til et menneske, ikke å prøve på nytt.
Hvordan gjøres en agents handling etterprøvbar i ettertid?
Ved å la én identifikator følge forespørselen gjennom hvert lag som berører den. Hvert kall bærer en X-Correlation-ID: send en gyldig UUID v4, og den gjenbrukes ordrett; send ingenting, og en blir generert. Den id-en ekkoes på responsheaderne og lagres på sporingsraden, regelevalueringen og tilstandsovergangen som samme kall produserte, slik at ett enkelt søk bygger opp igjen alt én agenthandling forårsaket.
Responsene beskriver også sitt eget opphav. Hver respons bærer en _meta-blokk med response_timestamp og response_hash, en SHA-256 over den kanoniserte kroppen, og legger igjen en tilsvarende rad i opphavsloggen. Den kombinasjonen svarer på et spørsmål logger alene ikke kan: ikke bare hva systemet ditt mottok, men at bytene det handlet på er de bytene som ble servert. For en autonom klient er dette forskjellen mellom et revisjonsspor og en påstand.
Bygg agenten mot sandkassen før noe av dette betyr noe i produksjon. Sandkassen gjør aldri et nettverkskall mot en offentlig kilde på noe verb, så en agent som oppfører seg dårlig der kan ikke nå Altinn i det hele tatt, og ?simulate_error= lar deg øve på manglende delegering og ugyldig token med vilje i stedet for å vente på å møte dem. Hver sandkasserespons er merket _meta.is_sandbox: true, slik at agenten kan skille et simulert resultat fra et reelt uten å gjette.
Gjør det første kallet
Sandkasseforespørselen under krever ingen legitimasjon i det hele tatt. TypeScript-snutten er produksjonsekvivalenten, som slår opp signaturrett før en agent binder seg til en plan.
# Nøkkelløs sandkasse: øv på agentens lesesti uten legitimasjon.
curl -s https://www.apier.no/api/v1/sandbox/public/company/999999999/authority// Sjekk fullmakt FØR du planlegger en handling, ikke etter at den feiler.
const res = await fetch(
"https://www.apier.no/api/v1/company/999999999/authority",
{
headers: {
Authorization: `Bearer ${process.env.APIER_API_KEY}`,
"X-Correlation-ID": crypto.randomUUID(),
},
},
);
if (!res.ok) {
// En feilkonvolutt bærer ingen `data`, så les koden i stedet for å
// dereferere undefined og miste årsaken til at sjekken feilet.
const { error_code, explanation } = await res.json();
throw new Error(`${error_code}: ${explanation.summary}`);
}
const { data } = await res.json();
// "sole" | "joint" | "by_role" | "prokura_only" | "no_authority" | "unknown"
if (data.classification !== "sole") {
throw new Error("Mer enn én signatur kreves. Overlat dette til et menneske.");
}Ofte stilte spørsmål
- Bør en AI-agent holde Maskinporten-legitimasjon direkte?
- Nei. Et virksomhetssertifikat er en langlevd organisasjonsidentitet, og en modellkontekst er feil sted for en slik: ledetekster blir logget, spilt av på nytt og noen ganger vist til brukere. Den tryggere formen er at agenten holder en avgrenset API-nøkkel som gir bestemte leserettigheter, mens sertifikatet og token-utvekslingen blir liggende bak API-et på infrastruktur agenten ikke når.
- Hva hindrer en agent i å gjøre mer enn den har fullmakt til?
- To uavhengige grenser. Nøkkelen bærer en eksplisitt liste med scopes på formen domain:action, og hvert beskyttet endepunkt oppgir hvilket scope det krever, så en nøkkel uten det scopet avvises før noen handlerlogikk kjører. Uavhengig av dette krever det å opptre for et selskap at selskapet har delegert fullmakt til systemet som kaller, noe ingen scope hos deg kan gi.
- Kan en agent sende inn en bindende innsending til Altinn i dag?
- Ikke gjennom dette API-et. Scope-prefiksene som ville gitt fullmakt til en bindende skriving er reservert og kan ikke tildeles en nøkkel, og bindende innsending kjører mot syntetiske testdata i stedet for et reelt Altinn-endepunkt. Lesing, fullmaktsoppslag og tørrkjørt validering av nyttelast er i drift, så en agent kan forberede og kontrollere en innsending uten å kunne sende den.
- Hvordan beviser jeg i ettertid hva en agent faktisk gjorde?
- Hvert kall bærer en X-Correlation-ID, gjenbrukt ordrett når du sender en gyldig UUID v4 og generert når du ikke gjør det. Den id-en lagres på sporingsloggen og på regelevalueringen for samme kall, slik at ett søk rekonstruerer hele skiven av historikk én agenthandling produserte.
- Er sandkassen trygg å peke en autonom agent mot?
- Ja, og det er hele hensikten. Sandkassestien gjør aldri et nettverkskall mot Altinn, Maskinporten, Brønnøysund, Skatteetaten eller NAV på noe verb, heller ikke skriving. En innsending i sandkassen er en lokal tilstandsendring, så en agent som går i løkke på den flaten kan ikke nå et offentlig system uansett hva den bestemmer seg for å gjøre.