Udostępnij za pośrednictwem


Interakcja człowieka w rozszerzeniu Durable Functions — przykład weryfikacji telefonu

W tym przykładzie pokazano, jak utworzyć aranżację rozszerzenia Durable Functions, która obejmuje interakcję człowieka. Za każdym razem, gdy prawdziwa osoba jest zaangażowana w zautomatyzowany proces, proces musi mieć możliwość wysyłania powiadomień do osoby i odbierania odpowiedzi asynchronicznie. Musi również umożliwić, że dana osoba jest niedostępna. (Ta ostatnia część polega na tym, że przekroczenia limitu czasu stają się ważne).

Ten przykład implementuje system weryfikacji telefonu oparty na wiadomościACH SMS. Te typy przepływów są często używane podczas weryfikowania numeru telefonu klienta lub uwierzytelniania wieloskładnikowego (MFA). Jest to zaawansowany przykład, ponieważ cała implementacja jest wykonywana przy użyciu kilku małych funkcji. Nie jest wymagany zewnętrzny magazyn danych, taki jak baza danych.

Uwaga

Wersja 4 modelu programowania Node.js dla usługi Azure Functions jest ogólnie dostępna. Nowy model w wersji 4 został zaprojektowany z myślą o bardziej elastycznym i intuicyjnym środowisku dla deweloperów języków JavaScript i TypeScript. Dowiedz się więcej o różnicach między wersjami 3 i v4 w przewodniku migracji.

W poniższych fragmentach kodu javaScript (PM4) oznacza model programowania W wersji 4, nowe środowisko.

Wymagania wstępne

Omówienie scenariusza

Weryfikacja telefonu służy do sprawdzania, czy użytkownicy końcowi aplikacji nie są rozsyłani i czy są osobami, które mówią, że są. Uwierzytelnianie wieloskładnikowe to typowy przypadek użycia ochrony kont użytkowników przed hakerami. Wyzwaniem implementacji własnej weryfikacji telefonu jest to, że wymaga stanowej interakcji z człowiekiem. Użytkownik końcowy zazwyczaj udostępnia kod (na przykład 4-cyfrowy) i musi odpowiedzieć w rozsądnym czasie.

Zwykłe funkcje platformy Azure są bezstanowe (podobnie jak wiele innych punktów końcowych chmury na innych platformach), dlatego tego typu interakcje obejmują jawne zarządzanie stanem zewnętrznym w bazie danych lub w innym trwałym magazynie. Ponadto interakcja musi być podzielona na wiele funkcji, które można koordynować razem. Na przykład potrzebujesz co najmniej jednej funkcji do podejmowania decyzji o kodzie, utrwalaniu go gdzieś i wysyłaniu go na telefon użytkownika. Ponadto potrzebujesz co najmniej jednej innej funkcji, aby otrzymać odpowiedź od użytkownika i w jakiś sposób zamapować ją z powrotem na oryginalne wywołanie funkcji, aby przeprowadzić walidację kodu. Przekroczenie limitu czasu jest również ważnym aspektem zapewniającym bezpieczeństwo. Może to być dość złożone szybko.

Złożoność tego scenariusza jest znacznie ograniczona w przypadku korzystania z rozszerzenia Durable Functions. Jak widać w tym przykładzie, funkcja orkiestratora może łatwo zarządzać interakcją stanową i bez angażowania zewnętrznych magazynów danych. Ponieważ funkcje orkiestratora są trwałe, te interaktywne przepływy są również wysoce niezawodne.

Konfigurowanie integracji z usługą Twilio

Ten przykład obejmuje użycie usługi Twilio do wysyłania wiadomości SMS na telefon komórkowy. Usługa Azure Functions obsługuje już usługę Twilio za pośrednictwem powiązania usługi Twilio, a przykład używa tej funkcji.

Pierwszą rzeczą, której potrzebujesz, jest konto usługi Twilio. Możesz utworzyć jedną bezpłatnie na stronie https://www.twilio.com/try-twilio. Po utworzeniu konta dodaj następujące trzy ustawienia aplikacji do aplikacji funkcji.

Nazwa ustawienia aplikacji Opis wartości
TwilioAccountSid Identyfikator SID konta usługi Twilio
TwilioAuthToken Token uwierzytelniania dla konta usługi Twilio
TwilioPhoneNumber Numer telefonu skojarzony z kontem usługi Twilio. Służy do wysyłania wiadomości SMS.

Funkcje

W tym artykule przedstawiono następujące funkcje w przykładowej aplikacji:

  • E4_SmsPhoneVerification: funkcja orkiestratora, która wykonuje proces weryfikacji telefonu, w tym zarządzanie limitami czasu i ponawiania prób.
  • E4_SendSmsChallenge: funkcja działania, która wysyła kod za pośrednictwem wiadomości SMS.

