Édition

Prise en charge du Kit de développement logiciel (SDK) client Azure IoT pour les serveurs de jetons tiers

Azure IoT
Azure IoT Hub

L’article Contrôler l’accès à IOT Hub montre comment un service de jetons tiers peut être intégré à IoT Hub. Cet article décrit la prise en charge de l’authentification par jeton de signature d’accès partagé (SAP) dans chacun des Kits de développement logiciel (SDK) Azure IoT. Il décrit également ce qui doit être mis en œuvre dans une application de périphérique à l’aide du Kit de développement logiciel (SDK) correspondant à chaque langue, et comment utiliser les jetons de périphérique ou de module pour les stratégies d’accès partagé de DeviceConnect ou ModuleConnect.

Contexte et problème

La documentation actuelle sur la sécurité Azure IoT Hub présente le modèle de serveur de jetons tiers pour l’authentification SAP avec IoT Hub par les appareils IoT à l’aide des Kits de développement logiciel (SDK) Azure IoT clients. Toutefois, les hypothèses incorrectes formulées par un client lors d’un engagement d’entreprise récent suggèrent que, sans clarification supplémentaire, vous pouvez développer une impression trompeuse sur le niveau de prise en charge implémenté par défaut dans les Kits de développement logiciel (SDK) Azure IoT clients.

Cet article présente les enseignements tirés de cet engagement et clarifie ce qu’il faut faire dans chaque SDK pour que les appareils permettent l’authentification par un serveur de jetons tiers. Cet article devrait également vous éviter de faire des hypothèses incorrectes similaires concernant la prise en charge du modèle de serveur de jetons tiers dans le SDK client Azure IoT.

Solution

Les Kits de développement logiciel (SDK) Azure IoT client fournissent différents niveaux de prise en charge pour l’authentification par jeton SAP, chacun nécessitant du code personnalisé pour effectuer les fonctionnalités d’authentification et de gestion des jetons.

La fréquence d’évaluation du jeton dépend du protocole de transport choisi (MQTT, AMQP ou HTTPS). La variation dépend de la capacité du protocole à prendre en charge le renouvellement proactif des jetons et des délais d’expiration de session. Seul AMQP implémente la prise en charge de renouvellement proactif. Cela signifie que les autres transports ferment la connexion en cas d’échec de l’authentification par jeton SAP, puis doivent effectuer une nouvelle opération de connexion. Il s’agit d’une opération de connectivité potentiellement coûteuse pour le client.

Si l’authentification SAP échoue, une erreur est générée par l’implémentation du transport qui peut être gérée dans l’application de l’appareil par un gestionnaire d’événements « État de la connexion modifié ». Si un tel gestionnaire n’est pas mis en œuvre, l’application de l’appareil s’arrêtera en raison de l’erreur. Avec une mise en œuvre correcte du gestionnaire d’événements et de la fonctionnalité de renouvellement des jetons, les transports peuvent tenter à nouveau la connexion.

La figure suivante illustre le modèle de serveur de jetons tiers :

Illustration of the third-party token-server pattern

La figure suivante illustre la prise en charge de l’implémentation dans le Kit de développement logiciel (SDK) Azure IoT client avec l’intégration de Mobile Net Operator :

Flowchart of implementation support in the Azure IoT client SDK with Mobile Net Operator integration

Des exemples d’implémentations sont inclus dans le référentiel Exemples Azure sur GitHub.

Problèmes et considérations

Prenez en compte les points suivants lorsque vous décidez de mettre en œuvre ce modèle :

Références :

Quand utiliser ce modèle

Vous devez utiliser ce modèle chaque fois que vous souhaitez vous authentifier auprès d’Azure IoT Hub à partir d’appareils IoT à l’aide des différents Kits de développement logiciel (SDK) Azure IoT clients. Au lieu d’utiliser les Kits de développement logiciel (SDK) clients pour l’authentification par jeton SAP, utilisez l’API REST DPS Azure pour garantir l’implémentation de la prise en charge du renouvellement proactif pour tous les mécanismes de transport.

Exemples

Les sections suivantes proposent des exemples que vous pouvez utiliser pour différents langages de programmation, tels que Embedded C, .NET, Java et Python.

