Skip to content

fix(generic): check local token expiry#1837

Open
becm wants to merge 2 commits intogit-ecosystem:mainfrom
becm:oauth-refresh-token
Open

fix(generic): check local token expiry#1837
becm wants to merge 2 commits intogit-ecosystem:mainfrom
becm:oauth-refresh-token

Conversation

@becm
Copy link
Contributor

@becm becm commented Feb 13, 2025

Not checking for expired tokens triggers failures on first fetch/push after expiration.
Many Oauth2 implementations use JWT, where expiration time stamp is stored in structured data.

Fixes #268
Fixes #1408
Fixes #1784

@becm becm changed the title fix(generic): save new refresh_token value fix(generic): refresh of local tokens Feb 14, 2025
@becm
Copy link
Contributor Author

becm commented Feb 15, 2025

I'd very much like to have any approach towards identifying local token expiration.
Alternative idea:

  • the username is (for the generic provider, at the moment) basically unused (fixed value).
  • we can change the stored value to OAUTH_USER@<expiration>.

The last approach changing the API for the storage format (#1464) is now stale for almost a year.
Neither suggestions here would need additional code to be touched.

The username approach is likely even waaayyy more compact.
For fully transparent handling of Git credential flow using store, the full username must be retained for the auth_token credential.
Due to limitations of Git using a BasicAuth header, a colon is not a valid separator.

@becm becm force-pushed the oauth-refresh-token branch from b467d37 to 482a1ea Compare February 15, 2025 16:37
@becm becm changed the title fix(generic): refresh of local tokens fix(generic): check local token expiry Feb 15, 2025
@becm
Copy link
Contributor Author

becm commented Feb 16, 2025

Some major caveat found while trying to prototype a "username@expiration" approach:
API may only return expires_in for the new access_token, not an update refresh_token!

Some storage back ends may also be thrown off by the changing username…

Relative time in protocol additionally indicates this is to be used as a transient element.
Committing that value to permanent storage (after calculating an absolute date) feels like a data flow violation.

For now I'd definitely favor using structured token detection (transparent, backward-compatible, non-intrusive).

@becm becm force-pushed the oauth-refresh-token branch from 482a1ea to c8a7e32 Compare February 25, 2025 19:53
@lostmsu
Copy link

lostmsu commented Jun 26, 2025

@mjcheetham @mpysson can you please take a look at this change? It's already been iterated upon.

@lostmsu
Copy link

lostmsu commented Jun 26, 2025

@mjcheetham @mpysson there've been no changes in this project for 5 months. Can I co-maintain it? Not sure if I can do it long term but I promise to go through the currently open pull requests at the very least. Better than nothing.

@itsjustnickdev
Copy link

any updates on this push?

@dscho
Copy link
Contributor

dscho commented Sep 30, 2025

any updates on this push?

@itsjustnickdev cautiously: yes. I opened #2059.

However, during the extended time of ownership limbo, Git Credential Manager's release process has withered to the point where no releases can be made as of time of writing (notarizing, for example, is broken, yet it is required for Git Credential Manager to work on macOS).

Therefore, the primary concern right now is to get that release process into a healthy shape.

If you want to look into #2059 in the meantime, that would be really cool, of course!

Copy link
Contributor

@mjcheetham mjcheetham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about the merits of using the JsonWebToken type from the https://www.nuget.org/packages/Microsoft.IdentityModel.JsonWebTokens package?

@becm becm force-pushed the oauth-refresh-token branch from d7e4f76 to 2efb138 Compare November 14, 2025 15:39
@becm becm requested a review from a team as a code owner November 14, 2025 15:39
@becm
Copy link
Contributor Author

becm commented Nov 14, 2025

@mjcheetham replaced handling of JWT with a generic StructuredToken placeholder.

This should make expanding support (or replace implementation) fairly easily later on.
Kept the minimalist JWT extraction approach (fancy 3-liner plus boilerplate) for now.

@becm
Copy link
Contributor Author

becm commented Jan 15, 2026

If I know how this needs to be improved, I may have a chance to get it done in time for the next release. 😅

@becm
Copy link
Contributor Author

becm commented Feb 19, 2026

@mjcheetham I'd prefer to apply the (optional) switch to a "proper Identity library" at a later date.
Doing this by hand also has the benefit of only having code for the actually needed operations.

The update to .Net 10 will be a major change in dependencies anyway and we can do it properly (if desired).
This will also be a good chance to replace the local Base64Url code (one centralized switch of dependencies).

In the current (revised) approach, changes to internal code should not require additional adjustments.

@becm becm requested a review from mjcheetham February 19, 2026 23:43
move definitions into private class scope
@becm becm force-pushed the oauth-refresh-token branch from 2efb138 to d86234f Compare March 12, 2026 20:08
@becm
Copy link
Contributor Author

becm commented Mar 12, 2026

Latest update in main massively reduced required adjustments to GitCredentialManager.GenericHostProvider as it already implements GetCredentialAsync().

Moved update to Base64UrlConvert into separate commit.
Tuned code flow in StructuredToken.TryCreate() in hope to ease later extension of token types (if required).

@becm becm force-pushed the oauth-refresh-token branch from d86234f to 32dae46 Compare March 13, 2026 08:48
@becm
Copy link
Contributor Author

becm commented Mar 13, 2026

Revised code flow in GetCredentialAsync to have distinct log output for missing and invalid credential.

@colejohnson66
Copy link

What is preventing this from being merged? The issue has existed for FIVE YEARS.

add token expiry check for credential `password` value
add generic StructuredToken class with expiration status property
include minimal JWT reader to decode and extract data
@becm becm force-pushed the oauth-refresh-token branch from 32dae46 to db20a7b Compare March 14, 2026 21:22
@becm
Copy link
Contributor Author

becm commented Mar 14, 2026

Removed the expiration check from the refresh_token, as there is basically no benefit.
The existing code (and request) paths should cope with and recover from using an expired token.

This should never cause issues in the proper single well-behaved client case.

If an update to a refresh_token is missed (bad client behavior or parallel use of same client ID)
the expiry date of the local token becomes invalid without notice
(immediately or after some minutes, depending on server setup).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

7 participants