The pen testers suggested using a token system where the user authenticates with the server and it stores a token in the local database which would be used while offline, but I don’t see how this solves the decompiling issue. Surely this could be bypassed in the same way?
Right, you could make a modified version that ignores the token.
Is my only option to tell the customer that if they want the application to be secure, then it can’t work offline?
I don’t know what secure means for the customer. Remember that your the customer is somebody who has the right to access anyway. Thus modifying the client to get access is probably not an issue. Less secure for the customer? Only to the extend that the customer does not know what they are doing. You can say in end user agreement that they lose support if they do it, and you are not liable for damages.
Next, consider a third party getting access to the machine of the costumer and modifying the client… Well, you can say that protecting the machine from physical access is their responsibility, and again you are not liable for damages.
I believe, instead, we should be talking about somebody who is not the customer getting an illegal copy and modifying it to get access. But that is not security of the customer anymore.
Thus, let us make it harder for a third party who got a copy to get access. Yes, the token helps in that case, but not really. If all that the client needs to work is contained in it, then it is a matter of invalidating the right conditional in the code.
I want to mention something stronger than the token idea: You would make custom packages cipher. The key to decipher them will be derived from a hash of the password (you may add other ingredients to the dish, such as activation date, user name, salt and pepper to taste). These packages will be necessary to work, downloaded from the server, and decipher locally.
Note that a malicious customer might be able to repackage them to create a version of the client that does not require this steps to work. Albeit much harder that the case that just requires to invalidate a check somewhere in the client code. And also, this requires a valid customer, not a random person who got a copy.
Of course, once these copies are online anybody would be able to use them. For that, only online authentication works.