Supporto dell'SDK client IoT di Azure per i server token di terze parti

Azure IoT
Hub IoT Azure

L'articolo Controllare l'accesso a hub IoT illustra come un servizio token di terze parti può essere integrato con hub IoT. Questo articolo descrive il supporto per l'autenticazione del token di firma di accesso condiviso in ognuno degli SDK client di Azure IoT. Descrive anche cosa deve essere implementato in un'applicazione del dispositivo usando l'SDK corrispondente per ogni lingua e come usare token con ambito dispositivo o con ambito modulo per i criteri di accesso condiviso di Device Connessione o Module Connessione.

Contesto e problema

La documentazione corrente sulla sicurezza di hub IoT di Azure illustra il modello token-server di terze parti per l'autenticazione sas con hub IoT dai dispositivi IoT usando gli SDK client IoT di Azure. Tuttavia, presupposti non corretti effettuati da un cliente durante un recente impegno aziendale suggeriscono che, senza ulteriori chiarimenti, è possibile sviluppare un'impressione fuorviante sul livello di supporto implementato per impostazione predefinita negli SDK del client IoT di Azure.

Questo articolo illustra l'apprendimento di tale impegno e illustra cosa deve essere fatto in ogni SDK per i dispositivi per ottenere l'autenticazione token-server di terze parti. Questo articolo dovrebbe anche impedire di fare presupposti non corretti simili sul supporto per il modello token-server di terze parti in Azure IoT CLIENT SDK.

Soluzione

Gli SDK client IoT di Azure offrono diversi livelli di supporto per l'autenticazione con token di firma di accesso condiviso, ognuno dei quali richiede codice personalizzato per completare la funzionalità di autenticazione e gestione dei token.

La frequenza di valutazione del token dipende dal protocollo di trasporto scelto, ovvero MQTT, AMQP o HTTPS. La variazione dipende dalla funzionalità del protocollo per supportare il rinnovo proattivo dei token e dei timeout della sessione. Solo AMQP implementa il supporto proattivo per il rinnovo. Ciò significa che gli altri trasporti chiuderanno la connessione in caso di errore di autenticazione del token di firma di accesso condiviso e quindi dovranno eseguire una nuova operazione di connessione. Si tratta di un'operazione di connettività potenzialmente costosa per il client.

Se l'autenticazione sas non riesce, viene generato un errore dall'implementazione del trasporto che può essere gestita all'interno dell'applicazione del dispositivo da un gestore eventi "Connessione ion Status Changed". Se non si implementa un gestore di questo tipo, l'applicazione del dispositivo viene in genere interrotta a causa dell'errore. Con l'implementazione corretta del gestore eventi e della funzionalità di rinnovo del token, i trasporti possono tentare nuovamente la connessione.

La figura seguente illustra il modello token-server di terze parti:

Illustration of the third-party token-server pattern

La figura seguente illustra il supporto dell'implementazione nell'SDK client IoT di Azure con l'integrazione di Mobile Net Operator:

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

Le implementazioni di esempio sono incluse nel repository Azure Samples in GitHub.

Considerazioni e problemi

Quando si decide se implementare questo modello, tenere presente quanto segue:

  • Gli SDK client del servizio Device Provisioning (Azure DPS) hub IoT di Azure non supportano l'autenticazione del token di firma di accesso condiviso. L'API REST del servizio Device Provisioning di Azure supporta l'autenticazione con token di firma di accesso condiviso. Pertanto, per usare il servizio Device Provisioning di Azure con un servizio token di terze parti per l'autenticazione sas, un'applicazione del dispositivo deve implementare il processo dps del dispositivo usando l'API REST del servizio Device Provisioning di Azure.

  • Ciò consiste nell'eseguire un'operazione di richiesta di registrazione iniziale e quindi eseguire il polling dell'API di stato operativo fino a quando il processo DPS non riesce o non riesce. In caso di esito positivo, i dettagli del provisioning dei dispositivi possono essere ottenuti richiedendoli dalla registrazione del runtime dell'API REST del servizio Device Provisioning di Azure.

