Integration with OIDC is misbehaving #14445

Closed
opened 2025-11-02 11:13:09 -06:00 by GiteaMirror · 17 comments
Owner

Originally created by @flixman on GitHub (May 4, 2025).

Description

I am using gitea behind authelia, for which I have already set up an OIDC client. The client is set up this way:

gitea admin auth add-oauth --name authelia --provider openidConnect --key gitea --secret {{ authelia.apps.gitea_password }} --auto-discover-url "https://auth.{{ domain }}/.well-known/openid-configuration" --scopes 'openid email profile groups' --admin-group gitea_admin

For unprivileged users, I get the consent form, login, logout, log back in and... everything works.

For admin users, that is not the case:

  • The first time I log in, I get the consent form, then I confirm the username and email and all works
  • For any other login, I get a 500 error with this message on the logs:
2025/05/04 09:03:29 ...rs/web/auth/oauth.go:351:handleOAuth2SignIn() [E] UpdateUser: can not delete the last admin user [uid: 1]

Should I create a static (local) admin user with a random password (so that there is one admin user left always), then using the OIDC user works... but, although it is still in the admin group, it gets created as an unprivileged user. Authelia is still sending the group information:

time="2025-05-04T07:12:48Z" level=debug msg="Check authorization of subject username=appadmin groups=harbor_admin,gitea_admin ip=10.42.0.1 and object https://gitea.test.local/user/oauth2/authelia/callback?code=authelia_ac_xuKzjjvjSDN5jChi3nH1onld9A8u1c6JiP6Qa8p82Uc.9qzzwoc214JC4BZ_pXBIozwfXU24dhsaIwAr7N_XQuE&iss=https%3A%2F%2Fauth.test.local&scope=openid+email+profile+groups&state=72f8a6cc-8a6d-4d5e-99fe-f6cdde3820c1 (method GET)."
time="2025-05-04T07:12:48Z" level=debug msg="Access Request with id '11cf7b62-12e9-4e6e-aab7-a9632d41e672' on client with id 'gitea' is being processed" method=POST path=/api/oidc/token remote_ip=10.42.0.1
time="2025-05-04T07:12:48Z" level=debug msg="Access Request with id '11cf7b62-12e9-4e6e-aab7-a9632d41e672' on client with id 'gitea' has successfully been processed" method=POST path=/api/oidc/token remote_ip=10.42.0.1

if I then list the users in gitea, I get the following:

gitea admin user list
ID   Username Email               IsActive IsAdmin 2FA
1    appadmin appadmin@test.local true     **false**   false
2    appuser  appuser@test.local  true     false   false
3    admin    admin@local         true     true    false

so... seems that the groups information is not properly read by gitea on any logins after the first?

Gitea Version

1.23.7

Can you reproduce the bug on the Gitea demo site?

No

Log Gist

No response

Screenshots

No response

Git Version

the one coming with the container docker.io/gitea/gitea:latest

Operating System

No response

How are you running Gitea?

through podman, from the image docker.io/gitea/gitea:latest

Database

PostgreSQL

