Skip to content

feat: add generic LDAP authentication strategy using ldapts#1496

Open
1saac-k wants to merge 7 commits into
finos:mainfrom
1saac-k:add-ldap-auth
Open

feat: add generic LDAP authentication strategy using ldapts#1496
1saac-k wants to merge 7 commits into
finos:mainfrom
1saac-k:add-ldap-auth

Conversation

@1saac-k
Copy link
Copy Markdown

@1saac-k 1saac-k commented Apr 7, 2026

Closes #1488

Add a new ldap authentication type that integrates with any standards-compliant LDAP server. The existing
activedirectory type relies on the activedirectory2 library which is AD-specific and incompatible with lightweight
LDAP servers (e.g. lldap). This PR introduces a generic LDAP strategy built on ldapts
and passport-custom, lowering the barrier to entry for teams that want simple, manageable user authentication without
setting up a full Active Directory.

This implementation is not lldap-specific — it supports any standards-compliant LDAP server, which means it should also work with Active Directory. As a result, it may be possible in the future to replace the deprecated activedirectory2 and ldapjs dependencies with ldapts.

Testing

  • 11 unit tests added covering authentication success/failure scenarios.
  • Manually verified end-to-end against a running lldap instance.

@1saac-k 1saac-k requested a review from a team as a code owner April 7, 2026 15:01
@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 7, 2026

Deploy Preview for endearing-brigadeiros-63f9d0 canceled.

Name Link
🔨 Latest commit c224238
🔍 Latest deploy log https://app.netlify.com/projects/endearing-brigadeiros-63f9d0/deploys/69fecb30282479000960886e

@linux-foundation-easycla
Copy link
Copy Markdown

linux-foundation-easycla Bot commented Apr 7, 2026

CLA Signed

The committers listed above are authorized under a signed CLA.

@1saac-k
Copy link
Copy Markdown
Author

1saac-k commented Apr 8, 2026

Since this work was done outside of my company, I changed the commit email to my personal email and added GPG signatures to sign the CLA as an individual contributor.

Copy link
Copy Markdown
Contributor

@dcoric dcoric left a comment

Choose a reason for hiding this comment

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

Requesting changes for the two required fixes in the inline comments:

  • LDAP group lookup errors are currently swallowed as “not a member”.
  • Missing LDAP email falls back to an empty string, which can break user upserts with the file DB unique email index.

The remaining inline comments are nits.

Comment thread config.schema.json
Comment thread src/service/routes/auth.ts
Comment thread src/service/passport/ldap.ts Outdated
Comment thread src/service/passport/ldap.ts
Comment thread src/service/passport/ldap.ts Outdated
Comment thread src/service/passport/ldap.ts Outdated
Comment thread src/service/passport/ldap.ts Outdated
@1saac-k
Copy link
Copy Markdown
Author

1saac-k commented Apr 30, 2026

@dcoric Thank you for the detailed review. I'm a bit tied up right now, but I'll take a look within the next few days!

1saac-k added 3 commits May 7, 2026 12:20
Add ldapts (v8.1.7) for modern LDAP client support and
passport-custom (v1.1.1) for custom Passport strategy creation.

Signed-off-by: Kwangjin Ko <kyet@me.com>
Add LDAP auth type definition to config.schema.json and
generated TypeScript types with LdapConfig interface.

Signed-off-by: Kwangjin Ko <kyet@me.com>
Add disabled ldap authentication entry with sensible defaults
for attribute mappings, and group settings.

Signed-off-by: Kwangjin Ko <kyet@me.com>
@1saac-k
Copy link
Copy Markdown
Author

1saac-k commented May 7, 2026

I addressed all review comments and rebased onto the latest main branch. Most of the changes were squashed into the existing commits, and one additional commit (fb0c588) was added.

Comment thread src/service/passport/ldap.ts Outdated
1saac-k added 4 commits May 9, 2026 14:34
Add new LDAP authentication strategy that uses ldapts for LDAP
operations and passport-custom for Passport integration.

The authentication flow:
1. Bind with service account
2. Search for user entry
3. Check group memberships (user/admin)
4. Verify user password via user bind
5. Sync user profile to database

Signed-off-by: Kwangjin Ko <kyet@me.com>
Add ldap module to passport strategy registry and include it
in the list of username/password login strategies.

Signed-off-by: Kwangjin Ko <kyet@me.com>
Test cases cover: successful auth with admin/non-admin roles,
user not found, user group rejection, invalid password,
connection errors, multiple entries in search result,
missing credentials, and escapeFilterValue with normal strings,
LDAP injection attempts, and RFC 4515 special characters.

Signed-off-by: Kwangjin Ko <kyet@me.com>
Signed-off-by: Kwangjin Ko <kyet@me.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented May 15, 2026

Codecov Report

❌ Patch coverage is 82.64840% with 38 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.92%. Comparing base (b6d72fc) to head (c224238).
⚠️ Report is 44 commits behind head on main.

Files with missing lines Patch % Lines
src/service/passport/ldap.ts 82.24% 38 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1496      +/-   ##
==========================================
- Coverage   90.21%   89.92%   -0.30%     
==========================================
  Files          69       70       +1     
  Lines        5511     5728     +217     
  Branches      944      996      +52     
==========================================
+ Hits         4972     5151     +179     
- Misses        521      559      +38     
  Partials       18       18              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@1saac-k
Copy link
Copy Markdown
Author

1saac-k commented May 26, 2026

This PR is still alive. I'm not trying to rush the maintainer; I'm just letting you know that this PR is not stale.

Copy link
Copy Markdown
Contributor

@jescalada jescalada left a comment

Choose a reason for hiding this comment

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

@1saac-k Thanks for the contribution! 🚀

Would you mind adding a short demo or some screenshots showing a working LDAP auth flow, and the resulting database entries when creating users? We want to make sure that database entries are not malformed: this might happen if LDAP gives an array of strings for the user.email among other fields.

// Step 6: Check optional admin group membership
let isAdmin = false;
try {
isAdmin = await isUserInGroup(client, ldapConfig, userDN, ldapConfig.adminGroupDN, username);
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.

Is the admin group membership check optional? If so, we wouldn't want to make adminGroupDN a required field 🤔

// Step 7: Extract profile attributes and sync to database
const userObj = {
username: String(entry[usernameAttr] || username).toLowerCase(),
email: String(email).toLowerCase(),
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.

Should we be handling potential array values in here (and other fields, if relevant)? I don't know how emails are typically stored in LDAP, but if array values are allowed to coexist with strings, this would add invalid emails on calling db.updateUser later.

Note that GitProxy doesn't currently support multiple emails (#1210), you'd have to either implement support for this, or just select the first entry in case entry.mail is an array of strings.

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.

Another thing to note: if you're aware of other fields being multi-valued, we ought to have a helper function that preprocesses them so we can write valid database entries 👍🏼

It could be as simple as:

Array.isArray(value) ? value[0] : value

Although this brings up the question of whether arrays in LDAP are ordered or not and what the default ordering might be...


const createClient = (ldapConfig: LDAPConfig): Client => {
return new Client({
url: ldapConfig.url,
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.

Just a comment after scanning the PR with AI (please verify for accuracy before implementing):

Should we throw an error here if starttls is disabled but the URL starts with ldap://? If starttls is disabled, the verifyPassword function below would send the user/password in plaintext when binding.

groupDN: string,
username: string,
): Promise<boolean> => {
const groupFilter = (ldapConfig.groupSearchFilter || '(member={{dn}})')
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.

Another AI-driven review comment:

Check if groupSearchFilter is wrapped in parentheses, and add them if not so. (Otherwise we should document the requirement so we don't end up with malformed filters)

return done(null, user || false);
} catch (error: unknown) {
const message = handleErrorAndLog(error, 'LDAP authentication error');
return done(message);
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.

passport has its own way of handling errors, we should check if calling done(error) instead of done(message) handles this better or more efficiently (worst case being that it silences the error for some reason).

I'm aware that we're already doing this in src/service/passport/activeDirectory.ts - however if passing the error is the right choice, we should also fix the calls in that file.

@1saac-k
Copy link
Copy Markdown
Author

1saac-k commented May 30, 2026

@1saac-k Thanks for the contribution! 🚀

Would you mind adding a short demo or some screenshots showing a working LDAP auth flow, and the resulting database entries when creating users? We want to make sure that database entries are not malformed: this might happen if LDAP gives an array of strings for the user.email among other fields.

Thank you for taking the time to review this.

  1. I will share configs, screenshots, LDAP-related logs, and DB values showing git-proxy working with lldap (as one example LDAP server).
  2. I will test whether git-proxy handles cases where array values or invalid values exist in LDAP.

I will review and address the items you pointed out as soon as possible!

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.

Discussion and Questions on Local/AD/LDAP Authentication

3 participants