Bookstack: LDAP Auto Login

Created on 20 Jun 2019  路  9Comments  路  Source: BookStackApp/BookStack

Describe the feature you'd like
I'd like a way for LDAP to support auto login, potentially using NTLM to pass the current users credentials enabling our users to be able to open the bookstack web page and automatically be logged in.

I'm not sure if this feature already exists but after looking through other feature/requests bugs i've not been able to find anyone else talking about it.

Describe the benefits this feature would bring to BookStack users
Ease of use for internal users not having to sign in every day to access documents. Links given to team members would be instantly available rather than having to login each time.

Authentication Feature Request

Most helpful comment

On this note, I've written a kludge of a solution to do header-based auth since I also like doing SSO, though via Traefik and a forward authentication endpoint. Admittedly it's been a number of years since I used PHP regularly, so it's ugly... I welcome suggestions on improvement!

I edited app/Http/Controllers/Auth/LoginController.php to add the following in getLogin(). Be forewarned, it's lacking some niceties since it's for specifically my use-case.

  • This needs a centralized setting to decide whether this is enabled and what header is used. The calls to setting() were hard to understand, as well as where I'd add this setting.
  • UserRepo's registerNew wasn't auto-adding my new user to the default role - attachDefaultRole's call to setting() appeared to be returning nothing, but I didn't feel like tracking down why. I force-add the new user to the 'admin' role.
  • Redirects always force to home.
// Handle proxy authentication
        if (!auth()->check() && $request->header('X-Auth-User') !== null) {
                $proxyUsername = $request->header('X-Auth-User');
                $existingUser = $this->userRepo->getByEmail($proxyUsername);
                if ($existingUser !== null) {
                        auth()->login($existingUser);
                        $path = baseUrl('/', true);
                        return redirect($path);
                }
                // Create new
                $registerData = [];
                $registerData['name'] = $proxyUsername;
                $registerData['email'] = $proxyUsername;
                $registerData['password'] = sha1(rand());
                $proxyUser = $this->userRepo->registerNew($registerData, true);
                $this->userRepo->attachSystemRole($proxyUser, "admin"); // TODO: Better?
                auth()->login($proxyUser);
                $path = baseUrl('/', true);
                return redirect($path);
        }

@ssddanbrown I welcome your input on this - is there a better place I can put this check?

All 9 comments

Thanks for writing up this request @MKCPC.

I can see how this may be useful. To be totally open, I'm not looking to spend much more efforts on the LDAP system unless it's a simple addition. Also, I'm not really familiar with NTLM in any way but from a quick search it appears to be microsoft/windows specific which would make me nervous about including support in this project.

If the re-logging-back-in is the main pain point here, you may be able to quickly alleviate that by setting the following in your .env file:

SESSION_LIFETIME=240

The number is the lifetime of a user session in minutes. The default is 120.

@MKCPC , seems like you want to use SSO(Single sign-on) auth mechanism.
@ssddanbrown , NTLM is not recommended to use. It old and a bit not secure.
Better way is using Kerberos authentication. But all component must understand Kerberos tickets:

  1. Web server
  2. BookStack
  3. System

Apache can do this after installing additional modules. Nginx you can build with third-party spnego module.
BookStack - don't support Kerberos.
Debian and RHEL based distributive supports Kerberos after some configuration.
So integration Kerberos-based SSO mechanism is not so easy as it seems.
P.S. If BookStack working on Windows server with IIS - 1 and 3 components support Kerberos auth by default.

@Mant1kor Yes that's correct, a much more elegant way of explaining it! I wasn't aware of the security issue around NTLM so Kerberos makes much more sense. SSO is exactly what I would like to request, all of our team members are logging on via a windows domain account so being able to pass the details of the current logged in user would make for a nice seamless user experience.

@ssddanbrown Thanks for session lifetime setting, i've updated that in the mean time to provide a session time spanning 8 hours so once a member of our team logs in once they will not need to login for the rest of the day at least.

I'm looking at using bookstack for a company-wide internal wiki, and SSO with kerberos is a key requirement. I recently set up a grails+tomcat+centos app to use kerberos (with activedirectory as the kdc) and it wasn't too difficult. kerberos setup on redhat is not too tricky (basically install some packages, generate a keytab file and put it on the server) and likewise grails was also fairly simple to configure (app needs to know where keytab file is, where the kdc server is, and what principal name to use). I didn't have to do anything with tomcat itself so I think that is more of an option than a requirement. In other words, (if my understanding is correct) either the web server or the application need to be "kerberized", not both.

A) If the webserver is kerberized, then it will pass the username to the app and the app just has to do the ldap lookup, which seems to be the approach taken by adldap2-laravel. (and here is a more detailed guide to the whole system setup)

B) Kerberizing the application itself is a valid approach, but would require much more work from the application developer. Grails/Spring makes it easy by providing a plugin, but I couldn't find anything like that for laravel.

Option A seems to be the way to go here, since it would require less work on bookstack's end. Of course, I am far from an expert on any of this, so please take it all with a grain of salt.

On this note, I've written a kludge of a solution to do header-based auth since I also like doing SSO, though via Traefik and a forward authentication endpoint. Admittedly it's been a number of years since I used PHP regularly, so it's ugly... I welcome suggestions on improvement!

