Maskinporten machine-to-machine authentication
Maskinporten is Digdir's OAuth2 service for machine-to-machine access to Norwegian government APIs. A client authenticates by signing a JWS assertion with its organization's certificate-backed keypair and receives a short-lived, scope-bound access token that identifies the calling organization rather than a human session.
Jurisdiction: Norway · Verified against Digitaliseringsdirektoratet as of 2026-05-19
Client registration
A Maskinporten client is registered in Samarbeidsportalen self-service (sjolvbetjening.samarbeid.digdir.no) against either the test (tt02) or production environment — the two environments have different token endpoints and issuer values, and a copy-paste of test config into production fails with an unhelpful 401. The client identifies the integrating organization and is bound to a virksomhetssertifikat, the hardware-issued digital certificate from Buypass or Commfides.
The JWS assertion and the kid
Token exchange works by signing a JWS assertion with the organization's private key. The public half is uploaded to Samarbeidsportalen's Nøkler page under a key identifier (kid); the assertion carries the same kid in its header so Maskinporten can select the right key to verify against. Rotating the key in the portal without synchronising the client's configured kid produces an authentication failure on every exchange.
Token lifetime and caching
Access-token lifetime is capped at 120 seconds. Software that hard-codes or persists a token to disk starts failing the moment it expires — cache in memory and refresh on demand. Because tokens are issued by Maskinporten itself, a token already in a consumer's hands keeps working until natural expiry even if the signing key that requested it is later removed.
Key rotation and the overlap window
Maskinporten lets a single client keep more than one key on file and selects which to check by the kid in the assertion header. Production rotation therefore runs with a validity-overlap window: upload the new public key under a new kid, flip the client's kid and private key configuration, verify, wait for old-kid traffic to drain (roughly three minutes given the 120-second token cap), and only then delete the old key. Removing the old key while any caller still signs with it makes Maskinporten reject the assertion outright.
Failure taxonomy
Apier normalises every Maskinporten 401 into the single MASKINPORTEN_AUTH_FAILED code — always a credential or configuration drift (wrong kid, host-clock skew beyond a few seconds of iat tolerance, environment mismatch, or a revoked client), never a Maskinporten outage. A genuine outage surfaces as TRANSIENT_UPSTREAM_FAILURE after the retry budget (one initial attempt plus three backoffs) is exhausted, or as a timeout.
Use cases
- Authenticate software to Norwegian government APIs (Altinn 3, Skatteetaten surfaces) without a human session
- Underpin an Altinn 3 System User so delegated actions can run unattended
- Operate a zero-downtime signing-key rotation using the validity-overlap window
Requirements
- A Maskinporten client registered in Samarbeidsportalen for the correct environment (test tt02 vs production)
- A virksomhetssertifikat from Buypass or Commfides as the signing credential
- The public key uploaded to Samarbeidsportalen under a kid that matches the client's configuration
- An NTP-synchronised host clock — Maskinporten rejects JWS assertions with iat skew of more than a few seconds
Authentication
Method: api_key · Scopes: read:altinn
Consumers never talk to Maskinporten directly — Apier holds the client, signs the JWS assertion, and caches tokens server-side. The consumer authenticates to Apier with a standard Bearer API key.
Common errors
MASKINPORTEN_AUTH_FAILED
Maskinporten returned 401 and rejected the token exchange — a credential or configuration drift (expired token, invalid certificate, withdrawn scope grant, kid mismatch, or clock skew), never an outage.
Fix: The API client cannot repair this itself — the cause is configuration drift on the operator side, and it persists until corrected there (readiness probe, kid vs Samarbeidsportalen, host clock, environment endpoints/issuer, client revocation). Report it with the X-Correlation-ID from the response headers so the operator can triage; use ?dry_run=true meanwhile to validate payloads without submitting. Reserve wait-and-retry for TRANSIENT_UPSTREAM_FAILURE.
TRANSIENT_UPSTREAM_FAILURE
The retry budget (1 initial + 3 backoff attempts) was exhausted on 5xx or 429 responses — Maskinporten is probably degraded; the credential is fine.
Fix: Check the government service's status surface and retry later with backoff.
Related topics
Related APIs
Capability ids — resolve each against the machine-readable manifest at /api/v1/capabilities for the endpoint, auth mode, and tier.
- auth.delegate
- auth.permissions
Related MCP tools
Registered tools on the Apier MCP server.
- check_authorization