Authentifier les utilisateurs avec Go


Les applications exécutées sur des plates-formes gérées par Google Cloud, telles que App Engine peuvent éviter de gérer l'authentification des utilisateurs et la gestion des sessions avec Identity-Aware Proxy (IAP) pour en contrôler l'accès. IAP permet non seulement de contrôler l'accès à l'application, mais fournit également des informations sur les utilisateurs authentifiés, comme l'adresse e-mail et un identifiant persistant sous forme de nouveaux en-têtes HTTP.

Objectifs

  • Obliger les utilisateurs de votre application App Engine à s'authentifier à l'aide de IAP

  • Accéder aux identités des utilisateurs dans l'application pour afficher l'adresse e-mail authentifiée de l'utilisateur actuel

Coûts

Dans ce document, vous utilisez les composants facturables suivants de Google Cloud :

Obtenez une estimation des coûts en fonction de votre utilisation prévue à l'aide du simulateur de coût. Les nouveaux utilisateurs de Google Cloud peuvent bénéficier d'un essai gratuit.

Une fois que vous avez terminé les tâches décrites dans ce document, vous pouvez éviter de continuer à payer des frais en supprimant les ressources que vous avez créées. Pour en savoir plus, consultez la section Effectuer un nettoyage.

Avant de commencer

  1. Connectez-vous à votre compte Google Cloud. Si vous débutez sur Google Cloud, créez un compte pour évaluer les performances de nos produits en conditions réelles. Les nouveaux clients bénéficient également de 300 $ de crédits gratuits pour exécuter, tester et déployer des charges de travail.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  3. Install the Google Cloud CLI.
  4. To initialize the gcloud CLI, run the following command:

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

    Go to project selector

  6. Install the Google Cloud CLI.
  7. To initialize the gcloud CLI, run the following command:

    gcloud init
  8. Préparer votre environnement de développement

Mettre en place le projet

  1. Dans votre fenêtre de terminal, clonez le dépôt de l'exemple d'application sur votre machine locale :

    git clone https://github.com/GoogleCloudPlatform/golang-samples.git
  2. Accédez au répertoire qui contient l'exemple de code :

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

Contexte

Dans ce tutoriel, les utilisateurs sont authentifiés à l'aide d'IAP. Il ne s'agit là que d'une approche parmi d'autres. Pour en savoir plus sur les différentes méthodes d'authentification des utilisateurs, consultez la section Concepts d'authentification.

Application Hello user-email-address

L'application utilisée dans le cadre de ce tutoriel est une application Hello World App Engine minimale, comprenant une fonctionnalité inhabituelle : elle affiche non pas "Hello World", mais "Hello user-email-address", où user-email-address est l'adresse e-mail de l'utilisateur authentifié.

L'application examine pour cela les informations authentifiées qu'IAP ajoute à chaque requête Web transmise. Trois nouveaux en-têtes de requête sont ajoutés à chaque requête Web atteignant l'application. Les deux premiers en-têtes sont des chaînes de texte brut qui permettent d'identifier l'utilisateur. Le troisième en-tête est un objet avec signature cryptographique qui contient les mêmes informations.

  • X-Goog-Authenticated-User-Email : l'adresse e-mail permettant d'identifier un utilisateur. Évitez de stocker des informations personnelles. Cette application ne stocke aucune donnée : elle ne fait que renvoyer les données à l'utilisateur.

  • X-Goog-Authenticated-User-Id : cet ID utilisateur attribué par Google ne fournit aucune information sur l'utilisateur, mais permet à une application de savoir si un utilisateur connecté est déjà apparu auparavant.

  • X-Goog-Iap-Jwt-Assertion : vous pouvez configurer des applications Google Cloud afin qu'elles acceptent des requêtes Web provenant d'autres applications cloud en plus des requêtes Web Internet, contournant ainsi IAP. Dans ce cas, il est possible que les en-têtes de ces requêtes soient falsifiés. Plutôt que d'utiliser l'un des en-têtes en texte brut précédemment mentionnés, vous pouvez employer et valider cet en-tête avec signature cryptographique afin de confirmer que les informations ont bien été fournies par Google. L'adresse e-mail de l'utilisateur et un ID utilisateur persistant sont disponibles dans cet en-tête signé.

Si vous êtes certain que l'application est configurée pour ne recevoir que des requêtes Web Internet et si personne ne peut désactiver le service IAP pour l'application, vous récupérez l'ID utilisateur unique à l'aide d'une simple ligne de code :

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

Toutefois, une application résiliente doit pouvoir faire face à diverses complications, telles que des problèmes de configuration ou d'environnement inattendus. Nous vous recommandons donc de créer une fonction qui utilise et valide l'en-tête avec signature cryptographique. La signature de l'en-tête ne peut pas être falsifiée et, une fois validé, celui-ci peut être utilisé pour renvoyer l'identification.

