Autentica usuarios con Go


En las apps que se ejecutan en las plataformas administradas de Google Cloud, como App Engine, puedes evitar la administración de la autenticación de usuarios y la administración de sesiones mediante el control de acceso de Identity‑Aware Proxy (IAP). Con IAP, no solo se puede controlar el acceso a la aplicación, sino también proporcionar información sobre los usuarios autenticados, incluida la dirección de correo electrónico y un identificador persistente para la aplicación en la forma de nuevos encabezados HTTP.

Objetivos

  • Solicita a los usuarios de tu aplicación de App Engine que se autentiquen mediante IAP.

  • Accede a las identidades de los usuarios en la aplicación para mostrar la dirección de correo electrónico autenticada del usuario actual.

Costos

En este documento, usarás los siguientes componentes facturables de Google Cloud:

Para generar una estimación de costos en función del uso previsto, usa la calculadora de precios. Es posible que los usuarios nuevos de Google Cloud califiquen para obtener una prueba gratuita.

Cuando finalices las tareas que se describen en este documento, puedes borrar los recursos que creaste para evitar que continúe la facturación. Para obtener más información, consulta Cómo realizar una limpieza.

Antes de comenzar

  1. Accede a tu cuenta de Google Cloud. Si eres nuevo en Google Cloud, crea una cuenta para evaluar el rendimiento de nuestros productos en situaciones reales. Los clientes nuevos también obtienen $300 en créditos gratuitos para ejecutar, probar y, además, implementar cargas de trabajo.
  2. En la página del selector de proyectos de la consola de Google Cloud, selecciona o crea un proyecto de Google Cloud.

    Ir al selector de proyectos

  3. Instala Google Cloud CLI.
  4. Para inicializar la CLI de gcloud, ejecuta el siguiente comando:

    gcloud init
  5. En la página del selector de proyectos de la consola de Google Cloud, selecciona o crea un proyecto de Google Cloud.

    Ir al selector de proyectos

  6. Instala Google Cloud CLI.
  7. Para inicializar la CLI de gcloud, ejecuta el siguiente comando:

    gcloud init
  8. Prepara tu entorno de desarrollo.

Configura el proyecto

  1. En la ventana de la terminal, clona el repositorio de la app de muestra en la máquina local:

    git clone https://github.com/GoogleCloudPlatform/golang-samples.git
  2. Ve al directorio que contiene el código de muestra:

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

Fondo

En este instructivo, se usa IAP para autenticar a los usuarios. Este es solo uno de varios de los enfoques posibles. Si deseas obtener más información sobre los distintos métodos para autenticar usuarios, consulta la sección Conceptos de autenticación.

La aplicación de Hello user-email-address

La aplicación en este instructivo es una aplicación Hello World de App Engine mínima, con una característica no típica: en lugar de "Hello World" muestra "Hello user-email-address", en la que user-email-address es la dirección de correo electrónico del usuario autenticado.

Esta funcionalidad es posible si se examina la información autenticada que se agrega con IAP a cada solicitud web que se pasa a tu aplicación. Hay tres encabezados de solicitud nuevos agregados a cada solicitud web que llega a tu aplicación. Los dos primeros encabezados son strings de texto sin formato que puedes usar para identificar al usuario. El tercer encabezado es un objeto firmado de manera criptográfica con esa misma información.

  • X-Goog-Authenticated-User-Email: Es la dirección de correo electrónico de un usuario que lo identifica. No almacenes información personal si tu aplicación puede evitarla. Esta aplicación no almacena ningún dato; solo lo repite al usuario.

  • X-Goog-Authenticated-User-Id: Con este ID de usuario que se asigna en Google no se muestra información sobre el usuario, pero se habilita a que en una aplicación se sepa que un usuario que accedió es el mismo que antes.

  • X-Goog-Iap-Jwt-Assertion: Puedes configurar las aplicaciones de Google Cloud para aceptar solicitudes web de otras aplicaciones en la nube, mediante la omisión de IAP, además de las solicitudes web de Internet. Si una aplicación está configurada de esta manera, es posible que estas solicitudes tengan encabezados falsificados. En lugar de usar cualquiera de los encabezados de texto sin formato antes mencionados, puedes usar y verificar este encabezado firmado de manera criptográfica para verificar que se haya proporcionado la información mediante Google. Tanto la dirección de correo electrónico del usuario como un ID de usuario persistente están disponibles como parte de este encabezado firmado.