I edited app/Http/Controllers/Auth/LoginController.php to add the following in getLogin(). Be forewarned, it's lacking some niceties since it's for specifically my use-case.

  • This needs a centralized setting to decide whether this is enabled and what header is used. The calls to setting() were hard to understand, as well as where I'd add this setting.
  • UserRepo's registerNew wasn't auto-adding my new user to the default role - attachDefaultRole's call to setting() appeared to be returning nothing, but I didn't feel like tracking down why. I force-add the new user to the 'admin' role.
  • Redirects always force to home.
// Handle proxy authentication
        if (!auth()->check() && $request->header('X-Auth-User') !== null) {
                $proxyUsername = $request->header('X-Auth-User');
                $existingUser = $this->userRepo->getByEmail($proxyUsername);
                if ($existingUser !== null) {
                        auth()->login($existingUser);
                        $path = baseUrl('/', true);
                        return redirect($path);
                }
                // Create new
                $registerData = [];
                $registerData['name'] = $proxyUsername;
                $registerData['email'] = $proxyUsername;
                $registerData['password'] = sha1(rand());
                $proxyUser = $this->userRepo->registerNew($registerData, true);
                $this->userRepo->attachSystemRole($proxyUser, "admin"); // TODO: Better?
                auth()->login($proxyUser);
                $path = baseUrl('/', true);
                return redirect($path);
        }

@ssddanbrown I welcome your input on this - is there a better place I can put this check?

On this note, I've written a kludge of a solution to do header-based auth since I also like doing SSO, though via Traefik and a forward authentication endpoint. Admittedly it's been a number of years since I used PHP regularly, so it's ugly... I welcome suggestions on improvement!

I edited app/Http/Controllers/Auth/LoginController.php to add the following in getLogin(). Be forewarned, it's lacking some niceties since it's for specifically my use-case.

* This needs a centralized setting to decide whether this is enabled and what header is used. The calls to `setting()` were hard to understand, as well as where I'd add this setting.

* UserRepo's `registerNew` wasn't auto-adding my new user to the default role - `attachDefaultRole`'s call to `setting()` appeared to be returning nothing, but I didn't feel like tracking down why. I force-add the new user to the 'admin' role.

* Redirects always force to home.
// Handle proxy authentication
        if (!auth()->check() && $request->header('X-Auth-User') !== null) {
                $proxyUsername = $request->header('X-Auth-User');
                $existingUser = $this->userRepo->getByEmail($proxyUsername);
                if ($existingUser !== null) {
                        auth()->login($existingUser);
                        $path = baseUrl('/', true);
                        return redirect($path);
                }
                // Create new
                $registerData = [];
                $registerData['name'] = $proxyUsername;
                $registerData['email'] = $proxyUsername;
                $registerData['password'] = sha1(rand());
                $proxyUser = $this->userRepo->registerNew($registerData, true);
                $this->userRepo->attachSystemRole($proxyUser, "admin"); // TODO: Better?
                auth()->login($proxyUser);
                $path = baseUrl('/', true);
                return redirect($path);
        }

@ssddanbrown I welcome your input on this - is there a better place I can put this check?

I am trying to implement this solution in our environment but I can't seem to get the header correctly. I have it set up in a docker. When I get to login, I print out all the headers (into a file) and it seems to be missing the headers.

Are you running this on a server or docker instance? Am i loosing the headers due to nginx proxy inside docker?

Thanks for the solution. If I can get it to work, it is exactly what we needed.

Grafana has an "auth proxy" login type that seems fit the needs for quite a few people. I think this would be a great, low-cost addition for Bookstack:

  • Minimal code to get it functioning (@FrankPetrilli's working code above, with some checks against settings or .env?).
  • Used along with an auth proxy, like louketo, this enabled support for basically any OIDC IdP.
  • Sessions are effectively managed outside of Bookstack, which is likely desirable for people wanting no-login-page SSO.

@ssddanbrown would you accept a PR for this? I would be happy to get the ball rolling!


Also, @ademxoy, are you using an auth proxy? You will need something like louketo to perform the authn/authz, and add the headers. It sits between Bookstack and Keycloak (or whatever your IdP is), as Keycloak won't add the headers on its own.

I'm happy to help you with the setup if you like!

Edit: Some relevant discussion happening in #1157 also.

Edit: Another relevant issue - #2180

Hmm maybe I'm missing something but I tried this snippet of code and it throws an internal server error. Just doing some debug statements the $this->userRepo is null. Not sure how this code works for others if there's no user object initialized yet.

Undefined property: BookStack\Http\Controllers\Auth\LoginController::$userRepo in file /var/www/bookstack/app/Http/Controllers/Auth/LoginController.php

@nelis249 The code above is a bit old now, so it may not work with the current version of Bookstack.

There's a work-in-progress auth proxy implementation at ryanc-me/Bookstack. Use AUTH_METHOD=auth-proxy to enable it, and pass a header named X-Webauth-User with the user email (that can be configured with AUTH_PROXY_HEADER_NAME=X-My-Header-Name).

Please be careful with this though - it is currently working and accepting logins via header, but has not been tested properly yet, and is missing some checks. I'll be making a pull request once it's finished.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Valiantiam picture Valiantiam  路  3Comments

gab-cass picture gab-cass  路  3Comments

ensemblebd picture ensemblebd  路  3Comments

hhk7734 picture hhk7734  路  3Comments

mtnyaeger picture mtnyaeger  路  3Comments