Skip to content

OAuth migration#54

Merged
NateEaton merged 6 commits into
mainfrom
feature/OAuth-migration
Feb 12, 2026
Merged

OAuth migration#54
NateEaton merged 6 commits into
mainfrom
feature/OAuth-migration

Conversation

@NateEaton

@NateEaton NateEaton commented Feb 12, 2026

Copy link
Copy Markdown
Owner

OAuth Device Code Authentication Migration

This PR migrates MyDeck from legacy username/password authentication to OAuth 2.0 Device Code Grant (RFC 8628), aligning with Readeck's authentication model.

Key Features

OAuth Device Code Flow

  • Client registration with dynamic client metadata (RFC 7591)
  • Device authorization with user-friendly code display (e.g., ABCD-EFGH)
  • Token polling with automatic retry on transient network errors
  • Token revocation on sign-out
  • Device identification: OAuth client name includes device manufacturer and model for easy identification on server authorization page

First-Time User Experience

  • Welcome screen shown on first launch with:
    • App branding (icon + name)
    • Server URL input field
    • Brief explanation of OAuth authorization process
  • Seamless transition to device authorization flow
  • Existing Account Settings screen remains unchanged for re-authentication

Background Polling Resilience

  • Polling runs on application-scoped coroutine (survives activity/ViewModel lifecycle)
  • Network errors are retryable — polling continues through transient failures (e.g., DNS restrictions when app is backgrounded)
  • Handles app backgrounding gracefully (e.g., user opens browser to authorize, then returns to app)
  • Proper cancellation handling prevents credential wipe on coroutine cancellation

UI/UX Improvements

  • Device authorization screen replaces dismissible AlertDialog (prevents accidental cancellation)
  • Full-screen authorization UI with:
    • Large, copyable user code
    • "Copy URL" and "Open in Browser" buttons
    • Countdown timer showing code expiration
    • Clear status messages (pending, success, denied, expired)
  • Removed redundant Cancel button (back arrow suffices)
  • Restored TopAppBar on Account Settings screen for consistency

Technical Details

Authentication Flow:

  1. User enters server URL → app validates OAuth support via /info endpoint
  2. App registers OAuth client → receives client_id
  3. App requests device code → receives device_code, user_code, verification URL
  4. User authorizes on server (in browser or on device)
  5. App polls /oauth/token endpoint until authorization completes
  6. Token saved → profile fetched → user logged in

Localization:

  • Welcome screen strings added to all 9 supported locales (de, es, fr, gl, pl, pt, ru, uk, zh) with English placeholders

Testing:

  • All 161 unit tests pass
  • Lint validation passes
  • Manual testing on Readeck nightly instance

Breaking Changes

⚠️ Users will need to re-authenticate after updating to this version. Existing username/password credentials are not compatible with OAuth tokens.


Compatibility: Requires Readeck server with OAuth support (Readeck 0.15.0+)

Additional changes

Sync + Bookmark Loading Fixes

  • Fixed a sync-breaking deserialization issue where certain bookmarks could fail to load if resources.thumbnail (or other image resources) omitted width/height fields in the API response. These fields now default safely, preventing the entire bookmark sync from aborting.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c127fe2a26

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +17 to +18
@SerialName("grant_types")
val grantTypes: List<String>? = null,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Model OAuth client response fields as strings

The OAuth client registration response DTO uses list types for grant_types/response_types, but Readeck’s oauthClientResponse schema (see _notes/openapi-spec-nightly.json) defines these fields as strings; when a compliant server returns strings, kotlinx deserialization will fail in registerOAuthClient, causing OAuthDeviceAuthorizationUseCase.initiateDeviceAuthorization() to error out and block login. Please align the response field types with the wire format.

Useful? React with 👍 / 👎.

TopAppBar(
title = { Text(stringResource(R.string.settings_account_title)) },
navigationIcon = {
IconButton(onClick = { navHostController.popBackStack() }) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Cancel polling when leaving the auth screen

The back button currently only pops navigation and does not call cancelAuthorization(), so if the user leaves during device authorization, polling keeps running in the application scope and can still complete login in the background even though the user effectively backed out; this is especially problematic because onCleared() intentionally does not stop polling. The back action should explicitly cancel the in-flight authorization job before navigating away.

Useful? React with 👍 / 👎.

@NateEaton NateEaton merged commit e5e57ab into main Feb 12, 2026
3 checks passed
@NateEaton NateEaton deleted the feature/OAuth-migration branch February 20, 2026 16:09
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