According to Google’s docs on OAuth, the server application gets refresh_token from Google (on exchanging the auth code) only if the original redirection request to Google contains the param access_type with value offline (documentation link).

But this is only for the first time a user authenticates with your app. If for some reason your flow takes the user through the authentication flow again without having changed anything (user’s Google account, permissions required, etc. Haven’t tested with changed permissions yet but definitely happens when permissions also remain the same) then when you exchange auth code for tokens from Google, the Google servers don’t return the refresh_token this second time.

This is based on anecdotal observation, and I couldn’t find any such mention in their documentation. An answer on Stack Overflow confirmed for me that it wasn’t something misconfigured on my end 😅.

A “solution” for this could be to always specify the param prompt with a value of consent in the original redirect request where we send user to Google’s flow.