Riferimenti:

Quando usare questo modello

È consigliabile usare questo modello ogni volta che si vuole eseguire l'autenticazione per hub IoT di Azure dai dispositivi IoT usando i vari SDK del client IoT di Azure. Anziché usare gli SDK client per l'autenticazione con token di firma di accesso condiviso, usare l'API REST del servizio Device Provisioning di Azure per garantire l'implementazione del supporto proattivo per il rinnovo per tutti i meccanismi di trasporto.

Esempi

Le sezioni seguenti offrono esempi che è possibile usare per linguaggi di programmazione diversi, ad esempio Embedded C, .NET, Java e Python.

SDK per dispositivi hub IoT di Azure per C e hub IoT di Azure per dispositivi per Embedded C

L'approccio seguente può essere usato nelle applicazioni per dispositivi compilate usando Azure IoT C SDK o Azure IoT Embedded C SDK. Nessuno degli SDK fornisce la gestione della durata dei token di firma di accesso condiviso, pertanto è necessario implementare una funzionalità di gestione della durata dei token di firma di accesso condiviso.

I token di firma di accesso condiviso possono essere usati tramite la struttura IOTHUB_CLIENT_CONFIG impostando il membro deviceSasToken sul token e impostando deviceKeynull. Anche altri valori inutilizzati, ad esempio protocolGatewayHostName, devono essere impostati su 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);

Il connection_status_callback può intercettare il IOTHUB_CLIENT_CONNECTION_STATUS_REASON di IOTHUB_CLIENT_CONNECTION_EXPIRED_SAS_TOKEN per attivare un rinnovo del token di firma di accesso condiviso tramite il servizio token di terze parti. Questa operazione è necessaria per tutti i trasporti per acquisire i problemi di connessione, ma è richiesta specificamente dai trasporti che non supportano il rinnovo proattivo del token di firma di accesso condiviso. La gestione proattiva della durata dei token di firma di accesso condiviso può essere implementata come funzione eseguita ripetutamente durante il ciclo "operativo" delle applicazioni del dispositivo. Garantire la durata del token viene valutata di frequente e il rinnovo del token può essere eseguito in modo proattivo quando necessario.

Riepilogo dell'implementazione dell'autenticazione con token di firma di accesso condiviso per gli SDK C:

  1. Implementare un gestore Connessione ionStatusCallback per acquisire IOTHUB_CLIENT_CONNECTION_EXPIRED_SAS_TOKEN evento e attivare il rinnovo del token.

  2. Usare un IOTHUB_CLIENT_CONFIG per fornire il token di firma di accesso condiviso del dispositivo per IoTHubDeviceClient_Create.

  3. Implementare la gestione proattiva della durata dei token di firma di accesso condiviso come parte del ciclo operativo dell'applicazione del dispositivo.

hub IoT di Azure SDK per dispositivi per .NET

Azure IoT Client SDK per .NET implementa il supporto per la gestione della durata dei token di firma di accesso condiviso tramite la classe astratta DeviceAuthenticationWithTokenRefresh. Un'implementazione concreta di questa classe, aggiungendo funzionalità di rinnovo dei token, può essere fornita come metodo di autenticazione a un metodo DeviceClient.Create. Le implementazioni del trasporto rinnovano automaticamente il token tramite il metodo di autenticazione in base alle esigenze. È necessario un Connessione ionStatusChangesHandler per acquisire le modifiche di connessione e impedire la generazione di eccezioni dai trasporti.

Implementazione di esempio basata sulla 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;
    }
}

