Como vincular vários métodos de login a uma conta

Um usuário logado vincula a segunda e a terceira forma de entrar, desvincula uma, troca a senha e encerra todas as sessões. Cada passo é uma chamada REST a um backend Parse, com os códigos respondidos e o caso que o servidor deixa quebrar.

O painel de métodos vinculados do app de demonstração para uma conta: e-mail/senha ativo como jane.doe.acme, id de convidado vinculado, Google não vinculado, com os botões Continuar com Google, Desvincular convidado, Sair e Sair em todo lugar, ao lado do registro que o backend armazenou: hasPassword true, authData anonymous, uma ACL só do dono e uma sessão que expira um ano depois de criada

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

A página de docs de todo provedor termina no mesmo lugar: um botão, um usuário, pronto. As perguntas começam na semana seguinte. Um convidado que jogou por uma hora quer manter o progresso, então entra com o Google. Alguém cria conta com senha e depois clica no botão do Google com o mesmo e-mail. Um celular se perde e a sessão dele precisa morrer.

Este post é a semana depois da página de docs. Uma conta, várias formas de entrar, num backend Parse hospedado no Back4app, sem servidor de auth próprio: o navegador fala com o backend, o backend fala com o Google. Tudo foi executado em 20 e 23 de outubro de 2026, contra a plataforma ao vivo, e os códigos e textos abaixo são citados das respostas dela.

Teste, faça o deploy, leia: a demo roda em socialloginstarter-6om29ktc.b4a.run com um botão do Google de verdade, login como convidado e e-mail/senha. Contas criadas lá podem ser apagadas a qualquer momento.

O repositório é github.com/templates-back4app/social-login-starter, MIT.

O que “vincular” significa num backend Parse

Um usuário Parse tem uma linha em _User, e essa linha pode carregar várias credenciais ao mesmo tempo: um username e uma senha, e um objeto authData com uma chave por provedor. Entrar com qualquer uma delas resolve para a mesma linha.

Não há tabela de “identidades” separada nem etapa de merge. Vincular é um PUT no seu próprio usuário com uma nova chave em authData, e desvincular é o mesmo PUT com essa chave definida como null.

Esse é o modelo inteiro, e a tabela abaixo é o protocolo inteiro. As linhas de convidado, senha, desvínculo e logout foram exercitadas com chamadas REST cruas contra o backend ao vivo; as duas linhas do Google descrevem o que o Parse Server faz com um token válido, o que esta rodada só verificou pelo lado da recusa, com tokens forjados.

Ação na interfaceO que o navegador enviaO que o Parse Server faz
Continuar como convidadoPOST /users {authData:{anonymous:{id}}}cria um usuário (201) ou loga o mesmo convidado de novo (200); nova sessão nos dois casos
Entrar com GooglePOST /users {authData:{google:{id, id_token}}}verifica o ID token junto ao Google, depois cria ou loga (caminho documentado; tokens forjados recusados, veja a tabela de segurança)
Criar conta / EntrarPOST /users {username,password,email} / POST /logino caminho clássico
Vincular e-mail/senhaPUT /users/<id> {username,password,email} com a sessãodefine a senha, responde com um novo session token e revoga os antigos, mantém o id de convidado
Vincular GooglePUT /users/<id> {authData:{google:{…}}}verifica o token, vincula; um id já usado em outra conta responde 208 (documentado; não executado com uma conta real)
Desvincular Google ou id de convidadoPUT /users/<id> {authData:{google:null}}remove o provedor; a sessão continua válida; remover a última forma de entrar é permitido pelo Parse Server e recusado pelo hook do repositório
SairPOST /logoutencerra só esta sessão
Sair de todos os dispositivosCloud function signOutEverywhereapaga toda outra linha de _Session do chamador (não é nativo)

A tela deslogada da demo: um botão Continue as guest, um botão Sign in with Google e um formulário de e-mail e senha com Log in e Sign up

