102 Commits

Author SHA1 Message Date
kolaente
ced7ebd97f fix(auth): tolerate string booleans in oidc provider config (#2599)
The four boolean OIDC provider fields (emailfallback, usernamefallback,
forceuserinfo, requireavailability) were parsed with a strict .(bool)
type assertion. That works for YAML/JSON config where leaves are native
bools, but fails for every other input path: env vars always arrive as
strings, and GetConfigValueFromFile (used by the *.file Docker secret
convention) also always returns strings. The assertion would silently
zero the field for emailfallback and usernamefallback, and log an error
and zero the field for forceuserinfo and requireavailability, which is
what #2599 reports.

Extract a small parseBoolField helper that accepts both native bools and
strings (via strconv.ParseBool) and logs a parse error from each call
site. This also fixes the previously-silent drop of stringified
emailfallback / usernamefallback values — those now log an error if the
input is garbage, matching the behaviour of the other two fields.

Fixes #2599
2026-04-11 19:10:26 +00:00
kolaente
3008dc09db test(auth): cover env-var string booleans for oidc providers (#2599)
Regression test for #2599. Exercises getProviderFromMap with native
bools and with stringified booleans ("true"/"false"/"1"/"0") for all
four boolean provider fields — emailfallback, usernamefallback,
forceuserinfo, requireavailability. From env vars and from the
GetConfigValueFromFile path every leaf arrives as a string, so the
current .(bool) assertion silently zeros these fields.
2026-04-11 19:10:26 +00:00
kolaente
d58dd7a7c6 fix(auth): enforce TOTP on OIDC callback for users with 2FA enabled
The OIDC callback handler previously issued a JWT without ever
checking TOTP state. For installations with EmailFallback (or
UsernameFallback) enabled, this allowed an attacker who could
authenticate at the IdP with a matching email to log in as a local
user with TOTP enrolled, bypassing the second factor entirely.

HandleCallback now runs enforceTOTPIfRequired after resolving the
user and before any team sync writes, returning 412/1017 when the
passcode is missing or invalid. Clients resubmit the OIDC flow with
the totp_passcode field populated.

Fixes GHSA-8jvc-mcx6-r4cg
2026-04-09 17:25:47 +00:00
kolaente
c52b2a4f83 feat(auth): add enforceTOTPIfRequired helper for OIDC flow
Extracts a TOTP gate that the OIDC callback will use to enforce 2FA
for users with TOTP enabled. Mirrors the local-login TOTP flow in
pkg/routes/api/v1/login.go. Not yet wired into HandleCallback.

Refs GHSA-8jvc-mcx6-r4cg
2026-04-09 17:25:47 +00:00
kolaente
d291e3effe test(auth): add failing unit tests for OIDC TOTP enforcement
Covers the four states the OIDC TOTP gate must handle: user without
TOTP, TOTP enabled with missing passcode, invalid passcode, and
valid passcode. The helper function under test does not exist yet,
so the package currently fails to compile.

Refs GHSA-8jvc-mcx6-r4cg
2026-04-09 17:25:47 +00:00
kolaente
2b980be20d refactor(auth): add TOTPPasscode to OIDC Callback payload
Prepares the OIDC callback struct to carry a TOTP passcode so the
handler can enforce 2FA for users with TOTP enabled. No behaviour
change yet.

Refs GHSA-8jvc-mcx6-r4cg
2026-04-09 17:25:47 +00:00
kolaente
119d7df796 fix: use assert.Empty instead of assert.Equal for empty string check 2026-04-08 09:49:14 +00:00
kolaente
a5fb01cc3d fix: reset SSO avatar provider to default when picture claim is removed 2026-04-08 09:49:14 +00:00
kolaente
1065bdd84c test: add tests for SSO avatar provider reset on empty picture URL 2026-04-08 09:49:14 +00:00
Claude
fc9c21915d feat(auth): enforce OpenID Connect issuer uniqueness across providers
Detect when two configured OIDC providers resolve to the same issuer URL
at startup and halt with a fatal error, preventing team sync data
corruption caused by ambiguous (external_id, issuer) matching.

Also adds duplicate issuer detection to the doctor service diagnostics
and comprehensive tests with mock OIDC discovery servers.
2026-03-30 22:41:50 +00:00
Claude
121fd3c9f1 feat: use openid provider name instead of generic "OIDC" in synced team names
Teams synced from OpenID Connect providers were always named with "(OIDC)"
suffix (e.g., "DevTeam (OIDC)"). This changes it to use the configured
provider name instead (e.g., "DevTeam (Keycloak)"), making it easier to
identify which provider a team came from when multiple OIDC providers are
configured. Existing team names will be updated automatically on next user
login.

https://claude.ai/code/session_012LXXPvYe6i27WTcha1PL7A
2026-03-24 12:30:06 +00:00
MidoriKurage
68a74416a4 fix(openid): Merge VikunjaGroups and ExtraSettingsLinks from userinfo
When `forceuserinfo: true`, `mergeClaims` discards `vikunja_groups`
and `extra_settings_links` claims fetched from the userinfo endpoint,
failing team sync for opaque tokens.

Fixes team sync for OIDC providers using opaque tokens.
2026-03-23 16:11:17 +00:00
kolaente
a7a8ae072a fix(auth): return correct error type for locked users in OIDC callback
Return ErrAccountLocked for locked users instead of ErrAccountDisabled.
Also skip profile updates and avatar sync for disabled/locked users
found during OIDC login — HandleCallback rejects the auth anyway.
2026-03-23 12:06:16 +00:00
kolaente
8409bdb120 refactor(user): export IsErrUserStatusError for use across packages
Make isErrUserStatusError public and replace all verbose
!IsErrAccountDisabled(err) && !IsErrAccountLocked(err) checks
with the shorter IsErrUserStatusError(err) call.
2026-03-23 12:06:16 +00:00
kolaente
22a4b6fbb8 fix(auth): reject disabled/locked users in OIDC callback 2026-03-23 12:06:16 +00:00
kolaente
ea4ba18def fix(user): handle status errors across the codebase, remove redundant checks 2026-03-23 12:06:16 +00:00
kolaente
49bba7f830 fix: eliminate nested database sessions to prevent table locks
Refactor functions that created their own sessions when called from
within existing transactions, which caused "database table is locked"
errors in SQLite's shared-cache mode.

Changes:
- Add files.CreateWithSession() to reuse caller's session
- Refactor DeleteBackgroundFileIfExists() to accept session parameter
- Add variadic session parameter to notifications.Notify() and
  Notifiable.ShouldNotify() interface
- Update all Notify callers (~17 sites) to pass their session through
- Use files.CreateWithSession in SaveBackgroundFile and NewAttachment
- Fix test code to commit sessions before assertions
2026-02-25 11:03:02 +01:00
kolaente
c9c250fb1c fix: add missing Commit() to write callers
After NewSession() auto-begins a transaction, callers that perform
writes must explicitly call Commit() for changes to persist. Without
this, writes are silently rolled back when Close() is called.

Affected callers:
- user deletion notification cron
- caldav token generation/deletion
- token cleanup cron
- mark-all-notifications-read endpoint
- saved filter view cron
- project background delete
- typesense reindex
- export cleanup cron
- task last-updated listener
- saved filter view listener
- SSO team cleanup cron
- migration status start/finish
- background set/remove handlers
- orphaned task position cleanup
- file creation
2026-02-25 11:03:02 +01:00
renovate[bot]
9a61453e86 fix(deps): update module github.com/labstack/echo/v4 to v5 (#2131)
Closes https://github.com/go-vikunja/vikunja/pull/2133

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: kolaente <k@knt.li>
2026-01-24 20:38:32 +01:00
kolaente
4df8da549e fix(auth): scope query binding
Resolves https://github.com/go-vikunja/vikunja/issues/2146
2026-01-24 17:51:35 +01:00
kolaente
39b4568bc5 refactor: centralize HTTP error handling (#2062)
This changes the error handling to a centralized HTTP error handler in `pkg/routes/error_handler.go` that converts all error types to proper HTTP responses. This simplifies the overall error handling because http handler now only need to return the error instead of calling HandleHTTPError as previously.
It also removes the duplication between handling errors with and without Sentry.

🐰 Hop along, dear errors, no more wrapping today!
We've centralized handlers in a shiny new way,
From scattered to unified, the code flows so clean,
ValidationHTTPError marshals JSON supreme!
Direct propagation hops forward with glee,
A refactor so grand—what a sight to see! 🎉
2026-01-08 10:02:59 +00:00
kolaente
c6fe4c1a6e fix(auth): retry up to three times when an auth provider cannot be reached
Resolves https://github.com/go-vikunja/vikunja/issues/2050
2026-01-05 21:50:40 +01:00
kolaente
541a38456e chore(deps): update golangci-lint to 2.6.0 (#1737) 2025-10-31 17:28:52 +00:00
Copilot
c7a26d81fe fix(auth): do not panic with invalid openid provider configuration (#1354) 2025-08-31 07:17:50 +00:00
Copilot
5ca637a7e6 feat(auth): add oauth require availability configuration on startup (#1358) 2025-08-30 22:15:20 +00:00
kolaente
da0f6fb366 feat(auth): allow passing custom settings links to user account via openid claims 2025-08-03 13:25:32 +02:00
kolaente
de917467cb fix(openid): manually fetch providers
Partially reverts fcdcdcf46a
Resolves https://github.com/go-vikunja/vikunja/issues/1165
2025-07-28 11:40:09 +02:00
kolaente
bbd3567e43 chore: add debug logging around provider failure
https://github.com/go-vikunja/vikunja/issues/1165
2025-07-24 16:00:03 +02:00
kolaente
7243a10fb2 fix(openid): check different provider types
Related to https://github.com/go-vikunja/vikunja/issues/1165
2025-07-23 15:40:51 +02:00
kolaente
2b497e6265 fix: pass pointer when fetching provider
Resolves https://github.com/go-vikunja/vikunja/issues/1165
2025-07-23 11:09:09 +02:00
kolaente
ca83ad1f98 feat: move to slog for logging 2025-07-21 18:15:39 +02:00
kolaente
566657c54a fix: correctly return cached provider 2025-07-18 18:38:12 +02:00
kolaente
fcdcdcf46a feat: use keyvalue.Remember where it makes sense 2025-07-17 16:19:13 +02:00
kolaente
0ecbd9e1a3 feat(user): add avatar cache flushing (#1041) 2025-06-27 14:01:43 +02:00
Weijie Zhao
a214d68a44 feat(auth): sync avatar from OpenID providers (#821) 2025-06-16 15:59:31 +02:00
Dominik Pschenitschni
296577a875 fix: correct license header references (#882)
See originals:
- https://www.gnu.org/licenses/agpl-3.0.txt
- https://www.gnu.org/licenses/gpl-3.0.txt
2025-06-10 12:18:38 +02:00
Weijie Zhao
00c4148f05 feat(auth): add ForceUserInfo option to OpenID provider (#797)
Problem:

When using Casdoor as an OpenID provider, there's an inconsistency between the user information in the JWT token and the UserInfo endpoint. The token contains the user's unique ID in the `name` field, while the UserInfo endpoint correctly returns the user's display name.

Solution:

This PR adds a new `ForceUserInfo` option to the OpenID provider configuration. When enabled, it forces the use of the UserInfo endpoint to retrieve user information instead of relying on claims from the ID token.

Impact:

- Default behavior remains unchanged (backward compatible)
- New option allows administrators to force using UserInfo endpoint data
- Particularly useful for providers like Casdoor that don't fully comply with OIDC standards

Related:

I've opened an issue in the Casdoor repository (https://github.com/casdoor/casdoor/issues/3806) to discuss the root cause. However, changing Casdoor's token structure might cause significant compatibility issues for existing integrations, so it's unclear if this can be fixed at the provider level. This PR provides a workaround in Vikunja that doesn't affect existing functionality.
2025-05-20 08:06:34 +00:00
kolaente
6847f44058 chore(openid): add more debug logging when retrieving token 2025-03-29 19:24:07 +01:00
kolaente
4ea3c01b5f chore: add more debug logging when returning error 2025-03-29 18:21:34 +01:00
kolaente
99213c66ee chore(openid): use general external team sync 2025-03-18 16:36:00 +00:00
kolaente
9f5c761fd9 chore(auth): rename error 2025-03-18 16:36:00 +00:00
kolaente
06851ca639 chore(auth): rename external team id find methods 2025-03-18 16:36:00 +00:00
kolaente
62beb3db2d feat(auth): rename oidc_id to external_id 2025-03-18 16:36:00 +00:00
kolaente
12aba8e9b1 chore(openid): move openid team struct to openid package 2025-03-17 17:34:49 +01:00
kolaente
87cfe89441 feat(ldap): add tests 2025-03-16 18:23:55 +01:00
Marc
f4a0c0ef31 feat(auth): sso fallback mapping (#3068)
Reviewed-on: https://kolaente.dev/vikunja/vikunja/pulls/3068
Reviewed-by: konrad <k@knt.li>
Co-authored-by: Marc <marc88@free.fr>
Co-committed-by: Marc <marc88@free.fr>
2025-03-02 15:21:09 +00:00
kolaente
4e93806a44 fix(auth): load oidc provider before trying to use it
Resolves https://kolaente.dev/vikunja/vikunja/issues/3067
2025-03-02 14:09:02 +01:00
kolaente
e9d9f04763 chore: improve debug logging 2025-02-03 17:42:17 +01:00
kolaente
71cad7aa13 chore(auth): refactor creating users in openid and ldap 2025-01-28 09:59:08 +00:00
kolaente
090dd4b2f6 fix(user): do not allow changing name in settings when the user originates from an external auth provider
This improves the UX because it does not allow external users to change their name in Vikunja, since that change would be overridden once they log in again.

Resolves https://github.com/go-vikunja/vikunja/issues/357
2025-01-21 16:27:06 +01:00