Riepilogo dell'implementazione dell'autenticazione con token di firma di accesso condiviso per hub IoT di Azure SDK per dispositivi per .NET:

  1. Implementare una classe concreta basata sulla classe astratta DeviceAuthenticationWithTokenRefresh, che implementa la funzionalità di rinnovo dei token.

  2. Implementare un Connessione ionStatusChangesHandler per acquisire lo stato della connessione di trasporto ed evitare eccezioni generate dall'implementazione del trasporto.

Riferimenti:

hub IoT di Azure SDK per dispositivi per Java

Azure IoT Client SDK per Java implementa il supporto per la gestione della durata dei token di firma di accesso condiviso tramite l'interfaccia SasTokenProvider. Una classe che implementa questa interfaccia con la funzionalità di rinnovo del token di firma di accesso condiviso può essere usata come SecurityProvider in un costruttore DeviceClient. Le implementazioni del trasporto rinnovano automaticamente il token tramite il provider di sicurezza in base alle esigenze. È necessario registrare un Connessione ionStatusChangeCallback per acquisire le modifiche di connessione e impedire che vengano generate eccezioni dai trasporti.

Implementazione di esempio del provider di sicurezza che implementa l'interfaccia 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();
    }
}

Riepilogo dell'implementazione dell'autenticazione con token di firma di accesso condiviso per hub IoT di Azure SDK per dispositivi per Java:

  1. Implementare l'interfaccia SasTokenProvider in una classe e includere la funzionalità di rinnovo del token.

  2. Implementare un gestore Connessione ionStatusChangeCallback per acquisire le modifiche dello stato della connessione di trasporto ed evitare eccezioni generate dall'implementazione del trasporto.

Riferimenti:

hub IoT di Azure SDK per dispositivi per Python

L'SDK del dispositivo hub IoT di Azure per Python implementa il supporto dei token di firma di accesso condiviso tramite metodi nell'oggetto IoTHubDeviceClient. Questi metodi consentono la creazione di un client del dispositivo usando un token e la possibilità di fornire un token aggiornato dopo la creazione del client del dispositivo. Non implementano la gestione della durata dei token, ma questa operazione può essere implementata facilmente come operazione asincrona.

Implementazione di esempio di Python 3.7 che mostra solo la struttura delle funzionalità:

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())

Riepilogo dell'SDK del dispositivo hub IoT di Azure per l'autenticazione del token di firma di accesso condiviso Python:

  1. Creare una funzione di generazione di token di firma di accesso condiviso.

  2. Creare un client del dispositivo usando IoTHubDeviceClient.create_from_sastoken.

  3. Gestire la durata dei token come attività separata, fornendo al client del dispositivo un token rinnovato quando richiesto dal metodo IoTHubDeviceClient.update_sastoken.

Riferimenti:

hub IoT di Azure SDK per dispositivi per Node.JS/JavaScript

Azure IoT per Node.JS/JavaScript implementa un oggetto SharedAccessSignatureAuthenticationProvider che servirà un token di firma di accesso condiviso al client del dispositivo e trasporta per l'autenticazione con hub IoT. Non implementa alcuna funzionalità di rinnovo del token. L'applicazione del dispositivo deve gestire la durata del token, rinnovando il token in base alle esigenze.

Usare i metodi client del dispositivo daSharedAccessSignature e updateSharedAccessSignature per avviare una connessione con hub IoT e fornire un token rinnovato a SharedAccessSignatuteAuthenticationProvider, che causerà la generazione di un evento newTokenAvailable ai trasporti.

Nell'esempio di simple_sample_device_with_sas.js viene fornito un esempio di token di firma di accesso condiviso di base.

Riepilogo di hub IoT di Azure SDK per dispositivi per Node.JS/JavaScript:

  1. Implementare la gestione e il rinnovo del token di firma di accesso condiviso.

  2. Usare il client del dispositivo daSharedAccessSignature per costruire un'istanza client del dispositivo.

  3. Usare l'aggiornamento del client del dispositivoSharedAccessSignature per fornire un token rinnovato.

Riferimenti:

Passaggi successivi