Si estás seguro de que la aplicación está configurada para que solo las solicitudes web de Internet puedan acceder a ella y que nadie pueda inhabilitar el servicio IAP para la aplicación, la recuperación de un ID de usuario único solo requiere la línea de código siguiente:

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

Sin embargo, en una aplicación resistente se debería esperar que algo salga mal, incluida la configuración inesperada o problemas ambientales, por lo que recomendamos crear una función con la que se use y verifique el encabezado firmado de manera criptográfica. La firma de ese encabezado no se puede falsificar y, cuando se verifica, se puede utilizar para mostrar la identificación.

Comprende el código

En esta sección, se explica cómo funciona el código. Si quieres ejecutar la app, puedes ir directamente a la sección Implementa la app.

  • El archivo go.mod define un módulo de Go y los módulos de los que 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
    
  • Con el archivo app.yaml se le indica a App Engine qué entorno de idioma se requiere para tu código.

    runtime: go112
  • La app comienza con la importación de paquetes y la definición de una función main. La función main registra un controlador de índices y, luego, inicia un 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
    }
    
  • La función index obtiene el valor del encabezado de aserción de JWT que IAP agregó de la solicitud entrante y llama a la función validateAssertion para validar el valor firmado de manera criptográfica. Luego la dirección de correo electrónico se usa en una respuesta 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)
    }
    
  • La función validateAssertion valida que la aserción se haya firmado de forma correcta y muestra la dirección de correo electrónico asociada y el ID de usuario.

    La validación de una aserción de JWT requiere conocer los certificados de clave pública de la entidad con la que se firmó la aserción (Google en este caso) y el público al que está destinado la aserción. En una aplicación de App Engine, el público es una string con información de identificación de proyecto de Google Cloud. La función validateAssertion obtiene los certificados de la función certs, y la string de público de la función 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
    }
    
  • Puedes buscar el ID numérico y el nombre del proyecto de Google Cloud y ponerlos en el código fuente de forma manual, pero la función audience lo hace por ti mediante consultas al servicio de metadatos estándar disponible para cada app de App Engine. Debido a que el servicio de metadatos es externo al código de la app, ese resultado se guarda en una variable global que se muestra sin tener que buscar metadatos en las llamadas posteriores.

    El servicio de metadatos de App Engine (y los servicios de metadatos similares de otros servicios de computación de Google Cloud) son similares a un sitio web y admiten consultas web estándar. Sin embargo, el servicio de metadatos no es un sitio externo, sino una función interna que muestra la información solicitada sobre la app en ejecución, por lo que es seguro usar http en lugar de solicitudes https. El servicio de metadatos se usa con el fin de obtener los identificadores actuales de Google Cloud necesarios para definir el público previsto de la aserción de 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
    }
    
  • La verificación de una firma digital requiere el certificado de clave pública del firmante. Google cuenta con un sitio web en el que se muestran todos los certificados de clave pública utilizados actualmente. Estos resultados se almacenan en caché en caso de que se necesiten nuevamente en la misma instancia de la 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
    }
    

Implementa la app

Ahora puedes implementar la aplicación y, luego, habilitar IAP para requerir que los usuarios se autentiquen antes de que puedan acceder a la aplicación.

  1. En la ventana de tu terminal, ve al directorio que contiene el archivo app.yaml y, luego, implementa la aplicación en App Engine con el comando siguiente:

    gcloud app deploy
    
  2. Cuando se te solicite, selecciona una región cercana.

  3. Cuando se te pregunte si deseas continuar con la operación de implementación, ingresa Y.

    En pocos minutos, tu aplicación estará en vivo en Internet.

  4. Ve la aplicación con el comando siguiente:

    gcloud app browse
    

    En el resultado, copia web-site-url (la dirección web de la aplicación).

  5. En una ventana del navegador, pega web-site-url para abrir la aplicación.

    No se muestra ningún correo electrónico porque todavía no usas IAP y, por lo tanto, no se envía información del usuario a la aplicación.

Habilitar IAP

