Documentation en ligne

Authentification

Gestion des accès aux API

La création d’accès aux API se gère globalement depuis le module de gestion d’identités (Authentic). L’écran de configuration est accessible depuis la page d’accueil de ce module, via le bouton « Clients d’API » dans sa barre latérale droite.

Capture d’écran de l’accueil du module de gestion d’identités, un bouton « Client d’API » apparait dans la colonne de droite

Le bouton « Ajouter un nouveau client d’API » permet d’ajouter un accès, et il faut indiquer :

  • Nom : le nom choisi pour l’accès, qui sera affiché dans la page des accès ;

  • Description : pour se rappeler de l’usage prévu pour cet accès ;

  • Identifiant : le nom de l’utilisateur à utiliser pour l’authentification HTTP Basic, ou le paramètre orig pour l’authentification par signature ;

  • Mot de passe : le mot de passe pour l’authentification HTTP Basic de cet utilisateur ;

  • Collectivité : dans un environnement multi-collectivités, la collectivité à laquelle attacher cet accès ;

  • Rôles : la liste des rôles automatiquement obtenus lorsque cet accès est utilisé. Par exemple, s’il s’agit de permettre de lister des demandes d’une certaine démarche, il faut indiquer un rôle qui permet de voir les demandes. Ce rôle est à déterminer selon le paramétrage du formulaire de la démarche et/ou de son workflow.

Il est conseillé de créer des «rôles techniques» dédiés aux API. Ces rôles ne sont jamais donnés à des utilisateurs de la plateforme, ils sont utilisés uniquement dans le paramétrage des accès. Typiquement une démarche est paramétrée pour qu’un certain rôle technique ait accès à ses demandes, et ce même rôle est indiqué dans l’accès aux API dédié.

Définitions de l’usager concerné

Si l’authentification utilisée passe par un accès aux API qui ne donne pas de rôle spécifique, alors il faut indiquer dans la query-string un usager qui aura les rôles nécessaires.

C'est aussi nécessaire pour les appels concernant un usager particulier, tel que la récupération de la liste de ses formulaires en cours.

L’usager est précisé en ajoutant dans la query string :

  • soit un paramètre email pour trouver l’usager selon son adresse électronique

  • soit un paramètre NameID pour trouver l’usager selon son NameID SAML.

Authentification simple HTTP Basic

Pour accéder aux API avec l’authentification HTTP Basic classique, il faut utiliser le nom d’utilisateur (identifiant d’accès) et le mot de passe (clé d’accès) de l’accès configuré ci-dessus.

Authentification par signature de l’URL

Historiquement un système d’authentification alternatif, basé sur une signature ajoutée à la fin de l’URL était également disponible. Avec la généralisation de l’HTTPS, il est obsolète.

Signature des requêtes

La signature d’un appel aux API passe par une clé partagée à configurer des deux cotés de la liaison, la signature est du type HMAC; l’algorithme de hash à employer est passé en paramètre.

En ce qui concerne l’algorithme de hash, il est préconisé d’utiliser SHA-256 par respect du Référentiel Général de Sécurité (RGS).

La signature est à calculer sur la query string encodée complète, en enlevant les paramètres terminaux algo, timestamp, nonce, orig et signature s’ils sont présents.

La formule de calcul de la signature est la suivante :

