>A carteira física vai morrer — não em 2026, mas para quem constrói software de identidade, autenticação e KYC, ela já morreu. A União Europeia está finalizando o European Digital Identity Wallet (EUDI Wallet), um sistema baseado em W3C Verifiable Credentials que vai unificar identidade digital em 27 países. E a maioria dos devs nem percebeu por onde esse assunto vai entrar na sua stack.
Segundo o Sapo.pt, o regulamento eIDAS 2.0 entrou em vigor em maio de 2024 e estabelece o final de 2026 como prazo para disponibilização das carteiras aos cidadãos. Mas o detalhe que mata para quem programa é outro: os prestadores de serviços digitais serão obrigados a aceitar a carteira se o cidadão quiser usá-la. E aí começa a parte interessante.
O que muda com o eIDAS 2.0 — e por que devs devem prestar atenção agora
O eIDAS original (2014) já regulava assinaturas eletrônicas e identidade digital na UE, mas era fragmentado: cada país tinha seu sistema nacional (Cartão de Cidadão em Portugal, SPID na Itália, FranceConnect na França). O eIDAS 2.0 impõe interoperabilidade técnica via especificações comuns publicadas no EUDI Wallet Architecture Reference Framework. Isso muda o jogo para qualquer dev que atende clientes europeus.
Na prática, três pilares técnicos vão definir o ecossistema:
- W3C Verifiable Credentials (VC) — modelo de dados para credenciais verificáveis criptograficamente.
- SD-JWT VC — variante de JWT com disclosure seletiva, que permite revelar só os atributos necessários (provar que tem mais de 18 anos sem mostrar a data de nascimento).
- ISO 18013-5 (mDoc) — padrão mobile usado também como base técnica em vários países.
O fluxo segue o modelo Issuer → Holder → Verifier. Um governo emite uma credencial (“João tem 32 anos, NIS 123…”), o cidadão guarda no app da wallet, e quando precisa provar algo para um serviço (banco, loja, hospital) apresenta apenas os atributos necessários com prova criptográfica. É KYC sem fricção e sem upload de documento.
A stack técnica por trás da carteira europeia
Quando comecei a ler os documentos do EUDI Wallet com olho de dev, percebi que o consórcio escolheu tecnologias pragmáticas — não reinventou a roda. O padrão de emissão é o OpenID for Verifiable Credential Issuance (OpenID4VCI) e o de apresentação é o OpenID for Verifiable Presentations (OpenID4VP). Ambos são extensões do OpenID Connect que você já conhece — só que com semântica de credenciais em vez de tokens de sessão.
Por que SD-JWT e não blockchain?
Pergunta justa, e a resposta me surpreendeu pela honestidade. Blockchain resolveria revogação distribuída, mas custaria caro em privacidade (tudo público) e em latência. A escolha por SD-JWT com status lists é um trade-off consciente: PKI tradicional funciona, e a UE já tem infraestruturas de chaves públicas robustas nos países-membros.
O esquema de disclosure seletiva funciona com salt-based hashing: cada campo tem um hash com salt, e o holder revela o salt apenas dos campos que quer expor. O verifier confirma o hash sem ver os outros campos. É elegante, e a biblioteca de referência está em openwallet-foundation-labs.
Key binding: o detalhe que ninguém fala
Uma credencial SD-JWT sem key binding é só um JSON bonito. Para evitar replay attacks, a credencial precisa ser vinculada à chave privada do holder via proof of possession no momento da apresentação. Isso usa normalmente ES256 ou EdDSA. Se você está implementando um verifier e ignora isso, qualquer um que copiar o JWT consegue apresentá-lo. Cuidado com essa armadilha.
Na Prática: como um Verifier valida uma SD-JWT VC
Imagine que você está construindo um e-commerce que precisa verificar a idade do cliente para vender álcool. Em vez de pedir upload de documento, você pede a apresentação via EUDI Wallet. O fluxo cross-device funciona via dynamic QR code ou deep link com um request_uri seguindo OpenID4VP.
Quando a wallet apresenta a credencial, seu backend recebe algo assim (formato compactado do SD-JWT):
eyJhbGciOiJFUzI1NiIsInR5cCI6Im1kb2Mrc2QtanQifQ.eyJpc3MiOiJodHRwczovL2lzc3Vlci5leGFtcGxlLmNvbSIsInN1YiI6ImRpZDpleGFtcGxlOmFiY2RlMTIzIiwiaWF0IjoxNzE3MDk1MjAwLCJpc3N1ZWRfYXQiOjE3MTcwOTUyMDAsImV4cGlyZXNfYXQiOjE5MjUwOTUyMDAsInN0YXR1cyI6eyJzdGF0dXNfbGlzdCI6eyJpZCI6Imh0dHBzOi8vZXhhbXBsZS5jb20vc3RhdHVzIiwiaW5kZXgiOjEzNH19LCJzZCI6eyJnaXZlbl9uYW1lIjoiX3BkUWd0VGFsN2VEemZ0RkpCejg1Wnd0bkMwUTcyVHJDSkQiLCJmYW1pbHlfbmFtZSI6Il9wZFFndFRhbDdZRGpNd2tHRnlvV0tVcW5oN05wNHRnYU4ifX0.WqU3Oi48H1nG5X5X9lJ2Fg~WyJnZW9yZ2UiLCJEZW1vIiwxOF0~WyJnaXZlbl9uYW1lIiwiSm9obyJd
Esse token tem três partes concatenadas por ~: o JWT base, as disclosures (cada uma é um array [salt, claim_name, claim_value] codificado em base64url) e a key binding JWT. Para validar tudo, o esqueleto fica assim:
import json, base64, hashlib
from typing import List
def decode_disclosure(disclosure_b64: str):
raw = base64.urlsafe_b64decode(disclosure_b64 + "==")
salt, name, value = json.loads(raw)
digest = hashlib.sha256(disclosure_b64.encode()).digest()
return name, value, digest.hex()
def verify_age_credential(
sd_jwt_compact: str,
issuer_public_key: bytes,
expected_min_age: int = 18
) -> dict:
parts = sd_jwt_compact.split("~")
jwt_part = parts[0]
disclosures = [p for p in parts[1:-1] if p]
kb_jwt = parts[-1] if parts[-1].startswith("eyJ") else None
# 1. Verifica assinatura do JWT contra chave do issuer (via JWKS em prod)
# payload = PyJWSClient.verify(jwt_part, issuer_public_key)
payload = {"_sd_algoritm": [hashlib.sha256(disclosures[0].encode()).digest().hex()]}
# 2. Calcula os hashes das disclosures e confere com o claim _sd_algoritm
revealed = {}
for disc in disclosures:
name, value, digest_hex = decode_disclosure(disc)
if digest_hex in payload["_sd_algoritm"]:
revealed[name] = value
# 3. Aplica a regra de negócio (apenas o necessário!)
birth_year = int(revealed.get("birthdate", "0")[:4])
if 2026 - birth_year < expected_min_age:
raise ValueError("Idade insuficiente")
# 4. Verifica key binding (ESSENCIAL, nunca pule isso)
if kb_jwt and not verify_key_binding(kb_jwt, jwt_part, expected_min_age):
raise ValueError("Falha no key binding")
return revealed
def verify_key_binding(kb_jwt: str, parent_jwt: str, nonce: int) -> bool:
# Pseudocódigo: verificar assinatura do kb_jwt contra pub key do holder
# e checar nonce + aud + cnf.hash do parent_jwt
return True # substitua pela verificação real
# Uso:
# claims = verify_age_credential(token, issuer_pub_key)
# print(claims) # {'given_name': 'João'}
Esse é o esqueleto de qualquer validador. Em produção, use a biblioteca oficial sd-jwt-python ou sua porta em TypeScript, sd-jwt-ts. Testei isso em ambiente de homologação e a lib oficial evita umas cinco classes de bugs sutis de padding base64 e ordenação de disclosures.
Erros comuns que devs vão cometer (e como evitar)
Depois de ler a spec e brincar com o wallet de referência da Comissão Europeia, anotei os tropeços mais prováveis:
- Tratar como OAuth 2.0 e parar por aí. OIDC entrega identidade do provedor para o app. EUDI entrega credenciais verificáveis que podem ser apresentadas offline e cross-organização. Modelos mentais diferentes — usar a mesma abstração leva a falhas de segurança.
- Esquecer o status list. Uma credencial revogada continua válida criptograficamente. Você precisa consultar o
status_listno JWT ou a URL apontada. Sem isso, credenciais canceladas ainda passam — e isso é um vetor sério de fraude. - Não validar a chain de confiança. O issuer não é a CA raiz. Existe uma hierarquia (Trust List da UE) que precisa ser baixada e validada via assinatura. Veja a spec de trust list.
- Assumir que disclosure seletiva é opcional. Se você pede a credencial inteira para validar idade, está vazando nome, NIS, foto, nacionalidade. Vai falhar compliance GDPR em poucas auditorias.
- Ignorar presentation definitions (PD). A spec DIF Presentation Exchange define exatamente quais campos você precisa. Pedir demais quebra UX; pedir de menos falha o fluxo inteiro.
- Não testar offline. A grande vantagem do modelo é apresentação presencial sem rede (NFC + BLE no padrão ISO 23220). Se sua lógica depende de internet, vai quebrar em filas de banco e portarias físicas.
Quando você vai precisar disso no seu código?
Não é teoria distante. Os casos reais vão chegar antes do que você pensa:
- KYC em fintechs e cripto: o regulamento MiCA já cita wallets compatíveis com eIDAS como caminho preferencial de onboarding.
- Saúde digital: prescrições e atestados europeus vão circular como VCs cross-border até 2027.
- e-Signature qualificada: o QES (Qualified Electronic Signature) passa a poder ser emitido direto da wallet, eliminando token USB e cartão.
- Verificação de diplomas e certificados profissionais: a European Digital Credentials for Education já usa o mesmo modelo e está ativa.
Se você atende clientes na UE e qualquer um desses fluxos toca seu sistema, 2026 é deadline de produção. Na minha experiência, integração que parece “para o ano que vem” vira incêndio em novembro — planeje agora.
FAQ — Perguntas que devs realmente fazem
O EUDI Wallet substitui autenticação por senha?
Não substitui, complementa. Você pode usar a wallet como provedor de identidade (login federado) via OpenID4VP, mas para sessões em apps você ainda vai precisar de sessão/token próprio. Pense nela como substituto do “Login com Google” e do upload de documentos ao mesmo tempo.
Funciona offline?
Sim, no fluxo presencial via NFC/BLE (ISO 23220-3 e 18013-5). Online, depende do verifier: se você aceita status list via cache local assinada, consegue validar sem rede por algumas horas.
Quanto custa integrar como relying party?
Se você já tem OIDC no stack, o esforço principal é implementar OpenID4VP no front (cross-device via DC API/QR) e adicionar validação de SD-JWT VC no back. Estimo de 2 a 6 semanas para um time sênior, dependendo do stack. A parte difícil é montar os testes — felizmente a suíte de testes de conformidade da UE existe e é bem completa.
Tem alternativa open source ao wallet de referência?
Sim. O consórcio OpenWallet Foundation (Linux Foundation) mantém implementações. O time do EUDI publicou tudo no GitHub oficial. Para issuers e verifiers, há SDKs em Python, Kotlin e Swift prontos para usar.
Como isso afeta devs fora da UE?
Indiretamente, é o maior case de identidade digital soberana do mundo. EUA, Reino Unido, Singapura e até o Brasil (gov.br com seus movimentos recentes de interoperabilidade) estão observando. Os padrões técnicos (W3C VC, SD-JWT, OpenID4VP) são globais — aprender agora te coloca à frente quando sua região decidir adotar.
Gostou? Me segue no GitHub e deixa um comentário se tiver dúvida ou quiser aprofundar algum ponto.