Extension de services Authentication (existante)

Extension de services Authentication (existante)

ℹ️
Cette rubrique fait référence à la syntaxe de configuration existante. Les passerelles d’applications sont désormais définies comme des applications Proxy. Un nouvel exemple de l’extension de services d’authentification est présenté ici

Les extensions de services permettent une flexibilité totale dans le processus d’établissement de l’identité d’un utilisateur et de sa gestion au cours d’une session.

Comment les passerelles d’applications gèrent le trafic

Un peu de contexte est nécessaire à la compréhension du fonctionnement de l’authentification et de sa place dans le flux d’identité d’un utilisateur. Voici comment le trafic circule dans une AppGateway (en pseudocode).

func Serve(appGateway *app.AppGateway, rw http.ResponseWriter, req *http.Request) {
  if !IsAuthenticated(){
    Authenticate(resp, req)
  }
  LoadAttrs()
  if IsAuthorized() {
    SetHeaders()
    Proxy()
  }
}

Il s’agit d’une simplification. La mise en œuvre effective met en cache les informations relatives à la session par souci d’efficacité, y compris les résultats de la vérification de l’authentification et de l’autorisation. Ainsi, ces méthodes ne sont généralement pas sollicitées pour chaque requête, mais uniquement lors du premier passage de l’utilisateur.

Authentification

Comme le montre le pseudocode ci-dessus, l’authentification se compose en réalité de deux éléments. L’extension de services isAuthenticatedSE en langage yaml permet de vérifier si une session est actuellement authentifiée de manière appropriée pour une ressource requise. Si ce n’est pas le cas, l’extension de services authenticateSE en langage yaml prend en charge l’interaction réelle avec l’utilisateur pour authentifier la session. Veuillez noter que les deux extensions dépendent de la clé d’authentification.

ℹ️

AuthN ou AuthZ

L’authentification (AuthN) sert simplement à confirmer l’identité de l’utilisateur, et non à déterminer s’il dispose d’un accès. Un utilisateur peut être authentifié, mais se voir refuser l’accès au stade de l’autorisation (AuthZ).

Extension de services IsAuthenticated

Cette extension de services est relativement peu complexe : elle se contente généralement de vérifier l’authentification de la session d’un utilisateur.

Bien que cette extension de services utilise http.ResponseWriter, elle n’envoie généralement pas de réponse directement à l’utilisateur. D’autres extensions, telles que authenticateSE, gèrent l’interaction avec l’utilisateur.

Extension de services Authenticate

L’extension de services AuthenticateSE permet de déterminer l’identité de l’utilisateur. Elle peut accéder aux champs et aux méthodes de la structure AppGateway qui sont destinés à faciliter l’authentification. Par exemple, la liste des fournisseurs d’identité disponibles peut être utilisée pour transférer le flux d’authentification à un fournisseur d’authentification externe spécifique.

Interaction avec l’utilisateur

L’extension de service authenticateSE permet d’interagir avec l’utilisateur afin d’établir son identité. Par exemple, l’utilisateur peut être redirigé vers un fournisseur d’authentification externe, ou un formulaire lui demandant de saisir son nom d’utilisateur et son mot de passe peut lui être soumis. Cela signifie que l’extension de services peut parfois être amenée à traiter différentes requêtes, en présentant d’abord la page de connexion puis en traitant le nom d’utilisateur et le mot de passe postés, ou en redirigeant vers Azure puis en traitant un jeton OIDC lorsque l’utilisateur est renvoyé vers la page de connexion.

Exemples

Authentification contre le fournisseur d’identité

Dans cet exemple très simple, l’extension de services demande simplement à un fournisseur d’identité défini dans la configuration de gérer la connexion de l’utilisateur.