A coisa toda em três requisições

Antes dos detalhes, aqui está o fluxo de vínculo como o navegador o envia, com o Parse JS SDK. Cada chamada é uma requisição REST, e nada mais é necessário para ir de um convidado a uma conta com três formas de entrar.

// 1. Start as a guest: POST /users with an anonymous id the SDK generates and keeps
const user = await Parse.AnonymousUtils.logIn();

// 2. Link e-mail and password: PUT /users/<id> with the current session.
//    The answer carries a NEW session token; every previous session of this user is revoked.
user.set('username', 'jane.doe.acme');
user.setPassword('a-long-passphrase');
user.setEmail('[email protected]');
await user.save();

// 3. Link Google: the Google Identity Services button hands you an ID token; PUT it under authData.
//    Parse Server verifies the signature and audience against the client id you configured.
await user.save({ authData: { ...(user.get('authData') || {}), google: { id: payload.sub, id_token: credential } } });

// Unlink: the same PUT with null. The hook refuses to remove the last method.
await user._unlinkFrom('anonymous');

O passo 3 usa save() puro em vez do linkWith() do SDK, e o passo 2 usa set('username') em vez de setUsername(), por um motivo que as próximas seções medem: os dois helpers descartam o id de convidado em silêncio. Tudo depois deste ponto é o que acontece ao redor dessas chamadas: colisões, sessões, as regras que faltam no servidor e os números.

Para quem é este post

Você tem um provedor funcionando a partir de uma página de docs e precisa do resto do ciclo de vida: um segundo método na mesma conta, colisões, o que expira quando e como encerrar sessões. Você aceita que o backend seja o dono da identidade e não quer rodar um servidor OAuth.

Duas coisas ficam fora do escopo de propósito. O app não tenta manter “um usuário por pessoa” entre provedores: uma conta Google e uma conta com senha com o mesmo e-mail são dois usuários, a menos que o usuário logado as vincule, e o post explica por quê mais abaixo.

Roles, edição de perfil e SSO pertencem a outros posts; o post sobre cadastro e recuperação de senha cobre o lado do e-mail.

Os provedores, e quais deles este post executa

A dashboard do Back4app lista cinco provedores sociais em Authentication: Apple, Google, Facebook, X e VK. Os nomes no Parse são apple, google, facebook, twitter e vkontakte. A demo roda o Google, mais os dois provedores que não precisam de console nenhum: convidados anônimos e e-mail/senha.

ProvedorNeste postPor quê
Googleao vivo na demoum console gratuito, um Web client id, sem revisão para os escopos básicos
Convidado anônimoao vivo na demonativo do Parse Server; o navegador guarda um id aleatório
E-mail/senhaao vivo na demonativo; o post anterior cobre os e-mails de redefinição
GitHubnão disponívelnão é oferecido pelo Back4app; authData.github respondeu HTTP 500 “Internal server error” no backend ao vivo
Appledocumentado, não executadoprecisa de uma conta de desenvolvedor paga e de um domínio verificado
Facebookdocumentado, não executadologin público exige App Review; o modo de desenvolvimento permite só usuários de teste (página de App Review da Meta, lida em outubro de 2026)
VKdocumentado, não executadoo adapter respondeu “Vk only works with enableInsecureAuth: true” a uma sondagem, então exige uma configuração de servidor que não perseguimos

A linha do GitHub merece uma frase, porque a rodada de viabilidade de 20 de outubro descobriu isso do jeito difícil. Um cliente que envia authData.github não recebe um erro limpo de “provedor não suportado”; o adapter do Parse Server falha antes, e a resposta é um 500.

O hook beforeSave do repositório mantém uma lista de permitidos com os cinco provedores mais anonymous, de modo que um provedor ligado na dashboard por acidente ainda não consegue criar um usuário até que a lista seja alterada de propósito.

A página, o hook e as duas funções

