OpenID Connect voor overheidsidentiteit
SPID en CIE evolueren naar OpenID Connect: hoe een Relying Party voor Italiaanse identiteitssystemen te implementeren, configureer OIDC-federatie, beheer niveaus van zekerheid (LoA), beveilig stromen met PKCE en JARM, en bereid u voor naar convergentie met het EUDI Wallet-ecosysteem.
De evolutie naar OIDC in de Italiaanse overheidsidentiteit
Al meer dan tien jaar is de Italiaanse digitale identiteit gebaseerd op het protocol SAML 2.0: robuust, goed gestandaardiseerd, maar ontworpen in een pre-mobiel tijdperk. Met de explosie van native applicaties voor smartphones en de verspreiding van Single Page Applications, SAML heeft zijn beperkingen getoond: uitwisseling van omvangrijke XML, slechte mobiele ondersteuning, complexe integratie in SPA's.
De overgang naar OpenID Connect (OIDC) het was onvermijdelijk. In januari 2023 publiceerde AgID de OpenID Connect-richtlijnen in SPID en de OpenID Connect-richtlijnen in CIE, waardoor de standaard gebaseerd op OAuth 2.0 officieel wordt als een pad van evolutie van de identiteit van de Italiaanse overheid. De PNRR-doelstelling vereist dat tegen maart 2026 alle 16.500 Italiaanse PA's elektronische identificatie via SPID of CIE adopteren.
Waarom OpenID Connect versus SAML voor PA
- Lichtgewicht tokens: JWT in plaats van XML SAML-bewering - payload verminderd met 60-70%
- Mobiel-eerst: OAuth 2.0 met PKCE ontworpen voor native apps en SPA's
- Modern ecosysteem: bibliotheken beschikbaar voor elke taal/framework
- Gestandaardiseerde federatie: OpenID Federation 1.0 voor automatische vertrouwensketen
- Op weg naar EUDI-portemonnee: OpenID4VP en OpenID4VCI gebruiken dezelfde OIDC-primitieven
Architectuur van de SPID/CIE OIDC Federatie
De OIDC-federatie voor SPID en CIE is gebaseerd op OpenID Federatie 1.0, een standaard die de kettingconstructie automatiseert vertrouwen tussen entiteiten. Dit overtreft het handmatige model van SAML-metadataregistratie, waarbij elke SP handmatig metadata moest uitwisselen XML bij elke IdP.
Entiteiten in de Federatie
- Vertrouwensanker (TA): AgID is het vertrouwensanker van de Italiaanse federatie. Publiceert de entiteitsconfiguratie naar het bekende eindpunt.
- Tussenliggend: door AgID geautoriseerde aggregators die meerdere RP's verenigen (bijv. regionale PA's, gemeentelijke aggregators).
- Vertrouwende Partij (RP): de applicatie die gebruikers authenticeert via SPID/CIE OIDC.
- Identiteitsprovider (OP): SPID-providers (Poste Italiane, Aruba, InfoCert, etc.) of de CIE IdP van het Ministerie van Binnenlandse Zaken.
Entiteitsconfiguratie en vertrouwensketen
// Entity Configuration di un Relying Party
// Disponibile al well-known endpoint:
// GET https://my-service.comune.it/.well-known/openid-federation
{
"iss": "https://my-service.comune.it",
"sub": "https://my-service.comune.it",
"iat": 1710000000,
"exp": 1741536000,
"jwks": {
"keys": [
{
"kty": "EC",
"kid": "rp-sig-key-2025",
"crv": "P-256",
"x": "...",
"y": "...",
"use": "sig"
}
]
},
"metadata": {
"openid_relying_party": {
"application_type": "web",
"client_id": "https://my-service.comune.it",
"client_registration_types": ["automatic"],
"redirect_uris": [
"https://my-service.comune.it/callback"
],
"response_types": ["code"],
"grant_types": ["authorization_code"],
"scope": "openid profile email",
"token_endpoint_auth_method": "private_key_jwt",
"id_token_signed_response_alg": "ES256",
"subject_type": "pairwise",
"contacts": ["tech-team@comune.it"]
}
},
"authority_hints": [
"https://registry.spid.gov.it"
]
}
Implementeer de autorisatiecodestroom met PKCE
De aanbevolen stroom voor SPID/CIE OIDC isAutorisatiecodestroom met PKCE (Proofsleutel voor code-uitwisseling, RFC 7636). PKCE beschermt tegen onderschepte autorisatiecodes en is verplicht voor mobiele apps en aanbevolen voor webapps.
Stap 1: PKCE-generatie en autorisatieverzoek
// TypeScript - Authorization Request con PKCE
import crypto from "crypto";
function generatePKCE() {
// code_verifier: stringa casuale 43-128 caratteri
const codeVerifier = crypto.randomBytes(32)
.toString("base64url");
// code_challenge: SHA256 del verifier, base64url encoded
const codeChallenge = crypto.createHash("sha256")
.update(codeVerifier)
.digest("base64url");
return { codeVerifier, codeChallenge };
}
async function initiateLogin(req: Request, res: Response) {
const { codeVerifier, codeChallenge } = generatePKCE();
const state = crypto.randomBytes(16).toString("base64url");
const nonce = crypto.randomBytes(16).toString("base64url");
// Salva in sessione (server-side)
req.session.pkce = { codeVerifier, state, nonce };
// Costruisci l'Authorization Request
const params = new URLSearchParams({
response_type: "code",
client_id: "https://my-service.comune.it",
redirect_uri: "https://my-service.comune.it/callback",
scope: "openid profile",
// Per SPID LoA 2 (identità verificata con password forte)
acr_values: "https://www.spid.gov.it/SpidL2",
state,
nonce,
code_challenge: codeChallenge,
code_challenge_method: "S256",
// Parametri SPID specifici
prompt: "login",
});
// Scopri il provider selezionato tramite OP Selector o discovery
const opAuthEndpoint = await discoverOPEndpoint("https://idp.poste.it");
res.redirect(`${opAuthEndpoint}?${params.toString()}`);
}
Stap 2: Terugbellen en tokenuitwisseling
// TypeScript - Gestione callback e token exchange
async function handleCallback(req: Request, res: Response) {
const { code, state, error } = req.query;
const session = req.session.pkce;
// Verifica state per prevenire CSRF
if (state !== session.state) {
return res.status(400).json({ error: "State mismatch" });
}
if (error) {
return res.status(400).json({ error });
}
// Token Request - usa private_key_jwt per autenticarsi al token endpoint
const clientAssertion = await createClientAssertion({
iss: "https://my-service.comune.it",
sub: "https://my-service.comune.it",
aud: "https://idp.poste.it/oauth/token",
jti: crypto.randomBytes(16).toString("hex"),
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 60,
});
const tokenResponse = await fetch("https://idp.poste.it/oauth/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "authorization_code",
code: code as string,
redirect_uri: "https://my-service.comune.it/callback",
client_id: "https://my-service.comune.it",
client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
client_assertion: clientAssertion,
code_verifier: session.codeVerifier,
}),
});
const tokens = await tokenResponse.json();
// Verifica e decodifica l'ID Token
const idToken = await verifyIdToken(tokens.id_token, {
expectedNonce: session.nonce,
expectedAudience: "https://my-service.comune.it",
});
// idToken.sub = identificatore opaco pairwise dell'utente
// idToken.acr = livello di garanzia (LoA) ottenuto
console.log("User authenticated:", idToken.sub);
console.log("LoA achieved:", idToken.acr);
}
Niveaus van zekerheid (LoA) in SPID en CIE
Een van de belangrijkste aspecten van de identiteit van de overheid is de Niveau van zekerheid (LoA), die aangeeft hoe betrouwbaar het authenticatieproces is. SPID definieert drie niveaus:
| Niveau | ACR-URI | Methode | Typisch gebruik |
|---|---|---|---|
| SpidL1 | https://www.spid.gov.it/SpidL1 | Gebruikersnaam + wachtwoord | Diensten met laag risico, niet-gevoelige gegevensraadpleging |
| SpidL2 | https://www.spid.gov.it/SpidL2 | Gebruikersnaam + wachtwoord + OTP/push | Standaard PA-diensten (declaraties, aanvragen, betalingen) |
| SpidL3 | https://www.spid.gov.it/SpidL3 | Gecertificeerd fysiek apparaat | Operaties met een hoog risico, gekwalificeerde elektronische handtekening |
Voor CIE (elektronische identiteitskaart), is het betrouwbaarheidsniveau altijd LoA3 (het maximum). gebruikte de CIE NFC-chip, omdat hiervoor fysiek bezit van het document en de pincode vereist is. Dit maakt CIE de voor de hand liggende keuze voor services die sterke authenticatie vereisen.
UserInfo-eindpunt- en SPID/CIE-kenmerken
Na het inloggen kan de Relying Party de kenmerken van de gebruiker ophalen van het UserInfo Endpoint met behulp van het Access Token. De attributen die beschikbaar zijn in SPID OIDC worden gedefinieerd in het technische profiel:
// GET /userinfo
// Authorization: Bearer <access_token>
// Risposta UserInfo SPID
{
"sub": "c5d9b2f3-1a4e-4b8d-9f2a-3e5c7d8f9b1a", // pairwise pseudonimo
"https://attributes.spid.gov.it/name": "Mario",
"https://attributes.spid.gov.it/familyName": "Rossi",
"https://attributes.spid.gov.it/fiscalNumber": "RSSMRA90A15H501Z",
"https://attributes.spid.gov.it/dateOfBirth": "1990-01-15",
"https://attributes.spid.gov.it/placeOfBirth": "Roma",
"https://attributes.spid.gov.it/countyOfBirth": "RM",
"https://attributes.spid.gov.it/idCard": "MRTMRA90A15H501Z",
"https://attributes.spid.gov.it/address": {
"formatted": "Via Roma 1, 00100 Roma RM"
},
"https://attributes.spid.gov.it/email": "mario.rossi@example.com",
"https://attributes.spid.gov.it/mobilePhone": "+39 333 1234567",
"acr": "https://www.spid.gov.it/SpidL2"
}
// ATTENZIONE: richiedere solo gli attributi strettamente necessari
// nell'Authorization Request tramite il parametro 'claims':
// "claims": {
// "userinfo": {
// "https://attributes.spid.gov.it/fiscalNumber": {"essential": true},
// "https://attributes.spid.gov.it/name": null
// }
// }
Privacy en minimalisatie van SPID-kenmerken
Het opvragen van de belastingcode van de gebruiker zonder dat dit echt nodig is, is een schending van het GDPR-minimalisatieprincipe. De AgID-richtlijnen specificeren dat elk aangevraagd attribuut moet worden gemotiveerd door de specifieke functionaliteit van de dienst, e aangegeven in het privacybeleid. Tijdens de AgID-audit kan het niet verantwoorden van de gevraagde kenmerken voorkomen leiden tot intrekking van de accreditatie.
SDK en officiële bibliotheken voor SPID/CIE OIDC
Het project Ontwikkelaars Italië (developers.italia.it) biedt officiële SDK's in verschillende talen om SPID/CIE OIDC-integratie te vereenvoudigen:
## SDK Ufficiali Developers Italia - SPID/CIE OIDC Federation
# Python (Django)
pip install spid-cie-oidc
# Configurazione in Django settings.py
INSTALLED_APPS = [
...
"spid_cie_oidc.entity",
"spid_cie_oidc.relying_party",
]
OIDCFED_DEFAULT_TRUST_ANCHOR = "https://registry.spid.gov.it"
OIDCFED_IDENTITY_PROVIDERS = {
"spid": "https://registry.spid.gov.it",
"cie": "https://idserver.servizicie.interno.gov.it"
}
# Configura automaticamente gli endpoint di federazione
# e gestisce la trust chain dinamicamente
# Java
# Maven: eu.italia.gov:spid-cie-oidc-java:latest
# .NET
# NuGet: AGID.SPID.OIDC
# Node.js (Express)
npm install spid-cie-oidc-node
# PHP
composer require italia/spid-php-lib
JARM: JWT-gebaseerde autorisatieresponsmodus
De SPID/CIE OIDC-richtlijnen bevelen het gebruik aan van JARM (JWT-autorisatieresponsmodus) beschermen
het autorisatieantwoord. In plaats van voorbij te gaan code e state in duidelijke tekst als queryparameters,
JARM vat het antwoord samen in een JWT ondertekend door de identiteitsprovider.
// Authorization Request con JARM
{
"response_type": "code",
"response_mode": "form_post.jwt", // JARM mode
"client_id": "https://my-service.comune.it",
...
}
// Risposta JARM dal IdP (form POST al redirect_uri)
// Il parametro 'response' contiene un JWT firmato:
// Header:
{ "alg": "ES256", "kid": "idp-key-2025" }
// Payload:
{
"iss": "https://idp.poste.it",
"aud": "https://my-service.comune.it",
"code": "SplxlOBeZQQYbYS6WxSbIA",
"state": "af0ifjsldkj",
"iat": 1710000000,
"exp": 1710000180
}
// Vantaggi JARM:
// 1. Autenticità: garantisce che la risposta venga dall'IdP corretto
// 2. Integrità: impossibile modificare code/state in transito
// 3. Non-repudiation: log auditabili con prova crittografica
Sessiebeheer en tokenvernieuwing
Een cruciaal aspect van de identiteit van de overheid is het correcte beheer van sessies. In tegenstelling tot de particuliere sector, PA-diensten moeten strenge beperkingen respecteren op het gebied van sessieduur en uitloggen.
// Gestione sessione sicura per servizi PA
interface SessionConfig {
maxAge: number; // durata massima sessione in secondi
inactivityTimeout: number; // timeout per inattività
requireReauth: boolean; // richiede riautenticazione per operazioni critiche
}
// Configurazione raccomandata per servizi PA
const SESSION_CONFIG: SessionConfig = {
maxAge: 3600, // 1 ora massimo (SPID raccomanda max 30 min per L2)
inactivityTimeout: 900, // 15 min di inattività
requireReauth: true // operazioni di firma richiedono nuovo login
};
// Logout corretto: Single Logout (SLO) obbligatorio per SPID
async function handleLogout(req: Request, res: Response) {
const session = req.session;
// 1. Costruisci logout request per l'IdP
const logoutRequest = createSignedLogoutRequest({
iss: "https://my-service.comune.it",
sub: session.userId,
sid: session.idTokenHint, // session ID dal ID Token
aud: session.idpLogoutEndpoint,
post_logout_redirect_uri: "https://my-service.comune.it/logout-complete",
});
// 2. Distruggi la sessione locale prima del redirect
await req.session.destroy();
// 3. Redirect all'IdP per SLO
res.redirect(
`${session.idpLogoutEndpoint}?` +
`id_token_hint=${session.idToken}&` +
`post_logout_redirect_uri=https://my-service.comune.it/logout-complete&` +
`state=${generateState()}`
);
}
Accreditatie en naleving van AgID
Om SPID in zijn dienst te integreren, moet de Vertrouwende Partij het AgID-accreditatieproces doorlopen. De vereenvoudigde procedure voor OIDC is beschikbaar vanaf 2023:
-
Technische registratie: Entiteitsconfiguratieconfiguratie en eindpuntverificatie
/.well-known/openid-federation. AgID valideert de naleving via automatische tools. - Functionele testen: Gebruik van spid-sp-test (open source Python-tool) om de juiste implementatie te verifiëren. De tool simuleert het gedrag van Identity Providers en verifieert ruim 200 testcases.
- Naleving van de AVG: documentatie van gegevensverwerking voor elk vereist SPID-attribuut.
- Ondertekening van de overeenkomst: aanvaarding van het SPID Technisch Reglement en de lidmaatschapsovereenkomst.
## Uso di spid-sp-test per verificare l'implementazione
# Installazione
pip install spid_sp_test
# Verifica metadata OIDC
spid_sp_test \
--metadata-url https://my-service.comune.it/.well-known/openid-federation \
--profile oidc_op
# Test flusso completo (richiede credenziali di test)
spid_sp_test \
--metadata-url https://my-service.comune.it/.well-known/openid-federation \
--authn-url https://my-service.comune.it/login/spid \
--profile oidc_sp_public \
--extra \
--debug
# Output: report HTML con dettagli dei 200+ test
# File: report_<timestamp>.html
Op weg naar convergentie: SPID/CIE OIDC en EUDI Wallet
De transitie van SAML naar OIDC is niet de eindbestemming. Het ecosysteem evolueert richting volledige integratie met EUDI-portemonnee via OpenID4VP. Het project iam-proxy-italia op GitHub demonstreert het al hoe een IAM-proxy tegelijkertijd SAML 2.0, OIDC en OpenID4VC (voor IT-Wallet/EUDI Wallet) kan ondersteunen.
De ideale architectuur voor een moderne PA-dienst moet alle drie de stromen transparant ondersteunen:
// Middleware di autenticazione multi-protocollo
interface AuthProvider {
protocol: "saml2" | "oidc" | "openid4vp";
name: string;
endpoint: string;
}
const AUTH_PROVIDERS: AuthProvider[] = [
{
protocol: "oidc",
name: "SPID",
endpoint: "https://registry.spid.gov.it"
},
{
protocol: "oidc",
name: "CIE",
endpoint: "https://idserver.servizicie.interno.gov.it"
},
{
protocol: "openid4vp",
name: "IT Wallet / EUDI Wallet",
endpoint: "eudi-openid4vp://"
}
];
function selectAuthFlow(userAgent: string, userPreference?: string): AuthProvider {
// Logica di selezione basata su contesto
if (userPreference === "wallet" || hasWalletApp(userAgent)) {
return AUTH_PROVIDERS.find(p => p.protocol === "openid4vp")!;
}
// Default: CIE OIDC (LoA3 per impostazione predefinita)
return AUTH_PROVIDERS.find(p => p.name === "CIE")!;
}
Beste praktijken en antipatronen
Best practices voor SPID/CIE OIDC
- Gebruik altijd PKCE, zelfs voor webapplicaties (verplicht voor native apps)
- Implementeer JARM om de autorisatiereactie te beschermen
- Roteer RP-ondertekeningssleutels elke 12 maanden (of vaker)
- VS
subpaarsgewijze als interne identificatie — nooit attributen zoals CF of e-mail - Implementeer Single Logout (SLO) — verplicht voor SPID-accreditatie
- Cache de vertrouwensketen gedurende maximaal 24 uur (en update vervolgens)
- Controleer de vervaldatum van de Entiteitsverklaring (doorgaans 24 uur)
Antipatronen die u moet vermijden
- Sla het ID-token niet op
localStorageof niet-HttpOnly-cookies - Niet gebruiken
response_mode=query(VSform_postoform_post.jwt) - Stel het toegangstoken niet zonder geldige reden bloot aan de frontend
- Vraag geen onnodige SPID-attributen aan (accreditatierisico)
- Negeer de verificatie van
noncein de Token-ID - Gebruik geen niet-aflopende sessies: PA-sessies moeten time-outs hebben
Conclusies
OpenID Connect vertegenwoordigt de toekomst van Italiaanse en Europese overheidsauthenticatie. De transitie van SAML naar OIDC het is niet alleen technisch; het brengt een moderner ecosysteem, betere hulpmiddelen en een natuurlijk pad met zich mee naar een zelf-soevereine identiteit met de EUDI Wallet.
Ontwikkelaars gaan vandaag SPID/CIE OIDC implementeren met de juiste patronen: PKCE, JARM, automatische federatie, paarsgewijs onderwerp - bevinden zich in een optimale positie voor latere integratie met OpenID4VP en de EUDI Wallet. Code die vandaag goed is geschreven, hoeft morgen niet te worden herschreven: het is een investering in de continuïteit van publieke digitale diensten.
De GovTech-serie gaat verder
Ontdek in het volgende artikel het ontwerp van Open Data API's en hoe u openbare gegevens efficiënt kunt publiceren en gebruiken.







