Applications OIDC

L’orchestrateur d’identité Maverics peut être utilisé en tant que fournisseur d’identité pour protéger les applications OIDC.

ℹ️
Pour définir les applications de type OIDC, la valeur oidcProvider doit être définie.

Options de configuration

Name

name est un identifiant unique pour l’application.

Type

type représente le type d’application. Lors de la définition des applications OIDC, le type doit être oidc.

ID du client

clientID est un identifiant unique utilisé par les applications clientes pour s’identifier.

Secret du client

clientSecret est un secret utilisé pour authentifier les applications clientes. La valeur du secret peut être stockée dans un fournisseur de secrets.

URL de redirection d’authentification

redirectURLs constituent une liste des URL autorisées auxquels la réponse à une requête d’authentification peut être envoyée.

URL de redirection de déconnexion

logoutRedirectURLs constituent une liste des URL autorisées auxquelles la réponse à une requête de déconnexion peut être envoyée.

Mappage des réclamations

claimsMapping permet la mise en correspondance des attributs de la session d’un utilisateur avec des réclamations standard sur le jeton d’identification. Les réclamations ajoutées au jeton d’identification sont déterminées par la valeur du paramètre « scope query » envoyé dans la requête d’autorisation. Les champs d’application pris en charge, en plus du champ d’application openid requis, sont l’adresse électronique, le profil, l’adresse et le téléphone. Le mappage des valeurs des réclamations à un champ d’application est répertorié dans le document OpenID Connect Core 1.0, section 5.4.

Le renvoi d’un nonce fourni dans la requête d’autorisation par le biais d’une réclamation dans la réponse du jeton d’identification est également pris en charge.

Par exemple, si les champs d’application de l’e-mail et du profil sont demandés dans la requête d’autorisation, qu’Azure est le fournisseur d’identité utilisé et que le mappage des réclamations contient des mappages vers des attributs Azure, alors les réclamations relatives à l’e-mail et au profil seront incluses dans la réponse IDtoken avec la valeur de l’attribut associé.

Attribute Providers

attrProviders constituent une configuration facultative pour un système d’identité ou un magasin de données à partir duquel l’OIDCProvider peut récupérer des attributs supplémentaires utilisés dans claimsMapping.

Connecteur

connecteur est une référence au nom du connecteur défini qui sera utilisé en tant que fournisseur d’attributs.

Mappage des noms d’utilisateur

usernameMapping définit l’attribut qui sera utilisé en tant que clé de recherche pour interroger les attributs de l’utilisateur.

Authentification

authentication définit la manière dont les utilisateurs sont authentifiés pour cette application.

IDPs

idps répertorie les fournisseurs d’identité utilisés pour authentifier l’utilisateur.

Extension de services IsAuthenticated

isAuthenticatedSE est une extension de services optionnelle qui peut être utilisée pour modifier le comportement par défaut qui détermine si un utilisateur est déjà authentifié. Cette extension doit être utilisée avec authenticateSE.

Extension de services Authenticate

authenticateSE est une extension de services optionnelle utilisée pour contrôler la manière dont l’authentification sera effectuée. Cette extension doit être utilisée avec isAuthenticatedSE.

Backchannel

Extension de services Authenticate

backchannel.authenticateSE est une extension de services facultative utilisée pour prendre le contrôle de la manière dont l’authentification de l’utilisateur final sera effectuée pour les flux OIDC backchannel tels que Resource Owner Password Credentials Grant.

Access Token

accessToken définit la configuration du jeton d’accès OAuth.

Type

typecandoit être défini sur jwt (par défaut) ou opaque.

Longueur

length définit la longueur d’un jeton d’accès opaque. La longueur peut être comprise entre 22 et 256 caractères. Si elle n’est pas définie, la longueur par défaut est de 28 caractères.

Durée de vie en secondes

lifetimeSeconds contrôle la durée de vie d’un jeton d’accès. Par défaut, les jetons d’accès disposent d’une durée de vie d’une heure.

Jeton d’actualisation

refreshToken définit la configuration du jeton d’actualisation OAuth. Les jetons d’actualisation sont utilisés pour obtenir un nouveau jeton d’accès sans interaction de l’utilisateur.

Les jetons d’actualisation sont automatiquement remplacés à chaque actualisation des jetons d’accès. En d’autres termes, à chaque fois qu’un jeton d’actualisation est utilisé pour générer un nouveau jeton d’accès, un nouveau jeton d’actualisation est également renvoyé. Si un jeton d’actualisation précédemment émis est réutilisé en dehors d’une fenêtre admissible, le jeton d’actualisation actif sera invalidé.

Les jetons d’actualisation sont renouvelés afin de se protéger contre les attaques par rejeu de jetons d’actualisation et contre les jetons d’actualisation compromis en raison d’une durée de vie prolongée. Pour plus d’informations, veuillez vous référer au document OAuth Security Topics RFC

Autorisation d’accès hors ligne

allowOfflineAccess définit si un client a la possibilité de demander des jetons d’actualisation. Les jetons d’actualisation ne seront délivrés que si l’indicateur allowOfflineAccess est défini sur « true » et que la requête d’autorisation inclut le champ d’application offline_access.

Longueur

longueur peut être fixée entre 22 et 256 caractères pour définir la longueur d’un jeton d’actualisation. Si elle n’est pas définie, la longueur par défaut est de 28 caractères.

Utilisateurs autorisés

