OAuth 2.1[0] (an RFC that has been around longer than I've been at my employer) recommends some protections around refresh tokens, either making them sender constrained (tied to the client application by public/private key cryptography) or one-time use with revocation if it is used multiple times.
This is recommended for public clients, but I think makes sense for all clients.
The first option is more difficult to implement, but is similar to the IP address solution you suggest. More robust though.
The second option would have made this attack more difficult because the refresh token held by the legit client, context.ai, would have stopped working, presumably triggering someone to look into why and wonder if the tokens had been stolen.
0: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1
That's standard in oidc I believe
nextjs app bake all env vars on the client side code!! it's all public, unless you prefix the name with private_ or something.
You preface with PUBLIC_ to expose them in client side code.