O cliente é uma página HTML, uma folha de estilo e 206 linhas de JavaScript, servidas por um app Express de 65 linhas cujas únicas tarefas são arquivos estáticos, um /config.js com as chaves públicas e um /healthz. O Parse JS SDK e o Google Identity Services são carregados das suas CDNs.

O botão do Google entrega à página um ID token, um JWT assinado pelo Google. A página decodifica o payload para mostrar o e-mail e então chama Parse.User.logInWith('google', { authData: { id, id_token } }). Isso é um POST /users; o servidor busca as chaves públicas do Google, confere a assinatura e a audience contra o client id da dashboard, e só então toca no banco de dados.

O arquivo de Cloud Code tem um hook e duas funções, e cada um existe por um motivo que o servidor não cobre:

  • beforeSave(_User) recusa mudanças de identidade por qualquer um que não seja o dono ou a master key, mesmo quando a ACL da linha foi alargada por engano; mantém a lista de provedores permitidos; e mantém um booleano hasPassword. O Parse nunca diz a um cliente se um usuário tem senha, nem com a master key, mas o hook roda antes do hashing e vê o texto puro, então consegue definir a flag. Um cliente que envia hasPassword: true tem o valor sobrescrito.
  • linkInfo devolve o que o painel precisa e o SDK não consegue ver: a flag acima, o createdAt e o expiresAt da sessão atual, e quantas sessões a conta tem.
  • signOutEverywhere apaga toda linha de _Session do chamador exceto a que fez a chamada, com a master key, e devolve a contagem.

O ciclo de vida, um resultado registrado por caso

Cada linha abaixo é um teste em test/lifecycle.test.mjs, executado contra o backend ao vivo em 23 de outubro de 2026, como chamadas REST cruas com a REST key. Nada na tabela é previsão; a suíte imprime os códigos e os textos, e são estes.

CasoO que o backend ao vivo fez
Novo convidado, depois o mesmo convidado de novo201 com um usuário novo, depois 200 com o mesmo objectId e um novo session token
Convidado vincula username, e-mail e senha200; a resposta trouxe um novo session token; o token antigo passou a responder 209 “Invalid session token”; authData.anonymous mantido
Uma segunda sessão de convidado aberta durante o vínculorevogada também: 209
Entrar com a senha vinculada200, mesmo objectId
Outro usuário pega o mesmo username400, código 202 “Account already exists for this username.”
Outro usuário pega o mesmo e-mail400, código 203 “Account already exists for this email address.”
Desvincular o id de convidado de um usuário com senha200; a sessão continuou válida; o id de convidado antigo então criou um usuário novo
Desvincular o último método de um convidado sem senha200 do Parse Server sozinho em 23 de outubro, conta órfã; 400, código 119 “Cannot remove the last sign-in method” com o hook no ar em 27 de outubro
Sair no dispositivo 2dispositivo 1 continua válido, dispositivo 2 responde 209
Trocar a senha por uma sessão200 com um token novo; o token antigo do chamador e toda outra sessão respondem 209
Sair de todos os dispositivosa função revogou as outras e o chamador continuou funcionando

A demo depois que um convidado vinculou e-mail e senha: o painel Linked methods mostra e-mail/senha ativo como jane.doe.acme, id de convidado vinculado, Google não vinculado, e o registro armazenado com hasPassword true e uma sessão criada às 13:18:03

Três dessas linhas mudam como você escreve o cliente.

Vincular uma senha substitui a sessão. A resposta ao PUT traz um token novo, e toda sessão anterior daquele usuário some, outros dispositivos incluídos. O SDK troca o token na aba atual; outra aba aberta descobre isso na próxima requisição, quando recebe um 209.

Na captura acima, o createdAt da sessão passou de 13:17:13 para 13:18:03 na mesma conta. A página mostra esse timestamp exatamente por esse motivo.

Colisões são trabalho do backend, e ele diz isso em palavras. A demo mostra o texto do próprio backend ao lado do código. Na captura abaixo, um segundo convidado tentou vincular o e-mail que a primeira conta já possui.

A demo mostrando o erro Error 203: Account already exists for this email address. acima do formulário de vínculo de uma segunda conta de convidado

O Parse Server permite deixar uma conta órfã, então o hook precisa impedir. Um convidado sem senha que desvincula o id de convidado fica com uma sessão funcionando e uma conta que nenhuma credencial alcança de novo; registramos HTTP 200 para essa requisição em 23 de outubro.

A primeira versão deste post deixou a proteção para a interface, que desabilita o último botão de desvínculo. Uma regra de interface não é regra quando o mesmo post mostra chamadas REST diretas.

O hook agora recusa uma atualização sem master key que deixaria a conta sem provedor e sem senha: a mesma requisição responde 400, código 119, Cannot remove the last sign-in method: link another one first, e a sessão e a conta ficam intactas. Vincule uma senha antes e o desvínculo passa.

O teste que antes documentava a conta órfã agora afirma a recusa, localmente e no backend ao vivo.

O Parse cumpre toda promessa sobre quem pode alterar uma linha. Se a linha continua alcançável é uma regra que você acrescenta, e o lugar dela é no servidor.

Por que não fundir contas pelo e-mail

Um login com Google cujo e-mail coincide com uma conta com senha existente é um usuário novo no Parse, e este post mantém assim. Fundir pelo e-mail significaria confiar que quem controla aquela conta Google também controla a conta com senha, e a única prova disso é entrar nas duas.

É isso que vincular a partir de uma sessão logada é: o usuário prova a primeira conta por estar nela, depois prova a segunda ao entrar nela.

A consequência prática é uma linha de interface. Quando um cadastro com senha falha com 203 porque um usuário do Google já possui aquele e-mail, diga ao usuário para entrar com o Google e vincular uma senha pelo painel. A demo mostra o texto do backend; seu app pode mostrar a frase melhor.

Sessões: dois relógios, e o que as encerra

Um session token neste backend foi emitido com expiração um ano depois da criação: o expiresAt no registro armazenado é createdAt mais 365 dias, em toda sessão que criamos.

O ID token do Google é um relógio separado. Ele é consumido uma vez, no login ou no vínculo, e o backend o guarda em authData mas nunca mais precisa dele para atender uma requisição. A expiração desse token não desloga ninguém.

O que encerra uma sessão, pela tabela acima: POST /logout para a sessão que chama; uma senha definida ou trocada por uma sessão, para todas elas; e signOutEverywhere para todas menos a do chamador. Nada mais no ciclo de vida tocou em _Session. Desvincular um provedor não tocou.

Testes de segurança, como requisições cruas

Um cliente no navegador tem o Application ID e a JavaScript key, e os dois são públicos. Os testes em test/security.test.mjs enviam o que um cliente hostil enviaria, direto para a REST API com essas chaves, e o backend ao vivo respondeu:

TentativaResposta do backend ao vivo
GET /users/me sem sessão400, código 209
GET /users sem sessão404, código 101 “Permission denied, user needs to be authenticated.”
GET /users com sessão200, e a lista contém só a linha do próprio chamador
GET /users/<other> com sessãocódigo 101, como se a linha não existisse
PUT /users/<other> {username} com a sessão de outro usuário400, código 206 “Insufficient auth.”
o mesmo, depois que a ACL da vítima foi alargada para escrita públicarecusado pelo hook: 119 “Only the account owner can change its sign-in methods.”
PUT no próprio usuário com hasPassword: true200, e a flag continua false
authData.google com um token que não é JWT404, código 101 “provided token does not decode as JWT”
authData.google sem id_token404, código 101 “id token is invalid for this user.”
authData.google com um JWT RS256 autoassinado404, código 101 “secret or public key must be provided”, depois que o servidor buscou as chaves do Google e não encontrou um key id correspondente
authData.github500, código 1 “Internal server error.”
qualquer Cloud function sem sessão400, código 209

A linha do token autoassinado é a que prova o caminho de verificação: o servidor foi ao endpoint de certificados do Google, procurou o key id do header forjado e recusou quando não havia nenhum. Um token forjado com um key id real do Google falharia na verificação de assinatura.

Duas configurações tornam o resto da tabela verdadeiro, e npm run setup aplica as duas por uma Cloud function, para que nenhum script precise da master key.

Usuários novos recebem uma ACL só do dono, e as permissões por classe de _User exigem sessão para find, get, update e delete e escondem email, authData, hasPassword e emailVerified de todos menos o dono.

Os campos protegidos importam mesmo que você nunca alargue uma ACL: sem eles, um usuário logado listando _User veria os e-mails dos outros usuários.

Latência, medida

npm run measure cria seus próprios usuários, roda 20 aquecimentos e 100 requisições cronometradas por cenário, e apaga o que criou. O cliente foi uma máquina fora dos EUA; os arquivos estão em docs/evidence/.

Cenário (backend ao vivo, 23 de outubro de 2026)p50p95mínmáxerros
Novo login como convidado (POST /users, id anônimo novo)160 ms430 ms150 ms457 ms0
Convidado que volta (POST /users, id anônimo conhecido)164 ms248 ms144 ms267 ms0
Login por e-mail (POST /login)228 ms288 ms215 ms313 ms0

Criar um usuário custa mais na cauda do que logar um: o p95 da primeira linha é o único outlier do conjunto. A linha do e-mail é cerca de 60 ms mais lenta que as linhas de convidado; a rodada local abaixo aponta o bcrypt como motivo, embora a rodada ao vivo não consiga separá-lo do resto do trabalho do servidor.

O caminho do Google está ausente dessa tabela de propósito. Ele precisa de um token assinado pelo Google para uma conta real, que só uma sessão de navegador consegue produzir, e um script não consegue entrar no Google. O roteiro manual do fim o mede com o painel Network do próprio navegador, cinco execuções, e o CSV tem uma linha reservada para o resultado.

Para dar escala, os mesmos três cenários num parse-server local com um PostgreSQL no mesmo processo levaram 1,7 ms, 1,8 ms e 42,6 ms no p50. Ali, sem rede, os 40 ms entre as linhas de convidado e a linha do e-mail são o hash da senha; a diferença entre local e ao vivo é a rede, e é por isso que os números ao vivo são os que importam.

O que encontramos enquanto construíamos

  • O primeiro deploy de Cloud Code num backend novo pode reportar sucesso e não enviar nada. A prova é uma requisição, não o diálogo: POST /functions/linkInfo com a REST key responde 209 quando o código está no ar e 141 “Invalid function” quando não está.
  • O Cloud Code roda em Node 19.9 e o Containers faz o build em Node 22. Nada neste repositório precisa de um runtime mais novo, mas um hook que usasse uma API do Node 20 só falharia depois do deploy.
  • A master key nunca saiu da dashboard. Setup, limpeza dos testes e o teste que alarga a ACL são Cloud functions protegidas por SETUP_ENABLED em cloud/config.js. Enquanto ele está em true, qualquer um com o app id e uma chave de cliente pode chamá-las, então o último passo antes dos usuários reais é defini-lo como false, fazer o upload, fazer o deploy e provar com o curl do README.

Três coisas menores custaram tempo e pertencem a uma lista de troubleshooting, não à história:

  • O botão do Google segue o idioma do navegador: numa máquina alemã ele dizia Über Google anmelden. hl=en na URL do script e locale: 'en' em renderButton o fixam.
  • Um botão do Google renderizado dentro de uma seção oculta tem 0 px de largura, porque o Google Identity Services o dimensiona no momento da renderização. A página renderiza os botões de novo sempre que uma view fica visível.
  • Um app criado com a origem Upload files do Containers não consegue fazer o próprio redeploy: uma troca de plano disparou um redeploy que falhou com This deployment has no uploaded code, e a borda respondeu 502 até que Action → Redeploy latest code o reconstruiu a partir do código que a página Source Code ainda guardava.

Como fazer o deploy

  1. Backend. Crie um app na dashboard do Back4app e copie o Application ID, a JavaScript key e a REST key para o .env. Deixe MASTER_KEY vazio; nada aqui precisa dele.

  2. Google. No console do Google Cloud, crie um cliente OAuth do tipo Web application, adicione suas origens em Authorized JavaScript origins (esquema e host exatos, sem path) e cole o client id em Back4app → Authentication → Google e no .env como GOOGLE_CLIENT_ID.

  3. Cloud Code. Dashboard → Cloud Code → faça o upload de cloud/main.js e cloud/config.js, confirme o badge Files pending deploy (2), Deploy. Verifique com a requisição acima.

  4. Schema. npm run setup adiciona a coluna hasPassword e as permissões por classe pela função setupSchema e imprime a sondagem: GET /users sem sessão respondendo 101.

  5. Testes e tempos. TEST_SERVER_URL=https://parseapi.back4app.com npm test, depois npm run measure. Os dois criam seus próprios usuários e os apagam.

  6. Containers. Faça o deploy de um web app a partir do repositório com o Node.js Buildpack, porta 8080, health check /healthz e exatamente quatro variáveis de ambiente: APP_ID, JS_KEY, SERVER_URL e GOOGLE_CLIENT_ID. O servidor imprime toda variável que conhece em /config.js, que todo visitante pode ler, então nunca adicione uma chave que não seja pública.

  7. Google de novo. Adicione a URL do deploy às origens autorizadas. Até propagar, o botão registra The given origin is not allowed for the given client ID.

  8. Antes de abrir ao público. Defina SETUP_ENABLED: false, faça o upload de cloud/config.js, Deploy, e confirme que POST /functions/setupSchema agora responde 119.

Quando não fazer desse jeito

Se você precisa ser o provedor de identidade de outras aplicações, ou seus clientes entram com o provedor SAML ou OpenID Connect da empresa deles, você precisa de um serviço de identidade, e produtos como Auth0, Keycloak ou Ory existem para isso. O authData do Parse é um repositório de credenciais por aplicação, não um broker de identidade.

Se “uma pessoa, uma conta” é uma exigência legal ou de cobrança, vincular a partir de uma sessão logada é necessário, mas não suficiente. Você também precisaria de um e-mail verificado antes de qualquer fusão, e de um caminho de suporte para a pessoa que tem duas contas e não lembra de nenhuma.

E note o que este post não executou: o roteiro manual do Google no navegador, que precisa de uma conta Google real. O docs/manual-google-check.md do repositório é o roteiro de dez minutos para isso.

Ele cobre um login novo, um login repetido, vincular uma senha a um usuário do Google, vincular o Google a um convidado, a colisão 208, cinco logins cronometrados, desvínculo, sair de todos os dispositivos e um client id errado. Quando tiver sido executado na demo, o arquivo de evidência dele substitui este parágrafo.

O repositório

github.com/templates-back4app/social-login-starter, MIT. public/ é o cliente inteiro, cloud/main.js o hook e as funções, scripts/ o setup, o servidor local e a medição de latência, test/ os 16 testes REST crus que rodam localmente contra o parse-server com PostgreSQL e, com uma variável de ambiente, contra o backend ao vivo.

docs/notes/build-diary.md registra o que foi observado durante a construção, incluindo os dez comportamentos vistos localmente que foram verificados de novo no Back4app, e docs/evidence/ guarda os CSVs por trás de cada número acima.

