Como autenticar usuários com o Go


Aplicativos em execução em plataformas gerenciadas pelo Google Cloud, como o App Engine podem evitar o gerenciamento de autenticação de usuários e de sessão usando o Identity-Aware Proxy (IAP) para controlar o acesso. O IAP não só controla o acesso ao aplicativo como também fornece informações sobre os usuários autenticados, incluindo o endereço de e-mail e um identificador permanente para o aplicativo na forma de novos cabeçalhos HTTP.

Objetivos

  • Exigir que os usuários do seu aplicativo do App Engine se autentiquem usando o IAP.

  • Acessar as identidades dos usuários no aplicativo para exibir o endereço de e-mail autenticado do usuário atual.

Custos

Neste documento, você usará os seguintes componentes faturáveis do Google Cloud:

Para gerar uma estimativa de custo baseada na projeção de uso deste tutorial, use a calculadora de preços. Novos usuários do Google Cloud podem estar qualificados para uma avaliação gratuita.

Ao concluir as tarefas descritas neste documento, é possível evitar o faturamento contínuo excluindo os recursos criados. Saiba mais em Limpeza.

Antes de começar

  1. Faça login na sua conta do Google Cloud. Se você começou a usar o Google Cloud agora, crie uma conta para avaliar o desempenho de nossos produtos em situações reais. Clientes novos também recebem US$ 300 em créditos para executar, testar e implantar cargas de trabalho.
  2. No console do Google Cloud, na página do seletor de projetos, selecione ou crie um projeto do Google Cloud.

    Acessar o seletor de projetos

  3. Instale a CLI do Google Cloud.
  4. Para inicializar a CLI gcloud, execute o seguinte comando:

    gcloud init
  5. No console do Google Cloud, na página do seletor de projetos, selecione ou crie um projeto do Google Cloud.

    Acessar o seletor de projetos

  6. Instale a CLI do Google Cloud.
  7. Para inicializar a CLI gcloud, execute o seguinte comando:

    gcloud init
  8. Prepare seu ambiente de desenvolvimento.

Como configurar o projeto

  1. Na janela de terminal, clone o repositório do app de amostra em sua máquina local:

    git clone https://github.com/GoogleCloudPlatform/golang-samples.git
  2. Acesse o diretório que contém o código de amostra:

    cd golang-samples/getting-started/authenticating-users

Contexto

Neste tutorial, o IAP é usado para autenticar usuários. Essa é apenas uma das várias abordagens possíveis. Para saber mais sobre os vários métodos de autenticação de usuários, consulte a seção Conceitos de autenticação.

O aplicativo Hello user-email-address

Neste tutorial, é usado um app Hello World do App Engine mínimo com um recurso atípico: em vez de "Hello world", ele exibe "Hello user-email-address", em que user-email-address é o endereço de e-mail do usuário autenticado.

Essa funcionalidade é possível por meio da análise das informações autenticadas que o IAP adiciona a cada solicitação da Web que passa no aplicativo. Há três novos cabeçalhos de solicitação adicionados a cada solicitação da Web que chega ao app. Os dois primeiros cabeçalhos são strings de texto simples que podem ser usadas para identificar o usuário. O terceiro cabeçalho é um objeto assinado criptograficamente com as mesmas informações.

  • X-Goog-Authenticated-User-Email: o endereço de e-mail do usuário o identifica. Não armazene informações pessoais se o aplicativo puder evitar isso. Esse aplicativo não armazena dados. Ele apenas os retorna ao usuário.

  • X-Goog-Authenticated-User-Id: esse ID de usuário atribuído pelo Google não mostra informações pessoais, mas permite que um aplicativo saiba que o usuário conectado é o mesmo que se conectou anteriormente.

  • X-Goog-Iap-Jwt-Assertion: é possível configurar aplicativos do Google Cloud para aceitar solicitações da web de outros apps na nuvem ignorando o IAP, além de solicitações da web via Internet. Se um aplicativo for configurado dessa forma, é possível que essas solicitações tenham cabeçalhos forjados. Em vez de usar um dos cabeçalhos de texto simples mencionados anteriormente, é possível usar e verificar esse cabeçalho assinado por criptografia para confirmar se as informações foram fornecidas pelo Google. O endereço de e-mail do usuário e um ID de usuário permanente estão disponíveis como parte do cabeçalho assinado.

Se você tiver certeza de que o aplicativo está configurado para que apenas solicitações da Web via Internet possam acessá-lo e que não seja possível desativar o serviço do IAP, apenas uma linha de código será necessária para recuperar um ID de usuário único:

userID := r.Header.Get("X-Goog-Authenticated-User-ID")

No entanto, um aplicativo resiliente está preparado para erros, como uma configuração inesperada ou problemas ambientais. Assim, recomendamos criar uma função que use e verifique o cabeçalho criptograficamente assinado. A assinatura do cabeçalho não pode ser forjada. Quando verificada, ela pode ser usada para retornar a identificação.

Noções básicas sobre o código

Esta seção explica como o código funciona. Se você quiser executar o aplicativo, avance para a seção Implantar o aplicativo.

  • O arquivo go.mod define um módulo Go e os módulos de que ele depende.

    module github.com/GoogleCloudPlatform/golang-samples/getting-started/authenticating-users
    
    go 1.19
    
    require (
    	cloud.google.com/go/compute/metadata v0.2.3
    	github.com/golang-jwt/jwt v3.2.2+incompatible
    )
    
    require cloud.google.com/go/compute v1.19.1 // indirect
    
  • O arquivo app.yaml informa ao App Engine o ambiente de linguagem que seu código requer.

    runtime: go112
  • O aplicativo inicia a importar pacotes e definir um main função. A função main registra um gerenciador de índice e inicia um servidor HTTP.

    
    // The authenticating-users program is a sample web server application that
    // extracts and verifies user identity data passed to it via Identity-Aware
    // Proxy.
    package main
    
    import (
    	"encoding/json"
    	"fmt"
    	"log"
    	"net/http"
    	"os"
    	"time"
    
    	"cloud.google.com/go/compute/metadata"
    	"github.com/golang-jwt/jwt"
    )
    
    // app holds the Cloud IAP certificates and audience field for this app, which
    // are needed to verify authentication headers set by Cloud IAP.
    type app struct {
    	certs map[string]string
    	aud   string
    }
    
    func main() {
    	a, err := newApp()
    	if err != nil {
    		log.Fatal(err)
    	}
    
    	http.HandleFunc("/", a.index)
    
    	port := os.Getenv("PORT")
    	if port == "" {
    		port = "8080"
    		log.Printf("Defaulting to port %s", port)
    	}
    
    	log.Printf("Listening on port %s", port)
    	if err := http.ListenAndServe(":"+port, nil); err != nil {
    		log.Fatal(err)
    	}
    }
    
    // newApp creates a new app, returning an error if either the Cloud IAP
    // certificates or the app's audience field cannot be obtained.
    func newApp() (*app, error) {
    	certs, err := certificates()
    	if err != nil {
    		return nil, err
    	}
    
    	aud, err := audience()
    	if err != nil {
    		return nil, err
    	}
    
    	a := &app{
    		certs: certs,
    		aud:   aud,
    	}
    	return a, nil
    }
    
  • A função index recebe o valor do cabeçalho da declaração do JWT que o IAP adicionou na solicitação recebida e chama uma função validateAssertion para validar esse valor assinado criptograficamente. O endereço de e-mail é usado em uma resposta da Web mínima.

    
    // index responds to requests with our greeting.
    func (a *app) index(w http.ResponseWriter, r *http.Request) {
    	if r.URL.Path != "/" {
    		http.NotFound(w, r)
    		return
    	}
    
    	assertion := r.Header.Get("X-Goog-IAP-JWT-Assertion")
    	if assertion == "" {
    		fmt.Fprintln(w, "No Cloud IAP header found.")
    		return
    	}
    	email, _, err := validateAssertion(assertion, a.certs, a.aud)
    	if err != nil {
    		log.Println(err)
    		fmt.Fprintln(w, "Could not validate assertion. Check app logs.")
    		return
    	}
    
    	fmt.Fprintf(w, "Hello %s\n", email)
    }
    
  • A função validateAssertion valida a declaração que foi assinada corretamente e retorna o endereço de e-mail associado e o ID do usuário.

    Para validar uma declaração do JWT, é preciso conhecer os certificados de chave pública da entidade que assinou a declaração (neste caso, o Google) e o público a que ela se destina. No caso de um aplicativo do App Engine, o público é uma string com informações de identificação de um projeto do Google Cloud. A função validateAssertion recebe os certificados da função certs e da string de público da função audience.

    
    // validateAssertion validates assertion was signed by Google and returns the
    // associated email and userID.
    func validateAssertion(assertion string, certs map[string]string, aud string) (email string, userID string, err error) {
    	token, err := jwt.Parse(assertion, func(token *jwt.Token) (interface{}, error) {
    		keyID := token.Header["kid"].(string)
    
    		_, ok := token.Method.(*jwt.SigningMethodECDSA)
    		if !ok {
    			return nil, fmt.Errorf("unexpected signing method: %q", token.Header["alg"])
    		}
    
    		cert := certs[keyID]
    		return jwt.ParseECPublicKeyFromPEM([]byte(cert))
    	})
    
    	if err != nil {
    		return "", "", err
    	}
    
    	claims, ok := token.Claims.(jwt.MapClaims)
    	if !ok {
    		return "", "", fmt.Errorf("could not extract claims (%T): %+v", token.Claims, token.Claims)
    	}
    
    	if claims["aud"].(string) != aud {
    		return "", "", fmt.Errorf("mismatched audience. aud field %q does not match %q", claims["aud"], aud)
    	}
    	return claims["email"].(string), claims["sub"].(string), nil
    }
    
  • Você pode procurar o ID numérico e o nome do projeto do Google Cloud e colocá-los no código-fonte, mas a função audience faz isso consultando o serviço de metadados padrão disponibilizado para cada aplicativo do App Engine. Como o serviço de metadados é externo ao código do aplicativo, esse resultado é salvo em uma variável global que é retornada sem a necessidade de procurar metadados nas chamadas subsequentes.

    O serviço de metadados do App Engine, assim como serviços de metadados semelhantes de outros serviços do Google Cloud Computing, tem a aparência de um site e recebe consultas padrão da web. No entanto, o serviço de metadados não é um site externo, mas um recurso interno que retorna informações solicitadas sobre o aplicativo em execução. Por isso, é seguro usar http em vez de solicitações https. O serviço de metadados é usado para obter os identificadores atuais do Google Cloud necessários para definir o público-alvo da declaração do JWT.

    
    // audience returns the expected audience value for this service.
    func audience() (string, error) {
    	projectNumber, err := metadata.NumericProjectID()
    	if err != nil {
    		return "", fmt.Errorf("metadata.NumericProjectID: %w", err)
    	}
    
    	projectID, err := metadata.ProjectID()
    	if err != nil {
    		return "", fmt.Errorf("metadata.ProjectID: %w", err)
    	}
    
    	return "/projects/" + projectNumber + "/apps/" + projectID, nil
    }
    
  • A verificação de uma assinatura digital requer o certificado de chave pública do signatário. O Google fornece um site que retorna todos os certificados de chave pública usados atualmente. Esses resultados são armazenados em cache para caso sejam necessários novamente na mesma instância do aplicativo.

    
    // certificates returns Cloud IAP's cryptographic public keys.
    func certificates() (map[string]string, error) {
    	const url = "https://www.gstatic.com/iap/verify/public_key"
    	client := http.Client{
    		Timeout: 5 * time.Second,
    	}
    	resp, err := client.Get(url)
    	if err != nil {
    		return nil, fmt.Errorf("Get: %w", err)
    	}
    
    	var certs map[string]string
    	dec := json.NewDecoder(resp.Body)
    	if err := dec.Decode(&certs); err != nil {
    		return nil, fmt.Errorf("Decode: %w", err)
    	}
    
    	return certs, nil
    }
    

Como implantar o app

Agora é possível implantar o aplicativo e permitir que o IAP solicite a autenticação dos usuários para que eles possam acessar o aplicativo.

  1. Na janela do terminal, acesse o diretório que contém o arquivo app.yaml e implante o aplicativo no App Engine:

    gcloud app deploy
    
  2. Quando solicitado, selecione uma região próxima.

  3. Quando receber uma mensagem perguntando se quer continuar com a operação de implantação, insira Y.

    Em alguns minutos, seu app está ativo na Internet.

  4. Ver o aplicativo:

    gcloud app browse
    

    Na saída, copie web-site-url, o endereço da Web do aplicativo.

  5. Em uma janela do navegador, cole web-site-url para abrir o aplicativo.

    Como você ainda não está usando o IAP, nenhum e-mail será exibido. Portanto, nenhuma informação de usuário será enviada para o aplicativo.

Ativar o IAP

Agora que existe uma instância do App Engine, é possível protegê-la com o IAP:

  1. No console do Google Cloud, acesse a página Identity-Aware Proxy.

    Acessar a página do Identity-Aware Proxy

  2. Como essa é a primeira vez que você ativa uma opção de autenticação para o projeto, você verá uma mensagem para configurar a tela de consentimento do OAuth antes de usar o IAP.

    Clique em Configurar tela de consentimento.

  3. Na guia Tela de consentimento OAuth da página Credenciais, preencha os seguintes campos:

    • Se a sua conta estiver em uma organização no Google Workspace, selecione Externo e clique em Criar. A princípio, o app só estará disponível para os usuários que você permitir explicitamente.

    • No campo Nome do aplicativo, digite IAP Example.

    • No campo E-mail para suporte, digite seu endereço de e-mail.

    • No campo Domínio autorizado, digite a parte do nome do host do URL do aplicativo, por exemplo, iap-example-999999.uc.r.appspot.com. Pressione a tecla Enter depois de inserir o nome do host no campo.

    • No campo Link da página inicial do aplicativo, insira o URL do seu aplicativo. Por exemplo, https://iap-example-999999.uc.r.appspot.com/.

    • No campo Linha da política de privacidade do aplicativo, use o mesmo URL do link da página inicial para fins de teste.

  4. Clique em Salvar. Quando for solicitado que você crie credenciais, feche a janela.

  5. No console do Google Cloud, acesse a página Identity-Aware Proxy.

    Acessar a página do Identity-Aware Proxy

  6. Para atualizar a página, clique em Atualizar . A página exibe uma lista de recursos que são possíveis proteger.

  7. Na coluna IAP, clique para ativar o IAP para o aplicativo.

  8. No seu navegador, acesse web-site-url novamente.

  9. Em vez da página da Web, há uma tela de login para autenticação. Ao fazer login, seu acesso será negado, porque o IAP não tem uma lista de usuários para permitir o acesso ao aplicativo.

