There is no way without additional hardware to do this that isn't circumventable. It's the DRM problem.
You could use hardware that has the key for generating the MAC. That's an expensive proposition, but may be worth it if the content/IP is sufficiently valuable.
Without additional hardware, the encryption key can usually be moved and information about the device can be forged. There may be platforms where the key can be locked down (TPM, cryptochip), but how do you trust the information the device shares about this key? It can always forge the key in userland by manipulating your program. Even if you supply the key it has to go through userland before getting to TPM or crytochip and it can be intercepted and stolen there.
The best solution without hardware I can think of is including machine information in the MAC and having server side checks for multiple clients. The machine information is forgeable: someone debugging your app will be able learn what factors you are using and put hooks in that forge the values you expect so it is insufficient.
If two or more IP adresses are making concurrent requests then you have some indication that the user is roaming or is violating your single device limit. Having some heurisic for a grace period on multiple IPs would balance that out and limit the risk of blocking a legitimate device.
Users will still be able to use multiple devices if they hack your client-sode, but they won't be able to use them concurrently which I think is a more meaningful restriction in practice.