Skip to content

feat: add OAuth 2.0 login with PKCE#236

Closed
kolaente wants to merge 16 commits intomainfrom
feat-oauth-login
Closed

feat: add OAuth 2.0 login with PKCE#236
kolaente wants to merge 16 commits intomainfrom
feat-oauth-login

Conversation

@kolaente
Copy link
Copy Markdown
Member

Summary

  • Add OAuth 2.0 Authorization Code with PKCE login flow, enabling users to authenticate via a system browser alongside existing username/password and webview login methods
  • Implement proactive token refresh via onBeforeRequest hook in Client, with serialized concurrent refresh handling
  • Support OAuth session restoration on cold start with automatic token refresh

Changes

New files

  • lib/data/data_sources/oauth_data_source.dart — PKCE generation, authorization URL builder, token exchange/refresh via form-encoded HTTP
  • lib/data/models/oauth_token_response.dart — OAuth token response DTO
  • test/oauth_data_source_test.dart — Tests for PKCE and URL builder (8 tests)
  • test/settings_datasource_oauth_test.dart — Tests for new storage methods (8 tests)

Modified files

  • pubspec.yaml — Added crypto and app_links dependencies
  • AndroidManifest.xml / Info.plist — Registered vikunja://callback deep link scheme
  • settings_data_source.dart / settings_repository.dart / settings_repository_impl.dart — Added refresh token, token expiry, and auth type storage
  • client.dart — Added token setter and onBeforeRequest hook (1 new test)
  • network_provider.dart — Added OAuthTokenManager notifier, AuthData.updateToken(), wired refresh into ClientProvider
  • data_source_provider.dart — Added oAuthDataSource provider
  • login_page.dart — OAuth button, deep link listener, PKCE flow, callback handler, auth-type persistence
  • init_page.dart — OAuth session restoration with token refresh on cold start

Test plan

  • Happy path: Enter server URL → tap "Login with OAuth" → browser opens → log in → app receives callback → navigated to home → tasks load
  • Already logged in browser: If browser has an active Vikunja session, the redirect should be immediate
  • Token refresh: Wait for token expiry → make an API call → should auto-refresh without user interaction
  • Cold start restoration: Force-kill the app → reopen → should auto-restore the OAuth session (refreshing if needed)
  • Session expiry: Expired refresh token → open app → refresh fails → redirected to login
  • Password login still works: Username/password login should work exactly as before
  • WebView login still works: "Login with Frontend" should work exactly as before
  • Cancel flow: Tap "Login with OAuth" → navigate away in browser → return to app → still on login page

// Persist tokens
final settingsRepo = ref.read(settingsRepositoryProvider);
await settingsRepo.saveUserToken(tokens.accessToken);
await settingsRepo.saveRefreshToken(tokens.refreshToken);
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tokens should be treated like credentials and stored as secrets

final challenge = OAuthDataSource.generateCodeChallenge(verifier);

// Manually compute expected: base64url(sha256(verifier)) without padding
final digest = sha256.convert(utf8.encode(verifier));
Copy link
Copy Markdown
Member Author

@kolaente kolaente Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this test does not seem like it's actually good. it seems to duplicate the logic of the code it's testing?

@kolaente
Copy link
Copy Markdown
Member Author

kolaente commented Mar 27, 2026

Replaced by #253

@kolaente kolaente closed this Mar 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant