shaozi / ldap-authentication

🔐🔐🔐 A simple Nodejs Async LDAP authentication library
BSD 2-Clause "Simplified" License
110 stars 28 forks source link

Error: d: user logged in, but user details could not be found. #67

Open levi-putna opened 5 months ago

levi-putna commented 5 months ago

Possibly a Next.js specific issue.

I'm using Next.js with ldap-authentication. There is no issue when running Next.js in development mode, but as soon as we do a production build, we encounter an issue retrieving user details after authentication.

The authentication process works, but fetching the details fails. There is no code change between the development and production build.

We receive the following error.

Error:

d: user logged in, but user details could not be found. Probabaly usernameAttribute or userSearchBase is wrong?

Authentication Error: d: user logged in, but user details could not be found. Probabaly usernameAttribute or userSearchBase is wrong?
nextjs-1    |     at f (/usr/src/app/.next/server/chunks/2227.js:1:113941)
nextjs-1    |     at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
nextjs-1    |     at async p (/usr/src/app/.next/server/chunks/2227.js:1:115642)
nextjs-1    |     at async j (/usr/src/app/.next/server/app/api/me/route.js:1:3469)
nextjs-1    |     at async /usr/src/app/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:34666
nextjs-1    |     at async eS.execute (/usr/src/app/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:25813)
nextjs-1    |     at async eS.handle (/usr/src/app/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:35920)
nextjs-1    |     at async doRender (/usr/src/app/node_modules/next/dist/server/base-server.js:1377:42)
nextjs-1    |     at async cacheEntry.responseCache.get.routeKind (/usr/src/app/node_modules/next/dist/server/base-server.js:1599:28)
nextjs-1    |     at async NextNodeServer.renderToResponseWithComponentsImpl (/usr/src/app/node_modules/next/dist/server/base-server.js:1507:28)

Example Code:

export const LDAP_CONFIG = {
    url: "ldap://ldap.company.com", 
    baseDN: "cn=users,dc=company,dc=com",
};

// Authenticate user against LDAP
   let authenticated = await authenticate({
       ldapOpts: { url: LDAP_CONFIG.url }, 
       userDn: `uid=${username},${LDAP_CONFIG.baseDN}`, 
       userPassword: password,
       userSearchBase: LDAP_CONFIG.baseDN, 
       usernameAttribute: "uid", 
       username: `${username}`,
       attributes: ["uid", "cn", "givenName", "mail"], 
});

We are using the latest version, but I have reverted back to 3.0.3 and am still encountering the same issue.

ldap-authentication@npm:3.2.2 ├─ Version: 3.2.2 └─ Dependencies └─ ldapjs@npm:^3.0.7 → npm:3.0.7

shaozi commented 5 months ago

Are the dev and production envs use the same LDAP/AD server? Maybe the search base is different? You can use ldapsearch command line to check if it can search the user.

levi-putna commented 5 months ago

I am running in the exact same server, environment, and pointing at the same LDAP/AD server, with identical environment variables except for NODE_ENV=production. The only difference is that one is pre-compiled and the other is running in a just-in-time development environment. I also tried a clean Next.js app and encountered the same issue.

I have been trying to debug the problem. I can see that the authentication part of the request is passing, so I think the issue is in _searchUser(). I can see result.status = 0 , so if (result.status != 0) is false and calling resolve(user), but the user is null. The searchEntry event is never triggering, updating the user details. When running the non-compiled version it is. I also wrapped result.status in parseInt if (parseInt(result.status) != 0) to ensure there wasn't a type issue.

Its pointing at an OpenLDAP server.

levi-putna commented 5 months ago

As mentioned above, I managed to identify and verify the location of the problem: the search function is not returning any results and never triggering the searchEntry event that is used to update the user value.

After some troubleshooting, it appears that the production build process might be the culprit. During minification, class, functions and some event names seem to be altered, which would likely be causing issues with the ldapjs library as its firing en event name that ldap-authentication is not listening for. This issue is likely due to the searchEntryevent having the same name as a function/method in the ldapjs and the magnification processes mistakingly minifying the event name and not just the function.

To address this, I excluded ldapjs from the build process by adding this setting to the Next.js configuration:

const nextConfig = {
  experimental: {
    serverComponentsExternalPackages: ["ldapjs"],
  },
};

Even though this example is specific to Next.js, this issue could happen with other frameworks that compile and minify code. Since ldapjs is now deprecated, there might not be much else we can do about it, so this may be a long term solution.

shaozi commented 5 months ago

That is interesting. I found this discuss on stack overflow: https://stackoverflow.com/questions/78448023/res-onsearchentry-from-ldap-js-does-not-returns-result-in-next-js-production Seems ldapjs constructs the event name during run time based on a class name, which is changed by the build process.