Originally created by @flixman on GitHub (May 4, 2025). ### Description I am using gitea behind authelia, for which I have already set up an OIDC client. The client is set up this way: ``` gitea admin auth add-oauth --name authelia --provider openidConnect --key gitea --secret {{ authelia.apps.gitea_password }} --auto-discover-url "https://auth.{{ domain }}/.well-known/openid-configuration" --scopes 'openid email profile groups' --admin-group gitea_admin ``` For unprivileged users, I get the consent form, login, logout, log back in and... everything works. For admin users, that is not the case: * The first time I log in, I get the consent form, then I confirm the username and email and all works * For any other login, I get a 500 error with this message on the logs: ``` 2025/05/04 09:03:29 ...rs/web/auth/oauth.go:351:handleOAuth2SignIn() [E] UpdateUser: can not delete the last admin user [uid: 1] ``` Should I create a static (local) admin user with a random password (so that there is one admin user left always), then using the OIDC user works... but, although it is still in the admin group, it gets created as an unprivileged user. Authelia is still sending the group information: ``` time="2025-05-04T07:12:48Z" level=debug msg="Check authorization of subject username=appadmin groups=harbor_admin,gitea_admin ip=10.42.0.1 and object https://gitea.test.local/user/oauth2/authelia/callback?code=authelia_ac_xuKzjjvjSDN5jChi3nH1onld9A8u1c6JiP6Qa8p82Uc.9qzzwoc214JC4BZ_pXBIozwfXU24dhsaIwAr7N_XQuE&iss=https%3A%2F%2Fauth.test.local&scope=openid+email+profile+groups&state=72f8a6cc-8a6d-4d5e-99fe-f6cdde3820c1 (method GET)." time="2025-05-04T07:12:48Z" level=debug msg="Access Request with id '11cf7b62-12e9-4e6e-aab7-a9632d41e672' on client with id 'gitea' is being processed" method=POST path=/api/oidc/token remote_ip=10.42.0.1 time="2025-05-04T07:12:48Z" level=debug msg="Access Request with id '11cf7b62-12e9-4e6e-aab7-a9632d41e672' on client with id 'gitea' has successfully been processed" method=POST path=/api/oidc/token remote_ip=10.42.0.1 ``` if I then list the users in gitea, I get the following: ``` gitea admin user list ID Username Email IsActive IsAdmin 2FA 1 appadmin appadmin@test.local true **false** false 2 appuser appuser@test.local true false false 3 admin admin@local true true false ``` so... seems that the groups information is not properly read by gitea on any logins after the first? ### Gitea Version 1.23.7 ### Can you reproduce the bug on the Gitea demo site? No ### Log Gist _No response_ ### Screenshots _No response_ ### Git Version the one coming with the container docker.io/gitea/gitea:latest ### Operating System _No response_ ### How are you running Gitea? through podman, from the image docker.io/gitea/gitea:latest ### Database PostgreSQL
GiteaMirror added the type/enhancementtopic/authentication labels 2025-11-02 11:13:09 -06:00
Author
Owner

@0dragosh commented on GitHub (May 5, 2025):

I have the exact same issue.

@0dragosh commented on GitHub (May 5, 2025): I have the exact same issue.
Author
Owner

@techknowlogick commented on GitHub (May 5, 2025):

@0dragosh are you able to confirm with the 1.24-RC0 that was just released?

@techknowlogick commented on GitHub (May 5, 2025): @0dragosh are you able to confirm with the 1.24-RC0 that was just released?
Author
Owner

@flixman commented on GitHub (May 5, 2025):

@techknowlogick I have just given it a try, and I am getting the same error with 1.24.0-rc0-rootless. This is the log in Gitea:

2025/05/05 21:10:20 HTTPRequest [I] router: completed GET /user/oauth2/authelia for 10.42.0.1:0, 307 Temporary Redirect in 1.6ms @ auth/oauth.go:37(auth.SignInOAuth)
2025/05/05 21:10:20 routers/web/auth/oauth.go:353:handleOAuth2SignIn() [E] UpdateUser: can not delete the last admin user [uid: 1]
2025/05/05 21:10:20 HTTPRequest [I] router: completed GET /user/oauth2/authelia/callback?code=authelia_ac_rEwIj1uSvjZqwld5OXjVbhrJxucGbMmeGB_gOJTCU_c.Zed6GXiXuBERtf0P--4pYUXJNHF_ub57YXwpo2G-9WA&iss=https%3A%2F%2Fauth.test.local&scope=openid+email+profile+groups&state=cccfc51d-e073-4f3c-99da-d3ab1f6d071f for 10.42.0.1:0, 500 Internal Server Error in 231.7ms @ auth/oauth.go:76(auth.SignInOAuthCallback)
@flixman commented on GitHub (May 5, 2025): @techknowlogick I have just given it a try, and I am getting the same error with 1.24.0-rc0-rootless. This is the log in Gitea: ``` 2025/05/05 21:10:20 HTTPRequest [I] router: completed GET /user/oauth2/authelia for 10.42.0.1:0, 307 Temporary Redirect in 1.6ms @ auth/oauth.go:37(auth.SignInOAuth) 2025/05/05 21:10:20 routers/web/auth/oauth.go:353:handleOAuth2SignIn() [E] UpdateUser: can not delete the last admin user [uid: 1] 2025/05/05 21:10:20 HTTPRequest [I] router: completed GET /user/oauth2/authelia/callback?code=authelia_ac_rEwIj1uSvjZqwld5OXjVbhrJxucGbMmeGB_gOJTCU_c.Zed6GXiXuBERtf0P--4pYUXJNHF_ub57YXwpo2G-9WA&iss=https%3A%2F%2Fauth.test.local&scope=openid+email+profile+groups&state=cccfc51d-e073-4f3c-99da-d3ab1f6d071f for 10.42.0.1:0, 500 Internal Server Error in 231.7ms @ auth/oauth.go:76(auth.SignInOAuthCallback) ```
Author
Owner

