Need a bit of advice from someone who knows about OAuth/OpenID security, since I've found a ton of conflicting advice online.

What's the recommended way of storing refresh tokens for web apps?

It looks to me as if, for example, AWS Amplify is storing it in localStorage on the client, but isn't that a bit defeating the point of refresh tokens? If the client knows both the ID token and refresh token, how is that more secure than just issuing ID tokens with longer expiry? localStorage is also readable by other scripts running in the same context, so extensions or XSS could steal the refresh token.

If you're storing it server-side, what's the recommended way of authorizing the client to refresh the token? does the client send the old ID token to the server and the server checks if that matches, or should the client somehow else prove that it has the right to get the new token?