Use of Voluntary Application Server Identification (VAPID) in JMAP Web Push
Some checks failed
CI / Build / x86_64-unknown-linux-gnu (push) Has been cancelled
CI / Build / x86_64-unknown-linux-musl (push) Has been cancelled
CI / Build / arm-unknown-linux-gnueabihf (push) Has been cancelled
CI / Build / arm-unknown-linux-musleabihf (push) Has been cancelled
CI / Build / armv7-unknown-linux-gnueabihf (push) Has been cancelled
CI / Build / armv7-unknown-linux-musleabihf (push) Has been cancelled
CI / Build / aarch64-unknown-linux-gnu (push) Has been cancelled
CI / Build / aarch64-unknown-linux-musl (push) Has been cancelled
CI / Build / x86_64-pc-windows-msvc (push) Has been cancelled
CI / Build / aarch64-apple-darwin (push) Has been cancelled
CI / Build / x86_64-apple-darwin (push) Has been cancelled
CI / Build / x86_64-unknown-freebsd (push) Has been cancelled
CI / Merge image / gnu (push) Has been cancelled
CI / Merge image / musl (push) Has been cancelled
CI / Release (push) Has been cancelled
CI / Publish release (push) Has been cancelled
CI / Cleanup failed release (push) Has been cancelled

This commit is contained in:
Maurus Decimus
2026-07-20 13:55:00 +02:00
parent 2553d8ee4c
commit 425d034ae7
27 changed files with 532 additions and 45 deletions

View File

@@ -7,21 +7,31 @@
use super::{Event, ece::ece_encrypt};
use crate::state_manager::PushRegistration;
use calcard::jscalendar::JSCalendarDateTime;
use common::ipc::PushNotification;
use common::{ipc::PushNotification, network::webpush::Vapid};
use email::push::PushSubscription;
use jmap_proto::{
response::status::{EmailPushObject, PushObject},
types::state::State,
};
use reqwest::header::{CONTENT_ENCODING, CONTENT_TYPE};
use std::time::{Duration, Instant};
use reqwest::header::{AUTHORIZATION, CONTENT_ENCODING, CONTENT_TYPE};
use std::{
sync::Arc,
time::{Duration, Instant},
};
use store::write::now;
use tokio::sync::mpsc;
use trc::PushSubscriptionEvent;
use types::{id::Id, type_state::DataType};
use utils::map::vec_map::VecMap;
impl PushRegistration {
pub fn send(&mut self, id: Id, push_tx: mpsc::Sender<Event>, push_timeout: Duration) {
pub fn send(
&mut self,
id: Id,
push_tx: mpsc::Sender<Event>,
push_timeout: Duration,
vapid: Option<Arc<Vapid>>,
) {
let server = self.server.clone();
let notifications = std::mem::take(&mut self.notifications);
@@ -81,6 +91,7 @@ impl PushRegistration {
&server,
serde_json::to_string(&response).unwrap().into_bytes(),
push_timeout,
vapid.as_deref(),
)
.await
{
@@ -99,6 +110,7 @@ pub(crate) async fn http_request(
details: &PushSubscription,
mut body: Vec<u8>,
push_timeout: Duration,
vapid: Option<&Vapid>,
) -> bool {
let client_builder = reqwest::Client::builder().timeout(push_timeout);
@@ -112,6 +124,10 @@ pub(crate) async fn http_request(
.header(CONTENT_TYPE, "application/json")
.header("TTL", "86400");
if let Some(authorization) = vapid.and_then(|vapid| vapid.authorization(&details.url, now())) {
client = client.header(AUTHORIZATION, authorization);
}
if let Some(keys) = &details.keys {
match ece_encrypt(&keys.p256dh, &keys.auth, &body) {
Ok(body_) => {

View File

@@ -220,6 +220,7 @@ pub fn spawn_push_manager(inner: Arc<Inner>) -> mpsc::Sender<Event> {
})
.unwrap_or(true)
{
let vapid = server.core.jmap.vapid.clone();
tokio::spawn(async move {
http_request(
&subscription,
@@ -234,6 +235,7 @@ pub fn spawn_push_manager(inner: Arc<Inner>) -> mpsc::Sender<Event> {
)
.into_bytes(),
push_timeout,
vapid.as_deref(),
)
.await;
});
@@ -350,6 +352,7 @@ pub fn spawn_push_manager(inner: Arc<Inner>) -> mpsc::Sender<Event> {
*id,
push_tx.clone(),
push_timeout,
server.core.jmap.vapid.clone(),
);
retry_ids.remove(id);
} else {
@@ -436,7 +439,12 @@ pub fn spawn_push_manager(inner: Arc<Inner>) -> mpsc::Sender<Event> {
&& last_request >= push_attempt_interval))
{
if subscription.num_attempts < push_attempts_max {
subscription.send(*retry_id, push_tx.clone(), push_timeout);
subscription.send(
*retry_id,
push_tx.clone(),
push_timeout,
server.core.jmap.vapid.clone(),
);
} else {
trc::event!(
PushSubscription(PushSubscriptionEvent::Error),