Receber
Conferir a assinatura Ed25519 de uma entrega do hub e ler o evento tipado. Feature webhook_receber.
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>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>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.
| Variante | status() | Quando |
|---|---|---|
SemIdentificacaoDoAplicativo(String) | 503 | sem segredo do app: o hub reenvia depois |
SemAssinatura | 401 | faltam os cabeçalhos de assinatura |
ForaDaJanela | 401 | X-TermoWebhook-Data fora da tolerância de 5 minutos |
ChaveDesconhecida(String) | 401 | a chave não está entre as chaves públicas do hub |
ChavesIndisponiveis(String) | 503 | não deu para buscar as chaves do hub |
AssinaturaInvalida | 401 | a assinatura não confere para este app |
CorpoInvalido(String) | 400 | o corpo não é o envelope esperado |
TipoDiferente { tipo, versao } | 200 | entrega de outro tipo: responda 200 para o hub não reenviar |