Dans ce cas, le connecteur Azure IDP se chargera de l’authentification nécessaire à la session de l’utilisateur. Une fois l’identité de l’utilisateur établie, celui-ci est redirigé vers l’URL initialement demandée. L’extension de services* isAuthenticatedSE ci-dessus renvoie alors une valeur « true », indiquant que l’utilisateur a été authentifié, et le flux de l’utilisateur à travers l’AppGateway se poursuit.

appgateways:
  - name: alpha
    # ...

    policies:
      - location: /
        authentication:
          isAuthenticatedSE:
            funcName: IsAuthenticated
            file: /etc/maverics/extensions/auth.go
          authenticateSE:
            funcName: Authenticate
            file: /etc/maverics/extensions/auth.go
        authorization:
          allowAll: true

/etc/maverics/extensions/auth.go

package main

import (
	"errors"
	"net/http"

	"maverics/app"
	"maverics/log"
	"maverics/session"
)

// IsAuthenticated determines if the user has been authenticated by Azure.
func IsAuthenticated(ag *app.AppGateway, rw http.ResponseWriter, req *http.Request) bool {
	log.Debug("msg", "determining if user is authenticated")

	if session.GetString(req, "azure.authenticated") == "true" {
		return true
	}

	return false
}

// Authenticate authenticates the user against Azure.
func Authenticate(ag *app.AppGateway, rw http.ResponseWriter, req *http.Request) error {
	log.Debug("msg", "authenticating user")

	azure, ok := ag.IDPs["azure"]
	if !ok {
		return errors.New("failed to find Azure IDP")
	}

	azure.CreateRequest().Login(rw, req)
	return nil
}

Interaction avec l’utilisateur

Dans cet exemple, l’utilisateur se voit présenter un formulaire lui permettant de se connecter s’il n’est pas déjà authentifié. Les informations d’identification de l’utilisateur sont collectées puis validées par rapport à un cache d’informations d’identification local.

appgateways:
  - name: alpha
    # ...

    policies:
      - location: /
        authentication:
          isAuthenticatedSE:
            funcName: IsAuthenticated
            file: /etc/maverics/extensions/auth.go
          authenticateSE:
            funcName: Authenticate
            file: /etc/maverics/extensions/auth.go
        authorization:
          allowAll: true
package main

import (
	"errors"
	"fmt"
	"net/http"

	"maverics/app"
	"maverics/log"
	"maverics/session"
)

// IsAuthenticated determines if the user has been authenticated by Azure.
func IsAuthenticated(ag *app.AppGateway, rw http.ResponseWriter, req *http.Request) bool {
	log.Debug("msg", "determining if user is authenticated")

	if session.GetString(req, "se.authenticated") == "true" {
		return true
	}

	return false
}

// Authenticate authenticates the user by challenging the user for credentials.
func Authenticate(ag *app.AppGateway, rw http.ResponseWriter, req *http.Request) error {
	log.Debug("msg", "authenticating user")

	if req.Method != http.MethodPost && req.Method != http.MethodGet {
		return errors.New("received unexpected request")
	}

	if req.Method == http.MethodPost {
		err := req.ParseForm()
		if err != nil {
			return fmt.Errorf("failed to parse form: %w", err)
		}

		username := req.Form.Get("username")
		password := req.Form.Get("password")
		if username == "username" && password == "password" {
			log.Debug("msg", "successfully logged in user")
			session.Set(req, "se.authenticated", "true")
			http.Redirect(rw, req, "/", http.StatusFound)
			return nil
		}

		log.Debug(
			"msg", "user entered invalid credentials",
			"username", username,
		)
	}

	_, _ = fmt.Fprintf(rw, htmlForm)
	return nil
}

const htmlForm = `
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Login</title>
</head>
<body>
<form action="/sonar/reports" method="POST">
    <label for="username">Username:</label><br>
    <input type="text" id="username" name="username"><br>
    <label for="password">Password:</label><br>
    <input type="password" id="password" name="password"><br><br>
    <input type="submit" value="Submit">
</form>
</body>
</html>
`