[PR #2308] [MERGED] feat: add OAuth 2.0 PKCE authorization provider #8181

Closed
opened 2026-04-20 18:04:30 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/go-vikunja/vikunja/pull/2308
Author: @kolaente
Created: 2/26/2026
Status: Merged
Merged: 3/27/2026
Merged by: @kolaente

Base: mainHead: feat-oauth-server


📝 Commits (9)

  • 216dfba fix: add ORDER BY to ListUsers query for deterministic ordering
  • e9e49b4 refactor: extract shared RefreshSession helper
  • 4bd26d3 feat: add OAuth 2.0 authorization code model and migration
  • 2969efc feat: add OAuth client validation and PKCE verification
  • 8bee8f9 feat: add OAuth 2.0 authorize endpoint
  • 9460acf feat: add OAuth 2.0 token endpoint
  • 09e8a6f feat: register OAuth authorize and token routes
  • f8090f7 feat: add frontend OAuth authorize route and component
  • 2e60929 test: add tests for OAuth 2.0 authorization flow

📊 Changes

25 files changed (+1349 additions, -89 deletions)

View changed files

📝 frontend/src/i18n/lang/en.json (+1 -0)
📝 frontend/src/router/index.ts (+5 -0)
📝 frontend/src/views/user/Login.vue (+1 -0)
frontend/src/views/user/OAuthAuthorize.vue (+77 -0)
📝 frontend/src/views/user/OpenIdAuth.vue (+2 -1)
frontend/tests/e2e/user/oauth-authorize.spec.ts (+80 -0)
📝 frontend/tests/e2e/user/session-refresh.spec.ts (+2 -6)
📝 frontend/tests/support/authenticateUser.ts (+16 -8)
pkg/db/fixtures/oauth_codes.yml (+2 -0)
pkg/migration/20260226172819.go (+53 -0)
📝 pkg/models/error.go (+179 -0)
📝 pkg/models/models.go (+1 -0)
pkg/models/oauth_codes.go (+99 -0)
📝 pkg/models/setup_tests.go (+1 -0)
📝 pkg/modules/auth/auth.go (+92 -0)
pkg/modules/auth/oauth2server/authorize.go (+100 -0)
pkg/modules/auth/oauth2server/client.go (+35 -0)
pkg/modules/auth/oauth2server/client_test.go (+50 -0)
pkg/modules/auth/oauth2server/pkce.go (+34 -0)
pkg/modules/auth/oauth2server/pkce_test.go (+51 -0)

...and 5 more files

📄 Description

Summary

This PR implements an OAuth 2.0 authorization server to enable the Flutter app to authenticate using the authorization code flow with PKCE.

Changes

Backend

  • OAuthCode model (pkg/models/oauth_codes.go) - Stores authorization codes with PKCE challenge, user ID, and expiry
  • Database migration (pkg/migration/20260226172819.go) - Creates oauth_codes table
  • OAuth error types (pkg/models/error.go) - Added 7 error types (17001-17007) for OAuth-specific errors
  • OAuth2 server package (pkg/modules/auth/oauth2server/):
    • client.go - Client validation (Flutter client ID: vikunja-flutter, redirect URI: vikunja://callback)
    • pkce.go - PKCE S256 verification
    • authorize.go - Authorization endpoint handler (GET /api/v1/oauth/authorize)
    • token.go - Token endpoint handler (POST /api/v1/oauth/token) with authorization_code and refresh_token grant types
  • Route registration (pkg/routes/routes.go) - Registered OAuth endpoints

Frontend

  • Login.vue and OpenIdAuth.vue - Handle OAuth redirect parameter after successful login

Tests

  • Unit tests for client validation and PKCE verification
  • Web tests (pkg/webtests/oauth2_test.go) - 11 integration test cases covering the full OAuth flow

OAuth Flow

  1. Flutter app opens /api/v1/oauth/authorize with client_id, redirect_uri, response_type, code_challenge, code_challenge_method
  2. If not logged in, redirects to login page with return URL
  3. After login, redirects back to authorize endpoint
  4. Authorize endpoint creates code and redirects to vikunja://callback with code
  5. Flutter app exchanges code for tokens via POST /api/v1/oauth/token
  6. Refresh tokens can be exchanged for new access/refresh token pairs

Security Features

  • PKCE (S256) required for all authorization requests
  • Single-use authorization codes (deleted on use)
  • 10-minute code expiry
  • Refresh token rotation
  • Same-origin validation for OAuth redirects in frontend

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/go-vikunja/vikunja/pull/2308 **Author:** [@kolaente](https://github.com/kolaente) **Created:** 2/26/2026 **Status:** ✅ Merged **Merged:** 3/27/2026 **Merged by:** [@kolaente](https://github.com/kolaente) **Base:** `main` ← **Head:** `feat-oauth-server` --- ### 📝 Commits (9) - [`216dfba`](https://github.com/go-vikunja/vikunja/commit/216dfba46c48554443ac621029860fcf493e2dc8) fix: add ORDER BY to ListUsers query for deterministic ordering - [`e9e49b4`](https://github.com/go-vikunja/vikunja/commit/e9e49b4bf502af0fd9b1c80d6fa4c1025ebc690e) refactor: extract shared RefreshSession helper - [`4bd26d3`](https://github.com/go-vikunja/vikunja/commit/4bd26d3e75c5edafe97262a757310e300fb29314) feat: add OAuth 2.0 authorization code model and migration - [`2969efc`](https://github.com/go-vikunja/vikunja/commit/2969efc4b95a2eee55e5f6c0e189ca6a420bb948) feat: add OAuth client validation and PKCE verification - [`8bee8f9`](https://github.com/go-vikunja/vikunja/commit/8bee8f97ee1f363b51800bea6907607c008ec346) feat: add OAuth 2.0 authorize endpoint - [`9460acf`](https://github.com/go-vikunja/vikunja/commit/9460acf1b0ecb9a2de396c36527750a06e2b3216) feat: add OAuth 2.0 token endpoint - [`09e8a6f`](https://github.com/go-vikunja/vikunja/commit/09e8a6fe85583ccfa180bb801c0db7632aec1976) feat: register OAuth authorize and token routes - [`f8090f7`](https://github.com/go-vikunja/vikunja/commit/f8090f75cae8cefdfad048e0a526ac7cb629f712) feat: add frontend OAuth authorize route and component - [`2e60929`](https://github.com/go-vikunja/vikunja/commit/2e60929f772f3032b6f38a9f278c4096b33e3d72) test: add tests for OAuth 2.0 authorization flow ### 📊 Changes **25 files changed** (+1349 additions, -89 deletions) <details> <summary>View changed files</summary> 📝 `frontend/src/i18n/lang/en.json` (+1 -0) 📝 `frontend/src/router/index.ts` (+5 -0) 📝 `frontend/src/views/user/Login.vue` (+1 -0) ➕ `frontend/src/views/user/OAuthAuthorize.vue` (+77 -0) 📝 `frontend/src/views/user/OpenIdAuth.vue` (+2 -1) ➕ `frontend/tests/e2e/user/oauth-authorize.spec.ts` (+80 -0) 📝 `frontend/tests/e2e/user/session-refresh.spec.ts` (+2 -6) 📝 `frontend/tests/support/authenticateUser.ts` (+16 -8) ➕ `pkg/db/fixtures/oauth_codes.yml` (+2 -0) ➕ `pkg/migration/20260226172819.go` (+53 -0) 📝 `pkg/models/error.go` (+179 -0) 📝 `pkg/models/models.go` (+1 -0) ➕ `pkg/models/oauth_codes.go` (+99 -0) 📝 `pkg/models/setup_tests.go` (+1 -0) 📝 `pkg/modules/auth/auth.go` (+92 -0) ➕ `pkg/modules/auth/oauth2server/authorize.go` (+100 -0) ➕ `pkg/modules/auth/oauth2server/client.go` (+35 -0) ➕ `pkg/modules/auth/oauth2server/client_test.go` (+50 -0) ➕ `pkg/modules/auth/oauth2server/pkce.go` (+34 -0) ➕ `pkg/modules/auth/oauth2server/pkce_test.go` (+51 -0) _...and 5 more files_ </details> ### 📄 Description ## Summary This PR implements an OAuth 2.0 authorization server to enable the Flutter app to authenticate using the authorization code flow with PKCE. ## Changes ### Backend - OAuthCode model (pkg/models/oauth_codes.go) - Stores authorization codes with PKCE challenge, user ID, and expiry - Database migration (pkg/migration/20260226172819.go) - Creates oauth_codes table - OAuth error types (pkg/models/error.go) - Added 7 error types (17001-17007) for OAuth-specific errors - OAuth2 server package (pkg/modules/auth/oauth2server/): - client.go - Client validation (Flutter client ID: vikunja-flutter, redirect URI: vikunja://callback) - pkce.go - PKCE S256 verification - authorize.go - Authorization endpoint handler (GET /api/v1/oauth/authorize) - token.go - Token endpoint handler (POST /api/v1/oauth/token) with authorization_code and refresh_token grant types - Route registration (pkg/routes/routes.go) - Registered OAuth endpoints ### Frontend - Login.vue and OpenIdAuth.vue - Handle OAuth redirect parameter after successful login ### Tests - Unit tests for client validation and PKCE verification - Web tests (pkg/webtests/oauth2_test.go) - 11 integration test cases covering the full OAuth flow ## OAuth Flow 1. Flutter app opens /api/v1/oauth/authorize with client_id, redirect_uri, response_type, code_challenge, code_challenge_method 2. If not logged in, redirects to login page with return URL 3. After login, redirects back to authorize endpoint 4. Authorize endpoint creates code and redirects to vikunja://callback with code 5. Flutter app exchanges code for tokens via POST /api/v1/oauth/token 6. Refresh tokens can be exchanged for new access/refresh token pairs ## Security Features - PKCE (S256) required for all authorization requests - Single-use authorization codes (deleted on use) - 10-minute code expiry - Refresh token rotation - Same-origin validation for OAuth redirects in frontend --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-04-20 18:04:30 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/vikunja#8181