0

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!

Chris
  • 4,009
  • 3
  • 21
  • 52

1 Answers1

1

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!

rexess
  • 729
  • 4
  • 7
  • Thank you! This works just fine - I stored the JWT in a separate database not accessible by users. I still wonder how the `node-apns` module does it, as it accepts your Apple developer info and manages the JWT behind the scenes. I wonder if it just sets the `issued at` (`iat`) property to, for example, the beginning of the current hour. Even if generated each time, it would still result in the same token if it used the same timestamp. – Chris Nov 16 '22 at 00:21
  • 1
    No problem! Looking through the source for node-apn, it appears that they have a simple token re-generation policy where they check if the generated token has expired (by checking the `iat` field of the JWT), and then re-generate it if necesary. This leads me to believe that the library possibly relies on the generated token being in memory for the period of execution. It also looks to regenerate the token whenever it gets an ExpiredProviderToken response back from apple. That is also a potential policy you could explore/add: i.e regenerate on getting an expired response. – rexess Nov 16 '22 at 05:16