Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Este exemplo demonstra como criar uma orquestração de Funções Duráveis que envolve interação humana. Sempre que uma pessoa real está envolvida em um processo automatizado, o processo deve ser capaz de enviar notificações para a pessoa e de receber respostas de forma assíncrona. Ele também deve contar com a possibilidade de a pessoa não estar disponível. (É nesta última parte que os tempos limite tornam-se importantes.)
Este exemplo implementa um sistema de verificação por telefone baseado em SMS. Esses tipos de fluxo costumam ser usados para verificar o número de telefone de um cliente ou para MFA (autenticação multifator). Este é um exemplo poderoso, porque toda a implementação é feita usando algumas funções pequenas. Não é necessário um armazenamento de dados externo, como um banco de dados.
Observação
A versão 4 do modelo de programação Node.js para o Azure Functions está em disponibilidade geral. O novo modelo v4 é projetado para oferecer uma experiência mais flexível e intuitiva para desenvolvedores de JavaScript e TypeScript. Saiba mais sobre as diferenças entre v3 e v4 na guia de migração.
Nos trechos de código a seguir, o JavaScript (PM4) denota o modelo de programação V4, a nova experiência.
Pré-requisitos
Visão geral do cenário
A verificação por telefone é usada para confirmar a identidade dos usuários finais de seu aplicativo e que eles não são remetentes de spam. A autenticação multifator é um caso de uso comum para proteger contas de usuário contra hackers. O desafio ao implementar sua própria verificação por telefone é que ela requer uma interação com estado com uma pessoa. Normalmente, um usuário final recebe um código (por exemplo, um número de 4 dígitos) e deve responder dentro de um período razoável.
O Azure Functions comum é sem estado (assim como muitos outros pontos de extremidade de nuvem em outras plataformas), portanto, esses tipos de interações envolvem explicitamente o gerenciamento de estado externamente em um banco de dados ou algum outro armazenamento persistente. Além disso, a interação deve ser dividida em várias funções que possam ser coordenadas juntas. Por exemplo, você precisa de pelo menos uma função para decidir sobre um código, persisti-lo em algum lugar e enviá-lo para o telefone do usuário. Além disso, você precisa de pelo menos uma outra função para receber uma resposta do usuário e, de alguma forma, mapeá-la de volta para a chamada de função original para fazer a validação do código. O tempo limite também é um aspecto importante para garantir a segurança. Mas pode ficar bastante complexo rapidamente.
A complexidade desse cenário é bastante reduzida quando você usa as Funções Duráveis. Como você verá neste exemplo, uma função de orquestrador pode gerenciar a interação com estado facilmente e sem envolver nenhum armazenamento de dados externo. Como as funções de orquestrador são duráveis, esses fluxos interativos também são altamente confiáveis.
Configurando a integração com o Twilio
Este exemplo envolve o uso do serviço Twilio para enviar mensagens SMS a um telefone celular. O Azure Functions já tem suporte para Twilio por meio da Associação com o Twilio e o exemplo usa esse recurso.
A primeira coisa de que você precisa é uma conta do Twilio. É possível criar uma gratuitamente em https://www.twilio.com/try-twilio. Quando tiver uma conta, adicione as três seguintes configurações de aplicativo ao seu aplicativo de função.
Nome da configuração do aplicativo | Descrição do valor |
---|---|
TwilioAccountSid | A SID de sua conta do Twilio |
TwilioAuthToken | O token de autenticação de sua conta do Twilio |
TwilioPhoneNumber | O número de telefone associado à sua conta do Twilio. Ele é usado para enviar mensagens SMS. |
As funções
Este artigo aborda as seguintes funções no aplicativo de exemplo:
E4_SmsPhoneVerification
: uma função de orquestrador que executa o processo de verificação de telefone, incluindo o gerenciamento de tempos e novas tentativa.E4_SendSmsChallenge
: uma função de atividade que envia um código por meio de mensagem de texto.
Observação
A HttpStart
função no aplicativo de exemplo e o início rápido atuam como cliente de orquestração que dispara a função de orquestrador.
E4_SmsPhoneVerification função de orquestrador
[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;
}
}
Observação
Pode não ser óbvio no início, mas esse orquestrador não viola a restrição de orquestração determinística. É determinística porque a CurrentUtcDateTime
propriedade é usada para calcular o tempo de expiração do temporizador e essa propriedade retorna o mesmo valor em cada reprodução nesse ponto no código do orquestrador. Este comportamento é importante para garantir que o mesmo winner
resulte de todas as chamadas repetidas para Task.WhenAny
.
Uma vez iniciada, essa função de orquestrador faz o seguinte:
- Obtém um número de telefone para o qual enviará a notificação de SMS.
- Chama E4_SendSmsChallenge para enviar uma mensagem SMS para o usuário e retorna o código de desafio de 4 dígitos esperado.
- Cria um temporizador durável que dispara 90 segundos a partir da hora atual.
- Em paralelo com o temporizador, aguarda um evento SmsChallengeResponse do usuário.
O usuário recebe uma mensagem SMS com um código de 4 dígitos. Eles têm 90 segundos para enviar o mesmo código de 4 dígitos para a instância da função de orquestrador para concluir o processo de verificação. Se enviar o código errado, ele terá mais três tentativas para acertar (na mesma janela de 90 segundos).
Aviso
É importante cancelar os temporizadores se você não precisar mais que eles expirem, como no exemplo acima quando uma resposta ao desafio é aceita.
E4_SendSmsChallenge função de atividade
A função E4_SendSmsChallenge usa a associação ao Twilio para enviar a mensagem SMS com o código de 4 dígitos para o usuário final.
[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;
}
Observação
Primeiro, você deve instalar o pacote Nuget Microsoft.Azure.WebJobs.Extensions.Twilio
para o Functions para executar o código de exemplo. Não instale também o pacote nuget Twilio principal, já que ele pode causar problemas de controle de versão que resultam em erros de build.
Execute o exemplo
Usando as funções disparadas por HTTP incluídas no exemplo, você pode iniciar a orquestração enviando a seguinte solicitação 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}"}
A função de orquestrador recebe o número de telefone fornecido e envia imediatamente uma mensagem SMS para ele com um código de verificação de 4 dígitos gerado aleatoriamente, por exemplo, 2168. Em seguida, a função espera durante 90 segundos por uma resposta.
Para responder com o código, você pode usar RaiseEventAsync
(.NET) ou raiseEvent
(JavaScript/TypeScript) dentro de outra função ou invocar o webhook de HTTP POST sendEventUrl, referenciado na resposta 202 acima, substituindo {eventName}
pelo nome do evento, 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
Se você enviá-lo antes que o temporizador expire, a orquestração será concluída e o campo output
será definido como true
, indicando uma verificação bem-sucedida.
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"}
Se deixar que o temporizador expire ou se inserir o código errado quatro vezes, você poderá consultar o status e ver uma saída de função de orquestração false
, indicando que houve falha na verificação por telefone.
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"}
Próximas etapas
Este exemplo demonstrou alguns dos recursos avançados de Durable Functions, especialmente as APIs WaitForExternalEvent
e CreateTimer
. Você viu como elas podem ser combinadas com Task.WaitAny
(C#)/context.df.Task.any
(JavaScript/TypeScript)/context.task_any
para implementar um sistema de tempo limite confiável, o que geralmente é útil para interagir com pessoas reais. Você pode aprender mais sobre como usar Funções Duráveis lendo uma série de artigos que oferecem uma cobertura aprofundada de tópicos específicos.