Adicionar usuários autorizados ao aplicativo

  1. No console do Google Cloud, acesse a página "Identity-Aware Proxy".

    Acessar a página do Identity-Aware Proxy

  2. Marque a caixa de seleção correspondente ao aplicativo do App Engine e clique em Adicionar principal.

  3. Insira allAuthenticatedUsers e selecione o papel Cloud IAP/Usuário do app da Web protegido pelo IAP.

  4. Clique em Salvar.

Agora qualquer usuário que o Google possa autenticar poderá acessar o aplicativo. Se quiser, restrinja o acesso adicionando apenas uma ou mais pessoas ou grupos como principais:

  • Qualquer endereço de e-mail do Gmail ou do Google Workspace

  • Um endereço de e-mail de Grupo do Google

  • Um nome de domínio do Google Workspace

Acessar o aplicativo

  1. No seu navegador, acesse web-site-url.

  2. Para atualizar a página, clique em Atualizar .

  3. Na tela de login, faça login com suas credenciais do Google.

    Será exibida uma página "Hello user-email-address" com seu endereço de e-mail.

    Se você ainda vir a mesma página de antes, talvez o navegador não esteja atualizando totalmente as novas solicitações, agora que você ativou o IAP. Feche todas as janelas do navegador, volte a abri-las e tente novamente.

Conceitos de autenticação

Existem várias formas de um aplicativo autenticar seus usuários e restringir o acesso somente a usuários autorizados. Veja nas seções a seguir os métodos de autenticação comuns, em nível de esforço decrescente para o aplicativo.

Opção Vantagens Desvantagens
Autenticação do aplicativo
  • O app pode ser executado em qualquer plataforma, com ou sem conexão de Internet
  • Os usuários não precisam usar nenhum outro serviço para gerenciar a autenticação
  • O app precisa gerenciar as credenciais do usuário com segurança e proteger contra divulgação
  • O app precisa manter os dados da sessão dos usuários conectados
  • O app precisa fornecer registro de usuários, alterações de senha e recuperação de senha
OAuth2
  • O app pode ser executado em qualquer plataforma conectada à Internet, incluindo uma estação de trabalho de desenvolvedor
  • O app não precisa de registro de usuários, alterações de senha ou funções de recuperação de senha.
  • O risco de divulgação de informações do usuário é delegado a outro serviço
  • Novas medidas de segurança de login são processadas fora do app
  • Os usuários precisam se registrar no serviço de identidade
  • O app precisa manter os dados da sessão dos usuários conectados
IAP
  • O app não precisa de códigos para gerenciar usuários, a autenticação ou o estado da sessão
  • O app não tem credenciais de usuário que possam ser violadas
  • O aplicativo só pode ser executado em plataformas compatíveis com o serviço. Especificamente, determinados serviços do Google Cloud compatíveis com o IAP, como o App Engine.