Ahora que existe una instancia de App Engine, puedes protegerla con IAP de la manera siguiente:

  1. En la consola de Google Cloud, ve a la página Identity-Aware Proxy.

    Ir a la página Identity-Aware Proxy

  2. Dado a que es la primera vez que habilitas una opción de autenticación para este proyecto, aparece un mensaje en el que se indica que debes configurar tu pantalla de consentimiento de OAuth para poder usar IAP.

    Haz clic en Configurar pantalla de consentimiento.

  3. En la pestaña Pantalla de consentimiento de OAuth de la página Credenciales, completa los campos siguientes:

    • Si tu cuenta es una organización de Google Workspace, selecciona Externa y haz clic en Crear. Para comenzar, la app solo estará disponible para los usuarios que permitas de forma explícita.

    • En el campo Nombre de la aplicación, ingresa IAP Example.

    • En el campo Correo de asistencia, ingresa tu dirección de correo electrónico.

    • En el campo Dominio autorizado, ingresa la parte del nombre de host de la URL de la aplicación, por ejemplo, iap-example-999999.uc.r.appspot.com. Presiona la tecla Enter después de ingresar el nombre de host en el campo.

    • En el campo Vínculo a la página principal de la aplicación, ingresa la URL de tu aplicación, como https://iap-example-999999.uc.r.appspot.com/.

    • En el campo Línea de Política de Privacidad de la aplicación, usa la misma URL del vínculo de la página principal para fines de prueba.

  4. Haz clic en Guardar. Cuando se solicite que crees credenciales, puedes cerrar la ventana.

  5. En la consola de Google Cloud, ve a la página Identity-Aware Proxy.

    Ir a la página Identity-Aware Proxy

  6. Para actualizar la página, haz clic en Actualizar . En la página, se muestra una lista de recursos que puedes proteger.

  7. En la columna IAP, haz clic a fin de activar IAP para la aplicación.

  8. En tu navegador, dirígete a web-site-url nuevamente.

  9. En lugar de la página web, hay una pantalla de acceso para autenticarse. Cuando accedes, se te niega el acceso porque IAP no tiene una lista de usuarios para permitir el acceso a la aplicación.

Agrega usuarios autorizados a la app

  1. En la consola de Google Cloud, ve a la página Identity-Aware Proxy.

    Ir a la página Identity-Aware Proxy

  2. Selecciona la casilla de verificación para la app de App Engine y, luego, haz clic en Agregar principal.

  3. Ingresa allAuthenticatedUsers y, luego, selecciona la función Usuario de aplicación web protegida con IAP/Cloud IAP.

  4. Haz clic en Guardar.

Ahora los usuarios que Google pueda autenticar tienen acceso a la app. Si lo deseas, puedes agregar solo algunas personas o grupos como principales para restringir aún más el acceso:

  • Cualquier dirección de correo electrónico de Gmail o Google Workspace

  • Una dirección de correo electrónico de Grupos de Google

  • Un nombre de dominio de Google Workspace

Accede a la app

  1. En el navegador, ve a web-site-url.

  2. Para actualizar la página, haz clic en Actualizar .

  3. En la pantalla de acceso, inicia sesión con tus credenciales de Google.

    En la página, se muestra una página "Hola, user-email-address" con tu dirección de correo electrónico.

    Si aún ves la misma página que antes, es posible que en el navegador no se hayan actualizado completamente las solicitudes nuevas después de la habilitación de IAP. Cierra todas las ventanas del navegador, vuelva a abrirlas y, luego, inténtalo nuevamente.

Conceptos de autenticación

Existen varias maneras con las que en una aplicación se puede autenticar a sus usuarios y restringir el acceso solo a usuarios autorizados. Los métodos de autenticación comunes, con los que se disminuye el nivel de esfuerzo para la aplicación, se enumeran en las secciones siguientes.

Opción Ventajas Desventajas
Autenticación de la aplicación
  • La app puede ejecutarse en cualquier plataforma, con o sin conexión a Internet.
  • Los usuarios no necesitan usar ningún otro servicio para administrar la autenticación.
  • La app debe administrar las credenciales de los usuarios de forma segura y evitar la divulgación.
  • La app debe mantener los datos de sesión para los usuarios registrados
  • La aplicación debe proporcionar funciones de registro de usuario y de cambio y recuperación de contraseña
OAuth2
  • La app puede ejecutarse en cualquier plataforma conectada a Internet, incluidas las estaciones de trabajo para desarrolladores.
  • La app no necesita funciones de registro de usuario ni de cambio o recuperación de contraseña.
  • El riesgo de divulgación de información del usuario se delega en otro servicio
  • Las nuevas medidas de seguridad en el acceso se gestionan fuera de la aplicación.
  • Los usuarios se deben registrar en el servicio de identidad
  • La app debe mantener los datos de sesión para los usuarios registrados
IAP
  • La app no necesita ningún código para administrar usuarios, la autenticación o el estado de la sesión
  • La app no tiene credenciales de usuario que puedan sufrir violaciones de seguridad
  • La aplicación solo se puede ejecutar en plataformas compatibles con el servicio. Específicamente, se trata de ciertos servicios de Google Cloud compatibles con IAP, como App Engine.

