iosjwtapple-push-notificationsmongodb-realmnode-apn

Apple Push Notifications from a serverless MongoDB App Services backend


I have been setting up Apple Push Notifications for an iOS app, using JSON Web Tokens rather than certificates.

I can generate a JWT and make the required POST request from within the app (using the Swift-JWT package) and the notification is delivered.

I am using MongoDB Realm, which has serverless functions (in JavaScript with a Node environment) that are called from the iOS app. A scheduled trigger updates my JWT, as Apple advise it should be refreshed every hour.

However, despite trying several Node modules for making the POST request there were always errors (like “BadDeviceToken” or “InvalidProviderToken”).


I finally got it working using the node-apn package! However, I have two queries about using it in this serverless function context:

  1. It tries to keep a connection open to the Apple server, which would be fine, except it might mean a new connection is opened every time the function is called. Calling Provider.shutdown() does not seem to stop the connection. I don’t think I can have a long-running process to receive future requests in a serverless context.
  2. Apple advise not refreshing the JWT more often than every 20 minutes. node-apn manages the JWT for you, but in a serverless context, will it be generating a new token every time the function is called? Notifications do seem to get delivered every time I test it in development mode (to the Apple sandbox endpoint).

I’d be grateful for clarity on these points, and whether node-apn is appropriate to use in serverless functions.


Update

Provider.shutdown() not working seems to be a recognised issue.

I was able to shut down using this workaround:

Provider.client.endpointManager._endpoints.forEach(endpoint => endpoint.destroy());

I would still like to know about whether it is reasonable for this to be used in a serverless function. I am concerned about JWT being refreshed with every request, which Apple may not like!


Solution

  • I have scanned through Apple's documentation on this and given some thought to your question about refreshing tokens within a serverless context.

    You could imagine the following approach for ensuring that you refresh the token no more than once every 20 minutes and at least once every hour, as per Apple's documentation:

    1. Generate the token for sending a single notification request
    2. Send the notification, and then after, in the background, save that token to some collection (e.g apn_tokens) inside of MongoDB (optionally alongside a createdAt timestamp field)
    3. On the next request to send a push notifcation, fetch the stored JWT token from your server.
      • If the token's createdAt date (or iat field on the JWT itself) is less than an hour (or within some threshold less than the hour e.g 50 mins), then reuse the token in sending the push notification request
      • Otherwise, restart the process from step 1!

    Note on this process: It would require that your database (or theapn_tokens collection) is only accessible from trusted sources (i.e your cloud application/functions alone), if they aren't already. Clients should not have access to this table in any way. You can imagine setting Collection-Level Access Control for your serverless environment. As an extra layer of security, you could imagine deleting "expired" tokens after re-generation in step 1, such that there is only one token present in the table at any time in order to prevent potentially active tokens from laying around in the database without use.

    I hope this helps!