Como banir e desbanir usuários do seu app, e fazer o banimento valer

Bloquear o login é a metade fácil. Um banimento que não encerra as sessões já abertas, ou que a pessoa banida consegue desfazer, não é um banimento.

A tabela _User depois deste guia: três contas com uma coluna isBanned lendo True para samir_h, undefined para dana_w e False para alex_k

Tradução do original em inglês, testado em 24 de setembro de 2026. Código, números e datas são os do original. Read in English

Alguém está abusando do seu app e precisa sair. Não advertido, não limitado por rate limiting: fora, hoje, sem apagar a conta e sem esperar um deploy.

A resposta usual é uma flag no registro do usuário e uma verificação no login, e esse é o formato certo. Também é, sozinha, mais ou menos metade de um banimento: a pessoa que você acabou de banir muito provavelmente continua logada no celular, e nada do que você fez afeta isso.

Este guia constrói a flag e a verificação de login e depois testa as duas coisas que decidem se o banimento é real: o que acontece com uma sessão que já existe e se o usuário banido consegue simplesmente virar a flag de volta. As duas foram medidas em 24 de setembro de 2026, com chaves de cliente, do jeito que um dispositivo real faria.

Arquitetura: as três rotas que uma conta banida pode usar para entrar no backend, e o trigger de Cloud Code que fecha cada uma

Onde fica a flag de banimento?

No registro do usuário, como um booleano. Abra a classe _User no Database Browser, adicione uma coluna chamada isBanned do tipo Boolean e deixe o padrão em false, para que contas novas comecem permitidas.

Esse é o modelo de dados inteiro. Nenhuma tabela separada de banimentos, nenhum join: a flag viaja com a conta, então toda verificação que tem o usuário tem a resposta.

A tabela _User com uma coluna isBanned lendo True para uma conta, undefined para outra e False para a terceira

Como bloquear o login?

Com um trigger beforeLogin, que roda antes de o session token ser emitido. Esse timing é o ponto: uma conta banida nunca recebe um token, então não há nada para revogar.

Parse.Cloud.beforeLogin(async (request) => {
  if (request.object.get('isBanned')) {
    throw new Error('This user has been banned');
  }
});

Faça o deploy disso e a flag passa a funcionar na hora. O login de uma conta marcada volta recusado, com a mensagem do trigger:

{ "code": 141, "error": "This user has been banned" }

Isso precisa viver no Cloud Code, não no seu app. Uma verificação no seu próprio código roda num dispositivo que o usuário controla, e qualquer pessoa pode chamar a REST API diretamente com as mesmas chaves de aplicação e de cliente. O trigger roda no servidor, seja qual for o cliente.

Bloquear o login realmente remove o usuário?

Não, e essa brecha é o motivo de este guia continuar. Um banimento que só roda no login não faz nada com as pessoas que já estão dentro.

Eis o teste. Faça login como o usuário e guarde o session token. Depois defina isBanned como true a partir do servidor e use o token de novo:

GET /users/me   with the token issued before the ban
→ 200   still valid, as alex_k

A conta banida continua autenticada, lendo e escrevendo, até que por acaso faça logout. Num app mobile, onde as sessões são longas por design, isso pode levar semanas.

Como medimos: um session token foi obtido fazendo login antes do banimento, a flag foi então definida pelo servidor, e o mesmo token foi reenviado contra /users/me só com o application id e a REST key. Sem master key, porque a master key ignora triggers e não provaria nada.

Trancar a porta não faz nada com as pessoas que já estão na sala.

Fica pior. O Parse deixa um usuário logado escrever no próprio registro (é assim que “mudar meu nome de exibição” funciona), e isBanned é um campo desse registro como qualquer outro. Com a mesma sessão anterior ao banimento:

PUT /users/<id>   { "isBanned": false }
→ 200   updated

GET /login        same credentials
→ 200   OK

O usuário banido desbaniu a si mesmo. Não por uma falha do Parse, mas porque a flag foi deixada como dado comum, gravável pelo usuário.

Como fazer o banimento valer?

Três triggers em vez de um. A verificação de login fica; dois a mais fecham o que ela deixa aberto.

// 1. The door. beforeLogin runs before a session token is issued.
Parse.Cloud.beforeLogin(async (request) => {
  if (request.object.get('isBanned')) {
    throw new Error('This user has been banned');
  }
});