Azure IoT Hub device SDK pour C et Azure IoT Hub device SDK pour Embedded C

L’approche suivante peut être utilisée dans les applications d’appareils créées à l’aide du Kit de développement logiciel (SDK) Azure IoT C ou du Kit de développement logiciel (SDK) Azure IoT Embedded C. Aucun des deux SDK ne fournit de gestion de la durée de vie des jetons SAP, vous devrez donc mettre en œuvre une capacité de gestion de la durée de vie des jetons SAP.

Les jetons SAP peuvent être utilisés via la structure IOTHUB_CLIENT_CONFIG en définissant le membre deviceSasToken sur le jeton et en attribuant la valeur Null à deviceKey. D’autres valeurs inutilisées, telles que protocolGatewayHostName, doivent également avoir la valeur Null.

IOTHUB_CLIENT_CONFIG* CONFIG = (IOTHUB_CLIENT_CONFIG*)malloc(sizeof(IOTHUB_CLIENT_CONFIG));

CONFIG->PROTOCOL = PROTOCOL;
CONFIG->DEVICEID = DEVICEID;
CONFIG->IOTHUBNAME = IOTHUBNAME;
CONFIG->IOTHUBSUFFIX = IOTHUBSUFFIX;
CONFIG->DEVICEKEY = 0;
CONFIG->DEVICESASTOKEN = TOKEN;
CONFIG->PROTOCOLGATEWAYHOSTNAME = 0;

// The created IOTHUB_CLIENT_CONFIG can then be provided to the IoTHubDeviceClient_Create function to establish a DeviceClient instance.
if ((IOTHUBCLIENTHANDLE = IoTHubDeviceClient_Create(CONFIG)) == NULL) {
    (void)printf("ERROR: IOTHUBCLIENTHANDLE IS NULL!\r\n");
}

// To capture SAS token authentication failures, a handler needs to be implemented for the IoTHubDeviceClient_SetConnectionStatusCallback.
(void)IoTHubDeviceClient_SetConnectionStatusCallback(IOTHUBCLIENTHANDLE, CONNECTION_STATUS_CALLBACK, NULL);

Le connection_status_callback peut intercepter le IOTHUB_CLIENT_CONNECTION_STATUS_REASON of IOTHUB_CLIENT_CONNECTION_EXPIRED_SAS_TOKEN pour déclencher un renouvellement du jeton SAP via le service de jetons tiers. Ceci est nécessaire pour tous les transports afin de capturer les problèmes de connexion mais est spécifiquement requis par les transports qui ne supportent pas le renouvellement proactif des jetons SAP. La gestion proactive de la durée de vie des jetons SAP peut être mise en œuvre sous la forme d’une fonction exécutée de manière répétée pendant la boucle « opérationnelle » des applications de l’appareil. La durée de vie du jeton est fréquemment évaluée, et le renouvellement du jeton peut être exécuté de manière proactive si nécessaire.

Résumé de la mise en œuvre de l’authentification par jeton SAP pour les SDK C :

  1. Implémentez un gestionnaire ConnectionStatusCallback pour capturer l’événement IOTHUB_CLIENT_CONNECTION_EXPIRED_SAS_TOKEN et déclencher le renouvellement du jeton.

  2. Utilisez une IOTHUB_CLIENT_CONFIG pour fournir le jeton SAP d’appareil à IoTHubDeviceClient_Create.

  3. Mettez en œuvre une gestion proactive de la durée de vie des jetons SAP dans le cadre de la boucle de fonctionnement de l’application de l’appareil.

Kit Azure IoT Hub device SDK pour .NET

Le Kit de développement logiciel (SDK) Azure IoT client pour .NET implémente la gestion de la durée de vie des jetons SAP via la classe abstraite DeviceAuthenticationWithTokenRefresh. Une implémentation concrète de cette classe, ajoutant une fonctionnalité de renouvellement de jeton, peut être fournie comme méthode d’authentification à une méthode DeviceClient.Create. Les implémentations de transport renouvelleront automatiquement le jeton via la méthode d’authentification, si nécessaire. Un ConnectionStatusChangesHandler est requis pour capturer les modifications de connexion et empêcher les exceptions déclenchées par les transports.

Exemple d’implémentation basé sur la classe DeviceAuthenticationWithTokenRefreash :

internal class StsDeviceAuthenticationWithTokenRefresh : DeviceAuthenticationWithTokenRefresh
{

    private readonly string _stsConnectUrl = "http://localhost:8080/sts/azure/token/operations?sr={0}/devices/{1}";

    private const int DEFAULTTIMETOLIVESECONDS = 1 * 60 * 60;

    private const int DEFAULTBUFFERPERCENTAGE = 15;

    public StsDeviceAuthenticationWithTokenRefresh(string deviceId, int suggestedTimeToLiveSeconds, int timeBufferPercentage) : BASE(deviceId, suggestedTimeToLiveSeconds, timeBufferPercentage)
    {
        If(String.IsNullOrWhitespace(deviceId)){
            throw new ArgumentNullException(nameof(deviceId));
        }
    }

    protected override async Task<string> SafeCreateNewToken(string iotHub, int suggestedTimeToLive)
    {
        string result;
        string url = string.Format(_stsConnectUrl, iotHub, deviceId);

        using (HttpClientHandler handler = new HttpClientHandler())
        using (HttpClient client = new HttpClient(handler))
        {
            try
            {
                HttpResponseMessage response = await client.GetAsync(url);
                if (response.IsSuccessStatusCode)
                {
                    result = await response.Content.ReadAsStringAsync();
                }
                else
                {
                    throw new HttpRequestException($"Request failed with status code {response.StatusCode}.");
                }
            }
            catch (HttpRequestException)
            {
                result = null;
            }
        }

        return result;
    }
}

Résumé de l’implémentation de l’authentification par jeton SAP pour Azure IoT Hub Device SDK pour .NET :

  1. Implémentez une classe concrète basée sur la classe abstraite DeviceAuthenticationWithTokenRefresh, qui implémente la fonctionnalité de renouvellement de jetons.

  2. Implémentez un ConnectionStatusChangesHandler pour capturer l’état de la connexion de transport et éviter les exceptions déclenchées par l’implémentation de transport.

Références :

Kit Azure IoT Hub device SDK pour Java

Le Kit de développement logiciel (SDK) Azure IoT client pour Java implémente la prise en charge de la gestion de la durée de vie des jetons SAP via l’Interface SasTokenProvider. Une classe qui implémente cette interface avec la fonctionnalité de renouvellement de jeton SAP peut être utilisée comme SecurityProvider dans un constructeur DeviceClient. Les implémentations de transport renouvelleront automatiquement le jeton via le fournisseur de sécurité, si nécessaire. Un ConnectionStatusChangeCallback doit être est inscrit pour capturer les modifications de connexion et empêcher les exceptions déclenchées par les transports.

Exemple d’implémentation du fournisseur de sécurité implémentant l’interface SasTokenProvider :

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

public class StsSecurityProvider implements SasTokenProvider {
    private final String hostname;
    private final String deviceId;
    private int renewalBufferSeconds;
    private long expiryTimeSeconds;
    private char[] sasToken;

    public StsSecurityProvider(String hostname, String deviceId) {
        this.hostname = hostname;
        this.deviceId = deviceId;
        this.renewalBufferSeconds = 120;
        this.expiryTimeSeconds = (System.currentTimeMillis() / 1000);
    }

    @Override
    public char[] getSasToken() {
        long currentTimeSeconds = (System.currentTimeMillis() / 1000);
        try {
            if (this.sasToken == null || this.expiryTimeSeconds + this.renewalBufferSeconds >= currentTimeSeconds) {
                this.sasToken = stsGetToken();
                assert this.sasToken != null;
                String t = String.copyValueOf(this.sasToken);
                String[] bits = t.split("SE=");
                long l = Long.parseLong(bits[1]);
                this.expiryTimeSeconds = l; // the SE= number
                this.renewalBufferSeconds = (int)(l * 0.15); // renew within 15% of expiry
            }
        } catch (InterruptedException | IOException e) {
            e.printStackTrace();
        }
        return this.sasToken;
    }