Comprendre le code

Cette section explique le fonctionnement du code. Si vous souhaitez exécuter l'application, vous pouvez passer directement à la section Déployer l'application.

  • Le fichier go.mod définit une Module Go et les modules dont il dépend.

    module github.com/GoogleCloudPlatform/golang-samples/getting-started/authenticating-users
    
    go 1.21
    
    require (
    	cloud.google.com/go/compute/metadata v0.5.0
    	github.com/golang-jwt/jwt v3.2.2+incompatible
    )
    
    require golang.org/x/sys v0.22.0 // indirect
    
  • Le fichier app.yaml indique à App Engine l'environnement de langage requis par votre code.

    runtime: go112
  • L'application commence par importer des packages et définir une fonction main. La fonction main enregistre un gestionnaire d'index et démarre un serveur 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 fonction index obtient la valeur de l'en-tête d'assertion JWT qu'IAP ajoute à la requête entrante, puis appelle la fonction validateAssertion pour valider cette valeur avec signature cryptographique. L'adresse e-mail est ensuite utilisée dans une réponse Web minimale.

    
    // 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 fonction validateAssertion valide que l'assertion a été correctement signée et renvoie l'adresse e-mail et l'ID utilisateur associés.

    Une assertion JWT ne peut être validée que si les certificats de clés publiques de l'entité qui a signé l'assertion (dans le cas présent, Google) et l'audience à laquelle l'assertion est destinée sont connus. Pour une application App Engine, l'audience est une chaîne contenant des informations d'identification de projet Google Cloud. La fonction validateAssertion récupère ces certificats grâce à la fonction certs et la chaîne d'audience grâce à la fonction 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
    }
    
  • Vous pouvez rechercher l'identifiant numérique et le nom du projet Google Cloud et les insérer vous-même dans le code source, mais la fonction audience le fait pour vous en interrogeant le service de métadonnées standard disponible pour chaque application App Engine. Étant donné que le service de métadonnées est externe au code de l'application, ce résultat est enregistré dans une variable globale renvoyée sans avoir à examiner les métadonnées lors des appels suivants.

    Le service de métadonnées App Engine (et les services de métadonnées semblables des autres services Google Cloud) ressemble à un site Web et est interrogé par des requêtes Web standards. Il ne s'agit toutefois pas d'un site externe, mais d'une fonctionnalité interne qui renvoie les informations demandées sur l'application en cours d'exécution. Vous pouvez donc utiliser des requêtes http en toute sécurité plutôt que des requêtes https. Le service de métadonnées permet d'obtenir les identifiants Google Cloud nécessaires à la définition de l'audience visée par l'assertion 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 validation d'une signature numérique nécessite le certificat de clé publique du signataire. Google fournit un site Web qui renvoie l'ensemble des certificats de clés publiques actuellement utilisés. Ces résultats sont mis en cache au cas où vous en auriez encore besoin dans la même instance d'application.

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

Déployer l'application

Vous pouvez désormais déployer l'application, puis activer IAP afin d'obliger les utilisateurs à s'authentifier pour y accéder.

  1. Dans votre fenêtre de terminal, accédez au répertoire contenant le fichier app.yaml, puis déployez l'application dans App Engine :

    gcloud app deploy
    
  2. Lorsque vous y êtes invité, sélectionnez une région proche de votre emplacement géographique.

  3. Lorsqu'un message vous demandant si vous souhaitez poursuivre l'opération de déploiement s'affiche, saisissez Y.

    Votre application devient disponible sur Internet au bout de quelques minutes.

  4. Affichez l'application :

    gcloud app browse
    

    Dans le résultat, copiez web-site-url, l'adresse Web de l'application.

  5. Dans une fenêtre de navigateur, collez web-site-url pour ouvrir l'application.

    Aucune adresse e-mail n'est affichée, car vous n'utilisez pas encore IAP. Par conséquent, aucune information utilisateur n'est envoyée à l'application.

Activer IAP

