I have a server side java application which send bulk push messages to apple server. Recently i'm getting multiple SSLSocketException: Connection closed by remote host OR SSLSocketException: Broken pipe expcetions.
I understand that in case i'm sending invalid tokens, apple forcefully closes the connection. I also have a feedback loop, but the failing tokens never appear on feedback loop since they are not even accepted by apple.
Is there any algorithm or method to know, why the device token is "invalid" ?
Why the device token is invalid?
Assuming you got all your device tokens from Apple and didn't create dummy tokens, the most likely cause for invalid tokens is a DB that has both production tokens and sandbox tokens.
How to find which tokens are invalid :
You should use the enhanced APN format, in which you specify the message ID. After each message you send to Apple, you should try reading from the socket after setting the timeout to a large enough value. Apple will send you error responses for the invalid tokens, which specify the message ID. This will let you find the invalid tokens and remove them from your DB.
Note that you should only use this method (reading responses after each message with a long timeout) in order to clean your DB. After your DB is clean from invalid tokens you don't want to use it, since it will make the notifications delivery very slow.
Related
My use case is that once I have a user signed into my app, I use the Oauth token, resulting from the sign-in, when I make endpoint calls from my app to my custom server-- to authenticate the caller. E.g., I use Google Sign In in this way.
This method (e.g., with Google Sign In) has several useful properties:
Updated tokens are created automatically on the client app.
My custom server can easily verify the validity of the token, using Google's endpoints.
Initial token verification can take place early in the endpoint request processing-- without access to the custom servers database (as in the style in https://github.com/IBM-Swift/Kitura-Credentials).
My question is: Given that we're being told we have to incorporate Apple Sign-In into our iOS apps (if we offer general purpose sign-in facilities), how can I do endpoint authentication with my custom server?
I see two alternatives, neither of which I like very much.
First, I can have my client app send an Apple Sign In id_token to my server and ignore the exp (expiry) field. I can regenerate the id_token periodically (apparently, no more than once a day) and send it back to my client. I don't like this idea both because of ignoring the expiry of the token, and because of the need to periodically send the token from server to client. (My app uses multiple sign in systems and this just creates extra difficulty).
Second, I could have my client send an Apple Sign In refresh token to my server. My server would need, of course, to initially generate that refresh token and send it back to the client. I like this idea even less than the first idea. My initial token verification in my custom server would need to access its database to look for a match this token. I can't generally use an Apple endpoint -- because, again, Apple is apparently going to throttle this verification.
Additionally, I don't really like the idea that my custom server can, at best, check on token validity once a day. If the user revokes the app's credentials, I would hope my custom sever would stop being able to operate on behalf of the user relatively quickly.
Thoughts?
10/5/19-- update to the first alternative above. Upon actual use of https://developer.apple.com/documentation/signinwithapplerestapi/generate_and_validate_tokens for refresh token validation, I find that it is not actually generating an updated id token. It is generating an access token (but Apple doesn't define a use for that), and is validating the refresh token. And so, there is no way to send an updated id token to the client iOS app. Thus, using the first alternative, the expiry date of the id token cannot be used.
10/10/19-- update: I've written a blog article on this subject-- https://medium.com/#crspybits/apple-sign-in-custom-servers-and-an-expiry-conundrum-d1ad63223870
8/6/20-- update: Follow on blog article with possible path forward, pending details from Apple: https://medium.com/#crspybits/part-ii-apple-sign-in-custom-servers-and-an-expiry-conundrum-b3e9735dc079
In Get the most out of Sign in with Apple in WWDC 2020, at 11:30 in their presentation, they introduce server-to-server notifications to enable your server to monitor user account state changes on a real-time basis.
So far, few details on this though.
----------------- UPDATE (12/23/20) -----------------
I now have these server-to-server notifications working in a testing environment with my server. Some notes:
I decided on the endpoint to use, on my server, to allow Apple to send my server these REST endpoint requests.
I pasted that into developer.apple.com > Account > Certificates, Identifiers & Profiles > Identifiers > Select your app identifier > Click 'Edit' next to 'Sign In with Apple' > Server to Server Notification Endpoint
This endpoint is effectively unauthorized. E.g., it is made by Apple with no OAuth credential access to your server. How this is setup will depend on your server. I had a means to set up a new endpoint/route for my server that was unauthorized.
I have the client side and other parts of my server set up to allow creation of accounts using Apple Sign In. So, using one of those accounts, I now started taking actions that would cause Apple to invoke their server-to-server notification endpoint on my server. I wanted to reverse engineer the details of the endpoint request Apple is making, since details are scarce.
This provides some ideas on how to cause the notification events to occur:
How to revoke Sign in with Apple credentials for a specific app?
You can revoke credentials, but it's easier (because you can do it repeatedly) to enable and disable the email relay. Of course, to do this, you have to initially sign-in with Apple using the private/email relay.
I next learned two things:
a) After you take the action (e.g., revoke the email relay), the server-to-server notification endpoint is accessed on your server within about 30 seconds. I had added various log output into my server, so could watch my server log and see this happening.
b) The endpoint request Apple makes to your server has body data containing JSON in the following format:
{"payload" : "-- SNIP -- JWT"}
I'm using the following Swift structure to decode this.
struct ApplePayload: Decodable {
let payload: String // JWT
}
As Apple has indicated in the WWDC 2020 video (https://developer.apple.com/videos/play/wwdc2020/10173/), the main content of the body data is a JWT. Above, this is the value of the key "payload" in the JSON.
The next step is decoding this JWT. I just guessed that it would use the same mechanism for decoding as with the JWT in other parts of the Apple Sign In server-side process. And specifically, in decoding the identity token (a JWT) passed up to your server by a client using Apple sign in. See https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api/authenticating_users_with_sign_in_with_apple
I had some code that did this JWT decoding, so I factored that out and put it in a common place:
https://github.com/SyncServerII/AppleJWTDecoder.git
Integrating that into my server-side processing of Apple's server-to-server notification requests, I found that indeed this JWT can be decoded in this manner.
Another aspect that became evident is that the structure indicated by Apple in the WWDC 2020 video isn't 100% what is present in the JWT, after decoding. Specifically, in my tests so far at least the events field is not an array, rather it has a single value. See https://github.com/SyncServerII/AppleJWTDecoder/blob/main/Sources/AppleJWTDecoder/AppleSignInClaims.swift for a Swift structure.
I am now successfully parsing the JWT. The next main step on my server is to actually utilize the different event types in my server to take actions. For me this is going to involve the two account (not email) related actions:
User decided to stop using their Apple Id with your application. And
should be treated as a sign-out by the user. E.g., when a user decides
to disconnect your application from Settings. (From
https://developer.apple.com/videos/play/wwdc2020/10173/)
Also considered a request from user to "delete their app account"
(broader context: "Server to Server Notification Endpoint Sign in with
Apple server to server notifications allow you to receive important
updates about your users and their accounts. Notifications are sent
for each app group when users change mail forwarding preferences,
delete their app account, or permanently delete their Apple ID. Each
group of apps can have one URL, which must be absolute and include the
scheme, host, and path. TLS 1.2 or higher is required to receive
notifications. Learn more.") To see these docs, go to:
developer.apple.com > Account > Certificates, Identifiers & Profiles >
Identifiers > Select your app identifier > Click 'Edit' next to 'Sign
In with Apple' > Server to Server Notification Endpoint
case consentRevoked = "consent-revoked"
User has asked Apple to delete their Apple Id. The user identifier will now no longer be valid.
case accountDelete = "account-delete"
My plan is to take both of these events as equivalent- and delete the user's account on my server. I'm then going to have to consider how to communicate this to my client (iOS app). It will need to know that the user has deleted their account.
I'm trying to send push notification for iOS via Google cloud functions but it returns error that The provided registration token is not registered. But I've checked it by debugging my app and the FCM registration token is correct. After that I've tried to send push notification via Firebase console to single device by providing FCM token but it failed due to Unregistered registration token. How this happens because there is no issue with device FCM token?
I think it should be some careless mistake, you should make sure the things below:
The token is same with -[FIRMessagingDelegate messaging:(FIRMessaging *)messaging didReceiveRegistrationToken:(NSString *)fcmToken]
The GoogleService-Info.plist is downloaded from the same account with your Firebase push console.
Updated to the latest Firebase SDK.
From the Google Firebase document are some description of your error The provided registration token is not registered.
The provided registration token is not registered. A previously valid
registration token can be unregistered for a variety of reasons,
including:
The client app unregistered itself from FCM.
The client app was automatically unregistered. This can happen if the user uninstalls the application or, on iOS, if the APNS Feedback
Service reported the APNS token as invalid.
The registration token expired. For example, Google might decide to refresh registration tokens or the APNS token may have expired for iOS
devices.
The client app was updated, but the new version is not configured to receive messages. For all these cases, remove this registration token
and stop using it to send messages.
I also got a similar error. Something like this.
FirebaseMessagingError: The provided registration token is not registered. A previously valid registration token can be unregistered for a variety of reasons. See the error documentation for more details. Remove this registration token and stop using it to send messages.
So I simply generated a new token and replaced it with the old one.
And it started working fine.
I also got a similar error on the released build iOS device.
The provided registration token is not registered. A previously valid registration token can be unregistered for a variety of reasons. See the error documentation for more details. Remove this registration token and stop using it to send messages.
I fixed it by reuploading correct .p12 certificate on the firebase messaging
I got a mail from firebase support team:
"In your case, please note that you can only upload up to 2 APN certificates. One for development and another for production. Since one of the APN certificates is invalid, you should delete the incorrect one. To be able to delete an APN certificate, the user must have an 'owner' permission/role. Upon checking, you don’t have enough permission to make this change. I suggest you reach out to the project owner to request the necessary permission or to delete the APN certificate.
Just to add, As of I/O 2017, developers can use either auth keys or certs for their APNs. We recommend that you use auth keys because they are easier to configure and do not expire. If both cert and an auth key is uploaded, our server will use the auth key.
Our system can not validate auth keys like it does certificates. We do not have a way of validating that APNs will accept them without sending an actual message. With certs, we open a connection to APNs before accepting them, and if they are invalid APNs reject them and we can return the error to the user."
I was experiencing this issue and it turned out that our server was sending a bad notification payload for a certain use case. When this happened, Firebase would send a return code of 400 and unregister the token.
The fix for us was to fix the use case sending the bad payload. The error in our payload was due to "DeviceTokenNotForTopic". Be sure to check that you aren't getting errors when posting notifications prior to when you get in this bad state.
I would like to know how to manage the iOS device token App & Server Side.
First, I registered my app to receive remote notification. So, I got the device Token.
My questions are :
App Side
When Should I send the token to the server ?
If Many times, how often ? Each log Request ?
I should send the token to the server directly from this method application:didRegisterForRemoteNotificationsWithDeviceToken: or store it, and send it in an other method ?
Server Side
Once I have the token on the server, how can I know if the token is still valid ? Or that I should delete it cause the device token is not available anymore.
App side
It's better to upload it in every api request to track the user devices as he may login in his account from multiple devices , so you should have array of device tokens in server , or only one token if you want to track the last device the user is active with so , store token from didRegisterForRemoteNotificationsWithDeviceToken say in user defaults and upload it for every api request
Server side
The token is still valid if the php script used to send it returns success operation but if not that may also mean .pem file is incorrect
My push notifications provider wants me to send a device-unique token to the server, which is not possible, as I figured out after hours of searching (neither with keychain nor identifierForVendor etc).
So atm server stores tons of inactive tokens (after app is reinstalled, token is being refreshed). What is the workaround in this situation?
If you are talking about the notification APN token to the server, the server can cull them when they attempt to use them.
The Apple Push Notification server will let them know the token is invalid.
For iOS applications that require push notifications, it must first request the user for permission to do so. After that, a device token is generated and with this, the remote server may communicate to the user through this token.
I have read a similar question here and I do not feel it is enough. The picture below is a trusted certificate, it allows me to view all traffic that happens on this device.
With Fiddler2 as well as CertMaker, I can sniff HTTPS traffic, which means the client can probably know what data they are sending, and to where.
My question is, knowing that SSL is not secure from protecting my clients from seeing what I send to the remote server, should I simply encypt with a secret key found within my application?
Such as encrypt("device_token","secretkey_a0a0a0a") (pretend this is Objective-C)?
Couldn't someone just find that key within my application? I also read this question, and it seems that it would be possible to get back the secret key.
My plan for this goes like this:
Within the iOS application, Generate a random string named activate.
Encrypt (not hash), the token by the random string and a secret key that I only know. (secretkey_a0a0a0)
Send the encrypted string along with the generated randomly generated string (active).
Within serverside, I check if I can decrypt a valid token from using the active and my secret key.
I save the token in my database if it is valid.
This prevents people from random entering tokens yes, however, secretkey_a0a0a0 is a string literal. It's very possible to get this within the application binary itself.
My question is, how do I protect this secret key? The answer can also be, how can I prevent people from sending invalid tokens to my server as well.
I have heard of encryption, but doesn't that only apply to resource files?
How should I approach this?
If you do SSL-Pinning ( AFNetworking has this implemented ) you won't be able to (in a reasonable timeframe) sniff the https traffic between the client and server if you don't have the servers private key.
If your fear is that man in the middle can steal your token and send fake push notifications to users of your application, be sure that this cant happend. Since requests to apple apn servers must be signed with pem file, the main concern should be how to keep certificate file secured, and not apn token. If you want to prevent writing invalid tokens in your database then you should implement some CRC or odd/even bit mechanism.
You might want to check the security section in the Push Notifications Guide, in particular the section titled "Token Generation and Dispersal".
The device token is generated by the device connecting through the Apple's APNS. My guess (they don't say in the docs) is that it's unique for a given app identifier.
The APNS then will probably match those identifiers with the pem certificate you use to communicate with it thus validating that the push notifications are actually originating from your app.
Encrypting the device token seems overkill in this scenario.
To prevent someone maliciously spamming your server with tokens, I would hash the token when a secret key and send both the token and the hash to the server. You can then hash the token again on the server, with your secret key, and check that the request is valid.