Como verificar um endereço de e-mail sem enviar nenhum e-mail
Veja como a verificação de e-mail no navegador comprova o acesso à caixa sem enviar mensagem, com formulário, verificador de token Node.js em cinco etapas e limites do teste.
O Email Verification Protocol permite que um site confirme que o usuário controla um endereço de e-mail sem enviar nenhuma mensagem. O navegador obtém um token assinado pelo provedor de e-mail do usuário e o envia junto com o formulário.
A maioria dos fluxos de cadastro ainda diz “verifique sua caixa de entrada”, e parte das pessoas que trocam de aba para buscar o código nunca volta. O Chrome está testando uma alternativa. O origin trial do Chrome anunciado em 8 de julho de 2026 começou no Chrome 150, e o Chrome publicou depois uma atualização em agosto de 2026. O protocolo em si é definido no Internet-Draft da IETF draft-hardt-email-verification.
Isto não é verificação SMTP nem limpeza de listas por MX. Essas ferramentas checam se uma caixa de correio existe. Este protocolo prova que a pessoa que está preenchendo o formulário está autenticada nessa caixa de correio. As seções abaixo tratam do modelo de três partes, da marcação do formulário, de um verificador completo em Node.js dividido em cinco etapas e de até que ponto você pode confiar nele durante o trial.
Principais pontos
- O Email Verification Protocol retorna um SD-JWT com key binding: um Email Verification Token assinado pelo emissor, seguido de um til e de um JWT de key binding assinado pelo navegador.
- O navegador fica entre o seu site e o provedor de e-mail, de modo que o provedor emite o token sem saber qual site o solicitou.
- Um token válido prova que existe uma sessão ativa com o provedor de e-mail para aquele endereço. Ele não prova que as mensagens enviadas para o endereço serão entregues.
- O recurso é uma proposta em um origin trial do Chrome, com limites de tráfego e sem data de término nas publicações do Chrome. Nos slides da IETF 126, os autores do draft (e não o Chrome) estimaram de três a seis meses. A documentação do Chrome cita apenas o Gmail como provedor participante.
- Se o campo oculto do token chegar vazio, ou se qualquer etapa da verificação falhar, envie o seu e-mail de confirmação habitual.
O que a verificação de e-mail pelo navegador substitui?
A verificação de e-mail pelo navegador substitui o e-mail com código de uso único ou com magic link, que tira o usuário do seu site. O draft da IETF descreve o que o fluxo com código exige do usuário: sair para o aplicativo de e-mail, aguardar a entrega, talvez vasculhar a pasta de spam, copiar o código e depois voltar para colá-lo. O draft acrescenta que muita gente desiste no meio do caminho. Um magic link elimina a etapa de copiar e colar, mas o usuário ainda precisa ir à caixa de entrada e esperar a mensagem.
O login social dispensa o e-mail por completo. O draft aponta o custo disso: o usuário precisa ter uma conta naquele provedor, e você precisa criar uma integração para cada provedor. O Email Verification Protocol segue o mesmo princípio (o provedor atesta o endereço), mas funciona por meio do navegador, sem nenhum SDK específico de provedor no seu site.
Como o Email Verification Protocol funciona: três partes
O Email Verification Protocol envolve três partes. O seu site é o verificador (a relying party). O navegador fica entre você e o emissor, que é o serviço responsável pela conta de e-mail do usuário. Como todas as trocas passam pelo navegador, o emissor nunca descobre qual site solicitou o token.
Verifier (your site) <--> Browser <--> Issuer (e.g. accounts.google.com)
nonce, validation binds token checks session,
to your origin signs EVT
O provedor de e-mail e o emissor podem ser hosts diferentes. No exemplo do Chrome, o provedor é gmail.com e o emissor é accounts.google.com.
Do lado do emissor, o domínio de e-mail publica um registro DNS TXT que delega ao emissor. O emissor disponibiliza metadados em /.well-known/email-verification, informa o estado de login ao navegador pela Login Status API e, antes de assinar qualquer coisa, verifica os cookies de sessão first-party já existentes do usuário (veja Secure Cookies and HttpOnly para saber como esses cookies devem ser configurados). O draft também permite que o navegador solicite endereços privados por site, que o EVT marca com is_private_email: true. A documentação do trial do Chrome não descreve endereços privados. Revisões anteriores do draft permitiam que o emissor recorresse a um desafio WebAuthn quando não houvesse cookie válido, mas a revisão -02 removeu essa opção. Agora, uma solicitação sem sessão simplesmente falha.
O lado do site: marcação do formulário e nonce
Do seu lado, o formulário precisa de dois inputs: um campo de e-mail comum com autocomplete="email" e um campo oculto com autocomplete="email-verification-token", que carrega um nonce vinculado à sessão do usuário. Quando o formulário é enviado, o navegador preenche o campo oculto com o token. Esta é a marcação da publicação do Chrome, com o nonce renderizado pelo servidor:
<input
name="email-address"
type="email"
autocomplete="email">
<input
type="hidden"
name="token"
nonce="rAnD0m-VaLuE"
autocomplete="email-verification-token">
As regras de vinculação de sessão do draft exigem um nonce aleatório com pelo menos 128 bits de entropia. Gere um novo para cada solicitação de verificação e faça-o expirar após pouco tempo. Armazene junto com ele o horário de renderização, pois a etapa 2 do verificador precisa dessa informação:
import { randomBytes } from "node:crypto";
const nonce = randomBytes(32).toString("base64url"); // 256 bits
req.session.evp = { nonce, renderedAt: Date.now() };
A página também precisa do seu token de origin trial. A documentação de origin trials do Chrome explica como fornecê-lo. Segundo a atualização de agosto, origin trials de terceiros não são compatíveis com este recurso.
Verificando o token no servidor
A verificação de um token do Email Verification Protocol no servidor tem cinco etapas: fazer o parsing, checar os valores esperados, checar o key binding, checar a delegação via DNS e verificar a assinatura do emissor. O valor do formulário é <EVT>~<KB-JWT>. O EVT é o token assinado pelo emissor e contém email, email_verified, iss, iat e uma chave do navegador em cnf.jwk. O KB-JWT é assinado pelo navegador e contém aud, nonce, iat e sd_hash. Não confie em nenhuma claim decodificada até que todas as etapas abaixo tenham sido concluídas com sucesso. O código usa jose e módulos nativos do Node. Cada bloco roda dentro de um bloco try em uma única função verifyEmailToken(), que retorna false se qualquer coisa lançar uma exceção.
Estes são os imports de que as cinco etapas precisam:
import { createHash } from "node:crypto";
import { resolveTxt } from "node:dns/promises";
import {
createRemoteJWKSet,
decodeJwt,
decodeProtectedHeader,
importJWK,
jwtVerify,
} from "jose";
1. Parsing. Divida o valor no til e verifique os cabeçalhos typ.
const [evt, kb, ...rest] = token.split("~");
if (!evt || !kb || rest.length) return false;
const evtHeader = decodeProtectedHeader(evt);
const kbHeader = decodeProtectedHeader(kb);
if (evtHeader.typ !== "evt+jwt" || kbHeader.typ !== "kb+jwt") return false;
const evtClaims = decodeJwt(evt);
const kbClaims = decodeJwt(kb);
Os tokens atuais não trazem divulgações seletivas, então o valor se divide de forma limpa em duas partes. A atualização de agosto considera esse atalho frágil, porque ele quebraria caso divulgações fossem adicionadas no futuro. O Chrome recomenda fazer o parsing do token com uma biblioteca de SD-JWT, como a @sd-jwt/core, que a própria demo do Chrome passou a usar.
2. Valores esperados. As etapas de validação do Chrome checam email_verified, email, nonce, aud e um iat posterior à renderização do formulário. Compare o e-mail sem diferenciar maiúsculas de minúsculas: a atualização de agosto alerta que os provedores podem retornar o endereço com capitalização diferente da que o usuário digitou.
const { nonce, renderedAt } = req.session.evp ?? {};
delete req.session.evp; // single use
if (evtClaims.email_verified !== true) return false;
if (String(evtClaims.email).toLowerCase() !== submittedEmail.toLowerCase()) return false;
if (!nonce || kbClaims.nonce !== nonce) return false;
if (kbClaims.aud !== "https://example.com") return false;
if (kbClaims.iat < Math.floor(renderedAt / 1000)) return false;
// Any maximum age is your own policy; the sources define none.
3. Key binding. sd_hash é o SHA-256 do EVT incluindo o ~ final. O KB-JWT também precisa ser validado com a chave presente em cnf.jwk.
const hash = createHash("sha256").update(evt + "~").digest("base64url");
if (hash !== kbClaims.sd_hash) return false;
const holderKey = await importJWK(evtClaims.cnf.jwk, kbHeader.alg);
await jwtVerify(kb, holderKey, { typ: "kb+jwt" });
4. Delegação via DNS. Consulte _email-verification.<domain>. O draft exige exatamente um registro TXT, e o valor dele deve começar com iss=. Use dig +short TXT _email-verification.gmail.com para verificar um domínio manualmente.
const domain = submittedEmail.split("@").pop().toLowerCase();
const records = (await resolveTxt(`_email-verification.${domain}`))
.map((chunks) => chunks.join(""));
if (records.length !== 1 || !records[0].startsWith("iss=")) return false;
const issuer = records[0].slice(4);
5. Descoberta e assinatura do EVT. Busque os metadados do emissor e, em seguida, verifique o EVT com base no jwks_uri dele. O draft define o identificador do emissor como https:// seguido do host informado no registro DNS, e a claim iss do EVT precisa corresponder exatamente a ele.
const meta = await (await fetch(`https://${issuer}/.well-known/email-verification`)).json();
const jwks = createRemoteJWKSet(new URL(meta.jwks_uri));
const { payload } = await jwtVerify(evt, jwks, {
algorithms: meta.signing_alg_values_supported,
typ: "evt+jwt",
}); // written so it does not depend on a kid header
// The draft's issuer identifier is "https://" plus the DNS host, compared byte for byte.
if (payload.iss !== `https://${issuer}`) return false;
return true;
| Verificação | Artefato | Em caso de falha |
|---|---|---|
| Parsing | divisão no ~, cabeçalhos typ | Enviar e-mail de confirmação |
| Valores esperados | email_verified, email, nonce, aud, iat | Enviar e-mail de confirmação |
| Key binding | sd_hash, assinatura do KB-JWT vs. cnf.jwk | Enviar e-mail de confirmação |
| DNS | registro TXT único com iss= | Enviar e-mail de confirmação |
| Assinatura do emissor | metadados, jwks_uri, iss | Enviar e-mail de confirmação |
O módulo DNS do Node retorna cada registro TXT como um array de fragmentos, e é por isso que a etapa 4 os concatena.
Quais são os limites da verificação de e-mail pelo navegador?
Uma verificação bem-sucedida do Email Verification Protocol mostra que o usuário está, naquele momento, autenticado no provedor de e-mail com aquele endereço. Ela não diz nada sobre se as mensagens que você enviar para lá serão entregues. Tanto o Chrome quanto o draft reforçam esse ponto, então continue enviando e-mails de boas-vindas.
O perfil do navegador em que o usuário preenche o formulário precisa já ter uma sessão autenticada com o provedor. Na primeira vez que um endereço é verificado, o usuário vê uma solicitação de permissão, uma vez por endereço. Na versão de julho, o endereço precisava vir do preenchimento automático. Segundo a atualização de agosto, digitar ou colar o endereço agora também aciona a verificação quando o campo perde o foco. O draft também aponta um pequeno vazamento de privacidade: a chegada ou não de um token revela ao seu site se o usuário está autenticado no emissor.
O que fazer agora
O Email Verification Protocol ainda não está pronto para substituir o seu e-mail de confirmação. A API do navegador está em incubação no WICG, e o protocolo é um Internet-Draft individual (revisão -02, agosto de 2026). O Chrome limita o tráfego no trial para que os sites não construam funcionalidades em cima do recurso antes do lançamento. As publicações do Chrome não informam data de término. A estimativa de três a seis meses vem dos autores do draft, nos slides da IETF 126 DISPATCH, e não do Chrome. O Chrome também alerta para mudanças incompatíveis com versões anteriores, e a atualização de agosto informa que o recurso é exclusivo para desktop até o Chrome 152, com o suporte ao Android ainda em estudo. A documentação do Chrome cita apenas o Gmail como provedor participante. O draft também lista o Hellō como emissor com implementação e menciona um origin trial no Microsoft Edge.
A emissão já mudou uma vez. A publicação de julho descrevia um JWT request_token codificado como formulário. O draft o substitui por HTTP Message Signatures (RFC 9421) e marca o formato antigo como obsoleto. A partir do Chrome 153, o Chrome envia application/json com os cabeçalhos Signature, Signature-Input e Signature-Key, e um corpo que contém apenas o endereço de e-mail. Essa é uma mudança do lado do emissor e não afeta o código do seu verificador.
Mantenha o seu fluxo de OTP ou magic link como caminho padrão. Se você participar do trial, trate o token como aprimoramento progressivo: quando ele for validado, dispense o e-mail. Após a verificação, crie a sessão da forma que você já costuma fazer (veja Cookies vs localStorage for JWT Auth). O EVT+KB é uma prova de uso único e não deve ser usado como token de sessão.
O protocolo troca o “vá verificar sua caixa de entrada” por uma checagem de assinatura executada no seu próprio servidor, e o verificador precisa de apenas cinco etapas. Adicione o input oculto e o nonce, coloque a verifyEmailToken() em produção antes do seu e-mail de confirmação atual, que continua como fallback, e leia a publicação de atualização do Chrome antes de cada nova versão do navegador para acompanhar as mudanças.
Perguntas frequentes
Qual é a diferença entre o Email Verification Protocol e o Sign in with Google?
O Sign in with Google é uma integração de login específica de um provedor, que cria uma relação de identidade entre o seu site e o Google. O Email Verification Protocol apenas confirma que o usuário controla um endereço de e-mail. Ele não exige SDK de provedor no seu site e, como o navegador intermedeia a troca, o emissor assina o token sem saber qual site o solicitou.
Posso usar crypto.randomUUID() para gerar o nonce?
Não. Um UUID versão 4 contém apenas 122 bits aleatórios, porque os campos de versão e de variante ocupam os 6 bits restantes. O draft da IETF exige um nonce com pelo menos 128 bits de entropia. Use randomBytes(32).toString('base64url') do node:crypto, que fornece 256 bits. Armazene o nonce na sessão do servidor e exclua-o após um único uso.
O Email Verification Protocol funciona com endereços em domínios próprios ou em provedores além do Gmail?
Somente se o domínio de e-mail publicar exatamente um registro TXT _email-verification, iniciado por iss=, que delegue a um emissor compatível com o protocolo. A documentação do Chrome cita o Gmail como provedor de e-mail participante. Para qualquer outro domínio sem esse registro, o navegador não consegue obter um token, então o seu verificador deve tratar o caso como uma verificação malsucedida e enviar o e-mail de confirmação normal.