@hrefcl/apn
v4.0.0
Published
Apple Push Notification service (APNs) for Node.js - Maintained fork with security updates and TypeScript support
Maintainers
Readme
@hrefcl/apn
Apple Push Notification service (APNs) for Node.js - Maintained fork with security updates and TypeScript support.
Why this fork?
The original apn package has been unmaintained since 2019 and contains 32 security vulnerabilities in its dependencies. This fork:
- Updates all dependencies to secure versions
- Rewrites the codebase in TypeScript with full type definitions
- Uses native Node.js
http2module (no deprecated packages) - Maintains API compatibility where possible
- Adds support for modern APNs features (VoIP, Live Activities, MDM, Safari, etc.)
Installation
npm install @hrefcl/apn
# or
yarn add @hrefcl/apnRequirements
- Node.js >= 16.0.0
- Apple Developer account with push notification capabilities
- APNs authentication key (.p8 file)
Quick Start
import { Provider, Notification } from '@hrefcl/apn';
// Create provider with token-based authentication
const provider = new Provider({
token: {
key: '/path/to/AuthKey_XXXXXXXXXX.p8',
keyId: 'XXXXXXXXXX',
teamId: 'YYYYYYYYYY'
},
production: false // Use true for production
});
// Create notification
const notification = new Notification({
alert: {
title: 'New Message',
body: 'Hello from APNs!'
},
badge: 1,
sound: 'default',
topic: 'com.example.myapp'
});
// Send to device(s)
const result = await provider.send(notification, [
'device-token-1',
'device-token-2'
]);
console.log(`Sent: ${result.sent.length}`);
console.log(`Failed: ${result.failed.length}`);
// Handle failures
for (const failure of result.failed) {
console.log(`Device: ${failure.device}`);
console.log(`Reason: ${failure.response?.reason}`);
}
// Shutdown when done
provider.shutdown();API Reference
Provider
const provider = new Provider(options);Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| token | Object | required | Token authentication options |
| token.key | string/Buffer | required | Path to .p8 file or key content |
| token.keyId | string | required | Key ID from Apple Developer |
| token.teamId | string | required | Team ID from Apple Developer |
| production | boolean | false | Use production environment |
| host | string | auto | Custom APNs host |
| port | number | 443 | Connection port |
| requestTimeout | number | 5000 | Request timeout (ms) |
Methods
send(notification, recipients)- Send notification to device(s)shutdown()- Close all connections
Notification
const notification = new Notification(options);Supported pushType values: alert, background, voip, complication, fileprovider, mdm, liveactivity, location, pushtotalk.
Options
| Option | Type | Description |
|--------|------|-------------|
| alert | string/Object | Alert content |
| badge | number | Badge number |
| sound | string | Sound filename |
| contentAvailable | boolean | Background fetch flag |
| mutableContent | boolean | Service extension flag |
| category | string | Action category |
| threadId | string | Thread identifier |
| targetContentId | string | Target content identifier |
| interruptionLevel | string | Focus interruption level |
| relevanceScore | number | Relevance score (0.0 to 1.0) |
| filterCriteria | string | Notification filter criteria |
| staleDate | number | Live Activity stale date |
| contentState | Object | Live Activity content state |
| timestamp | number | Live Activity timestamp |
| event | string | Live Activity event (start/update/end) |
| dismissalDate | number | Live Activity dismissal date |
| topic | string | Bundle ID |
| expiry | number/Date | Expiration time |
| priority | number | Priority (1-10) |
| collapseId | string | Collapse identifier |
| pushType | string | Push type |
| payload | Object | Custom data |
| id | string | apns-id header (UUID) |
| urlArgs | string[] | Safari push URL arguments |
| mdm | string | MDM magic string |
Send Result
interface SendResult {
sent: Array<{ device: string }>;
failed: Array<{
device: string;
status?: string;
response?: { reason: string };
error?: Error;
}>;
}Error Handling
const result = await provider.send(notification, tokens);
for (const failure of result.failed) {
switch (failure.response?.reason) {
case 'BadDeviceToken':
// Invalid token format
break;
case 'Unregistered':
// Token no longer valid - remove from database
await removeToken(failure.device);
break;
case 'PayloadTooLarge':
// Payload exceeds 4KB
break;
case 'TooManyRequests':
// Rate limited - implement backoff
break;
default:
console.error(`Unknown error: ${failure.response?.reason}`);
}
}Background Notifications
const notification = new Notification({
contentAvailable: true,
topic: 'com.example.app',
pushType: 'background',
payload: {
action: 'sync-data'
}
});Rich Notifications
const notification = new Notification({
alert: {
title: 'Photo from John',
subtitle: 'Vacation 2024',
body: 'Check out this amazing sunset!'
},
mutableContent: true, // Enable service extension
category: 'PHOTO',
payload: {
imageUrl: 'https://example.com/photo.jpg'
}
});VoIP Notifications (5KB payload)
const notification = new Notification({
pushType: 'voip',
topic: 'com.example.app', // auto-appends .voip
payload: {
callerId: 'user-123',
callerName: 'Jane Doe'
}
});Live Activities
const notification = new Notification({
pushType: 'liveactivity',
topic: 'com.example.app', // auto-appends .push-type.liveactivity
contentState: { score: '2-1' },
event: 'update',
timestamp: Date.now()
});Safari Push (url-args)
const notification = new Notification({
urlArgs: ['inbox', 'message-123'],
payload: { action: 'open' }
});MDM
const notification = new Notification({
mdm: 'a1b2c3d4e5f6'
});apns-id
const notification = new Notification({
id: '6c7f28b1-2a0d-4c72-b1c4-2d5f0d6b8a64',
alert: 'Hello'
});Migration from apn
This package is largely API-compatible with the original apn package:
// Before (apn)
const apn = require('apn');
const provider = new apn.Provider(options);
// After (@hrefcl/apn)
import { Provider } from '@hrefcl/apn';
const provider = new Provider(options);Breaking Changes
- Node.js 16+ required - Older versions not supported
- Token authentication only - Certificate auth removed
- No proxy support - May be added in future versions
Security
This package fixes all 32 security vulnerabilities present in the original apn package:
- CVE-2022-23529 (jsonwebtoken)
- CVE-2022-23540 (jsonwebtoken)
- CVE-2022-24772 (node-forge)
- CVE-2022-24771 (node-forge)
- CVE-2020-7720 (node-forge)
- And 27 more...
License
MIT - See LICENSE for details.
Credits
- Original authors: Andrew Naylor, Florian Reinhart
- Maintained by: Href SpA
