Como autenticar usuários no App Engine usando o Firebase

Neste tutorial, veja como recuperar, verificar e armazenar credenciais de usuário usando o Firebase Authentication, o ambiente padrão do Google App Engine e o Google Cloud Datastore.

No documento, você conhecerá um aplicativo simples de anotações denominado Firenotes, que armazena as anotações dos usuários nos respectivos cadernos pessoais. Os cadernos são armazenados por usuário e identificados pelo código exclusivo do Firebase Authentication de cada usuário. O aplicativo tem os seguintes componentes:

  • O front-end configura a interface do usuário de login e recupera o código do Firebase Authentication. Ele também processa alterações de estado de autenticação e permite que os usuários vejam as próprias anotações.

  • FirebaseUI é uma solução de código aberto de entrada que processa login de usuário, vinculando vários provedores a uma conta, recuperando senhas e muito mais. Ele implementa as práticas recomendadas de autenticação para uma experiência de login tranquila e segura.

    FirebaseUI

  • O back-end verifica o estado de autenticação do usuário e retorna as informações de perfil do usuário, bem como as anotações dele.

O aplicativo armazena as credenciais de usuário no Cloud Datastore usando a biblioteca de cliente NDB, mas você pode armazenar as credenciais no banco de dados que quiser.

O diagrama a seguir mostra como o front-end e o back-end se comunicam entre si e como as credenciais de usuário passam do Firebase para o banco de dados.

Diagrama da arquitetura

O Firenotes é baseado na biblioteca de aplicativos da Web Flask. No aplicativo de exemplo, optamos pela Flask devido à simplicidade e à facilidade de uso, mas as tecnologias e os conceitos explorados são aplicáveis independentemente da biblioteca usada.

Objetivos

  • Configurar a interface do usuário do Firebase Authentication.
  • Receber um token de código do Firebase e verificá-lo usando a autenticação do lado do servidor.
  • Armazenar credenciais de usuário e dados associados no Cloud Datastore.
  • Consultar um banco de dados usando a biblioteca de cliente NDB.
  • Implantar um aplicativo no App Engine.

Custos

Neste tutorial, há componentes do Cloud Platform que podem ser cobrados, entre eles os seguintes:

  • Google Cloud Datastore

Use a calculadora de preços para gerar uma estimativa de custo baseada na projeção de uso. Usuários novos do Cloud Platform podem estar qualificados para uma avaliação gratuita.

Antes de começar

  1. Instale o Git e o Python 2.7. Para mais informações sobre como configurar o ambiente de desenvolvimento do Python, como a instalação da versão mais recente do Python, consulte Como configurar um ambiente de desenvolvimento do Python para o Google Cloud Platform.
  2. Faça login na sua Conta do Google.

    Se você ainda não tiver uma, inscreva-se.

  3. Selecione ou crie um projeto do GCP.

    Acessar a página Gerenciar recursos

  4. Instale e inicialize o SDK do Cloud.

Se você já instalou e inicializou o SDK para outro projeto, defina o projeto gcloud como o código do projeto do App Engine que você está usando para o Firenotes. Em Como gerenciar configurações do Cloud SDK, consulte comandos específicos para atualizar um projeto com a ferramenta gcloud.

Como clonar o aplicativo de exemplo

Para fazer o download do exemplo na máquina local:

  1. Clone o repositório de aplicativos de exemplo na máquina local:

    git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git

    Também é possível fazer o download do exemplo como um arquivo zip e extraí-lo.

  2. Navegue até o diretório que contém o código de exemplo:

    cd python-docs-samples/appengine/standard/firebase/firenotes
    

Como adicionar a interface do usuário do Firebase Authentication

Para configurar o FirebaseUI e ativar provedores de identidade:

  1. Adicione o Firebase ao aplicativo.
  2. Edite o arquivo backend/app.yaml e insira o código do projeto do Firebase nas variáveis de ambiente:

    runtime: python27
    api_version: 1
    threadsafe: true
    service: backend
    
    handlers:
    - url: /.*
      script: main.app
    
    env_variables:
      # Replace with your Firebase project ID.
      FIREBASE_PROJECT_ID: '<PROJECT_ID>'
    

  3. Configure o widget de login do FirebaseUI selecionando quais provedores você quer oferecer aos usuários.

    // Firebase log-in widget
    function configureFirebaseLoginWidget() {
      var uiConfig = {
        'signInSuccessUrl': '/',
        'signInOptions': [
          // Leave the lines as is for the providers you want to offer your users.
          firebase.auth.GoogleAuthProvider.PROVIDER_ID,
          firebase.auth.FacebookAuthProvider.PROVIDER_ID,
          firebase.auth.TwitterAuthProvider.PROVIDER_ID,
          firebase.auth.GithubAuthProvider.PROVIDER_ID,
          firebase.auth.EmailAuthProvider.PROVIDER_ID
        ],
        // Terms of service url
        'tosUrl': '<your-tos-url>',
      };
    
      var ui = new firebaseui.auth.AuthUI(firebase.auth());
      ui.start('#firebaseui-auth-container', uiConfig);
    }

  4. Ative os provedores que você optou por manter no Firebase console clicando em Autenticação > Método de login. Em seguida, em Provedores de login, passe o cursor sobre um provedor e clique no ícone de lápis.

    Sign in providers

    1. Alterne o botão Ativar e, para provedores de identidade de terceiros, insira o código do provedor e a chave secreta do site do desenvolvedor do provedor. Os documentos do Firebase contém instruções específicas nas seções "Antes de começar" dos guias do Facebook, do Twitter e do GitHub. Depois de ativar um provedor, clique em Salvar.

      Alternar o botão Ativar

    2. No Firebase console, em Domínios autorizados, clique em Adicionar domínio e insira o domínio do aplicativo no App Engine no seguinte formato:

      [PROJECT_ID].appspot.com
      

      Não inclua http:// antes do nome do domínio.

Como instalar dependências

Navegue até o diretório backend e conclua a configuração do aplicativo:

  1. Configure e execute um ambiente virtual:

    pip install virtualenv
    virtualenv env
    source env/bin/activate
    
  2. Instale os requisitos de terceiros que não estão incluídos no SDK para App Engine:

    pip install -r requirements.txt -t lib
    

    Em appengine_config.py, o método vendor.add() registra as bibliotecas no diretório lib.

Como executar o aplicativo localmente

Para executar o aplicativo localmente, use o servidor de desenvolvimento local do App Engine:

  1. Adicione o seguinte URL como o backendHostURL em main.js:

    http://localhost:8081

  2. Navegue até o diretório raiz do aplicativo. Em seguida, inicie o servidor de desenvolvimento:

    dev_appserver.py frontend/app.yaml backend/app.yaml
    
  3. Acesse http://localhost:8080/ em um navegador da Web.

Como autenticar usuários no servidor

Agora que você configurou um projeto e inicializou um aplicativo para desenvolvimento, é possível percorrer o código para entender como recuperar e verificar os tokens de código do Firebase no servidor.

Como receber um token de código do Firebase

A primeira etapa na autenticação do lado do servidor é recuperar um token de acesso para verificar. As solicitações de autenticação são processadas com a escuta de onAuthStateChanged() do Firebase:

firebase.auth().onAuthStateChanged(function(user) {
  if (user) {
    $('#logged-out').hide();
    var name = user.displayName;

    /* If the provider gives a display name, use the name for the
    personal welcome message. Otherwise, use the user's email. */
    var welcomeName = name ? name : user.email;

    user.getToken().then(function(idToken) {
      userIdToken = idToken;

      /* Now that the user is authenicated, fetch the notes. */
      fetchNotes();

      $('#user').text(welcomeName);
      $('#logged-in').show();

    });

  } else {
    $('#logged-in').hide();
    $('#logged-out').show();

  }

Quando um usuário faz login, o método getToken() do Firebase no retorno de chamada retorna um token de código do Firebase no formato de um JSON Web Token (JWT).

Como verificar tokens no servidor

Depois que um usuário faz login, o serviço de front-end busca todas as anotações existentes no caderno do usuário por meio de uma solicitação GET do AJAX. Isso requer autorização para acessar os dados do usuário, portanto, o JWT é enviado no cabeçalho Authorization da solicitação usando o esquema Bearer:

// Fetch notes from the backend.
function fetchNotes() {
  $.ajax(backendHostUrl + '/notes', {
    /* Set header for the XMLHttpRequest to get data from the web server
    associated with userIdToken */
    headers: {
      'Authorization': 'Bearer ' + userIdToken
    }
  })

Antes que o cliente possa acessar os dados do servidor, o servidor precisa verificar se o token está assinado pelo Firebase. Você pode verificar esse token usando a biblioteca de autenticação do Google para Python. Use a função verify_firebase_token da biblioteca de autenticação para verificar o token do portador e extrair as declarações:

id_token = request.headers['Authorization'].split(' ').pop()
claims = google.oauth2.id_token.verify_firebase_token(
    id_token, HTTP_REQUEST)
if not claims:
    return 'Unauthorized', 401

Cada provedor de identidade envia um conjunto diferente de declarações, mas cada uma tem pelo menos uma declaração sub com um código de usuário único e uma declaração que fornece algumas informações de perfil, como name ou email, que você pode usar para personalizar a experiência do usuário no aplicativo.

Como gerenciar dados do usuário no Cloud Datastore

Depois de autenticar um usuário, você precisa armazenar os dados correspondentes para que ele persista após o término de uma sessão de login. Nas seções a seguir, explicamos como armazenar uma anotação como uma entidade do Cloud Datastore e segregar entidades por código de usuário.

Como criar entidades para armazenar dados de usuário

Você pode criar uma entidade no Cloud Datastore declarando uma classe de modelo do NDB com determinadas propriedades, como inteiros ou strings. O Cloud Datastore indexa entidades por tipo. No caso de Firenotes, o tipo de cada entidade é Note. Para fins de consulta, cada Note é armazenada com um nome de chave, que é o código de usuário recebido da declaração sub na seção anterior.

No código a seguir, demonstramos como definir propriedades de uma entidade, com o método construtor para a classe de modelo quando a entidade é criada e por meio da atribuição de propriedades individuais após a criação:

data = request.get_json()

# Populates note properties according to the model,
# with the user ID as the key name.
note = Note(
    parent=ndb.Key(Note, claims['sub']),
    message=data['message'])

# Some providers do not provide one of these so either can be used.
note.friendly_id = claims.get('name', claims.get('email', 'Unknown'))

Para escrever a Note recém-criada no Cloud Datastore, chame o método put() no objeto note.

Como recuperar dados de usuário

É possível recuperar dados de usuário associados a um código de usuário específico, basta usar o método query() do NDB para pesquisar o banco de dados quanto a anotações no mesmo grupo de entidades. As entidades no mesmo grupo, or_ancestor path_, têm o mesmo nome de chave, que, nesse caso, é o código de usuário.

def query_database(user_id):
    """Fetches all notes associated with user_id.

    Notes are ordered them by date created, with most recent note added
    first.
    """
    ancestor_key = ndb.Key(Note, user_id)
    query = Note.query(ancestor=ancestor_key).order(-Note.created)
    notes = query.fetch()

    note_messages = []

    for note in notes:
        note_messages.append({
            'friendly_id': note.friendly_id,
            'message': note.message,
            'created': note.created
        })

    return note_messages

Com isso, você pode buscar os dados da consulta e exibir as anotações no cliente:

// Fetch notes from the backend.
function fetchNotes() {
  $.ajax(backendHostUrl + '/notes', {
    /* Set header for the XMLHttpRequest to get data from the web server
    associated with userIdToken */
    headers: {
      'Authorization': 'Bearer ' + userIdToken
    }
  }).then(function(data){
    $('#notes-container').empty();
    // Iterate over user data to display user's notes from database.
    data.forEach(function(note){
      $('#notes-container').append($('<p>').text(note.message));
    });
  });
}

Como implantar o aplicativo

Você integrou com êxito o Firebase Authentication ao aplicativo do App Engine. Para ver o aplicativo em execução em um ambiente de produção ao vivo:

  1. Altere o URL do host de back-end em main.js para https://backend-dot-[PROJECT_ID].appspot.com. Substitua [PROJECT_ID] pelo código do projeto.
  2. Implante o aplicativo usando a interface da linha de comando do Cloud SDK:

    gcloud app deploy backend/index.yaml frontend/app.yaml backend/app.yaml
    
  3. Veja o aplicativo ativo em https://[PROJECT_ID].appspot.com.

Como limpar

Para evitar que os recursos usados neste tutorial sejam cobrados na conta do Google Cloud Platform, exclua o projeto do App Engine:

Como excluir o projeto

A maneira mais fácil de evitar a cobrança é excluir o projeto criado para o tutorial.

Para excluir o projeto:

  1. No Console do GCP, acesse a página "Projetos".

    Acessar a página Projetos

  2. Na lista de projetos, selecione um e clique em Excluir projeto.
  3. Na caixa de diálogo, digite o código do projeto e clique em Encerrar para excluí-lo.

Próximas etapas

Esta página foi útil? Conte sua opinião sobre:

Enviar comentários sobre…

Ambiente padrão do App Engine para Python