Maintenant que vous disposez d'une instance App Engine, vous pouvez la protéger à l'aide d'IAP :

  1. Dans Google Cloud Console, accédez à la page Identity-Aware Proxy.

    Accéder à la page "Identity-Aware Proxy"

  2. Comme c'est la première fois que vous activez une option d'authentification pour ce projet, un message vous indique que vous devez configurer votre écran d'autorisation OAuth pour pouvoir utiliser IAP.

    Cliquez sur Configurer l'écran d'autorisation.

  3. Dans l'onglet Écran d'autorisation OAuth de la page Identifiants, renseignez les champs suivants :

    • Si votre compte se trouve dans une organisation Google Workspace, sélectionnez Externe, puis cliquez sur Créer. Pour commencer, l'application ne sera disponible que pour les utilisateurs que vous autorisez explicitement.

    • Dans le champ Nom de l'application, saisissez IAP Example.

    • Dans le champ Adresse e-mail d'assistance, saisissez votre adresse e-mail.

    • Dans le champ Domaine autorisé, saisissez la partie nom d'hôte de l'URL de l'application. Par exemple, iap-example-999999.uc.r.appspot.com. Après avoir saisi le nom d'hôte, appuyez sur la touche Enter.

    • Dans le champ Lien vers la page d'accueil de l'application , saisissez l'URL de votre application. Par exemple, https://iap-example-999999.uc.r.appspot.com/.

    • Dans le champ Lien vers les règles de confidentialité de l'application, utilisez la même URL que le lien vers la page d'accueil à des fins de test.

  4. Cliquez sur Enregistrer. Lorsque vous êtes invité à créer des identifiants, vous pouvez fermer la fenêtre.

  5. Dans Google Cloud Console, accédez à la page Identity-Aware Proxy.

    Accéder à la page "Identity-Aware Proxy"

  6. Pour actualiser la page, cliquez sur Actualiser . La page affiche la liste de ressources que vous pouvez protéger.

  7. Dans la colonne IAP, cliquez pour activer IAP pour l'application.

  8. Dans votre navigateur, accédez de nouveau à web-site-url.

  9. Au lieu de la page Web, un écran de connexion s'affiche pour vous authentifier. Lorsque vous vous connectez, l'accès est refusé, car IAP ne dispose d'aucune liste d'utilisateurs autorisés à accéder à l'application.

Ajouter des utilisateurs autorisés à l'application

  1. Dans la console Google Cloud, accédez à la page Identity-Aware Proxy.

    Accéder à la page "Identity-Aware Proxy"

  2. Cochez la case correspondant à l'application App Engine, puis cliquez sur Ajouter un compte principal.

  3. Saisissez allAuthenticatedUsers, puis sélectionnez le rôle Utilisateur de l'application Web sécurisée par Cloud IAP/IAP.

  4. Cliquez sur Enregistrer.

Les utilisateurs que Google peut authentifier ont désormais accès à l'application. Si vous le souhaitez, vous pouvez restreindre davantage l'accès en n'ajoutant qu'un ou que quelques autres utilisateurs ou groupes en tant que comptes principaux :

  • Toute adresse e-mail Gmail ou Google Workspace

  • Une adresse e-mail de groupe Google

  • Un nom de domaine Google Workspace

Accéder à l'application

  1. Dans votre navigateur, accédez à web-site-url.

  2. Pour actualiser la page, cliquez sur Actualiser.

  3. Sur l'écran de connexion, connectez-vous avec vos identifiants Google.

    La page "Hello user-email-address" s'affiche et indique votre adresse e-mail.

    Si vous voyez la même page que précédemment, il est possible que le navigateur ne mette pas complètement à jour les nouvelles requêtes maintenant que vous avez activé IAP. Fermez toutes les fenêtres du navigateur, rouvrez-les, puis réessayez.

Concepts d'authentification

Une application peut authentifier ses utilisateurs et limiter l'accès aux utilisateurs autorisés de plusieurs façons. Les sections suivantes répertorient les méthodes courantes d'authentification, de la plus complexe à la plus simple du point de vue de l'application.

Option Avantages Inconvénients
Authentification par l'application
  • L'application peut s'exécuter sur n'importe quelle plate-forme, avec ou sans connexion Internet
  • Les utilisateurs n'ont pas besoin de gérer l'authentification à l'aide d'un autre service
  • L'application doit gérer les identifiants utilisateur de manière sécurisée et protéger les données contre toute divulgation
  • L'application doit gérer les données de session des utilisateurs connectés
  • L'application doit fournir des fonctionnalités d'inscription d'utilisateurs et de modification et récupération de mot de passe
OAuth2
  • L'application peut s'exécuter sur n'importe quelle plate-forme connectée à Internet, y compris sur un poste de travail de développement
  • L'application ne nécessite aucune fonctionnalité d'inscription d'utilisateurs ni de modification ou récupération de mot de passe
  • La gestion des risques de divulgation des informations utilisateur est déléguée à un autre service
  • De nouvelles mesures de sécurité liées à la connexion sont gérées en dehors de l'application
  • Les utilisateurs doivent s'inscrire auprès du service de gestion des identités
  • L'application doit gérer les données de session des utilisateurs connectés