BASE64(HMAC-HASH(query_string+'algo=HASH&timestamp=' + timestamp + '&nonce=' + nonce '&orig=" + orig, clé))
  • timestamp est la date dans la zone GMT au format ISO8601 en se limitant à la précision des secondes (ex : 2012-04-04T12:34:00Z),

  • nonce est un aléa, typiquement la réprésentation hexa d’un nombre pseudo-aléatoire de 128 bits,

  • orig est une chaîne précisant l’émetteur de la requête,

  • algo est une chaîne représentant l’algorithme de hachage utilisé, sont définis : sha1, sha256, sha512 pour les trois algorithmes correspondant. L’utilisation d’une valeur différente n’est pas définie. L’algorithme sha256 est préconisé.

La query string définitive est ainsi :

qs_initial&algo=algo&timestamp=timestamp&nonce=nonce&orig=orig&signature=signature

Configuration des clés partagées

La définition des clés se fait lors de la création des accès aux API (voir plus haut). Lors de la création d'un nouvel accès, l’identifiant d'accès correspond au «orig» et la clé d'accès est la clé de signature. Les rôles sont ceux qui seront obtenus lors de l’usage de l’accès.

Historiquement les clés partagées pouvaient aussi être définies dans le fichier site-options.cfg, dans une section [api-secrets], cette méthode n'est pas conseillée car elle ne permet pas de préciser finement les rôles. Exemple :

[api-secrets]
intranet = 12345

Exemples d'implémentation de l’algorithme de signature

Voici des exemples de code pour créer des URLs signées selon l’algorithme expliqué ci-dessus.

Python

#! /usr/bin/env python3

import base64
import datetime
import hashlib
import hmac
import random
import urllib.parse


def sign_url(url, key, algo='sha256', orig=None, timestamp=None, nonce=None):
    parsed = urllib.parse.urlparse(url)
    new_query = sign_query(parsed.query, key, algo, orig, timestamp, nonce)
    return urllib.parse.urlunparse(parsed[:4] + (new_query,) + parsed[5:])


def sign_query(query, key, algo='sha256', orig=None, timestamp=None, nonce=None):
    if timestamp is None:
        timestamp = datetime.datetime.utcnow()
    timestamp = timestamp.strftime('%Y-%m-%dT%H:%M:%SZ')
    if nonce is None:
        nonce = hex(random.getrandbits(128))[2:].rstrip('L')
    new_query = query
    if new_query:
        new_query += '&'
    new_query += urllib.parse.urlencode((('algo', algo), ('timestamp', timestamp), ('nonce', nonce)))
    if orig is not None:
        new_query += '&' + urllib.parse.urlencode({'orig': orig})
    signature = base64.b64encode(sign_string(new_query, key, algo=algo))
    new_query += '&' + urllib.parse.urlencode({'signature': signature})
    return new_query


def sign_string(s, key, algo='sha256', timedelta=30):
    if not isinstance(key, bytes):
        key = key.encode('utf-8')
    if not isinstance(s, bytes):
        s = s.encode('utf-8')
    digestmod = getattr(hashlib, algo)
    hash = hmac.HMAC(key, digestmod=digestmod, msg=s)
    return hash.digest()


# usage:
url = sign_url('https://www.example.net/uri/?arg=val&arg2=val2', 'user-key', orig='user')

PHP

<?php

function sign_url(string $url, string $orig, string $key) {
	$parsed_url = parse_url($url);
	$timestamp =  gmstrftime("%Y-%m-%dT%H:%M:%SZ");
	$nonce = bin2hex(random_bytes(16));
	$new_query = '';
	if (isset($parsed_url['query'])) {
		$new_query .= $parsed_url['query'] . '&';
	}
	$new_query .= http_build_query(array(
		'algo' => 'sha256',
		'timestamp' => $timestamp,
		'nonce' => $nonce,
		'orig' => $orig));
	$signature = base64_encode(hash_hmac('sha256', $new_query, $key, $raw_output = true));
	$new_query .= '&' . http_build_query(array('signature' => $signature));
	$scheme   = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
	$host     = isset($parsed_url['host']) ? $parsed_url['host'] : '';
	$port     = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '';
	$user     = isset($parsed_url['user']) ? $parsed_url['user'] : '';
	$pass     = isset($parsed_url['pass']) ? ':' . $parsed_url['pass']  : '';
	$pass     = ($user || $pass) ? "$pass@" : '';
	$path     = isset($parsed_url['path']) ? $parsed_url['path'] : '';
	$fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
	return "$scheme$user$pass$host$port$path?$new_query$fragment";
}

# usage:
url = sign_url("http://www.example.net/uri/?arg=val&arg2=val2", "user", "user-key");

?>

Shell (bash)

#!/bin/bash

url="http://www.example.net/uri/?arg=val&arg2=val2"
orig="user"
key="user-key"

function rawurlencode() {
  local string="${1}"
  local strlen=${#string}
  local encoded=""
  local pos c o
  for ((pos=0; pos<strlen; pos++)); do
     c=${string:$pos:1}
     case "$c" in
        [-_.~a-zA-Z0-9] ) o="${c}" ;;
        * )               printf -v o '%%%02x' "'$c"
     esac
     encoded+="${o}"
  done
  echo "${encoded}"
}

now=$(date -u +%FT%TZ);
nonce=$(od -N16 -txL /dev/urandom | cut -c8- | tr -d " ")
qs="algo=sha256&timestamp=$now&nonce=$nonce&orig=$orig"
sig=$(rawurlencode $(echo -n "$qs" | openssl dgst -binary -sha256 -hmac "$key" | base64))
signed="${url}?$qs&signature=$sig"
echo "$signed"
 

Dernière mise à jour le 17/09/2024 11:38 — Éditer