mirror of
https://github.com/go-gitea/gitea.git
synced 2026-03-09 21:10:00 -05:00
Invalid oauth RefreshToken makes user become inactive when syncing users from oauth provider #14611
Open
opened 2025-11-02 11:17:30 -06:00 by GiteaMirror
·
20 comments
No Branch/Tag Specified
main
release/v1.25
release/v1.24
release/v1.23
release/v1.22
release/v1.21
release/v1.20
release/v1.19
release/v1.18
release/v1.17
release/v1.16
release/v1.15
release/v1.14
release/v1.13
release/v1.12
release/v1.11
release/v1.10
release/v1.9
release/v1.8
v1.25.3
v1.25.2
v1.25.1
v1.25.0
v1.24.7
v1.25.0-rc0
v1.26.0-dev
v1.24.6
v1.24.5
v1.24.4
v1.24.3
v1.24.2
v1.24.1
v1.24.0
v1.23.8
v1.24.0-rc0
v1.25.0-dev
v1.23.7
v1.23.6
v1.23.5
v1.23.4
v1.23.3
v1.23.2
v1.23.1
v1.23.0
v1.23.0-rc0
v1.24.0-dev
v1.22.6
v1.22.5
v1.22.4
v1.22.3
v1.22.2
v1.22.1
v1.22.0
v1.23.0-dev
v1.22.0-rc1
v1.21.11
v1.22.0-rc0
v1.21.10
v1.21.9
v1.21.8
v1.21.7
v1.21.6
v1.21.5
v1.21.4
v1.21.3
v1.21.2
v1.20.6
v1.21.1
v1.21.0
v1.21.0-rc2
v1.21.0-rc1
v1.20.5
v1.22.0-dev
v1.21.0-rc0
v1.20.4
v1.20.3
v1.20.2
v1.20.1
v1.20.0
v1.19.4
v1.21.0-dev
v1.20.0-rc2
v1.20.0-rc1
v1.20.0-rc0
v1.19.3
v1.19.2
v1.19.1
v1.19.0
v1.19.0-rc1
v1.20.0-dev
v1.19.0-rc0
v1.18.5
v1.18.4
v1.18.3
v1.18.2
v1.18.1
v1.18.0
v1.17.4
v1.18.0-rc1
v1.19.0-dev
v1.18.0-rc0
v1.17.3
v1.17.2
v1.17.1
v1.17.0
v1.17.0-rc2
v1.16.9
v1.17.0-rc1
v1.18.0-dev
v1.16.8
v1.16.7
v1.16.6
v1.16.5
v1.16.4
v1.16.3
v1.16.2
v1.16.1
v1.16.0
v1.15.11
v1.17.0-dev
v1.16.0-rc1
v1.15.10
v1.15.9
v1.15.8
v1.15.7
v1.15.6
v1.15.5
v1.15.4
v1.15.3
v1.15.2
v1.15.1
v1.14.7
v1.15.0
v1.15.0-rc3
v1.14.6
v1.15.0-rc2
v1.14.5
v1.16.0-dev
v1.15.0-rc1
v1.14.4
v1.14.3
v1.14.2
v1.14.1
v1.14.0
v1.13.7
v1.14.0-rc2
v1.13.6
v1.13.5
v1.14.0-rc1
v1.15.0-dev
v1.13.4
v1.13.3
v1.13.2
v1.13.1
v1.13.0
v1.12.6
v1.13.0-rc2
v1.14.0-dev
v1.13.0-rc1
v1.12.5
v1.12.4
v1.12.3
v1.12.2
v1.12.1
v1.11.8
v1.12.0
v1.11.7
v1.12.0-rc2
v1.11.6
v1.12.0-rc1
v1.13.0-dev
v1.11.5
v1.11.4
v1.11.3
v1.10.6
v1.12.0-dev
v1.11.2
v1.10.5
v1.11.1
v1.10.4
v1.11.0
v1.11.0-rc2
v1.10.3
v1.11.0-rc1
v1.10.2
v1.10.1
v1.10.0
v1.9.6
v1.9.5
v1.10.0-rc2
v1.11.0-dev
v1.10.0-rc1
v1.9.4
v1.9.3
v1.9.2
v1.9.1
v1.9.0
v1.9.0-rc2
v1.10.0-dev
v1.9.0-rc1
v1.8.3
v1.8.2
v1.8.1
v1.8.0
v1.8.0-rc3
v1.7.6
v1.8.0-rc2
v1.7.5
v1.8.0-rc1
v1.9.0-dev
v1.7.4
v1.7.3
v1.7.2
v1.7.1
v1.7.0
v1.7.0-rc3
v1.6.4
v1.7.0-rc2
v1.6.3
v1.7.0-rc1
v1.7.0-dev
v1.6.2
v1.6.1
v1.6.0
v1.6.0-rc2
v1.5.3
v1.6.0-rc1
v1.6.0-dev
v1.5.2
v1.5.1
v1.5.0
v1.5.0-rc2
v1.5.0-rc1
v1.5.0-dev
v1.4.3
v1.4.2
v1.4.1
v1.4.0
v1.4.0-rc3
v1.4.0-rc2
v1.3.3
v1.4.0-rc1
v1.3.2
v1.3.1
v1.3.0
v1.3.0-rc2
v1.3.0-rc1
v1.2.3
v1.2.2
v1.2.1
v1.2.0
v1.2.0-rc3
v1.2.0-rc2
v1.1.4
v1.2.0-rc1
v1.1.3
v1.1.2
v1.1.1
v1.1.0
v1.0.2
v1.0.1
v1.0.0
v0.9.99
Labels
Clear labels
$20
$250
$50
$500
backport/done
💎 Bounty
docs-update-needed
good first issue
hacktoberfest
issue/bounty
issue/confirmed
issue/critical
issue/duplicate
issue/needs-feedback
issue/not-a-bug
issue/regression
issue/stale
issue/workaround
lgtm/need 2
modifies/api
modifies/translation
outdated/backport/v1.18
outdated/theme/markdown
outdated/theme/timetracker
performance/bigrepo
performance/cpu
performance/memory
performance/speed
pr/breaking
proposal/accepted
proposal/rejected
pr/wip
pull-request
reviewed/wontfix
💰 Rewarded
skip-changelog
status/blocked
topic/accessibility
topic/api
topic/authentication
topic/build
topic/code-linting
topic/commit-signing
topic/content-rendering
topic/deployment
topic/distribution
topic/federation
topic/gitea-actions
topic/issues
topic/lfs
topic/mobile
topic/moderation
topic/packages
topic/pr
topic/projects
topic/repo
topic/repo-migration
topic/security
topic/theme
topic/ui
topic/ui-interaction
topic/ux
topic/webhooks
topic/wiki
type/bug
type/deprecation
type/docs
type/enhancement
type/feature
type/miscellaneous
type/proposal
type/question
type/refactoring
type/summary
type/testing
type/upstream
Mirrored from GitHub Pull Request
Milestone
No items
No Milestone
Projects
Clear projects
No project
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: github-starred/gitea#14611
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @imhun on GitHub (Jun 17, 2025).
Description
oauth login: keycloak
Problem
git pull repo use app token,when oauth login session expired ,git pull error message:
/data.git/info/refs not valid: could not determine hash algorithm; is this a git repository?gitea home page display:
Your account is prohibited from signing in, please contact your site administrator.gitea service log message:
It works properly if we log in again through the oauth login.
Expected
I understand that if git pull uses the app token, it should be able to function normally as long as the token is not expired, and there should be no need for the user to log in again.
Gitea Version
1.37-1.24
Can you reproduce the bug on the Gitea demo site?
No
Log Gist
No response
Screenshots
No response
Git Version
2.49.0
Operating System
k8s on linux arm64
How are you running Gitea?
k8s 1.32,gitea helm charts:12.0.0
Database
PostgreSQL
@lunny commented on GitHub (Jun 17, 2025):
What did you mean
app token? Is that access token or oauth2 token?@imhun commented on GitHub (Jun 17, 2025):
The applications token

