Skip to content

Commit c6f5b62

Browse files
authored
Fix OIDC infinite loop if user is not in OIDC_USER_GROUP (#3487)
1 parent 84dad84 commit c6f5b62

File tree

5 files changed

+45
-18
lines changed

5 files changed

+45
-18
lines changed

frontend/pages/login.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ export default defineComponent({
201201
}
202202
203203
function isDirectLogin() {
204-
return router.currentRoute.query.direct
204+
return Object.keys(router.currentRoute.query).includes("direct")
205205
}
206206
207207
async function oidcAuthenticate() {

frontend/schemes/DynamicOpenIDConnectScheme.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ export default class DynamicOpenIDConnectScheme extends OpenIDConnectScheme {
1616
this.$auth.$storage
1717
)
1818

19-
2019
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
2120
return await super.mounted()
2221
}
@@ -79,6 +78,7 @@ export default class DynamicOpenIDConnectScheme extends OpenIDConnectScheme {
7978
// Update tokens with mealie token
8079
this.updateTokens(response)
8180
} catch {
81+
this.$auth.reset()
8282
const currentUrl = new URL(window.location.href)
8383
if (currentUrl.pathname === "/login" && currentUrl.searchParams.has("direct")) {
8484
return

mealie/core/security/providers/openid_provider.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,19 @@ async def authenticate(self) -> tuple[str, timedelta] | None:
3535
repos = get_repositories(self.session)
3636

3737
user = self.try_get_user(claims.get(settings.OIDC_USER_CLAIM))
38-
group_claim = claims.get("groups", [])
39-
is_admin = settings.OIDC_ADMIN_GROUP in group_claim if settings.OIDC_ADMIN_GROUP else False
40-
is_valid_user = settings.OIDC_USER_GROUP in group_claim if settings.OIDC_USER_GROUP else True
41-
42-
if not is_valid_user:
43-
self._logger.debug(
44-
"[OIDC] User does not have the required group. Found: %s - Required: %s",
45-
group_claim,
46-
settings.OIDC_USER_GROUP,
47-
)
48-
return None
38+
is_admin = False
39+
if settings.OIDC_USER_GROUP or settings.OIDC_ADMIN_GROUP:
40+
group_claim = claims.get("groups", [])
41+
is_admin = settings.OIDC_ADMIN_GROUP in group_claim if settings.OIDC_ADMIN_GROUP else False
42+
is_valid_user = settings.OIDC_USER_GROUP in group_claim if settings.OIDC_USER_GROUP else True
43+
44+
if not is_valid_user:
45+
self._logger.debug(
46+
"[OIDC] User does not have the required group. Found: %s - Required: %s",
47+
group_claim,
48+
settings.OIDC_USER_GROUP,
49+
)
50+
return None
4951

5052
if not user:
5153
if not settings.OIDC_SIGNUP_ENABLED:
@@ -68,7 +70,7 @@ async def authenticate(self) -> tuple[str, timedelta] | None:
6870
return self.get_access_token(user, settings.OIDC_REMEMBER_ME) # type: ignore
6971

7072
if user:
71-
if user.admin != is_admin:
73+
if settings.OIDC_ADMIN_GROUP and user.admin != is_admin:
7274
self._logger.debug(f"[OIDC] {'Setting' if is_admin else 'Removing'} user as admin")
7375
user.admin = is_admin
7476
repos.users.update(user.id, user)
@@ -91,6 +93,7 @@ def get_claims(self, settings: AppSettings) -> JWTClaims | None:
9193
self._logger.error(
9294
f"[OIDC] Unsupported algorithm '{algorithm}'. Unable to decode id token due to mismatched algorithm."
9395
)
96+
return None
9497

9598
try:
9699
claims.validate()

tests/e2e/docker/docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ services:
3030

3131
OIDC_AUTH_ENABLED: True
3232
OIDC_SIGNUP_ENABLED: True
33+
OIDC_USER_GROUP: user
3334
OIDC_ADMIN_GROUP: admin
3435
OIDC_CONFIGURATION_URL: http://localhost:8080/default/.well-known/openid-configuration
3536
OIDC_CLIENT_ID: default

tests/e2e/login.spec.ts

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ test('oidc initial login', async ({ page }) => {
5555
"sub": username,
5656
"email": `${username}@example.com`,
5757
"preferred_username": username,
58-
"name": name
58+
"name": name,
59+
"groups": ["user"]
5960
}
6061

6162
await page.goto('http://localhost:9000/login');
@@ -67,14 +68,35 @@ test('oidc initial login', async ({ page }) => {
6768
await expect(page.getByRole('link', { name: 'Settings' })).not.toBeVisible();
6869
});
6970

71+
test('oidc login with user not in propery group', async ({ page }) => {
72+
const username = "testUserNoGroup"
73+
const name = "Test User No Group"
74+
const claims = {
75+
"sub": username,
76+
"email": `${username}@example.com`,
77+
"preferred_username": username,
78+
"name": name,
79+
"groups": []
80+
}
81+
82+
await page.goto('http://localhost:9000/login');
83+
await page.getByRole('button', { name: 'Login with OAuth' }).click();
84+
await page.getByPlaceholder('Enter any user/subject').fill(username);
85+
await page.getByPlaceholder('Optional claims JSON value,').fill(JSON.stringify(claims));
86+
await page.getByRole('button', { name: 'Sign-in' }).click();
87+
await expect(page).toHaveURL(/.*\/login\/?\?direct=1/)
88+
await expect(page.getByRole('button', { name: 'Login with OAuth' })).toBeVisible()
89+
});
90+
7091
test('oidc sequential login', async ({ page }) => {
7192
const username = "testUser2"
7293
const name = "Test User 2"
7394
const claims = {
7495
"sub": username,
7596
"email": `${username}@example.com`,
7697
"preferred_username": username,
77-
"name": name
98+
"name": name,
99+
"groups": ["user"]
78100
}
79101

80102
await page.goto('http://localhost:9000/login');
@@ -100,7 +122,8 @@ test('settings page verify oidc', async ({ page }) => {
100122
"sub": username,
101123
"email": `${username}@example.com`,
102124
"preferred_username": username,
103-
"name": name
125+
"name": name,
126+
"groups": ["user"]
104127
}
105128

106129
await page.goto('http://localhost:9000/login');
@@ -133,7 +156,7 @@ test('oidc admin user', async ({ page }) => {
133156
"email": `${username}@example.com`,
134157
"preferred_username": username,
135158
"name": name,
136-
"groups": ["admin"]
159+
"groups": ["user", "admin"]
137160
}
138161

139162
await page.goto('http://localhost:9000/login');

0 commit comments

Comments
 (0)