// 2. The flag. Without this, a signed-in user can clear their own ban.
Parse.Cloud.beforeSave(Parse.User, async (request) => {
  if (request.master) return;              // server-side and dashboard writes
  const original = request.original;
  if (!original) return;                   // sign-up, nothing to compare
  if (request.object.get('isBanned') !== original.get('isBanned')) {
    throw new Error('isBanned can only be changed by the server');
  }
});

// 3. The sessions already out there.
Parse.Cloud.afterSave(Parse.User, async (request) => {
  const original = request.original;
  if (!original) return;
  const nowBanned = request.object.get('isBanned') === true;
  const wasBanned = original.get('isBanned') === true;
  if (!nowBanned || wasBanned) return;

  const query = new Parse.Query(Parse.Session);
  query.equalTo('user', request.object);
  const sessions = await query.find({ useMasterKey: true });
  await Parse.Object.destroyAll(sessions, { useMasterKey: true });
});

O trigger 2 compara o valor recebido com request.original e recusa só quando o campo realmente muda, então atualizações comuns de perfil continuam funcionando. request.master deixa o servidor e a dashboard passarem; sem isso, você não conseguiria banir ninguém.

O trigger 3 dispara só na transição para banido, não em todo save de um usuário banido, o que evita consultar as sessões de novo em escritas sem relação.

Os três triggers no editor de Cloud Code, prontos para o deploy

Os três triggers fecham as duas brechas?

Sim, nas duas, e os mesmos testes dizem isso. Com os três no ar, uma sessão nova foi obtida antes do banimento, o caso mais favorável a um atacante.

TesteUm triggerTrês triggers
Login com a conta banida400, código 141400, código 141
Sessão anterior ao banimento, GET /users/me200 — ainda válida400, código 209 — session token inválido
Cliente alterando isBanned200 — gravado400 — recusado

O código 209 é o erro de session token inválido do Parse: o token que o cliente segura não existe mais, então o app vê um usuário deslogado e o manda para a tela de login, onde o trigger 1 o barra.

Como medimos: cada linha é uma requisição por versão do Cloud Code (o trigger único do vídeo, depois os três), contra a mesma conta no mesmo backend em 24 de setembro de 2026, sempre com chaves de cliente.

Como desbanir alguém?

Coloque a flag de volta em false de qualquer lugar onde a master key se aplica: a dashboard, uma Cloud Function, sua ferramenta de admin. O trigger 2 deixa passar porque request.master é true; o trigger 3 ignora porque só age na transição para banido.

As sessões antigas do usuário continuam destruídas, e isso é o correto: desbanir devolve o direito de entrar, não as sessões que foram canceladas. A pessoa faz login de novo e recebe uma sessão nova.

Se você quer banimentos que expiram, guarde uma data em vez de um booleano e compare no beforeLogin. O resto da estrutura não muda, e as mesmas duas brechas precisam dos mesmos dois triggers.

Quais erros você vai ver, e o que diz o corpo deles?

Três, e todos chegam como HTTP 400 com um código do Parse no corpo. O código é o que seu cliente deve usar para decidir; a mensagem é sua, lançada pelo trigger.

{ "code": 141, "error": "This user has been banned" }
{ "code": 141, "error": "isBanned can only be changed by the server" }
{ "code": 209, "error": "Invalid session token" }

O código 141 é o erro genérico de “uma Cloud Code function falhou”, então as suas duas rejeições o compartilham; a mensagem é a única coisa que as distingue. Se você precisa que o cliente reaja de forma diferente a cada uma, lance uma mensagem estruturada e faça o parse dela, em vez de casar com um texto em inglês que talvez você queira traduzir depois.

O código 209 é o que seu app já conhece: é o que o Parse devolve para qualquer sessão morta. A maioria dos clientes trata isso como “deslogado” e manda o usuário para a tela de login, onde o primeiro trigger o recebe com a mensagem de banimento. Essa é a sequência que um usuário banido de fato vive.

Como medimos: cada linha acima é o corpo verbatim da resposta a uma requisição feita só com o application id e a REST key, capturada em 24 de setembro de 2026.

Quando a flag de banimento é a ferramenta errada?

Quando o problema é a pessoa, não a conta. A flag vive num único registro de usuário, então quem cria outra conta com um e-mail novo entra de volta direto, e nada neste guia atrapalha isso.

Se você está lidando com abuso persistente, e não com alguém que quebrou uma regra e que você consegue nomear, precisa de sinais que sobrevivam à conta, e esse é um design diferente e bem mais difícil.

