API TransfertPro

Authentification de l'utilisateur

TransfertPro gère une authentification basée sur des tokens de type Bearer via le protocole OAuth2. Récupération du Token auprès du serveur :

string login; // Login de l'utilisateur
string password; // Mot de passe de l'utilisateur
Token token; // Token d'authentification de l'utilisateur

using (var client = new HttpClient())
{
    client.BaseAddress = new Uri("http://vpn.issoneo.com:94/");
    client.Timeout = TimeSpan.FromSeconds(30);
    client.DefaultRequestHeaders.Accept.Clear();
    StringContent queryString = new StringContent(string.Format("grant_type={0}&username={1}&password={2}",
            "password", Uri.EscapeDataString(login), Uri.EscapeDataString(password)));

    var result = client.PostAsync("/Token", queryString).ContinueWith(task =>
    {
        try
        {
            var response = task.Result;

            if (response.IsSuccessStatusCode)
            {
                string json = response.Content.ReadAsStringAsync().Result;
                token = JsonConvert.DeserializeObject(json);
            }
            else
            {
                // Gérer les codes status 400, 401, 403...
            }
        }
        catch (Exception ex)
        {
            // Gérer les exceptions
        }
    });

    result.Wait();
}

Classe contenant les informations du Token :

public class Token
{
    /// <summary>
    /// Obtient ou définit la valeur du jeton d'accès
    /// </summary>
    public string Access_token { get; set; }

    /// <summary>
    /// Obtient ou définit le type de jeton
    /// </summary>
    public string Token_type { get; set; }

    /// <summary>
    /// Obtient ou définit la durée de validité du token
    /// </summary>
    public string Expires_in { get; set; }

    /// <summary>
    /// Obtient ou définit l'utilisateur associé au jeton
    /// </summary>
    public string UserName { get; set; }

    /// <summary>
    /// Obtient ou définit la date à laquelle le jeton a été demandé
    /// </summary>
    [JsonProperty(".issued")]
    public DateTime Issued { get; set; }

    /// <summary>
    /// Obtient ou définit la date à laquelle le jeton expire
    /// </summary>
    [JsonProperty(".expires")]
    public DateTime Expires { get; set; }
}

Passage du Token dans les requêtes :

protected HttpClient GetHttpClient()
{
    var client = new HttpClient();
    client.BaseAddress = new Uri("http://vpn.issoneo.com:94/");
    client.Timeout = TimeSpan.FromMinutes(30);
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); // Où token est l'instance du Token préalablement récupéré sur le serveur.
    return client;
}

Authentification via la clé l'API

Introduction

Pour utiliser l'API TransfertPro, chaque requête doit être authentifiée. Les requêtes sont authentifiées en calculant une signature numérique à l'aide de la méthode HMAC-SHA256. Un client doit signer chaque requête à l'API à l'aide d'une clé privée connue uniquement du client et des serveurs TransfertPro. L'API TransfertPro utilise également des informations d'horodatage pour s'assurer que personne d'autre n'utilise l'API au nom du client.

Authentifier les requêtes aux API TransfertPro

Chaque requête aux API TransfertPro doit inclure les paramètres suivants :
apiKeyName La clé publique fournie au client qui permet aux API TransfertPro de déterminer quel client réalise la requête. Il s'agit du nom de la clé d'API.
hashKey hashKey Une signature HMAC-SHA256 de la requête qui est généré par le client à l'aide de sa clé privée
nonce Un id unique généré aléatoirement par le client pour identifier la requête.
timestamp La date et l'heure à laquelle la requête a été générée.

Nom de la clé d'API

Quand une société souscrit à TransfertPro, elle a la possibilité de faire une demande d'accès aux API TransfertPro. Pour cela elle doit créer une clé d'API. TransfertPro fournit alors au client une clé publique (le nom de la clé d'API) et une clé privée (la valeur de la clé d'API). La clé privée est utilisée pour signer les requêtes et ne doit jamais être inclus dans la requête. La clé publique doit, au contraire, être inclus dans chaque requête afin que le système puisse déterminer la clé privée du client pour générer la même signature de requête afin d'autoriser ou de rejeter la requête.

HashKey

Il s'agit de la signature de la requête. La manière de signer une requête est décrite en détail dans la section suivante.

nonce

Nonce signifie "un nombre utilisé une seule fois", ce qui pour l'API signifie une chaine de caractère aléatoire unique utilisée pour identifier une requête. Le nonce doit être une chaîne de caractère aléatoire unique d'au moins 8 caractères. Le nonce est utilisé pour prévenir des attaques de type "man in the middle", où quelqu'un essaye de répéter une requête qu'un client a déjà faite.

date

Le timestamp est la date et l'heure à laquelle la requête est exécutée. La requête doit être transmise au serveur dans les 3 minutes qui suivent ou une erreur de type 403 - La requête a expirée sera retournée au client.

Signature d'une requête