Autenticação gerenciada pelo aplicativo

Com esse método, o aplicativo gerencia cada aspecto da autenticação de usuários por conta própria. O aplicativo precisa manter o próprio banco de dados de credenciais e gerenciar sessões de usuários, além de fornecer funções para gerenciar contas e senhas, verificar credenciais e emitir, verificar e atualizar sessões de usuário com cada login autenticado. O diagrama a seguir ilustra o método de autenticação gerenciado pelo aplicativo.

Fluxo gerenciado pelo aplicativo

Conforme mostrado no diagrama, depois que o usuário faz login, o aplicativo cria e mantém informações sobre a sessão do usuário. Quando o usuário faz uma solicitação ao aplicativo, nela, devem estar incluídas informações da sessão que o aplicativo é responsável por verificar.

A principal vantagem dessa abordagem é que ela é autossuficiente e está sob controle do aplicativo. O app não precisa estar disponível na Internet. A principal desvantagem é que agora o aplicativo é responsável por fornecer todos os recursos de gerenciamento de contas e por proteger todos os dados confidenciais das credenciais.

Autenticação externa com OAuth2

Uma boa alternativa para gerenciar tudo no aplicativo é usar um serviço de identidade externo, como o Google, que lida com todas as informações e funcionalidades da conta do usuário e é responsável por proteger credenciais confidenciais. Quando um usuário tenta fazer login no aplicativo, a solicitação é redirecionada ao serviço de identidade, que o autentica e redireciona a solicitação de volta ao aplicativo com as informações de autenticação necessárias. Para mais informações, consulte Usar o OAuth 2.0 para aplicativos de servidor da Web.

O diagrama a seguir ilustra a autenticação externa com o método OAuth2.

Fluxo do OAuth 2

O fluxo no diagrama começa quando o usuário envia uma solicitação para acessar o aplicativo. Em vez de responder diretamente, o aplicativo redireciona o navegador do usuário para a plataforma de identidade do Google, que exibe uma página de login no Google. Após o login, o navegador do usuário é direcionado de volta para o aplicativo. Essa solicitação inclui informações que o aplicativo pode usar para procurar informações sobre o usuário agora autenticado, e o aplicativo agora responde ao usuário.

Esse método tem muitas vantagens para o aplicativo. Ele delega todos os recursos e riscos de gerenciamento da conta ao serviço externo, o que pode melhorar o login e a segurança da conta sem precisar alterar o app. No entanto, como mostrado no diagrama anterior, o aplicativo precisa ter acesso à Internet para usar esse método. Além disso, o app é responsável por gerenciar as sessões depois que o usuário é autenticado.

Identity-Aware Proxy

A terceira abordagem discutida neste tutorial é usar o IAP para gerenciar todas as autenticações e as sessões com qualquer alteração no aplicativo. O IAP intercepta todas as solicitações da Web para o app, bloqueia todas que não foram autenticadas e transmite outros dados de identidade de usuário adicionados a cada solicitação.

O gerenciamento da solicitação é mostrado no diagrama a seguir.

Fluxo do IAP

Solicitações de usuários são interceptadas pelo IAP, que bloqueia solicitações não autenticadas. As solicitações autenticadas são transmitidas ao aplicativo, desde que o usuário autenticado esteja na lista de usuários permitidos. As solicitações transmitidas por meio do IAP têm cabeçalhos adicionados que identificam o usuário que fez a solicitação.

O aplicativo não precisa mais lidar com informações de conta ou sessão do usuário. Qualquer operação que precise de um identificador exclusivo do usuário pode consegui-lo diretamente de cada solicitação da Web recebida. No entanto, esse método só pode ser usado para serviços de computação compatíveis com o IAP, como o App Engine e os balanceadores de carga. Não é possível usar o IAP em uma máquina de desenvolvimento local.

Limpar

Para evitar cobranças na sua conta do Google Cloud pelos recursos usados no tutorial, exclua o projeto que os contém ou mantenha o projeto e exclua os recursos individuais.

  1. No Console do Google Cloud, acesse a página Gerenciar recursos.

    Acessar "Gerenciar recursos"

  2. Na lista de projetos, selecione o projeto que você quer excluir e clique em Excluir .
  3. Na caixa de diálogo, digite o ID do projeto e clique em Encerrar para excluí-lo.