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.
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.
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×tamp=$now&nonce=$nonce&orig=$orig" sig=$(rawurlencode $(echo -n "$qs" | openssl dgst -binary -sha256 -hmac "$key" | base64)) signed="${url}?$qs&signature=$sig" echo "$signed"