    private char[] stsGetToken() throws IOException, InterruptedException {
        String stsUrl = String.format("http://localhost:8080/sts/azure/token/operations?sr=%s/devices/%s", this.hostname, this.deviceId);
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(stsUrl))
            .timeout(Duration.ofMinutes(2))
            .header("Content-Type", "application/json")
            .build();
        HttpClient client = HttpClient.newBuilder()
            .version(HttpClient.Version.HTTP_1_1)
            .connectTimeout(Duration.ofSeconds(20))
            .build();
        HttpResponse < String > response = client.send(request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() < 200 || response.statusCode() >= 300) {
            return null;
        }
        if (response.body().isEmpty()) {
            return null;
        }
        return response.body().toCharArray();
    }
}

Résumé de l’implémentation de l’authentification par jeton SAP pour Azure IoT Hub device SDK pour Java :

  1. Implémentez l’interface SasTokenProvider sur une classe et incluez la fonctionnalité de renouvellement de jeton.

  2. Implémentez un gestionnaire ConnectionStatusChangeCallback pour capturer les changements d’état de la connexion de transport et éviter les exceptions déclenchées par l’implémentation de transport.

Références :

Kit Azure IoT Hub device SDK pour Python

Le Kit Azure IoT Hub device SDK pour Python implémente la prise en charge des jetons SAP via les méthodes sur l’objet IoTHubDeviceClient. Ces méthodes permettent de créer un client d’appareil à l’aide d’un jeton, et de fournir un jeton mis à jour une fois le client d’appareil a été créé. Elles n’implémentent pas la gestion de la durée de vie des jetons, mais cela peut être implémenté facilement comme une opération asynchrone.

Exemple d’implémentation de Python 3.7 montrant juste les grandes lignes de la fonctionnalité :

import asyncio
import iothub_device_client

async def main():
    # Get a SAS token you generated
    sastoken = get_new_sastoken()
    # The client object is used to interact with your Azure IoT Hub.
    device_client = iothub_device_client.create_from_sastoken(sastoken)

    # Connect the client
    await device_client.connect()

    # Define behavior for providing new SAS tokens to prevent expiry
    async def sastoken_keepalive():
        while True:
            await asyncio.sleep(new_token_interval)
            sastoken = get_new_sastoken()
            await device_client.update_sastoken(sastoken)

    # Also run the SAS token keepalive in the event loop
    keepalive_task = asyncio.create_task(sastoken_keepalive())

    # Cancel the SAS token update task
    keepalive_task.cancel()

    # Finally, shut down the client
    await device_client.shutdown()

if __name__ == "main":
    asyncio.run(main())

Résumé du Kit Azure IoT Hub device SDK pour Python, authentification par jeton SAP :

  1. Créer une fonction de génération de jeton SAP.

  2. Créez un client d’appareil en utilisant IoTHubDeviceClient.create_from_sastoken.

  3. Gérez la durée de vie des jetons en tant qu’activité distincte, en fournissant un jeton renouvelé au client de l’appareil lorsque requis par la méthode IoTHubDeviceClient.update_sastoken.

Références :

Kit Azure IoT Hub device SDK pour Node.JS/JavaScript

Azure IoT pour Node.JS/JavaScript implémente un SharedAccessSignatureAuthenticationProvider qui sert un jeton SAP au client d’appareil et des transports pour s’authentifier auprès d’IoT Hub. Il n’implémente pas de fonctionnalité de renouvellement de jetons. L’application de l’appareil doit gérer la durée de vie du jeton, en le renouvelant au besoin.

Utilisez les méthodes du client d’appareil fromSharedAccessSignature et updateSharedAccessSignature pour établir une connexion avec IoT Hub et fournir un jeton renouvelé à SharedAccessSignatuteAuthenticationProvider, ce qui amènera le fournisseur d’authentification à émettre un événement newTokenAvailable pour les transports.

Un exemple de jeton SAP de base figure dans l’exemple simple_sample_device_with_sas.js.

Résumé du Kit Azure IoT Hub device SDK pour Node.JS/JavaScript :

  1. Implémentez la gestion et le renouvellement de la durée de vie des jetons SAP.

  2. Utilisez le client d’appareil fromSharedAccessSignature pour construire une instance de client d’appareil.

  3. Utilisez le client d’appareil updateSharedAccessSignature pour fournir un jeton renouvelé.

Références :

Étapes suivantes