@imhun commented on GitHub (Jun 17, 2025):
When using applications tokens to access package,for example, pulling the docker image, the same problem will occur.
@wxiaoguang commented on GitHub (Jun 17, 2025):
What's your
app.iniconfig?Especially:
@wxiaoguang commented on GitHub (Jun 17, 2025):
This message should only be shown when the user's
is_active=falseorprohibit_login=trueCould you check your database about the user details when the problem occurs? If the values are not expected, and need to figure out why it becomes true.
@imhun commented on GitHub (Jun 18, 2025):
Without this config

when session expired,the user's is_active=f

when login again,the is_active=t

It seems to change dynamically along with the login status.
@wxiaoguang commented on GitHub (Jun 18, 2025):
Can you see some strange logs? For example, something like
log.Info("SyncExternalUsers[%s] disabling user %d", source.AuthSource.Name, user.ID)?@imhun commented on GitHub (Jun 18, 2025):
It seems to be so.

@wxiaoguang commented on GitHub (Jun 18, 2025):
The related logic is here. TBH I don't have more ideas about it at the moment. Just FYI.
@wxiaoguang commented on GitHub (Jun 18, 2025):
I think the problem is related to your SSO's RefreshToken behavior. For example: issuing/expiring/revoking.
@imhun commented on GitHub (Jun 18, 2025):
Should we disable the user when their session expires? Disabling the user will also affect the use of the app token, which feels a bit unreasonable.
@wxiaoguang commented on GitHub (Jun 18, 2025):
I don't have more ideas about this problem at the moment. And I don't know how your system works (especially the "RefreshToken" part).
Maybe you could try to debug it, and/or provide a reproducible setup with detailed steps then maybe when someone has time they could also take a look.
It is reasonable and correctly designed. The disabled users should have no access to the system.
@imhun commented on GitHub (Jun 23, 2025):
The default maximum session duration for gitea is one day,configured by parameter SESSION_LIFE_TIME.
The maximum session time for my sso is also one day.
It is possible that the sso session has expired, but the gitea session has not expired. This is why the problem occurred.
I set the maximum session time for sso to be longer than one day, say two days, and then this problem was resolved.
@wxiaoguang commented on GitHub (Jun 24, 2025):
IIRC every mid-night (by default) Gitea will use the RefreshToken to sync the users from oauth provider to its user table.
So if the RefreshToken becomes invalid, the related user will be marked as inactive.
@jon-nfc commented on GitHub (Jun 30, 2025):
I can also report the same problem. funnily enough, clicking signout and signing back in fixes the problem.
of note, reading through the questions it's clear there is a lack of understanding of the SSO process works that may possibly have led to the design decision of deactivating a user all because the OAUTH/OIDC flow was followed in accordance with how it works.
@wxiaoguang, how so? if the refresh token has expired that means it cant be used to obtain a new id token to authenticate, Gitea should be returning HTTP/401 for this not HTTP/200 like it is now.
@imhun, No you should not disable the user when the session expires. The normal process for this is to return HTTP/401 so they are forced to re-authenticate again. Disabling a user because their session has expired is ubsurd. Gitea knows nothing about the user until the user passes their token to gitea.
The normal flow for OAUTH (OIDC) is that when a user logs in they obtain their access token (which contains the refresh token), the former generally expires first with the refresh token set at a later datetime. other token also obtained if requested are the ID and user info tokens.
The idea behind the id/access token expiring is that if it's intercepted it is short lived so harm in reduced to the period of it's validity. As it's inconvenient for a user to re-login every 5 mins, this is where the refresh token comes in. The refresh token if still valid (should be, as it's expiry is normally much longer that id/access token), the user is re-authenticated.
If the refresh token has expired, access must be denied then the user must re-authenticate period. why deactivate the account? unless the user is passing an invalid token, which should trigger the re-login process. Gitea or any other app that uses this SSO method, has no way of knowing if the user is deactivated (if the sso provider deactivates the user, they cant get a token to even attempt to log in to gitea). You cant sync users from/to an OAUTH/OIDC provider. all gitea knows is if the signed token is valid (checks with sso provider) outside of that, how ever the token has been configured for use by the admin (re: claims etc) is the scope of giteas knowledge. marking a user as not allowed to login is absured especially since that's how OAUTH/OIDC work.
@jon-nfc commented on GitHub (Jun 30, 2025):
on a further note, this should not be occurring, the purpose of the tokens is that ONLY the user keeps a copy. This is a fundamental principle of the design of the OAUTH/OIDC protocol. OAUTH/OIDC is called stateless for that very reason. The decision to store users tokens is a serious security violation that needs to be addressed. Why? compromise one gitea server and you have essentially given the attacker the password of every OAUTH/OIDC user. In turn this security violation now affects every other service the user has access to, that the SSO provider is used to authenticate with. The impact area of this decision to store this token not only effects gitea but possibly thousands of other systems.
This needs to be fixed, how? dont store tokens you are not supposed to, why? simply put they are the equivalent of a password (technically better than, but I digress) and not just any password, one that can be used continually and are very difficult to revoke without significant impact
I implore the dev team to prioritize this as an immediate security fix.
Giteas only use of the token is to confirm validity for access. If this fails, the user MUST be DENIED access and forced to re-authenticate.
@jon-nfc commented on GitHub (Jun 30, 2025):
To further assist.... sorry for the spam.....
here's a docs with references for assistance ,thanks to chatGPT
OAuth 2.0 and OpenID Connect (OIDC) are modern authentication and authorization protocols widely used for secure, delegated access. Let's break this down in detail, with correct official references, and explain how and why statelessness and token handling are critical concepts in their security models.
📉 1. What is OAuth 2.0 and OIDC?
🔐 OAuth 2.0
OAuth 2.0 is a protocol that allows a user to grant limited access to their resources (usually on one site) to another site, without exposing their credentials.
https://datatracker.ietf.org/doc/html/rfc6749
🗝 OpenID Connect (OIDC)
OIDC is an identity layer on top of OAuth 2.0. It adds authentication (who the user is) in addition to authorization (what they can do).
https://openid.net/specs/openid-connect-core-1_0.html
OIDC introduces the ID Token, a JWT that contains user identity claims (e.g.,
sub,email, etc.).🔄 2. How the OAuth/OIDC Workflow Works (Statelessly)
Example: Authorization Code Flow (with PKCE)
This is the most common flow for web/mobile apps.
Step-by-step:
Client (e.g., web app) redirects the user to the Authorization Server.
User logs in and grants access.
Authorization Server redirects back to the client with a code.
Client exchanges the code (plus PKCE secret) for:
The client stores these in browser memory (not persistent storage).
✅ This whole flow is stateless on the client side because:
📘 PKCE Spec (RFC 7636):
https://datatracker.ietf.org/doc/html/rfc7636
🚫 3. Why Only the User (Client) Should Store Tokens
Tokens (especially access tokens and refresh tokens) are bearer credentials — anyone who has them can impersonate the user.
🧠 Design Principle: Statelessness
✅ Where to Store:
❌ 4. Why Other Systems Should NOT Store Tokens
Imagine a system (e.g., API gateway, third-party microservice) storing tokens in a DB or file system:
🚨 Risks:
🔒 Instead:
Downstream systems should validate the access token:
📘 Token Introspection Spec (RFC 7662):
https://datatracker.ietf.org/doc/html/rfc7662
📘 OIDC Discovery (Well-Known URL):
https://openid.net/specs/openid-connect-discovery-1_0.html
Example:
https://accounts.google.com/.well-known/openid-configuration🧪 5. Example: Good vs Bad Token Handling
✅ Good:
❌ Bad:
📦 Summary
🔗 Authoritative References
If you want working example code (e.g., Python Flask + Auth0, or Node.js + OIDC), I can provide those too.
@wxiaoguang commented on GitHub (Jun 30, 2025):
There are indeed many design problems in the oauth modules. Sometimes the problem is not that easy to fix as it looks.
Let’s not discuss whether this design is right or wrong at the moment, just take a look at the current behavior: it deactivates the user whose RefreshToken becomes invalid. If we change this behavior (not store it), then some site admins would complain that: "the user is still able to access the code when they has been disabled in my oauth SSO".
And FYI, there are some PRs like "Support OIDC RP-initiated logout #30072", in case anyone is interested to help.
Disclaimer: I didn't use oauth on my instance, I just happened to see this problem and tried to understand more about it. I am not oauth2 expert and I can't promise I have time on it, so feel free to propose proper fixes & PRs, thank you very much!
@jon-nfc commented on GitHub (Jun 30, 2025):
I do understand that pain!!
agreed, however addressing the security problem removes the current behaviour.
That is clearly a lack of understating of the protocol in the first place and not a reason to introduce a significant security problem. OAUTH/OIDC exists for the sole purpose of not knowing anything about the user except what they provide in a valid token. the issue of a disabled user on the sso side still accessing code is because the tokens validity period was not thought out enough. revoking system access in this sso systems case is a significant challenge. I do agree though that gitea does need to have the "user is deactivated box" as this allows for admins to prevent access immediately. (and for automations to do if they detect access revoking from sso server)
👍
An avid opensource user, that I can appreciate. I cant write in go so cant help there (well that's a lie, I refuse to learn another language especially when it will be of no use). My suggestion and the only thing that needs to be done to address this problem is the remove the storing of the tokens period. when that occurs the issue reported by OP goes away.
@neruthes commented on GitHub (Sep 13, 2025):
What I see is slightly different but I believe that we are facing the same fundamental problem in design decisions. I visited root admin dashboard and I see my account was deactivated unexpectedly.
I would like to mention a use case: a user account that commits and pushes code every day and visits web on every Friday. Deactivating the account on Sunday (due to refresh token, etc) is clearly absurd.
I would further argue that any automatic account deactivation is absurd. If necessary, site admin may have a button "Sync user-deactivations from SSO upstream" (choosing a particular upstream in params). At least I need an option to make sure that my Gitea instance never deactivates accounts unless I do it manually.