Uwaga

Funkcja HttpStart w przykładowej aplikacji i przewodniku Szybki start działa jako klient orchestration, który wyzwala funkcję orkiestratora.

funkcja orkiestratora E4_SmsPhoneVerification

[FunctionName("E4_SmsPhoneVerification")]
public static async Task<bool> Run(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    string phoneNumber = context.GetInput<string>();
    if (string.IsNullOrEmpty(phoneNumber))
    {
        throw new ArgumentNullException(
            nameof(phoneNumber),
            "A phone number input is required.");
    }

    int challengeCode = await context.CallActivityAsync<int>(
        "E4_SendSmsChallenge",
        phoneNumber);

    using (var timeoutCts = new CancellationTokenSource())
    {
        // The user has 90 seconds to respond with the code they received in the SMS message.
        DateTime expiration = context.CurrentUtcDateTime.AddSeconds(90);
        Task timeoutTask = context.CreateTimer(expiration, timeoutCts.Token);

        bool authorized = false;
        for (int retryCount = 0; retryCount <= 3; retryCount++)
        {
            Task<int> challengeResponseTask =
                context.WaitForExternalEvent<int>("SmsChallengeResponse");

            Task winner = await Task.WhenAny(challengeResponseTask, timeoutTask);
            if (winner == challengeResponseTask)
            {
                // We got back a response! Compare it to the challenge code.
                if (challengeResponseTask.Result == challengeCode)
                {
                    authorized = true;
                    break;
                }
            }
            else
            {
                // Timeout expired
                break;
            }
        }

        if (!timeoutTask.IsCompleted)
        {
            // All pending timers must be complete or canceled before the function exits.
            timeoutCts.Cancel();
        }

        return authorized;
    }
}

Uwaga

Początkowo nie może być oczywiste, ale ten orkiestrator nie narusza ograniczenia orkiestracji deterministycznej. Jest deterministyczny, ponieważ CurrentUtcDateTime właściwość jest używana do obliczania czasu wygaśnięcia czasomierza i zwraca tę samą wartość dla każdej powtórki w tym momencie w kodzie orkiestratora. To zachowanie jest ważne, aby upewnić się, że te same winner wyniki z każdego powtórzonego wywołania metody .Task.WhenAny

Po uruchomieniu ta funkcja orkiestratora wykonuje następujące czynności:

  1. Pobiera numer telefonu, do którego będzie wysyłać powiadomienie SMS.
  2. Wywołuje E4_SendSmsChallenge wysłać wiadomość SMS do użytkownika i zwraca oczekiwany 4-cyfrowy kod wyzwania.
  3. Tworzy trwały czasomierz, który wyzwala 90 sekund od bieżącego czasu.
  4. Równolegle z czasomierzem czeka na zdarzenie SmsChallengeResponse od użytkownika.

Użytkownik otrzymuje wiadomość SMS z czterocyfrowym kodem. Mają 90 sekund, aby wysłać ten sam czterocyfrowy kod z powrotem do wystąpienia funkcji orkiestratora w celu ukończenia procesu weryfikacji. Jeśli przesyłają niewłaściwy kod, otrzymają dodatkowe trzy próby uzyskania go w prawo (w tym samym 90-sekundowym oknie).

Ostrzeżenie

Ważne jest, aby anulować czasomierze , jeśli nie będą już potrzebne do wygaśnięcia, tak jak w powyższym przykładzie, gdy odpowiedź na żądanie zostanie zaakceptowana.

funkcja działania E4_SendSmsChallenge

Funkcja E4_SendSmsChallenge używa powiązania usługi Twilio do wysyłania wiadomości SMS z czterocyfrowym kodem do użytkownika końcowego.

[FunctionName("E4_SendSmsChallenge")]
public static int SendSmsChallenge(
    [ActivityTrigger] string phoneNumber,
    ILogger log,
    [TwilioSms(AccountSidSetting = "TwilioAccountSid", AuthTokenSetting = "TwilioAuthToken", From = "%TwilioPhoneNumber%")]
        out CreateMessageOptions message)
{
    // Get a random number generator with a random seed (not time-based)
    var rand = new Random(Guid.NewGuid().GetHashCode());
    int challengeCode = rand.Next(10000);

    log.LogInformation($"Sending verification code {challengeCode} to {phoneNumber}.");

    message = new CreateMessageOptions(new PhoneNumber(phoneNumber));
    message.Body = $"Your verification code is {challengeCode:0000}";

    return challengeCode;
}

