Receber

Conferir a assinatura Ed25519 de uma entrega do hub e ler o evento tipado. Feature webhook_receber.

Cargo.toml
termo_core = { version = "=0.1.5", features = ["webhook_receber"] }

Cada entrega do hub chega com os cabeçalhos X-TermoWebhook-Data, X-TermoWebhook-Chave e X-TermoWebhook-Assinatura. A assinatura cobre a data, o destino (o id do app no TermoAuth) e o corpo, e é conferida com a chave pública do hub (GET /.well-known/jwks.json, em cache, buscada de novo no máximo a cada 30 s quando aparece uma chave nova).

receber_evento_conferido_do_hub

O caminho mais curto quando a rota recebe um tipo só.

pub async fn receber_evento_conferido_do_hub<T: Evento>(
    cabecalhos: &HeaderMap,
    corpo: &[u8],
) -> Result<EventoRecebido<T>, ErroDeRecebimento>
Exemplo com axum
use axum::{body::Bytes, http::{HeaderMap, StatusCode}};
use termo_core::webhook::eventos::termo_auth::ColaboradorCriadoV2;

async fn receber_colaborador_criado(cabecalhos: HeaderMap, corpo: Bytes) -> StatusCode {
    match termo_core::webhook::receber_evento_conferido_do_hub::<ColaboradorCriadoV2>(&cabecalhos, &corpo).await {
        Ok(evento) => {
            // evento.dados é ColaboradorCriadoV2
            StatusCode::OK
        }
        Err(erro) => erro.status(),
    }
}

conferir_entrega_recebida_do_hub

Quando a mesma rota recebe vários tipos: confere a assinatura e devolve a Entrega, para você escolher o tipo.

pub async fn conferir_entrega_recebida_do_hub<'a>(
    cabecalhos: &HeaderMap,
    corpo: &'a [u8],
) -> Result<Entrega<'a>, ErroDeRecebimento>
Exemplo
use termo_core::webhook::eventos::termo_auth::{ColaboradorAtualizadoV2, ColaboradorCriadoV2};

let entrega = termo_core::webhook::conferir_entrega_recebida_do_hub(&cabecalhos, &corpo).await?;
if entrega.e::<ColaboradorCriadoV2>() {
    let evento = entrega.ler::<ColaboradorCriadoV2>()?;
} else if entrega.e::<ColaboradorAtualizadoV2>() {
    let evento = entrega.ler::<ColaboradorAtualizadoV2>()?;
}

conferir_entrega_recebida_do_hub_com_configuracao

O mesmo, com o endereço do hub e o id do app informados (ex.: testes).

pub async fn conferir_entrega_recebida_do_hub_com_configuracao<'a>(
    configuracao: &ConfiguracaoDeRecebimento,
    cabecalhos: &HeaderMap,
    corpo: &'a [u8],
) -> Result<Entrega<'a>, ErroDeRecebimento>

pub struct ConfiguracaoDeRecebimento {
    pub url: String,
    pub aplicativo_id: String,
}

impl ConfiguracaoDeRecebimento {
    pub fn nova(url: impl Into<String>, aplicativo_id: impl Into<String>) -> Self;
    /// Hub do ambiente atual e id do app pelo token de aplicativo.
    pub async fn carregar_do_ambiente_atual() -> Result<Self, ErroDeRecebimento>;
    pub fn endereco_das_chaves(&self) -> String; // "{url}/.well-known/jwks.json"
}

Entrega e EventoRecebido

pub struct Entrega<'a> {
    pub id: Uuid,
    pub tipo: String,
    pub versao: i32,
    // corpo: &'a [u8]
}

impl<'a> Entrega<'a> {
    pub fn e<T: Evento>(&self) -> bool;                                      // tipo e versão batem
    pub fn ler<T: Evento>(&self) -> Result<EventoRecebido<T>, ErroDeRecebimento>;
    pub fn corpo(&self) -> &'a [u8];
}

pub struct EventoRecebido<T> {
    pub id: Uuid,
    pub tipo: String,
    pub versao: i32,
    pub entidade_id: String,
    pub ocorrido_em: DateTime<Utc>,
    pub recebido_em: DateTime<Utc>,
    pub publicador: String,
    pub dados: T,
}

ErroDeRecebimento

status() já devolve o código HTTP certo para responder ao hub.

Variantestatus()Quando
SemIdentificacaoDoAplicativo(String)503sem segredo do app: o hub reenvia depois
SemAssinatura401faltam os cabeçalhos de assinatura
ForaDaJanela401X-TermoWebhook-Data fora da tolerância de 5 minutos
ChaveDesconhecida(String)401a chave não está entre as chaves públicas do hub
ChavesIndisponiveis(String)503não deu para buscar as chaves do hub
AssinaturaInvalida401a assinatura não confere para este app
CorpoInvalido(String)400o corpo não é o envelope esperado
TipoDiferente { tipo, versao }200entrega de outro tipo: responda 200 para o hub não reenviar

Nesta página