allowedAudiences est une configuration facultative qui présente une liste des utilisateurs autorisés à consommer des jetons d’accès. Lorsqu’un client adresse une requête au terminal d’autorisation, il est possible d’inclure un paramètre de ressource facultatif indiquant le public cible du jeton. La valeur fournie dans le paramètre de ressource doit figurer dans la liste des utilisateurs autorisés.

Cette configuration est utilisée lorsque les serveurs de ressources, tels que les API, procèdent à des autorisations par le biais du jeton d’accès. Pour plus d’informations, veuillez vous référer au document RFC 9068.

Extension de services BuildIDTokenClaims

Le buildIDTokenClaimsSE est une extension de services optionnelle qui permet de personnaliser la façon dont sont construites les réclamations dans le jeton d’identification.

Extension de services BuildAccessTokenClaims

L’extension de services optionnelle buildAccessTokenClaimsSE permet de personnaliser la façon dont sont construites les revendications dans le jeton d’accès.

Exemples

Exemple de configuration de base de l’application OIDC

apps:
  - name: exampleOIDCApp
    type: oidc
    clientID: exampleClientID
    clientSecret: <exampleClientSecret>
    redirectURLs:
      - https://app.enterprise.com/oidc
    logoutRedirectURLs:
      - https://app.enterprise.com/oidc/logout
    attrProviders:
      - connector: ldap
        usernameMapping: okta.email
    authentication:
      idps:
        - okta
    claimsMapping:
      email: ldap.mail
      given_name: ldap.givenname
      family_name: ldap.sn

Application OIDC avec extension de services

apps:
  - name: exampleOIDCApp
    type: oidc
    clientID: exampleClientID
    clientSecret: <exampleClientSecret>
    redirectURLs:
      - https://app.enterprise.com/oidc
    authentication:
      isAuthenticatedSE:
        funcName: IsAuthenticated
        file: /etc/maverics/extensions/auth.go
      authenticateSE:
        funcName: Authenticate
        file: /etc/maverics/extensions/auth.go
      backchannel:
        authenticateSE:
          funcName: BackchannelAuthenticate
          file: /etc/maverics/extensions/auth.go
    buildAccessTokenClaimsSE:
      funcName: BuildAccessTokenClaims
      file: /etc/maverics/extensions/auth.go
    buildIDTokenClaimsSE:
      funcName: BuildIDTokenClaims
      file: /etc/maverics/extensions/auth.go
    claimsMapping:
      email: okta.email

/etc/maverics/extensions/auth.go

package main

import (
	"net/http"
    "errors"
    "crypto/sha256"
    "encoding/hex"

	"github.com/strata-io/service-extension/orchestrator"
)

func IsAuthenticated(api orchestrator.Orchestrator, _ http.ResponseWriter, _ *http.Request) bool {
	logger := api.Logger()
	logger.Debug("se", "determining if user is authenticated")

	session, err := api.Session()
	if err != nil {
		logger.Error("se", "unable to retrieve session", "error", err.Error())
		return false
	}

	isOktaAuth, err := session.GetString("okta.authenticated")
	if err != nil {
		logger.Error("se", "unable to retrieve session value", "error", err.Error())
		return false
	}
	if isOktaAuth == "true" {
		return true
	}

	return false
}

func Authenticate(api orchestrator.Orchestrator, rw http.ResponseWriter, req *http.Request) {
	logger := api.Logger()
	logger.Debug("se", "authenticating user")

	oktaIDP, err := api.IdentityProvider("okta")
	if err != nil {
		logger.Error(
			"se", "failed to retrieve Okta IDP",
			"error", err.Error(),
		)
		http.Error(
			rw,
			http.StatusText(http.StatusInternalServerError),
			http.StatusInternalServerError,
		)
		return
	}
	oktaIDP.Login(rw, req)
}

func BackchannelAuthenticate(api orchestrator.Orchestrator, req *http.Request) error {
	logger := api.Logger()
	logger.Debug("se", "authenticating user in backchannel flow")

    username := req.Form.Get("username")
    password := req.Form.Get("password")

    secrets, err := api.SecretProvider()
	if err != nil {
		logger.Error(
			"se", "failed to retrieve secret provider",
			"error", err.Error(),
		)
		return errors.New("failed to retrieve secret provider")
	}
    pwdHash := secrets.GetString(username)
    if len(pwdHash) == 0 {
        return errors.New("invalid user credentials")
    }

	hash := sha256.Sum256([]byte(password))
	hashHex := hex.EncodeToString(hash[:])

    if pwdHash != hashHex {
        return errors.New("invalid user credentials")
    }

    return nil
}

func BuildAccessTokenClaims(api orchestrator.Orchestrator, _ *http.Request) (map[string]any, error) {
	logger := api.Logger()
	logger.Debug("se", "building access token claims")

	session, err := api.Session()
	if err != nil {
		logger.Error("se", "unable to retrieve session", "error", err.Error())
		return nil, err
	}

	roles, err := session.GetString("okta.roles")
	return map[string]any{
		"scope": roles,
	}, err
}

func BuildIDTokenClaims(api orchestrator.Orchestrator, _ *http.Request) (map[string]any, error) {
	logger := api.Logger()
	logger.Debug("se", "building ID token claims")

	session, err := api.Session()
	if err != nil {
		logger.Error("se", "unable to retrieve session", "error", err.Error())
		return nil, err
	}

	groups, err := session.GetString("okta.groups")
	return map[string]any{
		"groups": groups,
	}, err
}