Replies: 2 comments 2 replies
-
|
Hey! I never built anything with it, I'll do some proper research and see how feasible it is with the current setup, if it's an easy change I'll happily implement, more ways to authenticate are always welcome, just need to make sure security is tight and I do it properly, so may be a while! Thank you for giving Jotty a go and feel free to share any resources/videos/diagrams of how you imagine the Auth to work, that'll definitely help me see what you are envisioning |
Beta Was this translation helpful? Give feedback.
-
|
I had Claude come up with a plan for implementing LDAP support, giving it some specific guidance that I use on several projects, in particular regarding code re-use and preferring library solutions. DISCLAIMER: I have no background in TS and am not an expert in LDAP, but I also don't want to create needless work for you, especially if the AI produces bad code, so I will iterate on this a bit, and see if I can get it working. If you have architectural concerns or ideas, please let me know. If you'd rather not receive AI contributions, that's fine, as well, of course :) LDAP Authentication — Implementation PlanOverviewThis plan describes how to add LDAP/AD (Lightweight Directory Access Protocol) authentication to Jotty, as an alternative to local auth and OIDC. LDAP auth is credential-based: the user enters their username and password in the existing login form, and the server verifies them against the LDAP directory. No browser redirects are needed. This is intentionally a minimal viable implementation. Advanced features (LDAPS certificate pinning, attribute mapping, nested groups, etc.) are explicitly out of scope. Key Difference vs. OIDCOIDC delegates auth to an external provider via browser redirects. LDAP is different: the user's credentials are verified server-side by binding to the LDAP server. This means:
Recommended Library:
|
| Action | File | What changes |
|---|---|---|
| Create | app/_server/actions/auth/ldap.ts |
New file: ldapLogin(username, password) — full LDAP bind + group check logic |
| Modify | app/_server/actions/auth/index.ts |
login action: branch on SSO_MODE === "ldap" → call ldapLogin |
| Modify | app/(loggedOutRoutes)/auth/login/page.tsx |
Extend ssoEnabled / mode detection to cover ldap; ensure the setup redirect doesn't fire in LDAP mode when no local users exist |
| Modify | app/(loggedOutRoutes)/auth/setup/page.tsx |
Disable/hide user registration when SSO_MODE=ldap (users come from LDAP; local accounts don't make sense unless SSO_FALLBACK_LOCAL is set) |
| Extract | app/_server/actions/users/index.ts (or auth/sso.ts) |
Move ensureUser() out of the OIDC callback into a shared location |
| Create | howto/LDAP.md |
User-facing documentation (env vars, example docker-compose, AD-specific notes) |
UI changes
The existing username/password form requires no changes for LDAP mode — it already does what we need. The only login page change is to not show "Sign in with SSO" (OIDC button) when in LDAP mode, and to handle the redirect-to-setup edge case (currently !hasExistingUsers && !ssoEnabled triggers setup; this must also account for LDAP mode where no local users will ever exist).
Error Handling
- LDAP connection failure (server unreachable): surface as a generic "authentication service unavailable" error, log the underlying error server-side.
- Invalid credentials: return the same error message as local auth ("Invalid username or password") — do not distinguish "user not found" from "wrong password" to avoid user enumeration.
- User not in
LDAP_USER_GROUPS: redirect to/auth/login?error=unauthorized(same as OIDC). - Reuse existing brute-force protection from
logininauth/index.ts— it operates on the returned error before LDAP is ever contacted, so it applies naturally.
Uncertainty: Should LDAP connection errors lock out the account like wrong passwords do? Probably not — a misconfigured server shouldn't lock users out. The brute-force counter should only increment on actual credential failures (step 4e above), not on LDAP connectivity errors.
Security Notes
- The service account password should be stored via
LDAP_BIND_PASSWORD_FILEin production (Docker secrets). - For LDAPS (
ldaps://),ldaptsvalidates the server certificate by default using the system CA store. Self-signed certs would require either adding the CA to the container or disabling verification. Disabling verification (rejectUnauthorized: false) is a possible escape hatch but should be documented as insecure. - The user's plaintext password is only held in memory for the duration of the bind — it is never stored or logged.
- Do not log LDAP search filters that contain the submitted username at
INFOlevel — only atDEBUG/DEBUGGER(consistent with how the OIDC callback gates sensitive logging behindprocess.env.DEBUGGER).
Out of Scope (MVP)
- SASL / Kerberos / GSSAPI bind mechanisms
- Nested group membership (group-of-groups)
- LDAP attribute → Jotty profile field mapping (e.g., setting
avatarUrlfromjpegPhoto) - Automatic user deactivation when removed from LDAP
- LDAP referrals
- Connection pooling (one connection per login request is fine at MVP scale)
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Hi,
I am really liking this so far! I have been looking for a nice Note-keeping service and Jotty seems to check all the boxes, and do exactly what I want: simple UI, folders for notes, intuitive multi-user, TODO-lists and even plaintext-storage!!
It really stands out between the other apps. However, I currently don't have OIDC, because I have been trying to avoid that complexity on my server. I just have LDAP (in particular LLDAP).
Have you considered adding support for LDAP? Wouldn't it be simpler than the OIDC implementation?
Thank you in any case!
Beta Was this translation helpful? Give feedback.
All reactions