Skip to content

Desktop: Resolves #14372: Enable OAuth for Joplin Server#14888

Open
Kaushalendra-Marcus wants to merge 8 commits intolaurent22:devfrom
Kaushalendra-Marcus:fix/joplin-server-oauth-14372
Open

Desktop: Resolves #14372: Enable OAuth for Joplin Server#14888
Kaushalendra-Marcus wants to merge 8 commits intolaurent22:devfrom
Kaushalendra-Marcus:fix/joplin-server-oauth-14372

Conversation

@Kaushalendra-Marcus
Copy link
Copy Markdown
Contributor

fixes #14372

AI Usage

I used AI assistance (Claude) for exploring the codebase and debugging.

Problem

Joplin Server users could not use OAuth to authenticate - they were forced to manually enter email and password. The OAuth flow was only enabled for Joplin Cloud, even though Joplin Server backend supports it.

Solution

Made the existing Joplin Cloud OAuth login components generic so both sync targets share the same authentication flow.

Changes:

  • BaseSyncTarget.ts - Added shared isAuthenticatedViaJoplinServerCompatibleSession() method
  • SyncTargetJoplinServer.ts - Uses shared auth method, added authRouteName(), set requiresPassword() to false
  • SyncTargetJoplinCloud.ts - Refactored to use shared auth method
  • JoplinServerApi.ts - Made sessionId() public
  • joplinCloudUtils.ts - Made syncTargetId a required parameter, API key header only sent for Joplin Cloud
  • JoplinCloudLoginScreen.tsx - Made screen generic for both targets
  • ConfigScreen.tsx - Added "Login with Joplin Server" button, made checkSyncConfig_ generic
  • Root.tsx - Added JoplinServerLogin route

Test Plan

  1. Open Joplin Desktop → Settings → Synchronisation
  2. Select "Joplin Server" as sync target
  3. Enter server URL
  4. Click "Login with Joplin Server"
  5. Browser opens Joplin Server login page
  6. Login and authorise
  7. App detects authorisation and syncs
2026-03-24.14-37-53.mp4

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 24, 2026

📝 Walkthrough

Walkthrough

Config and login flows were made sync-target aware: settings are saved before auth checks; sync targets expose an auth route name and session-based auth checks; login screens and polling accept a syncTargetId; Root adds a Joplin Server login route and Config shows a server login button.

Changes

Cohort / File(s) Summary
Sync target auth core
packages/lib/BaseSyncTarget.ts, packages/lib/JoplinServerApi.ts, packages/lib/SyncTargetJoplinCloud.ts, packages/lib/SyncTargetJoplinServer.ts
Added shared session-check helper on BaseSyncTarget; made JoplinServerApi.sessionId() public; cloud target delegates to shared session check; server target now uses session check, no longer requires password, and exposes authRouteName() returning JoplinServerLogin.
Login screen & polling
packages/app-desktop/gui/JoplinCloudLoginScreen.tsx, packages/app-mobile/components/screens/JoplinCloudLoginScreen.tsx, packages/app-cli/app/command-sync.ts
Introduced reducer factory to parameterise UI text by target; polling and CLI now pass syncTargetId to login-success checker; signup CTA conditional on cloud target; component props updated to include syncTargetId.
Config UI & routing
packages/app-desktop/gui/ConfigScreen/ConfigScreen.tsx, packages/app-desktop/gui/Root.tsx
ConfigScreen saves settings before auth checks, queries syncTarget.isAuthenticated() and authRouteName() to decide navigation; Root adds JoplinServerLogin route rendering login screen with syncTargetId={9}; Config UI shows a Joplin Server login button when sync.target is joplinServer.
Credential handling utilities
packages/lib/services/joplinCloudUtils.ts
Added makeReducer(targetName); checkIfLoginWasSuccessful now accepts syncTargetId and parameterises headers, persisted keys and session loading by target; removed direct SyncTargetRegistry import.
Tests
packages/lib/components/shared/config/shouldShowMissingPasswordWarning.test.ts
Updated test mapping: joplinServer no longer requires a password; expectations adjusted accordingly.

Sequence Diagram