Uwaga

Aby uruchomić przykładowy kod, należy najpierw zainstalować Microsoft.Azure.WebJobs.Extensions.Twilio pakiet Nuget dla usługi Functions. Nie instaluj również głównego pakietu nuget usługi Twilio, ponieważ może to powodować problemy z wersją, które powodują błędy kompilacji.

Uruchamianie aplikacji przykładowej

Korzystając z funkcji wyzwalanych przez protokół HTTP zawartych w przykładzie, możesz uruchomić aranżację, wysyłając następujące żądanie HTTP POST:

POST http://{host}/orchestrators/E4_SmsPhoneVerification
Content-Length: 14
Content-Type: application/json

"+1425XXXXXXX"
HTTP/1.1 202 Accepted
Content-Length: 695
Content-Type: application/json; charset=utf-8
Location: http://{host}/runtime/webhooks/durabletask/instances/741c65651d4c40cea29acdd5bb47baf1?taskHub=DurableFunctionsHub&connection=Storage&code={systemKey}

{"id":"741c65651d4c40cea29acdd5bb47baf1","statusQueryGetUri":"http://{host}/runtime/webhooks/durabletask/instances/741c65651d4c40cea29acdd5bb47baf1?taskHub=DurableFunctionsHub&connection=Storage&code={systemKey}","sendEventPostUri":"http://{host}/runtime/webhooks/durabletask/instances/741c65651d4c40cea29acdd5bb47baf1/raiseEvent/{eventName}?taskHub=DurableFunctionsHub&connection=Storage&code={systemKey}","terminatePostUri":"http://{host}/runtime/webhooks/durabletask/instances/741c65651d4c40cea29acdd5bb47baf1/terminate?reason={text}&taskHub=DurableFunctionsHub&connection=Storage&code={systemKey}"}

Funkcja orkiestratora odbiera podany numer telefonu i natychmiast wysyła do niego wiadomość SMS z losowo wygenerowanym 4-cyfrowym kodem weryfikacyjnym — na przykład 2168. Następnie funkcja czeka 90 sekund na odpowiedź.

Aby odpowiedzieć za pomocą kodu, możesz użyć elementu RaiseEventAsync (.NET) lub raiseEvent (JavaScript/TypeScript) wewnątrz innej funkcji lub wywołać element webhook sendEventPostUri HTTP POST, do którego odwołuje się powyższy element webhook 202, zastępując {eventName} ciąg nazwą zdarzenia: SmsChallengeResponse

POST http://{host}/runtime/webhooks/durabletask/instances/741c65651d4c40cea29acdd5bb47baf1/raiseEvent/SmsChallengeResponse?taskHub=DurableFunctionsHub&connection=Storage&code={systemKey}
Content-Length: 4
Content-Type: application/json

2168

Jeśli wyślesz to przed wygaśnięciem czasomierza, aranżacja zostanie ukończona, a output pole zostanie ustawione na truewartość , co oznacza pomyślną weryfikację.

GET http://{host}/runtime/webhooks/durabletask/instances/741c65651d4c40cea29acdd5bb47baf1?taskHub=DurableFunctionsHub&connection=Storage&code={systemKey}
HTTP/1.1 200 OK
Content-Length: 144
Content-Type: application/json; charset=utf-8

{"runtimeStatus":"Completed","input":"+1425XXXXXXX","output":true,"createdTime":"2017-06-29T19:10:49Z","lastUpdatedTime":"2017-06-29T19:12:23Z"}

Jeśli czasomierz wygaśnie lub wprowadzisz nieprawidłowy kod cztery razy, możesz wykonać zapytanie o stan i wyświetlić false dane wyjściowe funkcji aranżacji wskazujące, że weryfikacja telefonu nie powiodła się.

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 145

{"runtimeStatus":"Completed","input":"+1425XXXXXXX","output":false,"createdTime":"2017-06-29T19:20:49Z","lastUpdatedTime":"2017-06-29T19:22:23Z"}

Następne kroki

W tym przykładzie przedstawiono niektóre zaawansowane funkcje rozszerzenia Durable Functions, zwłaszcza WaitForExternalEvent i CreateTimer interfejsy API. Wiesz już, jak można je połączyć z Task.WaitAny (C#)/context.df.Task.any (JavaScript/TypeScript)/context.task_any (Python) w celu zaimplementowania niezawodnego systemu limitu czasu, który jest często przydatny do interakcji z prawdziwymi ludźmi. Aby dowiedzieć się więcej na temat używania rozszerzenia Durable Functions, przeczytaj serię artykułów, które oferują szczegółowe omówienie konkretnych tematów.