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.
Demonstrates all plugin capabilities: authenticated and unauthenticated
routes, event listeners, and lifecycle hooks. Uses a shared singleton
for factory functions so Init() state is available to route handlers.
EvalPath-based loading of Go source directories with typed factory
functions for interface bridging (required by yaegi's wrapping model).
Supports all plugin capabilities: routes, events, and migrations.
Registers itself into the Manager via init() to avoid import cycles.
Generated symbol tables for echo and watermill, enabling yaegi plugins
to use HTTP routing and the event/message system.
Exclude pkg/yaegi_symbols/ from golangci-lint (generated code).
Add the core plugin system with four interfaces:
- Plugin: base lifecycle (Name, Version, Init, Shutdown)
- MigrationPlugin: database migrations
- AuthenticatedRouterPlugin: routes behind auth
- UnauthenticatedRouterPlugin: public routes
The Manager handles loading, initialization, shutdown, and route
registration. Includes native .so loader (marked deprecated) and
yaegi loader integration point.
Show a "You can close this tab now" message after the OAuth
authorize page redirects to the desktop app, instead of leaving
a stale "Authenticating..." message in the browser tab.
Add a server selection screen matching the mobile app UX with
Vikunja Cloud, Try the Demo, and Custom Server URL options.
Extract all desktop login logic into a dedicated DesktopLogin
component. Use the existing ApiConfig component for custom server
URL input. Skip loading server config on startup to avoid showing
motd/demo popups on the login screen.
Add a complete OAuth 2.0 PKCE flow for the Electron desktop app:
- Implement PKCE code generation and token exchange in Electron
- Register custom protocol handler (vikunja-desktop://) for deep links
- Handle deep link race conditions (buffered URLs, process.argv fallback)
- Prevent duplicate IPC listener accumulation on re-mount
- Preserve sub-paths in OAuth authorize URL for non-root deployments
- Add token refresh support using Electron's net module
Move the redoc HTML template and JavaScript bundle out of the Go const
in docs.go into separate files under pkg/routes/api/v1/redoc/, using
Go's embed directive. Update redoc.standalone.js to the latest version.
The JS is now served on a separate route (/api/v1/docs/redoc.standalone.js)
to keep the HTML and JS cleanly separated.
Add web tests covering the authorize endpoint, token exchange, PKCE
verification, single-use codes, and refresh token rotation. Add unit
tests for redirect URI validation and PKCE. Add E2E test for the full
browser-based authorization code flow with login redirect.
Extract setupApiUrl helper for E2E tests to avoid duplication.
Add /oauth/authorize frontend route with OAuthAuthorize.vue that
handles the OAuth authorization flow: validates required query params,
calls the API to generate an authorization code, and redirects to the
callback URI. Authentication is handled by the standard router guard.
Add POST /api/v1/oauth/token supporting authorization_code and
refresh_token grant types. Validates PKCE, exchanges codes for
JWT access tokens with refresh token rotation. Uses the shared
RefreshSession helper for the refresh grant.
Add POST /api/v1/oauth/authorize behind auth middleware. Validates
OAuth parameters (response_type, redirect_uri, PKCE), fetches the
authenticated user, creates an authorization code, and returns it
as JSON for the frontend to handle the redirect.
Add redirect URI validation that allowlists vikunja-* custom protocol
schemes, rejecting http/https and dangerous schemes like javascript:.
Add PKCE S256 verification following RFC 7636.
Add the OAuthCode model for storing short-lived authorization codes
with PKCE challenges. Codes are hashed (SHA-256) before storage and
are single-use with a 10-minute expiry. Add the database migration
and OAuth-specific error types.
The cookie-based /user/token/refresh handler had session refresh logic
(lookup, expiry check, token rotation, user fetch, JWT generation)
that will be reused by the OAuth token endpoint. Extract it into
auth.RefreshSession() and rewrite RefreshToken to use it.