@flixman commented on GitHub (May 12, 2025):

I done some more tests, today and indeed seems that the group information is not passed properly. I have made sure that in the token provided by authelia there is a "groups" claim, in this case, without membership to the gitea_admin group:

Image

I have also added the flag --group-claim-name groups to the add-oauth call, and now I am starting to log in (first user after a database wipe) with an unprivileged user... and it shows as if it was an admin :-O!

Image
Just for the record, there is no other user in the database:

Image
So.... what's wrong?

@flixman commented on GitHub (May 12, 2025): I done some more tests, today and indeed seems that the group information is not passed properly. I have made sure that in the token provided by authelia there is a "groups" claim, in this case, without membership to the gitea_admin group: ![Image](https://github.com/user-attachments/assets/c160617f-4c57-47ad-9ee4-bbf48960e569) I have also added the flag ```--group-claim-name groups``` to the add-oauth call, and now I am starting to log in (first user after a database wipe) with an unprivileged user... and it shows as if it was an admin :-O! ![Image](https://github.com/user-attachments/assets/aaee6d09-8f28-444a-84c3-eeefa34c5896) Just for the record, there is no other user in the database: ![Image](https://github.com/user-attachments/assets/375c1436-108d-44c4-88e1-324acf2ddb44) So.... what's wrong?
Author
Owner

@flixman commented on GitHub (May 17, 2025):

@techknowlogick @0dragosh with version 1.23.8 the problem is that the first user is set as admin no matter what. There seems to be a (possibly outdated) code block that returns the error we see although no admin is deleted anywhere. I know too little about the architecture to happily remove that part :-/. However, the solution seems to be to add a dummy admin as the first user, and then everything seems to be ok (for now)

	if opts.IsAdmin.Has() {
		if !opts.IsAdmin.Value() && user_model.IsLastAdminUser(ctx, u) {
			return models.ErrDeleteLastAdminUser{UID: u.ID}
		}

		u.IsAdmin = opts.IsAdmin.Value()

		cols = append(cols, "is_admin")
	}
@flixman commented on GitHub (May 17, 2025): @techknowlogick @0dragosh with version 1.23.8 the problem is that the first user is set as admin no matter what. There seems to be a (possibly outdated) code block that returns the error we see although no admin is deleted anywhere. I know too little about the architecture to happily remove that part :-/. However, the solution seems to be to add a dummy admin as the first user, and then everything seems to be ok (for now) ```go if opts.IsAdmin.Has() { if !opts.IsAdmin.Value() && user_model.IsLastAdminUser(ctx, u) { return models.ErrDeleteLastAdminUser{UID: u.ID} } u.IsAdmin = opts.IsAdmin.Value() cols = append(cols, "is_admin") } ```
Author
Owner

@0dragosh commented on GitHub (May 17, 2025):

Sorry for not replying sooner but I am running Forgejo and this seems to be a shared code path. My bad.

@0dragosh commented on GitHub (May 17, 2025): Sorry for not replying sooner but I am running Forgejo and this seems to be a shared code path. My bad.
Author
Owner

@techknowlogick commented on GitHub (May 17, 2025):

Yes, at least one admin user is required.

@techknowlogick commented on GitHub (May 17, 2025): Yes, at least one admin user is required.
Author
Owner

@lunny commented on GitHub (May 17, 2025):

@techknowlogick @0dragosh with version 1.23.8 the problem is that the first user is set as admin no matter what. There seems to be a (possibly outdated) code block that returns the error we see although no admin is deleted anywhere. I know too little about the architecture to happily remove that part :-/. However, the solution seems to be to add a dummy admin as the first user, and then everything seems to be ok (for now)

if opts.IsAdmin.Has() {
if !opts.IsAdmin.Value() && user_model.IsLastAdminUser(ctx, u) {
return models.ErrDeleteLastAdminUser{UID: u.ID}
}

  u.IsAdmin = opts.IsAdmin.Value()

  cols = append(cols, "is_admin")

}

Who can login with that dummy admin account? I don't think it can be simply removed.

@lunny commented on GitHub (May 17, 2025): > [@techknowlogick](https://github.com/techknowlogick) [@0dragosh](https://github.com/0dragosh) with version 1.23.8 the problem is that the first user is set as admin no matter what. There seems to be a (possibly outdated) code block that returns the error we see although no admin is deleted anywhere. I know too little about the architecture to happily remove that part :-/. However, the solution seems to be to add a dummy admin as the first user, and then everything seems to be ok (for now) > > if opts.IsAdmin.Has() { > if !opts.IsAdmin.Value() && user_model.IsLastAdminUser(ctx, u) { > return models.ErrDeleteLastAdminUser{UID: u.ID} > } > > u.IsAdmin = opts.IsAdmin.Value() > > cols = append(cols, "is_admin") > } Who can login with that dummy admin account? I don't think it can be simply removed.
Author
Owner

@flixman commented on GitHub (May 18, 2025):

@lunny Nobody is expected to use that dummy account. Maybe I am looking at this the wrong way, but what I am aiming towards to is: I want to have all the identities provided externally, in this case by Authelia. With my current configuration, when I get to gitea -> sign in, I just get a button to use authelia login (I'd like this to happen without having to click the button, but I think is not possible?) and then have the information about users, groups, etc., sent over by Authelia. All this works, as long as the first user in gitea is an admin.

I guess this should be stated as a requirement somewhere? Having an app relying only on externally provided ids is not that a corner case nowadays.

@flixman commented on GitHub (May 18, 2025): @lunny Nobody is expected to use that dummy account. Maybe I am looking at this the wrong way, but what I am aiming towards to is: I want to have all the identities provided externally, in this case by Authelia. With my current configuration, when I get to gitea -> sign in, I just get a button to use authelia login (I'd like this to happen without having to click the button, but I think is not possible?) and then have the information about users, groups, etc., sent over by Authelia. All this works, as long as the first user in gitea is an admin. I guess this should be stated as a requirement somewhere? Having an app relying only on externally provided ids is not that a corner case nowadays.
Author
Owner

@lunny commented on GitHub (May 18, 2025):

If nobody is admin, how could you maintain the site? I think the best way is keeping at least one user as admin in authelia side?

@lunny commented on GitHub (May 18, 2025): If nobody is admin, how could you maintain the site? I think the best way is keeping at least one user as admin in authelia side?
Author
Owner

@flixman commented on GitHub (May 18, 2025):

@lunny because, in the oauth2 client, I am setting that the users belonging to gitea_admin group are admins. There are admins, it's only that they are not local.

@flixman commented on GitHub (May 18, 2025): @lunny because, in the oauth2 client, I am setting that the users belonging to gitea_admin group are admins. There are admins, it's only that they are not local.
Author
Owner

@wxiaoguang commented on GitHub (Jun 8, 2025):

-> Fix last admin check when syncing users #34649 , also cc @yp05327

@wxiaoguang commented on GitHub (Jun 8, 2025): -> Fix last admin check when syncing users #34649 , also cc @yp05327
Author
Owner

@flixman commented on GitHub (Jun 8, 2025):

@wxiaoguang With your PR, is setting a dummy first admin still required (for the cases in which we'll rely exclusively on OIDC-provided IDs)?

@flixman commented on GitHub (Jun 8, 2025): @wxiaoguang With your PR, is setting a dummy first admin still required (for the cases in which we'll rely exclusively on OIDC-provided IDs)?
Author
Owner

@wxiaoguang commented on GitHub (Jun 8, 2025):

With your PR, is setting a dummy first admin still required (for the cases in which we'll rely exclusively on OIDC-provided IDs)?

It is a "fix", the goal of it is to make everything work as expected, make "a dummy first admin" not required.

But there is still an edge case: the first joined account will still be set as "admin" even if it isn't in "admin-group". When more admin accounts joins, the next login (sync) of the first account will be set to non-admin (by the "admin-group").

The "first account is admin" is a designed behavior of GItea's account system at the moment, if we'd like to change this behavior, it needs more work and tests.

@wxiaoguang commented on GitHub (Jun 8, 2025): > With your PR, is setting a dummy first admin still required (for the cases in which we'll rely exclusively on OIDC-provided IDs)? It is a "fix", the goal of it is to make everything work as expected, make "a dummy first admin" not required. But there is still an edge case: the first joined account will still be set as "admin" even if it isn't in "admin-group". When more admin accounts joins, the next login (sync) of the first account will be set to non-admin (by the "admin-group"). The "first account is admin" is a designed behavior of GItea's account system at the moment, if we'd like to change this behavior, it needs more work and tests.
Author
Owner

@flixman commented on GitHub (Jun 8, 2025):

@wxiaoguang in that case, would make sense to be explicit about it in the documentation (I could not find it)? For people that is not relying on local users but on oidc provided users, this is a security issue: it is too easy to automate the installation with an oidc connector, and anybody that logs is as the first user is suddenly admin.

@flixman commented on GitHub (Jun 8, 2025): @wxiaoguang in that case, would make sense to be explicit about it in the documentation (I could not find it)? For people that is not relying on local users but on oidc provided users, this is a security issue: it is too easy to automate the installation with an oidc connector, and anybody that logs is as the first user is suddenly admin.
Author
Owner

@wxiaoguang commented on GitHub (Jun 9, 2025):

I have no idea about how to correctly document it, or whether people have a chance to read it.

This is not only one issue, but two about the "first admin user". "Setting the first user as admin" is a quite old approach (since 47ac579f09 2015-08-19), but the two new features conflict with it:

  • Last admin check: #28337 (@yp05327)
    • it causes the error in this issue
    • #34649 is the fix for the error in this issue
  • Admin sync by group/filter
    • #16766 (@zeripath)
    • 24d7a86a8d (no PR maybe it is too old)
    • #1478 and #8849 (@lafriks & @6543)
    • They conflict with the "first user as admin"

So, maybe it needs a complete design and fix.

@wxiaoguang commented on GitHub (Jun 9, 2025): I have no idea about how to correctly document it, or whether people have a chance to read it. This is not only one issue, but two about the "first admin user". "Setting the first user as admin" is a quite old approach (since 47ac579f092cef9128fa0c74798ecaffa2c888f9 2015-08-19), but the two new features conflict with it: * Last admin check: #28337 (@yp05327) * it causes the error in this issue * #34649 is the fix for the error in this issue * Admin sync by group/filter * #16766 (@zeripath) * https://github.com/go-gitea/gitea/commit/24d7a86a8d35aa1fadf05deaa10e141d33ea6632 (no PR maybe it is too old) * #1478 and #8849 (@lafriks & @6543) * They conflict with the "first user as admin" So, maybe it needs a complete design and fix.
Author
Owner

@lunny commented on GitHub (Jun 9, 2025):

We could require creating an administrator account during installation, and remove the rule that automatically assigns admin privileges to the first registered account.

@lunny commented on GitHub (Jun 9, 2025): We could require creating an administrator account during installation, and remove the rule that automatically assigns admin privileges to the first registered account.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/gitea#14445