The register handler, local/LDAP login and the OIDC callback all queue
the user.created event via DispatchOnCommit but never called
DispatchPending, so the event was silently dropped and its queue entry
leaked. Flush after commit and discard on rollback.
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
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
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
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
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.
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.
Make isErrUserStatusError public and replace all verbose
!IsErrAccountDisabled(err) && !IsErrAccountLocked(err) checks
with the shorter IsErrUserStatusError(err) call.
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! 🎉
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.
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
The config values for openid providers now use a map with the provider as key instead of an array. For example before:
auth:
openid:
providers:
- name: foo
clientid: ...
now becomes:
auth:
openid:
providers:
foo:
clientid: ...
This allows us to read values for openid providers from files using the same syntax as everywhere and makes the configuration more predictable. It also allows configuring providers through env variables, though it is still required to set at least one value via the config file because Vikunja won't discover the provider otherwise.
This PR fixes an issue discussed in #2152. Before this PR, the user who triggered team creation automatically got the admin flag set for this group, which makes perfect sense for the normal UI workflow. OIDC managed teams cannot be edited in Vikunja, and they're created automatically by the first user logging in having this team assigned. This PR therefore makes sure that OIDC managed team members do not receive the admin flag.
Co-authored-by: Daniel Herrmann <daniel.herrmann1@gmail.com>
Reviewed-on: https://kolaente.dev/vikunja/vikunja/pulls/2161
Reviewed-by: konrad <k@knt.li>
Co-authored-by: waza-ari <daniel.herrmann@makerspace-darmstadt.de>
Co-committed-by: waza-ari <daniel.herrmann@makerspace-darmstadt.de>
The change introduced in #2150 introduces a bug where a Team would be re-created every time a user logs in, as the check if a team already exists was based on both the unique `oidcID` and the `name`. This PR proposes to only base the check on the ID, as this should be unique.
Co-authored-by: Daniel Herrmann <daniel.herrmann1@gmail.com>
Reviewed-on: https://kolaente.dev/vikunja/vikunja/pulls/2152
Reviewed-by: konrad <k@knt.li>
Co-authored-by: waza-ari <daniel.herrmann@makerspace-darmstadt.de>
Co-committed-by: waza-ari <daniel.herrmann@makerspace-darmstadt.de>