IAP
  • L'application n'a pas besoin de code pour gérer les utilisateurs, l'authentification ni l'état des sessions
  • L'application ne contient pas d'identifiants utilisateur pouvant être compromis
  • L'application ne peut s'exécuter que sur les plates-formes compatibles avec le service. Plus précisément, certains services Google Cloud compatibles avec IAP, tels qu'App Engine.

Authentification gérée par l'application

Avec cette méthode, l'application gère tous les aspects de l'authentification des utilisateurs. L'application doit tenir à jour la base de données de ses propres comptes utilisateur et gérer les sessions utilisateur. Elle doit également fournir des fonctionnalités permettant de gérer les comptes utilisateur et mots de passe, de vérifier les identifiants utilisateur, ainsi que d'émettre, de vérifier et de mettre à jour des sessions utilisateur à chaque connexion authentifiée. Le schéma suivant illustre la méthode d'authentification gérée par l'application.

Schéma de principe de l'authentification gérée par l'application

Comme le montre le diagramme, une fois que l'utilisateur s'est connecté, l'application crée et conserve des informations sur la session de l'utilisateur. Lorsque l'utilisateur envoie une requête à l'application, celle-ci doit inclure les informations de session que l'application est chargée de vérifier.

Le principal avantage de cette approche est qu'elle est autonome et placée sous le contrôle de l'application. L'application n'a même pas besoin d'être disponible sur Internet. Le principal inconvénient est que l'application doit fournir toutes les fonctionnalités de gestion des comptes et protéger toutes les données d'identification sensibles.

Authentification externe OAuth2

Plutôt que de tout gérer au sein de l'application, une bonne alternative consiste à utiliser un service d'identité externe, comme Google, qui gère toutes les fonctionnalités et informations liées aux comptes utilisateur, et protège les identifiants sensibles. Lorsqu'un utilisateur tente de se connecter à l'application, la requête est redirigée vers le service de gestion des identités, qui authentifie l'utilisateur, puis renvoie la requête à l'application avec les informations d'authentification nécessaires. Pour en savoir plus, consultez Utiliser OAuth 2.0 pour les applications de serveur Web

Le schéma suivant illustre l'authentification externe avec la méthode OAuth2.

Schéma de principe de l'authentification OAuth 2.0

Dans ce schéma, le processus commence lorsque l'utilisateur envoie une requête d'accès à l'application. Au lieu de répondre directement, l'application redirige l'utilisateur vers la plate-forme d'identité de Google, qui affiche une page permettant de se connecter à Google. Une fois connecté, l'utilisateur est redirigé vers l'application. Cette requête inclut des informations permettant à l'application d'obtenir des données sur l'utilisateur désormais authentifié. L'application répond ensuite à l'utilisateur.

Cette méthode présente de nombreux avantages pour l'application. Elle permet de déléguer l'ensemble des fonctionnalités et des risques liés à la gestion des comptes au service externe, renforçant ainsi la sécurité des comptes et des connexions sans exiger de modifications au niveau de l'application. Toutefois, comme décrit dans le schéma précédent, l'application doit avoir accès à Internet pour employer cette méthode. Elle est également responsable de la gestion des sessions une fois l'utilisateur authentifié.

Cloud IAP

La troisième approche, qui est l'objet de ce tutoriel, consiste à gérer l'authentification et la gestion des sessions à l'aide d'IAP, sans avoir à modifier l'application. IAP intercepte toutes les requêtes Web envoyées à l'application, bloque celles qui n'ont pas été authentifiées et laisse passer les autres en y incluant les données d'identité de l'utilisateur.

Le traitement des requêtes est décrit dans le schéma suivant :

Schéma de principe de l'authentification Cloud IAP

IAP intercepte les requêtes envoyées par les utilisateurs et bloque les requêtes non authentifiées. Les requêtes authentifiées sont transmises à l'application, à condition que l'utilisateur authentifié figure dans la liste des utilisateurs autorisés. Les requêtes transmises par IAP incluent des en-têtes qui identifient l'utilisateur à l'origine de la requête.

L'application n'a plus besoin de gérer les données de session ni les informations des comptes utilisateur. Les opérations nécessitant un identifiant unique pour l'utilisateur peuvent l'obtenir directement à partir de chaque requête Web entrante. Toutefois, cette fonctionnalité ne peut être utilisée que pour les services compatibles avec IAP, tels qu'App Engine et les équilibreurs de charge. Vous ne pouvez pas employer IAP sur un ordinateur de développement local.

Effectuer un nettoyage

Pour éviter que les ressources utilisées lors de ce tutoriel soient facturées sur votre compte Google Cloud, supprimez le projet contenant les ressources, ou conservez le projet et les ressources individuelles.

  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.