sequenceDiagram
    actor User
    participant ConfigScreen as ConfigScreen
    participant SettingsManager as SettingsManager
    participant SyncTarget as SyncTarget
    participant LoginScreen as LoginScreen
    participant JoplinServerApi as JoplinServerApi

    User->>ConfigScreen: Select sync target & click Login
    ConfigScreen->>SettingsManager: Save settings
    SettingsManager-->>ConfigScreen: Save result
    ConfigScreen->>SyncTarget: isAuthenticated()
    SyncTarget->>JoplinServerApi: fileApi -> sessionId()
    JoplinServerApi-->>SyncTarget: sessionId (or empty)
    alt Not authenticated
        SyncTarget-->>ConfigScreen: false
        ConfigScreen->>ConfigScreen: authRouteName()
        ConfigScreen->>LoginScreen: Navigate (syncTargetId)
        LoginScreen->>User: Show auth UI
        User->>LoginScreen: Complete OAuth flow
        LoginScreen->>SettingsManager: Persist credentials (sync.<id>.*)
        SettingsManager-->>LoginScreen: Persisted
    else Authenticated
        SyncTarget-->>ConfigScreen: true
        ConfigScreen->>SettingsManager: Proceed with sync config check
    end
Loading

Suggested labels

cli, mobile

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: enabling OAuth for Joplin Server, which is the primary objective of this pull request.
Description check ✅ Passed The description is well-detailed and directly related to the changeset, explaining the problem, solution, and changes made across multiple files.
Linked Issues check ✅ Passed The pull request addresses the core requirement of enabling OAuth authentication for Joplin Server by generalizing the existing Joplin Cloud OAuth components and making them work for both sync targets [#14372].
Out of Scope Changes check ✅ Passed All changes are directly aligned with the objective to enable OAuth for Joplin Server; no out-of-scope modifications were introduced.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Pr Description Must Follow Guidelines ✅ Passed PR description includes all required sections: clear problem statement (OAuth limited to Joplin Cloud), solution explanation (generalise login components), and concrete test plan with three verification steps.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai bot added enhancement Feature requests and code enhancements desktop All desktop platforms sync sync related issue server Issues related to Joplin Server labels Mar 24, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/app-desktop/gui/ConfigScreen/ConfigScreen.tsx`:
- Around line 69-81: The auth gate in checkSyncConfig_ uses the persisted
reg.syncTarget() and its isAuthenticated() which reads Setting.value(...),
causing stale validation when the user has unsaved edits in this.state.settings;
fix by using the draft settings or persisting them before running the auth
check: either (A) persist the draft settings first (call the existing
save/persist method used by this screen) and then re-read
reg.syncTarget()/isAuthenticated(), or (B) construct a sync target instance from
this.state.settings (instead of reg.syncTarget()) and call isAuthenticated() on
that instance, then call shared.checkSyncConfig(this, this.state.settings) as
before; update checkSyncConfig_ to use one of these approaches and keep
references to reg.syncTarget(), isAuthenticated(), shared.checkSyncConfig(this,
this.state.settings), and this.state.settings to locate the change.
- Around line 272-277: The handler goToJoplinServerLogin currently awaits
shared.saveSettings(this) but ignores its boolean result, allowing navigation
even when saveSettings failed; change the flow in goToJoplinServerLogin to store
the result of await shared.saveSettings(this), and if it returns false (or
otherwise indicates failure), abort and do not dispatch the NAV_GO action to
'JoplinServerLogin'—only dispatch when saveSettings succeeds.

In `@packages/app-desktop/gui/JoplinCloudLoginScreen.tsx`:
- Line 105: state.message() is translated and should not be mutated with
.replace(); instead determine the target label from props.syncTargetId (e.g.,
label = props.syncTargetId === 10 ? _('Joplin Cloud') : _('Joplin Server')) and
pass that label into the localized message when calling the translation function
(i.e., build the string via the i18n call used by state.message() or change
state.message() to accept a targetLabel parameter) so you inject the correct
localized target before rendering rather than replacing the English text after
the fact.

In `@packages/lib/services/joplinCloudUtils.ts`:
- Around line 115-119: The login flow writes credentials for syncTargetId but
doesn't update the active target or use the target-specific syncTarget instance;
after persisting the username/password via
Setting.setValue(`sync.${syncTargetId}.*`) you must also persist the active
target with Setting.setValue('sync.target', syncTargetId) and instantiate/use
the target-specific registry (e.g. call reg.syncTarget(syncTargetId) and assign
it to a variable) before calling fileApi.driver().api().loadSession() so the
session load and subsequent sync operate against the newly-logged-in target
rather than the previously saved one.

In `@packages/lib/SyncTargetJoplinServer.ts`:
- Around line 72-74: Update the failing test expectation in
shouldShowMissingPasswordWarning.test to reflect that
SyncTargetJoplinServer.requiresPassword() now returns false: change the
'joplinServer' expected value from true to false (or otherwise adjust the
expected object) so the test aligns with the OAuth-only flow; verify the
assertion that references the 'joplinServer' key and update it accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 639fb9fe-bc15-4ce0-a16e-2a981db038ea

📥 Commits

Reviewing files that changed from the base of the PR and between 18cf0a9 and 77c2811.

📒 Files selected for processing (8)
  • packages/app-desktop/gui/ConfigScreen/ConfigScreen.tsx
  • packages/app-desktop/gui/JoplinCloudLoginScreen.tsx
  • packages/app-desktop/gui/Root.tsx
  • packages/lib/BaseSyncTarget.ts
  • packages/lib/JoplinServerApi.ts
  • packages/lib/SyncTargetJoplinCloud.ts
  • packages/lib/SyncTargetJoplinServer.ts
  • packages/lib/services/joplinCloudUtils.ts

Comment on lines 72 to 74
public static requiresPassword() {
return true;
return false;
}
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot Mar 24, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect every caller of the shared missing-password warning helper.
rg -n -C2 '\bshouldShowMissingPasswordWarning\s*\(' packages

# Inspect every Joplin Server OAuth/login entry point added in the UI.
rg -n -C2 "routeName:\s*'JoplinServerLogin'|Login with Joplin Server|Joplin Server Login" packages

Repository: laurent22/joplin

Length of output: 25297


🏁 Script executed:

cat -n packages/lib/components/shared/config/shouldShowMissingPasswordWarning.ts

Repository: laurent22/joplin

Length of output: 588


🏁 Script executed:

# Find the sync target ID for Joplin Server
rg -n "class SyncTargetJoplinServer|Joplin Server" packages/lib/SyncTargetJoplinServer.ts | head -5

Repository: laurent22/joplin

Length of output: 298


🏁 Script executed:

# Check if there are any other potential surfaces consuming the warning
rg -n "hasMissingSyncCredentials|shouldShowMissingPasswordWarning" packages --type tsx --type ts | grep -v test | grep -v locale

Repository: laurent22/joplin

Length of output: 87


🏁 Script executed:

# Find the sync target ID for Joplin Server (should be 9)
rg -A 5 "class SyncTargetJoplinServer" packages/lib/SyncTargetJoplinServer.ts | head -20

Repository: laurent22/joplin

Length of output: 167


🏁 Script executed:

# Check how hasMissingSyncCredentials is used in MainScreen
rg -B 3 -A 3 "hasMissingSyncCredentials" packages/app-desktop/gui/MainScreen.tsx

Repository: laurent22/joplin

Length of output: 1422


🏁 Script executed:

# Look for other surfaces that might use the missing password warning or require password flow
rg -n "shouldShowMissingPasswordWarning\|hasMissingSyncCredentials" packages --type ts --type jsx

Repository: laurent22/joplin

Length of output: 87


🏁 Script executed:

# Check if web/CLI have their own handling for sync target 9
rg -n "sync\.target\|SyncTarget" packages/app-web packages/app-cli --type ts 2>/dev/null | head -20

Repository: laurent22/joplin

Length of output: 42


🏁 Script executed:

# Search for any password requirement logic specific to Joplin Server
rg -n "JoplinServer\|syncTargetId.*9\|sync\.9\." packages/lib --type ts | head -30

Repository: laurent22/joplin

Length of output: 42


🏁 Script executed:

# Check if there are other mobile or non-desktop surfaces
fd "SsoLoginScreen\|OAuthFlow\|JoplinServerLogin" packages --type f

Repository: laurent22/joplin

Length of output: 42


🏁 Script executed:

# Check SyncTargetRegistry to see which targets are available in each surface
rg -n "register\|add" packages/lib/SyncTargetRegistry.ts | head -20

Repository: laurent22/joplin

Length of output: 42


🏁 Script executed:

# Look at the full ConfigScreen context around the Joplin Server login button
sed -n '260,295p' packages/app-desktop/gui/ConfigScreen/ConfigScreen.tsx

Repository: laurent22/joplin

Length of output: 1283


🏁 Script executed:

# Check if mobile SsoLoginScreen handles both OAuth and password-based login
sed -n '1,100p' packages/app-mobile/components/screens/SsoLoginScreen.tsx

Repository: laurent22/joplin

Length of output: 2886


🏁 Script executed:

# Check the test expectations for Joplin Server (sync target 9)
cat -n packages/lib/components/shared/config/shouldShowMissingPasswordWarning.test.ts

Repository: laurent22/joplin

Length of output: 1763


🏁 Script executed:

# Verify sync target name to ID mapping
rg -B 2 -A 2 "joplinServer\|nameToId" packages/lib/SyncTargetRegistry.ts | head -30

Repository: laurent22/joplin

Length of output: 42


🏁 Script executed:

# Check if the test file was modified in this PR
git diff HEAD~1 packages/lib/components/shared/config/shouldShowMissingPasswordWarning.test.ts 2>/dev/null | head -50

Repository: laurent22/joplin

Length of output: 42


🏁 Script executed:

# Check the git status to see what files were modified
git diff --name-only HEAD~1 2>/dev/null | grep -i test

Repository: laurent22/joplin

Length of output: 42


🏁 Script executed:

# Look at the full SyncTargetJoplinServer implementation to understand the change
cat -n packages/lib/SyncTargetJoplinServer.ts

Repository: laurent22/joplin

Length of output: 6248


Test expectations need updating for the requiresPassword() change.

The wider-impact concern is addressed: both desktop and mobile surfaces provide OAuth alternatives (ConfigScreen's "Login with Joplin Server" button and SsoLoginScreen's browser-based code flow). However, the test file packages/lib/components/shared/config/shouldShowMissingPasswordWarning.test.ts still expects 'joplinServer': true on line 10, which will cause failures since shouldShowMissingPasswordWarning() now returns false for sync target 9. Update the test expectations to reflect the OAuth-only flow.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/lib/SyncTargetJoplinServer.ts` around lines 72 - 74, Update the
failing test expectation in shouldShowMissingPasswordWarning.test to reflect
that SyncTargetJoplinServer.requiresPassword() now returns false: change the
'joplinServer' expected value from true to false (or otherwise adjust the
expected object) so the test aligns with the OAuth-only flow; verify the
assertion that references the 'joplinServer' key and update it accordingly.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@Kaushalendra-Marcus

I don’t know what the issue is, but I’ve already changed it.
Let me fix it again.
image

image

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
packages/app-desktop/gui/ConfigScreen/ConfigScreen.tsx (1)

72-83: ⚠️ Potential issue | 🟠 Major

Re-indent these blocks with tabs only.

Line 72-83 and Line 274-290 mix spaces and tabs, which triggers no-mixed-spaces-and-tabs and can fail CI. Please normalise both blocks to tab indentation only.

As per coding guidelines, **/*.{ts,tsx,js,jsx}: Use tabs for indentation.

Also applies to: 274-290

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/app-desktop/gui/ConfigScreen/ConfigScreen.tsx` around lines 72 - 83,
The highlighted blocks using syncTarget/authRouteName/isAuthenticated and the
later block around shared.checkSyncConfig are mixing spaces and tabs; fix by
re-indenting both code blocks to use tabs only (remove any leading spaces and
replace with tabs) so they conform to the repository's tab-only indentation rule
and eliminate the no-mixed-spaces-and-tabs lint error; ensure the indentation
for the if/await/return lines and any nested lines inside the
syncTarget/authRouteName checks and the block containing shared.checkSyncConfig
is consistently tab-indented.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/app-desktop/gui/JoplinCloudLoginScreen.tsx`:
- Around line 120-126: Indentation in mapStateToProps is using spaces which
triggers lint rules (indent/@typescript-eslint/indent); update the block that
declares syncTargetId, isCloud and the returned object so all leading
indentation uses tabs instead of spaces, keeping the same code and alignment for
the expressions joplinCloudWebsite, joplinCloudApi and syncTargetId (look for
the const syncTargetId = ownProps.syncTargetId ?? 10; / const isCloud =
syncTargetId === 10; and the return { ... } lines) to satisfy the project's
tabs-for-indentation rule.

---

Duplicate comments:
In `@packages/app-desktop/gui/ConfigScreen/ConfigScreen.tsx`:
- Around line 72-83: The highlighted blocks using
syncTarget/authRouteName/isAuthenticated and the later block around
shared.checkSyncConfig are mixing spaces and tabs; fix by re-indenting both code
blocks to use tabs only (remove any leading spaces and replace with tabs) so
they conform to the repository's tab-only indentation rule and eliminate the
no-mixed-spaces-and-tabs lint error; ensure the indentation for the
if/await/return lines and any nested lines inside the syncTarget/authRouteName
checks and the block containing shared.checkSyncConfig is consistently
tab-indented.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a42df756-81d9-4305-b3a7-fa6a3c86bdd7

📥 Commits

Reviewing files that changed from the base of the PR and between 77c2811 and 4763e6d.

📒 Files selected for processing (6)
  • packages/app-cli/app/command-sync.ts
  • packages/app-desktop/gui/ConfigScreen/ConfigScreen.tsx
  • packages/app-desktop/gui/JoplinCloudLoginScreen.tsx
  • packages/app-mobile/components/screens/JoplinCloudLoginScreen.tsx
  • packages/lib/components/shared/config/shouldShowMissingPasswordWarning.test.ts
  • packages/lib/services/joplinCloudUtils.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/lib/services/joplinCloudUtils.ts

@coderabbitai coderabbitai bot added mobile All mobile platforms cli CLI app specific issue and removed enhancement Feature requests and code enhancements desktop All desktop platforms sync sync related issue server Issues related to Joplin Server labels Mar 24, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/lib/BaseSyncTarget.ts (1)

88-100: Missing blank line between methods.

For consistency with the rest of the file (e.g., between lines 86–87, 102–103), add a blank line after the closing brace of isAuthenticatedViaJoplinServerCompatibleSession() and before authRouteName().

🔧 Suggested fix
 		}
 	}
+
 	public authRouteName(): string {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/lib/BaseSyncTarget.ts` around lines 88 - 100, Add a blank line
between the end of the isAuthenticatedViaJoplinServerCompatibleSession() method
and the start of authRouteName(): insert an empty line after the closing brace
of isAuthenticatedViaJoplinServerCompatibleSession() so the method separation
matches the file's style/consistency (look for the closing brace of
isAuthenticatedViaJoplinServerCompatibleSession and the declaration of
authRouteName to place the blank line).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/lib/BaseSyncTarget.ts`:
- Around line 88-100: Add a blank line between the end of the
isAuthenticatedViaJoplinServerCompatibleSession() method and the start of
authRouteName(): insert an empty line after the closing brace of
isAuthenticatedViaJoplinServerCompatibleSession() so the method separation
matches the file's style/consistency (look for the closing brace of
isAuthenticatedViaJoplinServerCompatibleSession and the declaration of
authRouteName to place the blank line).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 706e4067-8c2a-41ca-a9cb-1d89d725fa0e

📥 Commits

Reviewing files that changed from the base of the PR and between 4763e6d and 0020398.

📒 Files selected for processing (4)
  • packages/app-desktop/gui/Root.tsx
  • packages/lib/BaseSyncTarget.ts
  • packages/lib/SyncTargetJoplinCloud.ts
  • packages/lib/SyncTargetJoplinServer.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/lib/SyncTargetJoplinCloud.ts
  • packages/app-desktop/gui/Root.tsx
  • packages/lib/SyncTargetJoplinServer.ts

@coderabbitai coderabbitai bot added enhancement Feature requests and code enhancements desktop All desktop platforms api Joplin API sync sync related issue server Issues related to Joplin Server and removed mobile All mobile platforms cli CLI app specific issue labels Mar 25, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
packages/app-desktop/gui/JoplinCloudLoginScreen.tsx (1)

119-127: Consider renaming props to be target-agnostic.

The prop names joplinCloudWebsite and joplinCloudApi are now used for both Joplin Cloud and Joplin Server, which may confuse future maintainers. Consider renaming to more generic names like syncTargetWebsite and syncTargetApi.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/app-desktop/gui/JoplinCloudLoginScreen.tsx` around lines 119 - 127,
The prop names joplinCloudWebsite and joplinCloudApi in mapStateToProps are
misleading because they are used for both Joplin Cloud and Joplin Server; rename
them to neutral names such as syncTargetWebsite and syncTargetApi across
mapStateToProps return object and any consumers (components/props interfaces)
that reference these props so the values remain the same but the identifiers are
target-agnostic; update mapStateToProps (and corresponding prop types/usages) to
use syncTargetWebsite and syncTargetApi and ensure any imports/prop
destructuring in JoplinCloudLoginScreen and related components are adjusted
accordingly.
packages/app-desktop/gui/ConfigScreen/ConfigScreen.tsx (1)

255-270: Consider aligning goToJoplinCloudLogin with the save-before-navigate pattern.

The existing goToJoplinCloudLogin handler navigates directly without saving settings first, whereas the new goToJoplinServerLogin saves settings before navigation. For consistency and to prevent potential data loss if users have unsaved changes, consider updating the Cloud handler to follow the same pattern:

Suggested change
 if (settings['sync.target'] === SyncTargetRegistry.nameToId('joplinCloud')) {
-	const goToJoplinCloudLogin = () => {
+	const goToJoplinCloudLogin = async () => {
+		const ok = await shared.saveSettings(this);
+		if (!ok) return;
 		this.props.dispatch({
 			type: 'NAV_GO',
 			routeName: 'JoplinCloudLogin',
 		});
 	};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/app-desktop/gui/ConfigScreen/ConfigScreen.tsx` around lines 255 -
270, Update the goToJoplinCloudLogin handler to follow the same
save-before-navigate pattern used by goToJoplinServerLogin: instead of directly
dispatching NAV_GO, call the existing save settings helper (the same function
used by the server flow, e.g. saveSettingsAndThen or the method invoked by
goToJoplinServerLogin) and pass a callback that dispatches { type: 'NAV_GO',
routeName: 'JoplinCloudLogin' } so settings are saved before navigation; keep
the handler name goToJoplinCloudLogin and ensure it still uses
this.props.dispatch inside the callback.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/app-desktop/gui/ConfigScreen/ConfigScreen.tsx`:
- Around line 255-270: Update the goToJoplinCloudLogin handler to follow the
same save-before-navigate pattern used by goToJoplinServerLogin: instead of
directly dispatching NAV_GO, call the existing save settings helper (the same
function used by the server flow, e.g. saveSettingsAndThen or the method invoked
by goToJoplinServerLogin) and pass a callback that dispatches { type: 'NAV_GO',
routeName: 'JoplinCloudLogin' } so settings are saved before navigation; keep
the handler name goToJoplinCloudLogin and ensure it still uses
this.props.dispatch inside the callback.

In `@packages/app-desktop/gui/JoplinCloudLoginScreen.tsx`:
- Around line 119-127: The prop names joplinCloudWebsite and joplinCloudApi in
mapStateToProps are misleading because they are used for both Joplin Cloud and
Joplin Server; rename them to neutral names such as syncTargetWebsite and
syncTargetApi across mapStateToProps return object and any consumers
(components/props interfaces) that reference these props so the values remain
the same but the identifiers are target-agnostic; update mapStateToProps (and
corresponding prop types/usages) to use syncTargetWebsite and syncTargetApi and
ensure any imports/prop destructuring in JoplinCloudLoginScreen and related
components are adjusted accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 68865619-786c-497e-ae65-c374aeb3daa8

📥 Commits

Reviewing files that changed from the base of the PR and between 0020398 and aff8a68.

📒 Files selected for processing (2)
  • packages/app-desktop/gui/ConfigScreen/ConfigScreen.tsx
  • packages/app-desktop/gui/JoplinCloudLoginScreen.tsx

@laurent22
Copy link
Copy Markdown
Owner

There are still CodeRabbit comments

@Kaushalendra-Marcus
Copy link
Copy Markdown
Contributor Author

There are still CodeRabbit comments

got it, let me fix

@coderabbitai coderabbitai bot added mobile All mobile platforms cli CLI app specific issue and removed enhancement Feature requests and code enhancements desktop All desktop platforms api Joplin API sync sync related issue server Issues related to Joplin Server labels Mar 30, 2026
@Kaushalendra-Marcus
Copy link
Copy Markdown
Contributor Author

All CodeRabbit comments have been addressed. Ready for review.

@personalizedrefrigerator personalizedrefrigerator changed the title Desktop: Enable OAuth for Joplin Server Desktop: Resolves #14372: Enable OAuth for Joplin Server Mar 31, 2026
@coderabbitai coderabbitai bot added desktop All desktop platforms sync sync related issue and removed mobile All mobile platforms cli CLI app specific issue labels Mar 31, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/lib/SyncTargetJoplinServer.ts`:
- Around line 72-74: The method requiresPassword in class SyncTargetJoplinServer
has mixed spaces and tabs; fix by normalizing indentation to use tabs only for
all leading whitespace in the method body and signature (e.g., adjust the
requiresPassword() { return false; } block) so it conforms to the TypeScript
file indentation standard.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: dfab280d-a037-41f2-a759-455c8c679449

📥 Commits

Reviewing files that changed from the base of the PR and between aff8a68 and 1c3c188.

📒 Files selected for processing (2)
  • packages/lib/SyncTargetJoplinServer.ts
  • packages/lib/services/joplinCloudUtils.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/lib/services/joplinCloudUtils.ts

@coderabbitai coderabbitai bot added mobile All mobile platforms cli CLI app specific issue and removed desktop All desktop platforms sync sync related issue labels Mar 31, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/lib/SyncTargetJoplinServer.ts`:
- Line 69: The current isAuthenticated() call returns only
isAuthenticatedViaJoplinServerCompatibleSession(), which can false-negative on
cold start because newFileApi() uses a session provider that returns null;
update newFileApi() to provide a persisted session provider (read stored OAuth
session/token from storage and return a Session object) or, alternatively, add a
fallback path inside isAuthenticated() to check for stored OAuth credentials
(e.g., inspect persisted OAuth token/refresh token) and treat that as
authenticated until verification; locate newFileApi() and
isAuthenticatedViaJoplinServerCompatibleSession() and implement the persisted
session retrieval or the fallback credential check so auth status is accurate
after restart.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8f4009cc-9020-424b-92bc-0063e2f3f75a

📥 Commits

Reviewing files that changed from the base of the PR and between 1c3c188 and 61496ac.

📒 Files selected for processing (1)
  • packages/lib/SyncTargetJoplinServer.ts

@Kaushalendra-Marcus
Copy link
Copy Markdown
Contributor Author

Hi @laurent22, all issues have been addressed:

  • Dead code return true; removed from requiresPassword()
  • Test expectations updated in shouldShowMissingPasswordWarning.test.ts
  • All CodeRabbit comments resolved

Tested locally - OAuth flow works end-to-end with a local Joplin Server instance.

@laurent22
Copy link
Copy Markdown
Owner

OAuth is optional for Joplin Server. Could you please also provide a video showing that password login still works as expected?

@Kaushalendra-Marcus
Copy link
Copy Markdown
Contributor Author

OAuth is optional for Joplin Server. Could you please also provide a video showing that password login still works as expected?

Hi @laurent22, here's a video showing that password login still works as expected.

2026-04-03-11-23-40_0NwdQklz.mp4

@laurent22
Copy link
Copy Markdown
Owner

I'm not sure this is working as expected. What is this UUID in the email field? And you didn't demonstrate auth with email/password in that video so I'm wondering if it's still working. It would be a disaster if we break this and people can't sync anymore

@Kaushalendra-Marcus
Copy link
Copy Markdown
Contributor Author

I'm not sure this is working as expected. What is this UUID in the email field? And you didn't demonstrate auth with email/password in that video so I'm wondering if it's still working. It would be a disaster if we break this and people can't sync anymore

Hi @laurent22, to clarify what's shown in the video:

The UUID in the email field (5c054d9d-...) is not a manually entered email, it's the user ID automatically stored by checkIfLoginWasSuccessful after OAuth completes. This is the same pattern used by Joplin Cloud.

Password login still works - if a user enters their email and password manually in the fields and clicks "Check synchronisation configuration", it works as before. The OAuth flow is an additional option, not a replacement.

I can record a separate video showing manual password login if needed.

@laurent22
Copy link
Copy Markdown
Owner

Yes that's the video I meant, that shows that password login is still working

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli CLI app specific issue mobile All mobile platforms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Joplin Server OAuth not enabled on app side

2 participants