Também é o formato errado para qualquer coisa temporária. Um booleano não tem data de fim, então uma suspensão de sete dias vira uma anotação na agenda e a tarefa de alguém lembrar. Guarde um timestamp em vez disso e compare no beforeLogin; os outros dois triggers ficam exatamente como estão.

E é bruta demais quando o problema é um recurso só. Se alguém está abusando dos comentários, banir a conta tira tudo o mais que a pessoa faz legitimamente. Uma flag por recurso, verificada no Cloud Code que é dono daquele recurso, mantém a punição do tamanho da infração.

O que você acrescentaria ao sistema de banimento em seguida?

Registre quem baniu quem e por quê. Um booleano não diz nada daqui a três meses, e decisões de moderação recebem recurso: um bannedAt, bannedBy e banReason no mesmo registro não custam nada e respondem à pergunta depois.

Pense no que uma sessão banida deve ver no momento em que morre. O cliente recebe o código 209 e, se o seu app trata isso como “deslogado”, o usuário simplesmente cai na tela de login sem explicação. Capturar esse código e mostrar a mensagem de banimento é um fim mais gentil do que um logout silencioso.

E aproveite para conferir as permissões por classe (CLP) de _User. Os triggers protegem um campo; eles não impedem um usuário de ler os registros de outras pessoas se as permissões permitirem.

Tagsparse-servercloud-codeauthenticationsessionssecuritytriggers

Perguntas frequentes

Como banir um usuário no Parse Server?

Adicione um campo booleano ao registro do usuário e verifique-o num trigger beforeLogin, que roda antes de o session token ser emitido. Em 24 de setembro de 2026 o login de uma conta banida devolveu HTTP 400 com código 141 e a mensagem lançada pelo trigger. Isso bloqueia logins novos, mas sozinho não mexe nas sessões que o usuário já tem, e não impede que o próprio usuário limpe a flag.

Banir um usuário encerra a sessão dele no Parse Server?

Não por si só. Um session token emitido antes do banimento continua funcionando: num teste medido, GET /users/me com o token anterior ao banimento ainda devolveu HTTP 200 e o registro do usuário depois de o banimento ser aplicado. Para encerrá-lo, consulte a classe _Session daquele usuário e destrua os resultados com a master key quando a flag virar; depois disso a mesma requisição devolve HTTP 400 com código 209, session token inválido.

Um usuário banido consegue se desbanir sozinho?

Sim, a menos que você impeça. O Parse deixa um usuário logado escrever no próprio registro, então, com uma sessão obtida antes do banimento, um PUT definindo isBanned como false devolveu HTTP 200 e o login seguinte teve sucesso. Um trigger beforeSave em Parse.User que rejeita qualquer alteração do campo vinda do cliente fecha isso: a mesma requisição passa a devolver HTTP 400 e a escrita é recusada.

O que acontece com os usuários criados antes de você adicionar o campo de banimento?

Eles não têm valor nenhum, o que é lido como undefined, não como false. Isso é inofensivo para esta verificação, porque undefined é falsy e o trigger deixa esses usuários passarem, mas significa que você não pode consultar isBanned igual a false e esperar todos os usuários permitidos de volta. Consulte os banidos em vez disso, ou preencha o campo retroativamente.

A verificação de banimento deve ficar no Cloud Code ou no cliente?

No Cloud Code, sempre. Uma verificação no código do seu app roda num dispositivo que o usuário controla e pode ser pulada por qualquer pessoa que chame a REST API diretamente com as mesmas chaves. beforeLogin, beforeSave e afterSave rodam no servidor em toda requisição, independentemente de qual cliente a fez, e é isso que torna a regra uma regra, e não uma sugestão.

Leitura complementar

Revisado e verificado por Back4app Engineering, Editorial review na Back4app

Testado em 24 de setembro de 2026 — Backend: Back4app, plano Free, USA East · Cliente: REST API só com a application key e a REST key · Terminal: curl e node no macOS.

Mais da Back4app Engineering

Backend · tutorial

Your Frontend Talks Straight to the Database. Here Is How to Lock It Down [Full Repo]

Backend · tutorial

How to Add Authentication to a Web or Flutter App Without Writing an Auth Server [Full Repo]

Backend · tutorial

Build and Deploy a Node.js REST API in 15 Minutes — Without Owning a Server [Full Code Inside]