Autenticación administrada con la aplicación

Con este método, en la aplicación se administra cada uno de los aspectos de la autenticación de usuarios. En la aplicación, se mantiene la base de datos de las credenciales de usuarios y se administran las sesiones de usuarios. La aplicación se necesita para proporcionar funciones que permitan administrar cuentas de usuarios y contraseñas, verificar credenciales de usuarios y, además, iniciar, verificar y actualizar sesiones de usuarios con cada acceso autenticado. En el siguiente diagrama, se ilustra el método de autenticación administrado con la aplicación.

Flujo administrado de la aplicación

Como se muestra en el diagrama, una vez que el usuario accede, la app crea y guarda la información sobre la sesión. Cuando el usuario realiza una solicitud a la app, tiene que incluir la información de la sesión que la app debe verificar.

La principal ventaja de este enfoque es que es autónomo y está bajo el control de la aplicación. La aplicación ni siquiera necesita estar disponible en Internet. La principal desventaja es que con la aplicación ahora se deben proporcionar todas las funciones de administración de cuentas y proteger todos los datos confidenciales de las credenciales.

Autenticación externa con OAuth2

Una buena alternativa para manejar todo dentro de la aplicación es usar un servicio de identidad externo, como Google, con el que se maneja toda la información y funcionalidad de la cuenta del usuario y se protegen las credenciales sensibles. Cuando un usuario intenta acceder en la aplicación, la solicitud se redirecciona al servicio de identidad, en el que se autentica al usuario y, luego, se redirecciona la solicitud a la aplicación con la información de autenticación necesaria proporcionada. Si deseas obtener más información, consulta Usa OAuth 2.0 para aplicaciones de servidor web.

En el siguiente diagrama, se ilustra la autenticación externa con el método OAuth2.

Flujo de OAuth2

El flujo en el diagrama comienza cuando el usuario envía una solicitud para acceder a la aplicación. En lugar de responder directamente, en la aplicación se redirecciona el navegador del usuario a la plataforma de identidad de Google, en la que se muestra una página para acceder a Google. Después de acceder con éxito, con el navegador del usuario se vuelve a la aplicación. Esta solicitud incluye datos que con la aplicación se puede usar para buscar información sobre el usuario ahora autenticado y responder al usuario.

Este método tiene muchas ventajas para la aplicación. Permite delegar todas las funciones y riesgos de la administración de cuentas al servicio externo, lo que puede mejorar el acceso y la seguridad de la cuenta sin que se tenga que cambiar la aplicación. Sin embargo, como se muestra en el diagrama anterior, con la aplicación se debe tener acceso a Internet para usar este método. Con la aplicación, también se deben administrar las sesiones después de que el usuario se autentica.

Identity-Aware Proxy

El tercer enfoque que se analiza en este instructivo consiste en usar IAP para manejar toda la autenticación y la administración de sesiones con cualquier cambio en la aplicación. Con IAP, se interceptan todas las solicitudes web de tu aplicación, se bloquean las que no se autenticaron y las otras se pasan con datos de identidad del usuario agregados a cada solicitud.

El manejo de la solicitud se muestra en el diagrama siguiente.

Flujo de IAP

Las solicitudes de los usuarios se interceptan con IAP y las solicitudes no autenticadas se bloquean. Las solicitudes autenticadas se pasan a la aplicación, siempre que el usuario autenticado esté en la lista de usuarios permitidos. Las solicitudes que se pasan por IAP tienen encabezados agregados en los que se identifica al usuario que realizó la solicitud.

En la aplicación, ya no es necesario manejar ninguna cuenta de usuario ni información de sesión. Si en la operación se necesita conocer un identificador único para el usuario, este se puede obtener directamente de cada solicitud web entrante. Sin embargo, solo se puede usar para los servicios de computación que admiten IAP, como App Engine y los balanceadores de cargas. No puedes usar IAP en una máquina de desarrollo local.

Limpia

Para evitar que se apliquen cargos a tu cuenta de Google Cloud por los recursos usados en este instructivo, borra el proyecto que contiene los recursos o conserva el proyecto y borra los recursos individuales.

  1. En la consola de Google Cloud, ve a la página Administrar recursos.

    Ir a Administrar recursos

  2. En la lista de proyectos, elige el proyecto que quieres borrar y haz clic en Borrar.
  3. En el diálogo, escribe el ID del proyecto y, luego, haz clic en Cerrar para borrar el proyecto.