Quand le client crée une requête aux API TransfertPro, il doit d'abord générer une signature numérique de la requête en utilisant sa clé privée. Cette signature est incluse dans la requête ainsi que la clé publique du client. Lorsque l'API TransfertPro reçoit la requête, le système détermine la clé privée du client à partir de la clé publique fournie. Le système essaye alors de générer la même signature en utilisant la clé privée du client. Si la signature générée correspond à la signature dans la requête la requête est acceptée et l'action est effectuée. Autrement, le serveur retournera un message d'erreur.
Puisque seuls le client et le serveur ont accès à la clé privée du client, personne d'autre ne peut générer une signature de requête valide au nom du client. Pour s'assurer que la signature du client et la signature du serveur correspondent, les étapes suivantes doivent être suivies par les 2 parties.
Listez tous les paramètres de la requête (ceux attendus par l'action ainsi que les 3 paramètres liés à la clé d'API : apiKeyName, hashKey et nonce) et triez les par ordre alphabétique. L'ordre est très important. Concaténez les paramètres et leur valeurs dans l'ordre indiqué avec comme séparateur le caractère '|' aussi bien entre chaque couple clé/valeur qu'entre la clé et la valeur (ne pas utiliser '&' et '='). Au résultat obtenu, concaténez la valeur de la clé d'API (la clé privée) avec comme séparateur le caractère '|'. Calculez le HMAC (Hash-based Message Authentication Code) de la chaîne de caractères obtenue en utilisant la fonction de hachage SHA512. Exemple de signature de requête :
Secret key : 68f4bf5c-58a0-4b88-9fbc-1c4540e0e5dc Url sans la signature : http://vpn.issoneo.com:94/api/v3/Workspace/Files/0BE01D3D-7BF8-4CE9-A00A-EDDF15A0C5C8?permanently=true&apiKeyName=1854-SalesforceKey×tamp=22/06/2016%2013:34:19&nonce=636021993082569669 Chaîne concaténée : apiKeyName|1854-SalesforceKey|nonce|636021993082569669|permanently|true|timestamp|22/06/2016%2013:34:19|68f4bf5c-58a0-4b88-9fbc-1c4540e0e5dc Signature : 90321a61b2fcc3cce7d7f712098b84e617d4ab91cef648532b98d344161921cd Url avec la signature : http://vpn.issoneo.com:94/api/v3/Workspace/Files/0BE01D3D-7BF8-4CE9-A00A-EDDF15A0C5C8?permanently=true&apiKeyName=1854-SalesforceKey×tamp=22/06/2016%2013:34:19&nonce=636021993082569669&hashkey=90321a61b2fcc3cce7d7f712098b84e617d4ab91cef648532b98d344161921cd
Utilisation de la clé d'API dans les requêtes :
    
protected string privateApiKeyName; // Nom de la clé d'API utilisé pour réaliser vos requêtes.
protected HttpRequestMessage createRequest(string url, string mediaType, HttpMethod method, Dictionary<string, string> requestParameters)
{
    Dictionary<string, string> globalParameters = new Dictionary<string, string>();
    globalParameters["apiKeyName"] = privateApiKeyName.ToString();
    globalParameters["timestamp"] = DateTime.UtcNow.ToString();
    globalParameters["nonce"] = DateTime.Now.Ticks.ToString();
    globalParameters = globalParameters.Concat(requestParameters).ToDictionary(x => x.Key, x => x.Value);
    string hashKey = GetHashKey(globalParameters, privateApiKeyValue);
    globalParameters.Add("hashKey", hashKey);
    
    StringBuilder urlParameters = new StringBuilder();
    
    foreach (string parameterName in globalParameters.Keys)
    {
        if (urlParameters.Length != 0)
        {
            urlParameters.Append("&");
        }

        urlParameters.Append(parameterName);
        urlParameters.Append("=");
        urlParameters.Append(globalParameters[parameterName]);
    }

    var request = new HttpRequestMessage();
    request.RequestUri = new Uri(string.Format("{0}?{1}",url, urlParameters.ToString()));
    request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(mediaType));
    request.Method = method;
    return request;
}

private string GetHashKey(Dictionary<string, string> urlParameters, string keyPassValue)
{
    StringBuilder sb = new StringBuilder();
    SortedSet<string> urlParameterNames = new SortedSet<string>(urlParameters.Keys);

    foreach (string urlParameterName in urlParameterNames)
    {
        if (sb.Length != 0)
        {
            sb.Append(hashSeparator);
        }

        sb.Append(urlParameterName);
        sb.Append(hashSeparator);
        sb.Append(urlParameters[urlParameterName]);
    }

    sb.Append(hashSeparator);
    sb.Append(keyPassValue);
    return ConvertSHA256(keyPassValue, sb.ToString());
}

private string ConvertSHA256(string keyPassValue, string value)
{
    byte[] key = Encoding.UTF8.GetBytes(keyPassValue);
    byte[] valueBytes = Encoding.UTF8.GetBytes(value);
    HMACSHA512 myhmacsha512 = new HMACSHA512(key);
    byte[] hashValue = myhmacsha512.ComputeHash(valueBytes);
    StringBuilder sb = new StringBuilder();

    for (int i = 0; i < hashValue.Length; i++)
    {
        sb.Append(hashValue[i].ToString("x2"));
    }

    return sb.ToString();
}