Tagsauthenticationsocial-logingoogle-sign-inanonymous-usersaccount-linkingsessionsparsecloud-codesecurity

Perguntas frequentes

Preciso de um servidor OAuth próprio ou de um endpoint de troca de token para o login com Google?

Não. O navegador obtém um ID token do Google Identity Services e o envia ao Parse Server dentro de authData. O servidor busca as chaves públicas do Google, confere a assinatura e a audience contra o client id que você configurou na dashboard, e só então cria ou loga o usuário. Enviamos três tipos de token forjado e os três foram recusados com código 101.

O que acontece quando um convidado vincula uma senha?

O backend mantém o id de convidado em authData, define username, e-mail e senha e responde com um novo session token, revogando toda sessão anterior daquele usuário, incluindo outros dispositivos. O JavaScript SDK troca o token por você. Observamos o createdAt da sessão mudar de 13:17:13 para 13:18:03 na mesma conta.

Um usuário pode vincular uma conta Google que já está vinculada a outro usuário?

Não. O Parse Server responde com código 208, 'this auth is already used', e nada muda. Usernames e e-mails colidem do mesmo jeito: 202 'Account already exists for this username.' e 203 'Account already exists for this email address.', os dois citados do backend ao vivo.

O que 'Sign out everywhere' faz de fato?

O Parse não tem isso nativo. POST /logout encerra só a sessão que chama, e uma troca de senha encerra toda sessão, incluindo a do chamador. O repositório acrescenta uma Cloud function de 15 linhas que apaga toda outra linha de _Session do chamador com a master key e informa quantas revogou. Testado ao vivo: as outras sessões responderam 209 depois, o chamador continuou funcionando.

Quais provedores o Back4app oferece?

Em outubro de 2026, a página Authentication da dashboard lista Apple, Google, Facebook, X e VK. GitHub não é oferecido: enviar authData.github ao backend ao vivo respondeu HTTP 500 com 'Internal server error' porque nenhum adapter está configurado. A demo roda Google, convidados anônimos e e-mail/senha; Apple, Facebook e VK estão documentados no repositório, mas não foram executados.

Os botões de desvincular podem deixar uma conta órfã?

O Parse Server permite: um convidado sem senha que desvincula o id de convidado fica com uma sessão funcionando e uma conta em que ninguém consegue entrar de novo, e registramos HTTP 200 para essa requisição. O hook beforeSave do repositório agora recusa isso com código 119, Cannot remove the last sign-in method, então o botão desabilitado da interface é uma cortesia e a regra no servidor é a proteção. Testado localmente e no backend ao vivo.

Leitura complementar

Revisado e verificado por Back4app Engineering, Editorial review na Back4app

Testado em 23 de outubro de 2026 — Backend: Back4app, Parse Server 7.5.2, Cloud Code em Node 19.9, plano Free · Cliente: Parse JS SDK 8.6.2, Google Identity Services, Express 5 no Back4app Containers · Checagem local: parse-server 7.5.4 em PostgreSQL 17.

Histórico de alterações

  • 27 de outubro de 2026 — Revisão após a revisão editorial: um exemplo de três requisições logo no início; as linhas do Google marcadas como documentadas em vez de medidas; o último método de login agora é protegido no servidor (hook, teste, verificação ao vivo) em vez de só na interface; achados da construção movidos para uma lista de troubleshooting; contagens de linhas e a afirmação sobre o bcrypt tornadas precisas.
  • 27 de outubro de 2026 — Título alterado de 'Social Login Without an Auth Server: Link Google, Guest and Password Sign-In to One Account'; URL alterada de /blog/link-several-sign-in-methods-to-one-account, que agora redireciona para cá.

Mais da Back4app Engineering

Backend · tutorial

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

Backend · tutorial

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

Backend · error-guide

Why Does the API Return 101, 119, 142 or 209? Every Backend Error Code, Reproduced [Full Repo]