Autenticar utilizadores com o Go

As apps executadas em Google Cloud plataformas geridas, como o App Engine, podem evitar a gestão da autenticação de utilizadores e da gestão de sessões através da utilização do Identity-Aware Proxy (IAP) para controlar o acesso às mesmas. A IAP não só pode controlar o acesso à app, como também fornece informações sobre os utilizadores autenticados, incluindo o endereço de email e um identificador persistente para a app sob a forma de novos cabeçalhos HTTP.

Objetivos

  • Exigir que os utilizadores da sua app do App Engine se autentiquem através da IAP.

  • Aceder às identidades dos utilizadores na app para apresentar o endereço de email autenticado do utilizador atual.

Custos

Neste documento, usa os seguintes componentes faturáveis do Google Cloud:

Para gerar uma estimativa de custos com base na sua utilização projetada, use a calculadora de preços.

Os novos Google Cloud utilizadores podem ser elegíveis para uma avaliação gratuita.

Quando terminar as tarefas descritas neste documento, pode evitar a faturação contínua eliminando os recursos que criou. Para mais informações, consulte o artigo Limpe.

Antes de começar

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  3. Install the Google Cloud CLI.

  4. Se estiver a usar um fornecedor de identidade (IdP) externo, tem primeiro de iniciar sessão na CLI gcloud com a sua identidade federada.

  5. Para inicializar a CLI gcloud, execute o seguinte comando:

    gcloud init
  6. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  7. Install the Google Cloud CLI.

  8. Se estiver a usar um fornecedor de identidade (IdP) externo, tem primeiro de iniciar sessão na CLI gcloud com a sua identidade federada.

  9. Para inicializar a CLI gcloud, execute o seguinte comando:

    gcloud init
  10. Prepare o seu ambiente de desenvolvimento.
  11. Configurar o projeto

    1. Na janela do terminal, clone o repositório da app de exemplo para a sua máquina local:

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

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

    Contexto

    Este tutorial usa o IAP para autenticar utilizadores. Esta é apenas uma das várias abordagens possíveis. Para saber mais acerca dos vários métodos de autenticação de utilizadores, consulte a secção Conceitos de autenticação.

    A app Hello user-email-address

    A app para este tutorial é uma app do App Engine minimalista do tipo Olá mundo, com uma funcionalidade não típica: em vez de "Olá mundo", apresenta "Olá user-email-address", onde user-email-address é o endereço de email do utilizador autenticado.

    Esta funcionalidade é possível através da análise das informações autenticadas que a IAP adiciona a cada pedido Web que transmite à sua app. Existem três novos cabeçalhos de pedidos adicionados a cada pedido Web que chega à sua app. Os dois primeiros cabeçalhos são strings de texto simples que pode usar para identificar o utilizador. O terceiro cabeçalho é um objeto assinado criptograficamente com essas mesmas informações.

    • X-Goog-Authenticated-User-Email: o endereço de email de um utilizador identifica-o. Não armazene informações pessoais se a sua app puder evitá-lo. Esta app não armazena dados; apenas os repete ao utilizador.

    • X-Goog-Authenticated-User-Id: este ID de utilizador atribuído pela Google não mostra informações sobre o utilizador, mas permite que uma app saiba que um utilizador com sessão iniciada é o mesmo que foi visto anteriormente.

    • X-Goog-Iap-Jwt-Assertion: pode configurar as apps Google Cloud para aceitarem pedidos Web de outras apps na nuvem, ignorando o IAP, além de pedidos Web da Internet. Se uma app estiver configurada desta forma, é possível que esses pedidos tenham cabeçalhos falsificados. Em vez de usar qualquer um dos cabeçalhos de texto simples mencionados anteriormente, pode usar e validar este cabeçalho assinado criptograficamente para verificar se as informações foram fornecidas pela Google. O endereço de email do utilizador e um User-ID persistente estão disponíveis como parte deste cabeçalho assinado.

    Se tiver a certeza de que a app está configurada para que apenas os pedidos Web da Internet a possam alcançar e que ninguém pode desativar o serviço de CAs para a app, a obtenção de um ID de utilizador exclusivo requer apenas uma linha de código:

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

    No entanto, uma app resiliente deve esperar que as coisas corram mal, incluindo problemas inesperados de configuração ou ambientais. Por isso, recomendamos que crie uma função que use e valide o cabeçalho assinado criptograficamente. Não é possível falsificar a assinatura desse cabeçalho e, quando validada, pode ser usada para devolver a identificação.

    Compreender o código

    Esta secção explica como funciona o código. Se quiser executar a app, pode avançar para a secção Implemente a app.

    • O ficheiro go.mod define um módulo Go e os módulos dos quais depende.

      module github.com/GoogleCloudPlatform/golang-samples/getting-started/authenticating-users
      
      go 1.24.0
      
      require (
      	cloud.google.com/go/compute/metadata v0.6.0
      	github.com/golang-jwt/jwt v3.2.2+incompatible
      )
      
      require golang.org/x/sys v0.29.0 // indirect
      
    • O ficheiro app.yaml indica ao App Engine o ambiente de linguagem de que o seu código precisa.

      runtime: go112
    • A app começa por importar pacotes e definir uma função main. A função main regista um controlador 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 JWT que o IAP adicionou ao pedido recebido e chama a função validateAssertion para validar o valor assinado criptograficamente. O endereço de email é, em seguida, usado numa resposta 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 se a declaração foi devidamente assinada e devolve o endereço de email e o ID do utilizador associados.

      A validação de uma declaração JWT requer o conhecimento dos certificados de chave pública da entidade que assinou a declaração (neste caso, a Google) e o público-alvo a que a declaração se destina. Para uma app do App Engine, o público-alvo é uma string com Google Cloud informações de identificação do projeto. A função validateAssertion obtém esses certificados da função certs e a string de público-alvo 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
      }
      
    • Pode procurar o ID numérico e o nome do Google Cloud projeto e inseri-los manualmente no código-fonte, mas a função audience faz isso por si ao consultar o serviço de metadados padrão disponibilizado a todas as apps do App Engine. Uma vez que o serviço de metadados é externo ao código da app, esse resultado é guardado numa variável global que é devolvida sem ter de procurar metadados em chamadas subsequentes.

      O serviço de metadados do App Engine (e serviços de metadados semelhantes para outros Google Cloud serviços de computação) tem o aspeto de um Website e é consultado por consultas Web padrão. No entanto, o serviço de metadados não é, na verdade, um site externo, mas sim uma funcionalidade interna que devolve informações pedidas sobre a app em execução. Por isso, é seguro usar http em vez de pedidos https. O serviço de metadados é usado para obter os identificadores atuais Google Cloud necessários para definir o público-alvo pretendido da afirmação 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 validação de uma assinatura digital requer o certificado de chave pública do signatário. A Google disponibiliza um Website que devolve todos os certificados de chave pública usados atualmente. Estes resultados são colocados em cache caso sejam necessários novamente na mesma instância da app.

      
      // 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
      }
      

    Implementar a app

    Agora, pode implementar a app e, em seguida, ativar as CAsI para exigir que os utilizadores se autentiquem antes de poderem aceder à app.

    1. Na janela do terminal, aceda ao diretório que contém o ficheiro app.yaml e implemente a app no App Engine:

      gcloud app deploy
      
    2. Quando lhe for pedido, selecione uma região próxima.

    3. Quando lhe for perguntado se quer continuar com a operação de implementação, introduza Y.

      Em poucos minutos, a sua app está disponível na Internet.

    4. Ver a app:

      gcloud app browse
      

      No resultado, copie web-site-url, o endereço Web da app.

    5. Numa janela do navegador, cole web-site-url para abrir a app.

      Não é apresentado nenhum email porque ainda não está a usar as CNAs, pelo que não são enviadas informações do utilizador para a app.

    Ative a IAP

    Agora que existe uma instância do App Engine, pode protegê-la com o IAP:

    1. Na Google Cloud consola, aceda à página Identity-Aware Proxy.

      Aceda à página do Identity-Aware Proxy

    2. Como esta é a primeira vez que ativa uma opção de autenticação para este projeto, é apresentada uma mensagem a indicar que tem de configurar o ecrã de consentimento de OAuth antes de poder usar as IAP.

      Clique em Configurar ecrã de consentimento.

    3. No separador Ecrã de consentimento OAuth da página Credenciais, preencha os seguintes campos:

      • Se a sua conta estiver numa organização do Google Workspace, selecione Externo e clique em Criar. Para começar, a app só está disponível para os utilizadores que autorizar explicitamente.

      • No campo Nome da aplicação, introduza IAP Example.

      • No campo Email de apoio técnico, introduza o seu endereço de email.

      • No campo Domínio autorizado, introduza a parte do nome de anfitrião do URL da app, por exemplo, iap-example-999999.uc.r.appspot.com. Prima a tecla Enter após introduzir o nome do anfitrião no campo.

      • No campo Link da página inicial da aplicação, introduza o URL da sua app, por exemplo, https://iap-example-999999.uc.r.appspot.com/.

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

    4. Clique em Guardar. Quando lhe for pedido que crie credenciais, pode fechar a janela.

    5. Na Google Cloud consola, aceda à página Identity-Aware Proxy.

      Aceda à página do Identity-Aware Proxy

    6. Para atualizar a página, clique em Atualizar . A página apresenta uma lista de recursos que pode proteger.

    7. Na coluna IAP, clique para ativar a IAP para a app.

    8. No navegador, aceda novamente a web-site-url.

    9. Em vez da página Web, aparece um ecrã de início de sessão para se autenticar. Quando inicia sessão, o acesso é recusado porque o IAP não tem uma lista de utilizadores a permitir o acesso à app.

    Adicione utilizadores autorizados à app

    1. Na Google Cloud consola, aceda à página Identity-Aware Proxy.

      Aceda à página do Identity-Aware Proxy

    2. Selecione a caixa de verificação da app App Engine e, de seguida, clique em Adicionar principal.

    3. Introduza allAuthenticatedUsers e, de seguida, selecione a função Utilizador da app Web protegida/IAP na nuvem.

    4. Clique em Guardar.

    Agora, qualquer utilizador que a Google possa autenticar pode aceder à app. Se quiser, pode restringir ainda mais o acesso adicionando apenas uma ou mais pessoas ou grupos como principais:

    • Qualquer endereço de email do Gmail ou Google Workspace

    • Um endereço de email de um Grupo Google

    • Um nome de domínio do Google Workspace

    Aceda à app

    1. No navegador, aceda a web-site-url.

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

    3. No ecrã de início de sessão, inicie sessão com as suas credenciais Google.

      A página apresenta a mensagem "Olá, user-email-address" com o seu endereço de email.

      Se continuar a ver a mesma página que antes, pode haver um problema com o navegador que não está a atualizar totalmente os novos pedidos agora que ativou as CAPs. Feche todas as janelas do navegador, volte a abri-las e tente novamente.

    Conceitos de autenticação

    Existem várias formas de uma app autenticar os respetivos utilizadores e restringir o acesso apenas a utilizadores autorizados. Os métodos de autenticação comuns, por ordem decrescente do nível de esforço para a app, estão listados nas secções seguintes.

    Opção Vantagens Desvantagens
    Autenticação de apps
    • A app pode ser executada em qualquer plataforma, com ou sem uma ligação à Internet
    • Os utilizadores não precisam de usar nenhum outro serviço para gerir a autenticação
    • A app tem de gerir as credenciais do utilizador de forma segura e proteger contra a divulgação
    • A app tem de manter os dados da sessão para utilizadores com sessão iniciada
    • A app tem de fornecer registo de utilizadores, alterações de palavras-passe e recuperação de palavras-passe
    OAuth2
    • A app pode ser executada em qualquer plataforma com ligação à Internet, incluindo uma estação de trabalho do programador
    • A app não precisa de registo de utilizadores, alterações de palavras-passe nem funções de recuperação de palavras-passe.
    • O risco de divulgação de informações do utilizador é delegado a outro serviço
    • Novas medidas de segurança de início de sessão processadas fora da app
    • Os utilizadores têm de se registar no serviço de identidade
    • A app tem de manter os dados da sessão para utilizadores com sessão iniciada
    IAP
    • A app não precisa de ter código para gerir utilizadores, autenticação ou estado da sessão
    • A app não tem credenciais de utilizador que possam ser violadas
    • A app só pode ser executada em plataformas suportadas pelo serviço. Especificamente, determinados Google Cloud serviços que suportam as CAs, como o App Engine.

    Autenticação gerida pela app

    Com este método, a app gere todos os aspetos da autenticação do utilizador por si própria. A app tem de manter a sua própria base de dados de credenciais de utilizador e gerir as sessões de utilizador, e tem de fornecer funções para gerir contas de utilizador e palavras-passe, verificar credenciais de utilizador, bem como emitir, verificar e atualizar sessões de utilizador com cada início de sessão autenticado. O diagrama seguinte ilustra o método de autenticação gerido pela app.

    Fluxo gerido pela aplicação

    Conforme mostrado no diagrama, depois de o utilizador iniciar sessão, a app cria e mantém informações sobre a sessão do utilizador. Quando o utilizador faz um pedido à app, o pedido tem de incluir informações da sessão que a app é responsável por validar.

    A principal vantagem desta abordagem é que é autónoma e está sob o controlo da app. A app nem sequer tem de estar disponível na Internet. A principal desvantagem é que a app passa a ser responsável por fornecer todas as funcionalidades de gestão da conta e proteger todos os dados de credenciais confidenciais.

    Autenticação externa com OAuth2

    Uma boa alternativa para processar tudo na app é usar um serviço de identidade externo, como o Google, que processa todas as informações e funcionalidades da conta do utilizador e é responsável por salvaguardar as credenciais confidenciais. Quando um utilizador tenta iniciar sessão na app, o pedido é redirecionado para o serviço de identidade, que autentica o utilizador e, em seguida, redireciona o pedido de volta para a app com as informações de autenticação necessárias. Para mais informações, consulte o artigo Usar o OAuth 2.0 para aplicações de servidor Web.

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

    Fluxo OAuth2

    O fluxo no diagrama começa quando o utilizador envia um pedido para aceder à app. Em vez de responder diretamente, a app redireciona o navegador do utilizador para a plataforma de identidade da Google, que apresenta uma página para iniciar sessão no Google. Depois de iniciar sessão com êxito, o navegador do utilizador é redirecionado de volta para a app. Este pedido inclui informações que a app pode usar para procurar informações sobre o utilizador agora autenticado, e a app responde agora ao utilizador.

    Este método tem muitas vantagens para a app. Delega todas as funcionalidades e os riscos de gestão de contas ao serviço externo, o que pode melhorar o início de sessão e a segurança da conta sem que a app tenha de ser alterada. No entanto, conforme mostrado no diagrama anterior, a app tem de ter acesso à Internet para usar este método. A app também é responsável pela gestão das sessões após a autenticação do utilizador.

    Identity-Aware Proxy

    A terceira abordagem, que este tutorial aborda, é usar a IAP para gerir toda a autenticação e gestão de sessões com quaisquer alterações à app. A IAP interceta todos os pedidos Web para a sua app, bloqueia todos os que não foram autenticados e encaminha outros com dados de identidade do utilizador adicionados a cada pedido.

    O processamento de pedidos é apresentado no diagrama seguinte.

    Fluxo de CNAs

    Os pedidos dos utilizadores são intercetados pela IAP, que bloqueia os pedidos não autenticados. Os pedidos autenticados são transmitidos à app, desde que o utilizador autenticado esteja na lista de utilizadores permitidos. Os pedidos transmitidos através da IAP têm cabeçalhos adicionados que identificam o utilizador que fez o pedido.

    A app já não precisa de processar informações de contas de utilizadores nem de sessões. Qualquer operação que precise de saber um identificador exclusivo do utilizador pode obtê-lo diretamente de cada pedido Web recebido. No entanto, só pode ser usado para serviços de computação que suportem a CAs, como o App Engine e os balanceadores de carga. Não pode usar as CAsI numa máquina de desenvolvimento local.

    Limpar

    Para evitar incorrer em custos na sua conta do Google Cloud pelos recursos usados neste tutorial, elimine o projeto que contém os recursos ou mantenha o projeto e elimine os recursos individuais.

    1. In the Google Cloud console, go to the Manage resources page.

      Go to Manage resources

    2. In the project list, select the project that you want to delete, and then click Delete.
    3. In the dialog, type the project ID, and then click Shut down to delete the project.