Sdílet prostřednictvím


Human interaction in Durable Functions - Phone verification sample

Tato ukázka ukazuje, jak vytvořit orchestraci Durable Functions , která zahrnuje lidskou interakci. Kdykoli je skutečná osoba zapojená do automatizovaného procesu, musí být proces schopný odeslat oznámení osobě a přijímat odpovědi asynchronně. Musí také umožňovat, aby tato osoba byla nedostupná. (V poslední části jsou důležité časové limity.)

Tato ukázka implementuje systém ověření telefonu založený na SMS. Tyto typy toků se často používají při ověřování telefonního čísla zákazníka nebo vícefaktorového ověřování (MFA). Jedná se o výkonný příklad, protože celá implementace se provádí pomocí několika malých funkcí. Nevyžaduje se žádné externí úložiště dat, například databáze.

Poznámka:

Verze 4 programovacího modelu Node.js pro Azure Functions je obecně dostupná. Nový model v4 je navržený tak, aby měl flexibilnější a intuitivnější prostředí pro vývojáře v JavaScriptu a TypeScriptu. Další informace o rozdílech mezi v3 a v4 najdete v průvodci migrací.

V následujících fragmentech kódu JavaScript (PM4) označuje programovací model V4, nové prostředí.

Požadavky

Přehled scénáře

Ověření telefonu se používá k ověření, že koncoví uživatelé vaší aplikace nejsou spammery a že se jedná o uživatele, kteří říkají, že jsou. Vícefaktorové ověřování je běžným případem použití pro ochranu uživatelských účtů před hackery. Výzvou při implementaci vlastního ověření telefonu je, že vyžaduje stavovou interakci s člověkem. Koncový uživatel obvykle poskytuje nějaký kód (například 4místné číslo) a musí reagovat v přiměřeném časovém intervalu.

Běžné funkce Azure Functions jsou bezstavové (stejně jako mnoho dalších koncových bodů cloudu na jiných platformách), takže tyto typy interakcí zahrnují explicitní správu stavu externě v databázi nebo jiném trvalém úložišti. Kromě toho musí být interakce rozdělena do několika funkcí, které je možné koordinovat společně. Například potřebujete alespoň jednu funkci pro rozhodování o kódu, jeho uchování někde a odeslání na telefon uživatele. Kromě toho potřebujete alespoň jednu další funkci, která obdrží odpověď od uživatele, a nějakým způsobem ji namapovat zpět na původní volání funkce, aby bylo možné provést ověření kódu. Časový limit je také důležitým aspektem zabezpečení. Může to být poměrně složité.

Složitost tohoto scénáře se výrazně sníží, když používáte Durable Functions. Jak vidíte v této ukázce, může funkce orchestrátoru snadno spravovat stavovou interakci a bez zásahu do externích úložišť dat. Protože jsou funkce orchestrátoru odolné, jsou tyto interaktivní toky také vysoce spolehlivé.

Konfigurace integrace Twilio

Tato ukázka zahrnuje použití služby Twilio k odesílání sms zpráv na mobilní telefon. Azure Functions už podporuje Twilio prostřednictvím vazby Twilio a ukázka tuto funkci používá.

První věc, kterou potřebujete, je účet Twilio. Můžete vytvořit jednu zdarma na https://www.twilio.com/try-twilio. Jakmile budete mít účet, přidejte do aplikace funkcí následující tři nastavení aplikace.

Název nastavení aplikace Popis hodnoty
TwilioAccountSid Identifikátor SID pro váš účet Twilio
TwilioAuthToken Ověřovací token pro váš účet Twilio
TwilioPhoneNumber Telefonní číslo přidružené k vašemu účtu Twilio. Slouží k odesílání zpráv SMS.

Funkce

Tento článek vás provede následujícími funkcemi v ukázkové aplikaci:

  • E4_SmsPhoneVerification: Funkce orchestrátoru, která provádí proces ověření telefonu, včetně správy časových limitů a opakování.
  • E4_SendSmsChallenge: Funkce aktivity, která odesílá kód prostřednictvím textové zprávy.

Poznámka:

Funkce HttpStart v ukázkové aplikaci a rychlý start funguje jako klient Orchestraation, který aktivuje funkci orchestrátoru.

funkce orchestrátoru 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;
    }
}

Poznámka:

Nemusí to být zpočátku zřejmé, ale tento orchestrátor neporušuje deterministické omezení orchestrace. Je deterministický, protože CurrentUtcDateTime vlastnost se používá k výpočtu času vypršení platnosti časovače a vrací stejnou hodnotu při každém přehrání v tomto okamžiku v kódu orchestrátoru. Toto chování je důležité, aby se zajistilo, že stejné winner výsledky z každého opakovaného volání Task.WhenAny.

Po spuštění tato funkce orchestrátoru provede následující akce:

  1. Získá telefonní číslo, na které odešle SMS oznámení.
  2. Zavolá E4_SendSmsChallenge odeslat uživateli zprávu SMS a vrátí zpět očekávaný 4místný kód výzvy.
  3. Vytvoří trvalý časovač, který od aktuálního času aktivuje 90 sekund.
  4. Souběžně s časovačem čeká od uživatele na událost SmsChallengeResponse .

Uživatel obdrží zprávu SMS se čtyřmístným kódem. Mají 90 sekund k odeslání stejného čtyřmístného kódu zpět do instance funkce orchestrátoru k dokončení procesu ověření. Pokud odešle nesprávný kód, získá další tři pokusy o jeho správné získání (ve stejném 90sekundovém okně).

Upozorňující

Je důležité zrušit časovače , pokud už je nepotřebujete k vypršení platnosti, stejně jako v příkladu výše, když se přijme odpověď na výzvu.

funkce aktivity E4_SendSmsChallenge

Funkce E4_SendSmsChallenge používá vazbu Twilio k odeslání zprávy SMS se čtyřmístným kódem koncovému uživateli.

[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;
}

Poznámka:

Abyste mohli spustit vzorový kód, musíte nejprve nainstalovat Microsoft.Azure.WebJobs.Extensions.Twilio balíček NuGet pro službu Functions. Neinstalujte také hlavní balíček NuGet Twilio, protože to může způsobit problémy s verzí, které vedou k chybám sestavení.

Spuštění ukázky

Pomocí funkcí aktivovaných protokolem HTTP, které jsou součástí ukázky, můžete orchestraci spustit odesláním následujícího požadavku 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}"}

Funkce orchestrátoru obdrží zadané telefonní číslo a okamžitě mu pošle zprávu SMS s náhodně vygenerovaným 4místným ověřovacím kódem , například 2168. Funkce pak počká 90 sekund na odpověď.

Pokud chcete odpovědět s kódem, můžete použít RaiseEventAsync (.NET) nebo raiseEvent (JavaScript/TypeScript) uvnitř jiné funkce nebo vyvolat webhook sendEventPostUri HTTP POST odkazovaný ve výše uvedené odpovědi 202 a nahradit {eventName} názvem události, 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

Pokud ji odešlete před vypršením platnosti časovače, orchestrace se dokončí a output pole se nastaví na truehodnotu označující úspěšné ověření.

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"}

Pokud necháte časovač vypršet nebo zadáte nesprávný kód čtyřikrát, můžete se dotazovat na stav a zobrazit false výstup funkce orchestrace, který značí, že ověření telefonu selhalo.

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"}

Další kroky

Tato ukázka ukázala některé pokročilé funkce Durable Functions, zejména WaitForExternalEvent rozhraní CreateTimer API. Viděli jste, jak je možné je kombinovat s jazykem Task.WaitAny (C#)/context.df.Task.any (JavaScript/TypeScript)/context.task_any (Python) k implementaci spolehlivého systému časového limitu, který je často užitečný pro interakci se skutečnými lidmi. Další informace o tom, jak používat Durable Functions, najdete v řadě článků, které nabízejí podrobné pokrytí konkrétních témat.