You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Add refresh token support as an opt-in feature #141
This will allow a client to opt in to requesting expiring tokens and refresh tokens, which helps improve secirity.
We should add refresh token support to both the device flow and web app flow. For now it can be disabled by default, but can be opted into by the client using the library. If opted into, we need to:
Send the offline_access scope during the initial request
Catch the refresh token in the token response as well as the expiration times, and store them appropriately like the access token. Importantly: if no refresh token is returned, assume that the server doesn't support them and fall back to unexpiring token behavior.
When calling APIs, if the token is rejected attempt to refresh the token once and try again, before failing hard. We don't want to accidentally create an infinite loop of call, fail, refresh, loop.
I'll get this kicked off with an agent to get a sketch of what this would look like, and get feedback on best practices.
I understand that this started as an agent generated sketch to explore the implementation and gather feedback, rather than as a final design expected to be merged unchanged. It has been a very useful source of inspiration and has helped clarify the pieces required for refresh token support.
After reviewing the approach, I think we should keep the token metadata parsing and the explicit api.Refresh operation, but not carry forward the reactive Transport and NewHTTPClient implementation.
The transport takes responsibility for several policies beyond the core OAuth flow, including interpreting API responses, coordinating refreshes, replaying requests, draining responses, and handling persistence failures. Request replay also depends on Request.GetBody, which is not available for streaming bodies, custom readers, pipes, and many manually constructed requests. This means reactive refresh would work differently depending on how the original request was created.
For gh and extensions, we should continue using the shared HTTP client stack provided by cli/go-gh rather than introducing a competing client in cli/oauth. The response based behavior is also necessarily heuristic because we cannot rely on every API endpoint consistently using 401, 403, and WWW-Authenticate to indicate token expiry. In the current implementation, a refresh failure such as bad_refresh_token is also hidden behind the original API response, preventing the caller from knowing that reauthorization is required.
I think a simpler direction is to let applications refresh proactively based on the token expiration metadata before submitting their API request. api.Refresh already works independently of the OAuth HTTP client, so consumers can use it with their existing HTTP stack, persist the rotated credentials, and handle reauthorization according to their own application needs.
I am currently working on shaping these changes into a smaller and more focused implementation. I will close the associated PR while that work continues.
Thank you again for exploring this space. The PR gives us a strong foundation for that work.
Thanks so much! Sorry I wasn't able to get it into a better shape, was still figuring out the architecture of the relationship between the library and the clients.
This will be really useful for a lot of future work too like ID JAG and token binding, so the modular approach seems best.
Activity
babakks commentedon Sep 4, 2026
Thanks for putting this together, @hpsin! 🙏
I understand that this started as an agent generated sketch to explore the implementation and gather feedback, rather than as a final design expected to be merged unchanged. It has been a very useful source of inspiration and has helped clarify the pieces required for refresh token support.
After reviewing the approach, I think we should keep the token metadata parsing and the explicit
api.Refreshoperation, but not carry forward the reactiveTransportandNewHTTPClientimplementation.The transport takes responsibility for several policies beyond the core OAuth flow, including interpreting API responses, coordinating refreshes, replaying requests, draining responses, and handling persistence failures. Request replay also depends on
Request.GetBody, which is not available for streaming bodies, custom readers, pipes, and many manually constructed requests. This means reactive refresh would work differently depending on how the original request was created.For
ghand extensions, we should continue using the shared HTTP client stack provided bycli/go-ghrather than introducing a competing client incli/oauth. The response based behavior is also necessarily heuristic because we cannot rely on every API endpoint consistently using401,403, andWWW-Authenticateto indicate token expiry. In the current implementation, a refresh failure such asbad_refresh_tokenis also hidden behind the original API response, preventing the caller from knowing that reauthorization is required.I think a simpler direction is to let applications refresh proactively based on the token expiration metadata before submitting their API request.
api.Refreshalready works independently of the OAuth HTTP client, so consumers can use it with their existing HTTP stack, persist the rotated credentials, and handle reauthorization according to their own application needs.I am currently working on shaping these changes into a smaller and more focused implementation. I will close the associated PR while that work continues.
Thank you again for exploring this space. The PR gives us a strong foundation for that work.
hpsin commentedon Sep 4, 2026
Thanks so much! Sorry I wasn't able to get it into a better shape, was still figuring out the architecture of the relationship between the library and the clients.
This will be really useful for a lot of future work too like ID JAG and token binding, so the modular approach seems best.
babakks commentedon Oct 21, 2026
Closing this as completed by #143
hpsin commentedon Oct 21, 2026
Heck yeah, thank you @babakks !