Skip to content

fpush-apns: add APNs token-based authentication (p8)#42

Open
infobug wants to merge 3 commits into
monal-im:masterfrom
infobug:feature/apns-token-auth
Open

fpush-apns: add APNs token-based authentication (p8)#42
infobug wants to merge 3 commits into
monal-im:masterfrom
infobug:feature/apns-token-auth

Conversation

@infobug

@infobug infobug commented Apr 25, 2026

Copy link
Copy Markdown

Summary

  • Adds support for token-based APNs authentication (p8 keys) as an alternative to certificate-based auth (p12)
  • Token-based auth uses a .p8 key file that never expires, eliminating annual certificate renewal
  • Fully backward compatible — existing p12 configs continue to work without any changes

Configuration

Either auth method can be used by providing the appropriate fields:

Certificate-based (existing, unchanged):

"apns": {
  "certFilePath": "/path/to/cert.p12",
  "certPassword": "password",
  "topic": "com.example.app",
  "environment": "production"
}

Token-based (new):

"apns": {
  "keyPath": "/path/to/AuthKey_XXXXXXXXXX.p8",
  "keyId": "XXXXXXXXXX",
  "teamId": "XXXXXXXXXX",
  "topic": "com.example.app",
  "environment": "production"
}

Implementation

  • config.rs: Made certFilePath/certPassword optional, added keyPath/keyId/teamId fields, added ApnsAuth enum to express the two auth modes
  • push.rs: Added a second init path using Client::token() from the existing a2 crate (which already supports token auth), with clear error messaging if neither auth method is configured

Test plan

  • Verify existing p12 config still works unchanged
  • Verify p8 config successfully authenticates with APNs and delivers notifications
  • Verify startup error is shown if neither cert nor token fields are provided

Add support for token-based APNs authentication as an alternative to
certificate-based (p12) auth. Token-based auth uses a .p8 key file
that never expires, eliminating the need for annual certificate renewal.

Config now accepts either:
- certFilePath + certPassword (existing p12 flow, unchanged)
- keyPath + keyId + teamId (new p8 token flow)

Both auth methods are fully supported and backward compatible.
Existing p12 configs continue to work without modification.
@infobug infobug force-pushed the feature/apns-token-auth branch from 5b2f29b to 2a119da Compare April 25, 2026 03:12
infobug added 2 commits May 27, 2026 18:32
SystemTime::now().elapsed() returns the duration between two
consecutive now() calls — effectively zero. Adding 4 weeks gave
apns-expiration=2,419,200 (~Jan 28 1970) on every push, which APNs
silently drops per Apple's documented behavior for past-timestamp
expirations (see Apple Developer Forums thread #708937).

Result: every push that did not deliver immediately on first attempt
was discarded by APNs with no error feedback to fpush. Devices that
were briefly offline / sleeping / handing off cellular missed pushes
entirely with no log evidence anywhere in the pipeline.

Replace with SystemTime::now().duration_since(UNIX_EPOCH), which
returns the current unix timestamp. Adding 4 weeks now gives a
correctly-future expiration so APNs holds and retries deliveries
per its normal store-and-forward semantics.
…und)

Two changes to align the APNs payload with Signal's
APN_NSE_NOTIFICATION_PAYLOAD:

1. Add apns-collapse-id "incoming-message". When the NSE fails to
   post a user-visible notification (the fallback case) and several
   pushes arrive while the device is locked, the fallback alerts
   collapse to one entry on the lock screen instead of stacking N
   copies of "New Message". Per-conversation notifications NSE posts
   via UNUserNotificationCenter use their own identifiers and are
   unaffected.

2. Drop the default sound from the APNs payload. NSE-posted
   notifications carry the user's chosen sound from app settings;
   when NSE fails and the fallback alert is what surfaces, match
   Signal's silent-failure UX rather than inconsistent audio cues.

Reference: signalapp/Signal-Server APNSender.java —
  static final String APN_NSE_NOTIFICATION_PAYLOAD = new
      SimpleApnsPayloadBuilder()
      .setMutableContent(true)
      .setLocalizedAlertMessage("APN_Message")
      .build();
  apns-collapse-id: notification.urgent() ? "incoming-message" : null
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant