Componentes
66. Credenciais — senha e código de uso único
seed-componentes.md v1.43 · §66seção 72 de 9866-credenciais-senha-e-codigo-de-uso-unico-estavel.md · MD5 e3789c03Título completo no canon: Credenciais — senha e código de uso único — estável (F7.5, 2026-08-16 · promovido em 2026-08-17 pelo gate visual do Rafael — verbatim: "aprovado") · CD1–CD16 · fecha C13 e C14 · bancada banco-credencial.html v0.2 · suite-credencial.mjs 30 · 0 · 0
Por que os dois campos moram na MESMA seção — e o que foi descartado. C13 (senha com revelar) e C14 (campo de código/OTP) chegaram na fila como duas lacunas. Viraram uma seção porque são governados pela mesma norma e falham pelo mesmo mecanismo: o SC 3.3.8 Accessible Authentication (Minimum) da WCAG 2.2 proíbe teste de função cognitiva em qualquer etapa da autenticação — memorizar, transcrever, soletrar, calcular. Bloquear o colar, atrapalhar o gerenciador de senha e exigir transcrição são o mesmo defeito nos dois campos, e o conserto é o mesmo. Descartado: duas seções (§66 e §67). Separá-las duplicaria os contratos idênticos — colar,
autocomplete, mensagem de erro — e cópia de contrato é a classe de problema que otokens_bloco.pyexiste para impedir. Se um dia um dos dois ganhar contrato que o outro não pode ter, a separação nasce por supersede formal, não por conveniência.
66.0 Rodada zero — NÃO VISITADO, com o motivo dito#
| O quê | Veredito | Motivo |
|---|---|---|
| Tela de autenticação do produto de referência (arquétipo A19) | NÃO VISITADO | Toda a leitura do estudo-clickup-completo.md foi feita dentro de uma sessão logada. Ver a tela de login exigiria sair da sessão da conta corporativa — risco operacional real, e a autorização de navegação é de leitura, não de mexer em estado de conta |
| Fluxo de recuperação com código | NÃO VISITADO — e bloqueado | Ele só existe depois de disparar um e-mail de verdade. Disparar é escrita, e escrita não está autorizada (mesma barreira nomeada da PS-P2) |
E aqui a ausência de leitura visual custa pouco, e é preciso dizer por quê para não parecer conveniência: campo de credencial é o arquétipo do sistema em que a norma é mais densa e mais específica — o SC 3.3.8 chega ao ponto de dizer que colar tem de funcionar. Onde a norma decide, copiar o desenho do vizinho não acrescenta; mede-se contra a norma. É o oposto do §57 (quadro), em que a norma diz pouco e a leitura visual decidiu quase tudo.
66.1 RITO — três rodadas encadeadas#
| Rodada | Fonte | O que trouxe |
|---|---|---|
| R1 — canon | W3C, Understanding SC 3.3.8 Accessible Authentication (Minimum) | Define teste de função cognitiva como "memorização, transcrição, uso de ortografia correta, cálculos ou resolução de puzzles". Verbatim que decide o C14: "um serviço que requer transcrição manual de código de verificação não é compatível. Como com usuários e senhas, deve ser possível ao usuário pelo menos COLAR o código." E: bloquear paste ou exigir "o 1º, 3º e 5º caractere" falha o critério. Gerenciador de senha e copiar-colar são listados como mecanismos conformantes |
| R2 — mercado/stack | GOV.UK Design System — Password input · Cloud Four — Simple One-Time Passcode Inputs | GOV.UK: o revelar é button com aria-label “Show password”/“Hide password”, anúncio “Your password is visible/hidden”, type volta a password no envio (senão o navegador guarda o valor como texto e o oferece em autofill de campo comum), spellcheck="false" contra spell-jacking, sem maxlength, e o achado de pesquisa: “ter um segundo campo não ajuda os usuários” — o campo “confirme a senha” sai. Cloud Four: OTP é um <input> com inputmode="numeric", autocomplete="one-time-code", pattern e maxlength; a aparência de caixinhas se faz com tipografia (mono + letter-spacing), não com seis elementos |
| R3 — normas | WCAG 2.2: SC 1.3.5 Identify Input Purpose · SC 1.4.11 Non-text Contrast · SC 2.2.1 Timing Adjustable · SC 4.1.3 Status Messages | autocomplete correto é o que permite o preenchimento automático (1.3.5) e o que torna o gerenciador um mecanismo (3.3.8). A borda do campo é elemento não-textual e cobra 3:1 (1.4.11) — foi por aí que apareceu o achado do 66.4. O prazo do código cai na exceção Essential do 2.2.1: estender invalidaria a atividade; o contrato então não é estender, é declarar o prazo e oferecer reenvio |
66.2 Os contratos CD1–CD16#
| # | Contrato | Por quê | Descartado |
|---|---|---|---|
| CD1 | Colar nunca é bloqueado — senha e código | SC 3.3.8 literal. E é o contrato que a suíte prova usando a área de transferência de verdade (Ctrl+V), não lendo atributo | “Bloquear paste por segurança”: empurra a pessoa para senha memorizável, que é menos segura |
| CD2 | autocomplete correto e obrigatório: username · current-password · new-password · one-time-code |
É o que faz o gerenciador de senha funcionar — e gerenciador é mecanismo conformante do 3.3.8, além do SC 1.3.5 | autocomplete="off" em campo de senha (prática comum, hostil, e ignorada pelos navegadores modernos) |
| CD3 | Senha com spellcheck="false" e autocapitalize="none" |
Spell-jacking: o corretor pode enviar o conteúdo do campo a um serviço remoto. E capitalizar a primeira letra corrompe a senha em teclado móvel | Deixar o padrão do navegador (o padrão não conhece o contexto) |
| CD4 | Senha sem maxlength; o limite vira mensagem de erro |
Truncar em silêncio faz a pessoa guardar uma senha que ela não digitou — e descobrir no próximo login | maxlength “para proteger o banco” (o hash tem tamanho fixo; o limite é do formulário, não do dado) |
| CD5 | Sem campo “confirme a senha” | Achado de pesquisa do GOV.UK: “um segundo campo não ajuda”. Com revelar disponível, ele é digitação dobrada sem ganho | Confirmação obrigatória (convenção herdada de quando não existia revelar) |
| CD6 | O revelar é button type="button", com aria-controls e nome que diz o efeito |
Dentro de um <form>, botão sem type é SUBMIT — um “mostrar senha” que envia o formulário é defeito que só aparece no primeiro uso real. E “olho” mudo não é nome |
checkbox “mostrar senha” (o estado do controle não é o estado do campo); ícone sem nome acessível |
| CD7 | A visibilidade é anunciada, não só refletida no botão | Quem não vê a tela precisa saber que a senha está exposta em texto — é informação de segurança física (alguém atrás), não de UI | Só trocar o rótulo do botão (não é anúncio; a AT não relê o botão sozinha) |
| CD8 | Ao enviar, o campo volta a type="password" |
GOV.UK, verbatim: senão o navegador memoriza o valor como texto digitado e passa a oferecê-lo em autofill de campos não-senha | Deixar revelado (vazamento silencioso, e a pessoa não fez nada errado) |
| CD9 | A senha começa oculta; revelar é ação deliberada | Padrão seguro. O estado inicial não pode depender de quem não escolheu nada | Começar revelada “porque é mais fácil” |
| CD10 | O código é UM campo — nunca N caixinhas | Com maxlength="1", colar o código deixa um dígito. Isso é transcrição manual obrigatória: falha do 3.3.8. Some-se seis paradas de tabulação para um dado só e o autofill do sistema sem alvo |
Seis caixinhas — o desenho mais comum do mercado. Está impresso na bancada, operante e na sua MELHOR forma, e a suíte mede os dois (ver 66.4-b) |
| CD11 | No código, inputmode="numeric", pattern e maxlength são legítimos |
Aqui o comprimento é do sistema (o código tem 6 dígitos por definição), não da pessoa. É o oposto do CD4, e o porquê está dito — a mesma propriedade é certa num campo e errada no outro | Tratar os dois campos com a mesma regra “sem maxlength” (regra sem motivo vira dogma) |
| CD12 | O prazo do código é declarado em texto, e existe reenvio | SC 2.2.1, exceção Essential: estender o prazo invalidaria a atividade. O que se pode fazer é não esconder e dar caminho de saída | Contador regressivo animado (pressão, e vira ruído em leitor de tela); prazo só no e-mail |
| CD13 | Erro de credencial não diz qual metade errou; erro de formato diz | “Essa senha está errada” confirma que o usuário existe — enumeração de contas. Mas “o código tem 6 dígitos” não revela nada e evita uma tentativa perdida. A fronteira é: formato ajuda, existência não | Mensagem específica sempre (vaza); mensagem genérica sempre (esconde erro de digitação do próprio usuário) |
| CD14 | Força da senha com texto e forma, e ela não bloqueia o envio | Barra colorida sozinha é o mesmo defeito das quatro bandeiras em quatro cores do §63 (C131): em cinza viram a mesma barra. Aqui a quantidade de blocos preenchidos é a forma | Bloquear envio por “senha fraca” (regra de política do produto, não do DS — e empurra para senha decorável) |
| CD15 | Caps Lock ligado é aviso anunciado, não erro | Com a senha oculta a pessoa vê pontinhos idênticos e não tem como saber por que falhou | Detectar só depois do erro (tarde); nada (o padrão, e é o motivo de metade dos “esqueci a senha”) |
| CD16 | Nenhuma transcrição parcial (“digite o 1º, 3º e 5º caractere”) | Falha citada nominalmente no Understanding do SC 3.3.8, e incompatível com gerenciador de senha | O padrão bancário brasileiro de “letras da senha” (é exatamente o caso que a norma proíbe) |
66.3 Os números medidos#
| Medida | Claro | Escuro |
|---|---|---|
| Erro de credencial (texto sobre fundo de perigo) | 6,58 | 9,47 |
| Erro de formato | 6,58 | 9,47 |
| Aviso de Caps Lock | 9,05 | 9,27 |
| Confirmação do código | 5,89 | 9,35 |
| Dica do campo · rótulo da força | 7,75 | 8,11 |
| Borda do campo (não-textual, piso 3,00) | 3,38 | 4,50 |
A prova do CD10, medida em casa e não citada: colado o mesmo 482913 nos dois desenhos, com
clipboard real e Ctrl+V —
| Desenho | O que ficou |
|---|---|
| Campo único (canônico) | 482913 — 6 de 6 dígitos |
| Seis caixinhas (contraexemplo) | 4 · vazio · vazio · vazio · vazio · vazio — 1 de 6 |
66.4 O achado da BORDA — e a decisão, tomada depois de ler o gêmeo inteiro#
A guarda de contraste reprovou a borda do campo: --seed-border-default (#C8D6DF) sobre
--seed-surface-raised (#FFFFFF) mede 1,49 no claro e 2,32 no escuro. Quando o fundo do
campo é igual ao da superfície em volta — que é o caso —, a borda é o único indicador de onde
se pode clicar, e o SC 1.4.11 cobra 3:1.
Medidos os quatro candidatos do gêmeo, nos dois temas, contra a superfície elevada:
| Token | Claro | Escuro |
|---|---|---|
--seed-border-default |
1,49 ✗ | 2,32 ✗ |
--seed-border-strong |
2,53 ✗ | 4,50 ✓ |
--seed-border-focus |
3,22 ✓ | 8,31 ✓ |
--seed-border-interactive |
3,38 ✓ | 4,50 ✓ |
E aí a leitura do gêmeo mudou a decisão que eu ia tomar. A primeira conclusão desta rodada foi
"o §13 declara --seed-field-border (cinza-600, 4,74:1) e esse token não existe no
seed-tokens.css; logo, promova-o ao gêmeo". Errado — e o próprio gêmeo diz por quê. O comentário
da v1.12 do seed-tokens.css, escrito ao criar o --seed-border-interactive:
"fronteira de COMPONENTE INTERATIVO (SC 1.4.11, piso 3:1) — nenhum outro semântico de borda passava no claro (default 1.49, strong 2.53) … medido contra as seis superfícies 3.38/3.38/3.11/4.50/5.51/5.05, todos ≥3."
O sistema já tinha resolvido esta pergunta, com os mesmos números que eu remedi. Promover o
--seed-field-border ao gêmeo criaria um segundo semântico para a mesma coisa — exatamente o
drift que o tokens_bloco.py existe para impedir.
Decisão, com supersede formal: o --seed-border-interactive é o semântico canônico de
fronteira de controle em todo o sistema. O --seed-field-border do §13 — declarado localmente
dentro da banco-formfield.html, nunca no gêmeo — fica SUPERSEDIDO por ele.
E a decisão não é só de consistência: é melhor NOS DOIS TEMAS somados.
| Claro | Escuro | |
|---|---|---|
--seed-field-border (local, cinza-600) |
4,74 | 3,21 |
--seed-border-interactive (gêmeo, cinza-500) |
3,38 | 4,50 |
O token local é melhor no claro e pior no escuro; o do gêmeo é equilibrado e passa nos dois. Um par que se degrada na inversão é o defeito BT3 do §64 — e o token local se degrada.
Nada muda no gêmeo seed-tokens.css nesta rodada. §65, §66, §67 e §68 já consomem o
--seed-border-interactive. O que sobra é migrar a banco-formfield.html do token local para o
semântico do gêmeo — artefato estável, então entra por auditoria e não por edição solta
(CD-P2).
66.4-b O contraexemplo estava fraco — o gate consertou, e o argumento ficou mais forte#
Apontamento do Rafael, verbatim: "eu tentei colar e não consegui, quando digito o cursor pula pro próximo campo, mas quando clico no backspace não consigo apagar cada campo, era pra ir voltando e apagando."
Ele está certo, e o erro era meu: a primeira versão das seis caixinhas só avançava o foco. Qualquer implementação séria de OTP em caixinhas escreve pelo menos três remendos — avançar ao digitar, voltar apagando no Backspace e navegar por setas. Comparar o campo único com uma versão capenga das caixinhas é espantalho, e argumento apoiado em espantalho cai na primeira vez que alguém implementa direito.
Corrigido: o contraexemplo agora tem os três remendos. E é aqui que o conserto fortalece a tese em vez de enfraquecê-la — porque o argumento verdadeiro nunca foi "caixinhas não funcionam", e sim quanto custa fazê-las funcionar:
| Campo único | Seis caixinhas | |
|---|---|---|
| Avançar ao digitar | — (não se aplica) | remendo 1 |
| Voltar apagando | nativo | remendo 2 |
| Navegar por setas | nativo | remendo 3 |
| Colar o código | nativo — 6 de 6 dígitos | remendo 4, que ninguém escreveu — 1 de 6 |
| Paradas de tabulação | 1 | 6 |
Alvo do autocomplete="one-time-code" |
1 campo | nenhum |
Regra de método que sai daqui: contraexemplo tem de estar na MELHOR forma que o desenho concorrente consegue ter. Contraexemplo enfraquecido mede a nossa implementação dele, não a decisão que ele representa — e o placar verde vira autoengano.
A guarda CD10-c passa a medir isso: ela exercita os três remendos no contraexemplo e reprova se
algum faltar. E foi provada capaz de reprovar antes de ser aceita: removidos os remendos, o placar
vai a 26 · 1.
66.5 Dois defeitos de instrumento na mesma rodada#
Décimo sexto — o parser de cor não entendia HEXADECIMAL (nasceu no §65 e vale para toda guarda
do projeto; o registro completo está em 65.4). Resumo: getComputedStyle devolve rgb(), mas o
valor de um token lido por getPropertyValue volta como está escrito no gêmeo (#242E34). O
regex de dígitos casava só o final e a razão saiu 232,67 — fisicamente impossível — com a
guarda emitindo PASS. Regra nova: número fora da faixa [1,00 … 21,00] é MEDIDOR QUEBRADO, não
veredito.
Décimo sétimo — a guarda leu a DOCUMENTAÇÃO do contrato como se fosse o contrato violado. A
primeira forma do CD16 varria o innerText do <body> inteiro procurando “1º, 3º e 5º caractere”.
Ela achou — dentro da própria tabela de contratos da bancada, onde o CD16 cita a frase proibida
entre aspas para explicar o que proíbe. O artefato foi reprovado por explicar a si mesmo.
Regra nova: guarda mede o CONTROLE, nunca o texto que descreve o controle. O escopo passou a ser rótulos, legendas, dicas e
placeholderdos campos. Mesma família dos defeitos de recorte (8º e 14º): escopo errado inventa achado ou esconde achado — aqui, inventou.E a guarda foi provada capaz de reprovar antes de ser aceita: injetada a frase proibida num rótulo real, o placar vai a 25 · 1. Guarda que nunca falha não é guarda.
66.6 A bancada e a suíte#
banco-credencial.html v0.1, gerada por validacao/gen-banco-credencial.py sobre
validacao/banco-credencial-template.html. Barra fixa e seletor de largura desde a v0.1 (BT1/BT2).
Nada nesta bancada envia nada a lugar nenhum, e o selo diz isso.
suite-credencial.mjs — 27 PASS · 0 FAIL · 1 medida. É a primeira suíte do projeto que usa a
área de transferência: o contexto do navegador é aberto com permissão de clipboard, o código é
escrito nele e colado com Ctrl+V. Colar não se prova lendo atributo.
| Guarda | O que ela faz de fato |
|---|---|
CD1-senha / CD1-codigo |
escreve no clipboard e cola nos dois campos |
CD10-b |
cola o mesmo código nos dois desenhos e compara: 6/6 contra 1/6 |
CD7 |
clica no revelar e confere tipo, nome do botão e anúncio |
CD8 |
envia o formulário e confere que o tipo voltou a password |
CD15 |
dispara keyup com modifierCapsLock — e a guarda declara que o modificador é sintético, em vez de fingir que ligou a tecla |
CD12-OP |
clica em reenviar e confere que o campo limpou e o prazo foi anunciado |
CD13-OP |
digita código incompleto, clica em confirmar e confere erro, aria-invalid e foco |
66.7 Pendências#
| # | Pendência | Por que fica aberta |
|---|---|---|
| CD-P1 | ✅ FECHADA — gate visual APROVADO em 2026-08-17. O olho achou que o contraexemplo das seis caixinhas era espantalho (não voltava apagando no Backspace); corrigido para a melhor forma de mercado, com guarda CD10-c. A seção é promovida a estável |
|
| CD-P2 | Migrar a banco-formfield.html do --seed-field-border local para o --seed-border-interactive do gêmeo |
Decidido em 2026-08-16 (66.4), com supersede formal: o gêmeo já tem o semântico canônico de fronteira de controle desde a v1.12, criado com os mesmos números. O token local é melhor no claro (4,74 × 3,38) e pior no escuro (3,21 × 4,50). A migração é de artefato estável: entra por auditoria, junto com a CD-P3 |
| CD-P3 | Auditoria de todos os campos do sistema contra o piso 3,00 de borda | Esta rodada mediu banco-formfield (passa, 4,74/3,60) e os dois artefatos novos. As telas (tela-detalhe, tela-lista, tela-painel) não foram medidas nesta régua |
| CD-P4 | ✅ FECHADA em 2026-08-16, no gate. Dado real num artefato estável |
A banco-formfield.html trazia dois endereços de e-mail reais — linha 333 (estado success, campo s4) e linha 756 (campo g3). Os dois viraram operador@exemplo.com.br. Achado colateral do gate: a varredura foi feita porque o Rafael perguntou onde verificar — e a resposta honesta exigiu procurar, o que revelou a segunda ocorrência que o registro original não mencionava |
| CD-P5 | O fluxo completo de autenticação (arquétipo A19) segue AUSENTE | O §66 entrega os campos; a tela de entrar/recuperar/criar conta não existe como tela-*.html. O mapa de cobertura registra A19 como arquétipo, e ele continua sem gabarito |
Também cita o §66: tela-autenticacao.