I think the security of the signed payload (the JWS thing that looks like a JWT) is solid. Probably it will be just fine.
The one problem is will the milliseconds vs seconds values. The header of your signed output contains a claim, typ=JWT. And that tells receivers the payload should be interpreted as a JWT payload, which means exp and iat are in seconds.
What happens if a receiver looks at the exp claim and “does the right thing” for interpreting JWT claims? It will assume that he JWT expires in …the year 2300. I don’t know, I didn’t do the arithmetic. But the point is it will be very far in the future. Practically speaking, there is no expiry. And that means a receiver who is not careful will accept the JWT after it “should have” expired. This could be the result of what is known as a replay attack.
A defensive receiver would also look at the iat claim, and if it is in the future, especially the far future, a defensively-designed receiver should and would reject that JWT. But not all receivers validate on the iat claim, and in fact the JWT spec does not explicitly state that the iat claim should be used to evaluate validity. So a receiver that is not carefully designed may simply evaluate the exp claim and be satisfied.
You can say, “well, our receiver is already known to us, and it needs milliseconds. We will never send this JWS to any other receiver. Therefore there will never be a problem.” That might be true, but there is a possibility in the future that a new maintenance team will come along and will not be aware of all these special arrangements and then you’re in trouble. Or someone will see the GenerateJWS policy in your proxy and re-use it in a fresh system that uses a different receiver, and then again you have a problem.
Suppose you say “Well let’s just do it this way for now, and later, in three months or so, we’ll fix the receiver so that it does not require milliseconds in the exp quantity.” Fine. What happens if some party stores a JWS that you issue this way, and then after the receiver gets fixed, that malicious party replays the “old” JWS to the “fixed” receiver. Now the receiver will look for exp to be in seconds, and may treat the old JWS as non-expired. Replay attack.
The reason standards work is because everyone conforms. If you claim “typ=JWT” in the header and then explicitly put something that is not standard in the payload, then, I would say, you are “asking for trouble.”
In summary, the security of the signed output is not in question. The way it is being employed will work for you today. There is the potential in the future for confusion or problems.
The real fix is to correct the system that “insists” on exp and iat being expressed in milliseconds. That system is broken, clearly. It’s a security bug that it behaves that way, and I would treat it as a P1 bug in my company. It needs to be corrected. It’s easy for me to say, because I don’t know the deadlines and constraints you are operating under, but I will say it anyway: I would not allow such a system in my enterprise if I were chief system architect. It would be triaged and fixed at the highest priority.
Maybe you should explicitly NOT set the typ=JWT in the header - maybe select a new custom typ, something else, anything else. Like “non-compliant-JWT”. Something that indicates to readers “this is not really a compliant JWT”. That might make it marginally safer for future readers and maintainers.
This is all just my opinion.