OpenID Connect dla tożsamości rządowej
SPID i CIE ewoluują w kierunku OpenID Connect: jak wdrożyć stronę ufającą dla włoskich systemów tożsamości, skonfiguruj federację OIDC, zarządzaj poziomami pewności (LoA), zabezpiecz przepływy za pomocą PKCE i JARM oraz przygotuj się do konwergencji z ekosystemem portfela EUDI.
Ewolucja w kierunku OIDC we włoskiej tożsamości rządowej
Od ponad dekady włoska tożsamość cyfrowa opiera się na protokole SAML 2.0: solidny, dobrze ustandaryzowany, ale zaprojektowany w erze przed mobilną. Wraz z eksplozją natywnych aplikacji na smartfony i rozpowszechnieniem aplikacji jednostronicowych, SAML pokazał swoje ograniczenia — wymiana nieporęcznych plików XML, słaba obsługa mobilna, złożona integracja z SPA.
Przejście do Połączenie OpenID (OIDC) było to nieuniknione. W styczniu 2023 r. AgID opublikował Wytyczne OpenID Connect w SPID e le Wytyczne OpenID Connect w CIE, czyniąc standard oparty na OAuth 2.0 oficjalnym jako ścieżkę ewolucji tożsamości włoskiego rządu. Cel PNRR wymaga, aby do marca 2026 r. wszystkie 16 500 włoskich agencji płatniczych przyjęły identyfikację elektroniczną za pośrednictwem SPID lub CIE.
Dlaczego OpenID Connect vs SAML dla PA
- Lekkie tokeny: JWT zamiast asercji XML SAML — ładowność zmniejszona o 60-70%
- Najpierw mobilnie: OAuth 2.0 z PKCE przeznaczony dla aplikacji natywnych i SPA
- Nowoczesny ekosystem: biblioteki dostępne dla dowolnego języka/frameworka
- Standaryzowana federacja: OpenID Federation 1.0 dla automatycznego łańcucha zaufania
- W stronę portfela EUDI: OpenID4VP i OpenID4VCI używają tych samych prymitywów OIDC
Architektura Federacji SPID/CIE OIDC
Na jej podstawie opiera się federacja OIDC dla SPID i CIE Federacja OpenID 1.0, standard automatyzujący budowę łańcucha zaufanie między podmiotami. To przewyższa ręczny model rejestracji metadanych SAML, w którym każdy dostawca usług musiał ręcznie wymieniać metadane XML z każdym dostawcą tożsamości.
Podmioty w Federacji
- Kotwica zaufania (TA): AgID jest kotwicą zaufania włoskiej federacji. Publikuje konfigurację jednostki w dobrze znanym punkcie końcowym.
- Mediator: autoryzowani agregatorzy AgID, którzy łączą wiele RP (np. regionalne agencje płatnicze, agregatorzy gminni).
- Strona uzależniona (RP): aplikacja uwierzytelniająca użytkowników poprzez SPID/CIE OIDC.
- Dostawca tożsamości (OP): dostawcy SPID (Poste Italiane, Aruba, InfoCert itp.) lub CIE IdP Ministerstwa Spraw Wewnętrznych.
Konfiguracja jednostki i łańcuch zaufania
// 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"
]
}
Wdrażaj przepływ kodów autoryzacyjnych w PKCE
Zalecany przepływ dla SPID/CIE OIDC toPrzepływ kodu autoryzacyjnego z PKCE (Klucz próbny do wymiany kodów, RFC 7636). PKCE chroni przed przechwyconymi kodami autoryzacyjnymi i jest obowiązkowe w przypadku aplikacji mobilnych i zalecane w przypadku aplikacji internetowych.
Krok 1: Żądanie wygenerowania i autoryzacji PKCE
// 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()}`);
}
Krok 2: Oddzwonienie i wymiana tokenów
// 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);
}
Poziomy pewności (LoA) w SPID i CIE
Jednym z najważniejszych aspektów tożsamości rządu jest Poziom pewności (LoA), co wskazuje, jak niezawodny jest proces uwierzytelniania. SPID definiuje trzy poziomy:
| Poziom | URI ACR | Metoda | Typowe zastosowanie |
|---|---|---|---|
| SpidL1 | https://www.spid.gov.it/SpidL1 | Nazwa użytkownika + hasło | Usługi niskiego ryzyka, konsultacje dotyczące danych niewrażliwych |
| SpidL2 | https://www.spid.gov.it/SpidL2 | Nazwa użytkownika + hasło + OTP/push | Standardowe usługi PA (deklaracje, wnioski, płatności) |
| SpidL3 | https://www.spid.gov.it/SpidL3 | Certyfikowane urządzenie fizyczne | Operacje wysokiego ryzyka, kwalifikowany podpis elektroniczny |
Dla CIE (elektroniczny dowód tożsamości), poziom pewności to zawsze LoA3 (maksymalny), jeśli chodzi użył chipa CIE NFC, gdyż wymaga to fizycznego posiadania dokumentu i PIN-u. To sprawia, że CIE jest oczywistym wyborem dla usług wymagających silnego uwierzytelniania.
Punkt końcowy UserInfo i atrybuty SPID/CIE
Po zalogowaniu Strona Uzależniona może pobrać atrybuty użytkownika z Punktu Końcowego UserInfo za pomocą Tokena Dostępu. Atrybuty dostępne w SPID OIDC zdefiniowane są w profilu technicznym:
// 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
// }
// }
Prywatność i minimalizacja atrybutów SPID
Żądanie kodu podatkowego użytkownika bez rzeczywistej potrzeby stanowi naruszenie zasady minimalizacji RODO. Wytyczne AgID określają, że każdy żądany atrybut musi być motywowany konkretną funkcjonalnością usługi, np wskazane w polityce prywatności. Podczas audytu AgID może wystąpić brak uzasadnienia żądanych atrybutów prowadzić do cofnięcia akredytacji.
SDK i oficjalne biblioteki dla SPID/CIE OIDC
Projekt Deweloperzy Włochy (developers.italia.it) udostępnia oficjalne pakiety SDK w różnych językach aby uprościć integrację SPID/CIE OIDC:
## 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: Tryb odpowiedzi autoryzacyjnej oparty na JWT
Wytyczne SPID/CIE OIDC zalecają stosowanie JARM (tryb odpowiedzi autoryzacyjnej JWT) chronić
odpowiedź autoryzacyjną. Zamiast przejść code e state w postaci zwykłego tekstu jako parametry zapytania,
JARM hermetyzuje odpowiedź w JWT podpisanym przez dostawcę tożsamości.
// 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
Zarządzanie sesjami i odświeżanie tokenów
Krytycznym aspektem tożsamości rządu jest prawidłowe zarządzanie sesjami. W odróżnieniu od sektora prywatnego, Usługi PA muszą przestrzegać rygorystycznych ograniczeń dotyczących czasu trwania sesji i wylogowania.
// 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()}`
);
}
Akredytacja i zgodność z AgID
Aby zintegrować SPID ze swoją usługą, Strona Ufająca musi przejść proces akredytacji AgID. Od 2023 r. dostępna będzie uproszczona procedura OIDC:
-
Rejestracja techniczna: Konfiguracja konfiguracji jednostki i weryfikacja punktu końcowego
/.well-known/openid-federation. AgID sprawdza zgodność za pomocą automatycznych narzędzi. - Testy funkcjonalne: Użycie spid-sp-test (narzędzie Pythona typu open source) w celu sprawdzenia poprawności implementacji. Narzędzie symuluje zachowanie Dostawców Tożsamości i weryfikuje ponad 200 przypadków testowych.
- Zgodność z RODO: dokumentacja przetwarzania danych dla każdego wymaganego atrybutu SPID.
- Podpisanie umowy: akceptacja Regulaminu Technicznego SPID i umowy członkowskiej.
## 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
W stronę konwergencji: portfel SPID/CIE OIDC i EUDI
Przejście z SAML na OIDC nie jest ostatecznym celem. Ekosystem zmierza w stronę całkowitej integracji z Portfel EUDI poprzez OpenID4VP. Projekt iam-proxy-italia na GitHub już demonstruje w jaki sposób serwer proxy IAM może jednocześnie obsługiwać SAML 2.0, OIDC i OpenID4VC (dla portfela IT/EUDI Wallet).
Idealna architektura nowoczesnej usługi PA powinna w przejrzysty sposób obsługiwać wszystkie trzy przepływy:
// 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")!;
}
Najlepsze praktyki i anty-wzorce
Najlepsze praktyki dla SPID/CIE OIDC
- Zawsze używaj PKCE nawet w przypadku aplikacji internetowych (obowiązkowe w przypadku aplikacji natywnych)
- Zaimplementuj JARM, aby chronić odpowiedź autoryzacyjną
- Rotuj klucze podpisywania RP co 12 miesięcy (lub częściej)
- USA
subparami jako identyfikator wewnętrzny — nigdy atrybuty takie jak CF lub e-mail - Wdrożenie pojedynczego wylogowania (SLO) — obowiązkowe w przypadku akredytacji SPID
- Buforuj łańcuch zaufania przez maksymalnie 24 godziny (następnie zaktualizuj)
- Monitoruj wygaśnięcie wyciągu z jednostki (zazwyczaj 24h)
Anty-wzorce, których należy unikać
- Nie zapisuj tokena identyfikacyjnego w
localStoragelub pliki cookie inne niż HttpOnly - Nie używać
response_mode=query(USAform_postoform_post.jwt) - Nie udostępniaj tokenu dostępu frontendowi bez ważnego powodu
- Nie żądaj niepotrzebnych atrybutów SPID (ryzyko akredytacji)
- Nie ignoruj weryfikacji
noncew identyfikatorze tokena - Nie używaj sesji, które nie wygasają: sesje PA muszą mieć limity czasu
Wnioski
OpenID Connect reprezentuje przyszłość uwierzytelniania rządów włoskich i europejskich. Przejście z SAML na OIDC to nie tylko kwestia techniczna — niesie ze sobą bardziej nowoczesny ekosystem, lepsze narzędzia i naturalną ścieżkę w kierunku suwerennej tożsamości z portfelem EUDI.
Programiści rozpoczynający dziś wdrażanie SPID/CIE OIDC z poprawnymi wzorcami — PKCE, JARM, automatyczna federacja, Temat w parach — znajdują się w optymalnej pozycji do późniejszej integracji z OpenID4VP i portfelem EUDI. Kod napisany dzisiaj dobrze nie wymaga jutro pisania od nowa: to inwestycja w ciągłość publicznych usług cyfrowych.
Seria GovTech trwa
W następnym artykule poznasz konstrukcję interfejsów API Open Data oraz sposoby efektywnego publikowania i wykorzystywania danych publicznych.







