> ## Documentation Index
> Fetch the complete documentation index at: https://ayakaleaf-pro.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# LDAP Authentication

<Info>
  This feature is developed by [yu-i-i/overleaf-cep](https://github.com/yu-i-i/overleaf-cep). Here we offers some documents for your configuration.
</Info>

<Warning>
  Overleaf uses the **passport-ldapauth** library, which is relatively outdated, LDAP compatibility cannot be fully guaranteed. With certain LDAP identity providers (for example, [https://goauthentik.io/](https://goauthentik.io/)), login failures may occur. Therefore, if possible, it is recommended to use OAuth/SAML methed before. For goauthentik, follow [Step-by-step: goauthentik](/on-premises/configuration/overleaf-toolkit/authentication/ldap-authentication#step-by-step-goauthentik) below, which is tested.
</Warning>

### What' LDAP

LDAP is an authentication protocol used for external identity verification. Overleaf Server Pro provides a dedicated LDAP login form in the web interface, separate from the standard authentication method. When a user submits their LDAP username and password, the Overleaf backend verifies the credentials against the configured LDAP server, for example `ldap://ldap:10389`.

<Frame caption="A Server Pro example for LDAP">
  <img src="https://mintcdn.com/ayakaleaf-pro/x9kfDjtWlyyhG_mR/images/on-premises/image-29.png?fit=max&auto=format&n=x9kfDjtWlyyhG_mR&q=85&s=f3ebcacecec45115cf9b1fa55eb1ba94" alt="" width="2804" height="2005" data-path="images/on-premises/image-29.png" />
</Frame>

### Configuration

Internally, Overleaf LDAP uses the [passport-ldapauth](https://github.com/vesse/passport-ldapauth) library. Most of these configuration options are passed through to the `server` config object which is used to configure `passport-ldapauth`. If you are having issues configuring LDAP, it is worth reading the README for `passport-ldapauth` to get a feel for the configuration it expects.

The environment variable `EXTERNAL_AUTH` is required to enable the LDAP authentication module. This environment variable specifies which external authentication methods are activated. The value of this variable is a list. If the list includes `ldap` then LDAP authentication will be activated.

For example: `EXTERNAL_AUTH=ldap saml`

Different from Overleaf CEP, in our ayaka-notes edition, we limit LDAP authentication as a pure authentication method, which is available at `http://your-overleaf.com/ldap/login`.

When using LDAP authentication methods, a user enters a `username` and `password` in the login form, it is attempted:

1. An LDAP user is searched for in the LDAP directory using the filter defined by `OVERLEAF_LDAP_SEARCH_FILTER` and authenticated.
2. If authentication is successful, the Overleaf users database is checked for a user with the primary email address that matches the email address of the authenticated LDAP user:
   * If a matching user is found, the `hashedPassword` field for this user is deleted (if it exists). This ensures that the user can only log in via LDAP authentication in the future.
   * If no matching user is found, a new Overleaf user is created using the email, first name, and last name retrieved from the LDAP server.

<Danger>
  For users who log in via LDAP, we do not store (or remove existed) hashed passwords in Overleaf mongo database.
</Danger>

#### Environment Variables

* `OVERLEAF_LDAP_URL` <strong>(required)</strong>
  * URL of the LDAP server.
    * Example: `ldaps://ldap.example.com:636` (LDAP over SSL)
    * Example: `ldap://ldap.example.com:389` (unencrypted or STARTTLS, if configured).
* `OVERLEAF_LDAP_IDENTITY_SERVICE_NAME`
  * Display name for the LDAP identity service, used on the login page.
  * Default to `Log in with LDAP Provider`.
* `OVERLEAF_LDAP_EMAIL_ATT`
  * The email attribute returned by the LDAP server, default `mail`. Each LDAP user must have at least one email address. If multiple addresses are provided, only the first one will be used.
* `OVERLEAF_LDAP_FIRST_NAME_ATT`
  * The property name holding the first name of the user which is used in the application, usually `givenName`.
* `OVERLEAF_LDAP_LAST_NAME_ATT`
  * The property name holding the family name of the user which is used in the application, usually `sn`.
* `OVERLEAF_LDAP_NAME_ATT`
  * The property name holding the full name of the user, usually `cn`. If either of the two previous variables is not defined, the first and/or last name of the user is extracted from this variable. Otherwise, it is not used.
* `OVERLEAF_LDAP_PLACEHOLDER`
  * The placeholder for the login form, defaults to `Username`.
* `OVERLEAF_LDAP_UPDATE_USER_DETAILS_ON_LOGIN`
  * If set to `true`, updates the LDAP user `first_name` and `last_name` field on login, and turn off the user details form on the `/user/settings` page for LDAP users. Otherwise, details will be fetched only on first login.
* `OVERLEAF_LDAP_BIND_DN`
  * The distinguished name of the LDAP user that should be used for the LDAP connection (this user should be able to search/list accounts on the LDAP server), e.g., `cn=ldap_reader,dc=example,dc=com`. If not defined, anonymous binding is used.
* `OVERLEAF_LDAP_BIND_CREDENTIALS`
  * Password for `OVERLEAF_LDAP_BIND_DN`.
* `OVERLEAF_LDAP_BIND_PROPERTY`
  * Property of the user to bind against the client, defaults to `dn`.
* `OVERLEAF_LDAP_SEARCH_BASE` <strong>(required)</strong>
  * The base DN from which to search for users. E.g., `ou=people,dc=example,dc=com`.
* `OVERLEAF_LDAP_SEARCH_FILTER`
  * LDAP search filter with which to find a user. Use the literal '\{\{username}}' to have the given username be interpolated in for the LDAP search.
    * Example: `(|(uid={{username}})(mail={{username}}))` (user can login with email or with login name).
    * Example: `(sAMAccountName={{username}})` (Active Directory).
* `OVERLEAF_LDAP_SEARCH_SCOPE`
  * The scope of the search can be `base`, `one`, or `sub` (default).
* `OVERLEAF_LDAP_SEARCH_ATTRIBUTES`
  * JSON array of attributes to fetch from the LDAP server, e.g., `["uid", "mail", "givenName", "sn"]`. By default, all attributes are fetched.
* `OVERLEAF_LDAP_STARTTLS`
  * If `true`, LDAP over TLS is used.
* `OVERLEAF_LDAP_TLS_OPTS_CA_PATH`
  * Path to the file containing the CA certificate used to verify the LDAP server's SSL/TLS certificate. If there are multiple certificates, then it can be a JSON array of paths to the certificates. The files must be accessible to the docker container.
    * Example (one certificate): `/var/lib/overleaf/certs/ldap_ca_cert.pem`
    * Example (multiple certificates): `["/var/lib/overleaf/certs/ldap_ca_cert1.pem", "/var/lib/overleaf/certs/ldap_ca_cert2.pem"]`
* `OVERLEAF_LDAP_TLS_OPTS_REJECT_UNAUTH`
  * If `true`, the server certificate is verified against the list of supplied CAs.
* `OVERLEAF_LDAP_CACHE`
  * If `true`, then up to 100 credentials at a time will be cached for 5 minutes.
* `OVERLEAF_LDAP_TIMEOUT`
  * How long the client should let operations live for before timing out, ms (Default: Infinity).
* `OVERLEAF_LDAP_CONNECT_TIMEOUT`
  * How long the client should wait before timing out on TCP connections, ms (Default: OS default).
* `OVERLEAF_LDAP_IS_ADMIN_ATT` and `OVERLEAF_LDAP_IS_ADMIN_ATT_VALUE`
  * When both environment variables are set, the login process updates `user.isAdmin = true` if the LDAP profile contains the attribute specified by `OVERLEAF_LDAP_IS_ADMIN_ATT` and its value either matches `OVERLEAF_LDAP_IS_ADMIN_ATT_VALUE` or is an array containing `OVERLEAF_LDAP_IS_ADMIN_ATT_VALUE`, otherwise `user.isAdmin` is set to `false`. If either of these variables is not set, then the admin status is only set to `true` during admin user creation in Launchpad.

The following five variables are used to configure how user contacts are retrieved from the LDAP server.

* `OVERLEAF_LDAP_CONTACTS_FILTER`
  * The filter used to search for users in the LDAP server to be loaded into contacts. The placeholder '\{\{userProperty}}' within the filter is replaced with the value of the property specified by `OVERLEAF_LDAP_CONTACTS_PROPERTY` from the LDAP user initiating the search. If not defined, no users are retrieved from the LDAP server into contacts.
* `OVERLEAF_LDAP_CONTACTS_SEARCH_BASE`
  * Specifies the base DN from which to start searching for the contacts. Defaults to `OVERLEAF_LDAP_SEARCH_BASE`.
* `OVERLEAF_LDAP_CONTACTS_SEARCH_SCOPE`
  * The scope of the search can be `base`, `one`, or `sub` (default).
* `OVERLEAF_LDAP_CONTACTS_PROPERTY`
  * Specifies the property of the user object that will replace the '\{\{userProperty}}' placeholder in the `OVERLEAF_LDAP_CONTACTS_FILTER`.
* `OVERLEAF_LDAP_CONTACTS_NON_LDAP_VALUE`
  * Specifies the value of the `OVERLEAF_LDAP_CONTACTS_PROPERTY` if the search is initiated by a non-LDAP user. If this variable is not defined, the resulting filter will match nothing. The value `*` can be used as a wildcard.

<Accordion title="Example">
  ```text theme={null}
  OVERLEAF_LDAP_CONTACTS_FILTER=(gidNumber={{userProperty}})
  OVERLEAF_LDAP_CONTACTS_PROPERTY=gidNumber
  OVERLEAF_LDAP_CONTACTS_NON_LDAP_VALUE=1000
  ```

  The above example results in loading into the contacts of the current LDAP user all LDAP users who have the same UNIX `gid`. Non-LDAP users will have all LDAP users with UNIX `gid=1000` in their contacts.
</Accordion>

<Accordion title="Sample variables.env file">
  ```text theme={null}
  OVERLEAF_APP_NAME="Our Overleaf Instance"

  ENABLED_LINKED_FILE_TYPES=project_file,project_output_file,url

  # Enables Thumbnail generation using ImageMagick
  ENABLE_CONVERSIONS=true

  # Disables email confirmation requirement
  EMAIL_CONFIRMATION_DISABLED=true

  ## Nginx
  # NGINX_WORKER_PROCESSES=4
  # NGINX_WORKER_CONNECTIONS=768

  ## Set for TLS via nginx-proxy
  # OVERLEAF_BEHIND_PROXY=true
  # OVERLEAF_SECURE_COOKIE=true

  OVERLEAF_SITE_URL=http://my-overleaf-instance.com
  OVERLEAF_NAV_TITLE=Our Overleaf Instance
  # OVERLEAF_HEADER_IMAGE_URL=http://somewhere.com/mylogo.png
  OVERLEAF_ADMIN_EMAIL=support@example.com

  OVERLEAF_LEFT_FOOTER=[{"text": "Contact your support team", "url": "mailto:support@example.com"}]
  OVERLEAF_RIGHT_FOOTER=[{"text":"Hello, I am on the Right", "url":"https://github.com/yu-i-i/overleaf-cep"}]

  OVERLEAF_EMAIL_FROM_ADDRESS=team@example.com
  OVERLEAF_EMAIL_SMTP_HOST=smtp.example.com
  OVERLEAF_EMAIL_SMTP_PORT=587
  OVERLEAF_EMAIL_SMTP_SECURE=false
  # OVERLEAF_EMAIL_SMTP_USER=
  # OVERLEAF_EMAIL_SMTP_PASS=
  # OVERLEAF_EMAIL_SMTP_NAME=
  OVERLEAF_EMAIL_SMTP_LOGGER=false
  OVERLEAF_EMAIL_SMTP_TLS_REJECT_UNAUTH=true
  OVERLEAF_EMAIL_SMTP_IGNORE_TLS=false
  OVERLEAF_CUSTOM_EMAIL_FOOTER=This system is run by department x

  OVERLEAF_PROXY_LEARN=true
  NAV_HIDE_POWERED_BY=true

  #################
  ## LDAP for CE ##
  #################

  EXTERNAL_AUTH=ldap
  OVERLEAF_LDAP_URL=ldap://ldap.example.com:389
  OVERLEAF_LDAP_STARTTLS=true
  OVERLEAF_LDAP_TLS_OPTS_CA_PATH=/var/lib/overleaf/certs/ldap_ca_cert.pem
  OVERLEAF_LDAP_SEARCH_BASE=ou=people,dc=example,dc=com
  OVERLEAF_LDAP_SEARCH_FILTER=(|(uid={{username}})(mail={{username}}))
  OVERLEAF_LDAP_BIND_DN=cn=ldap_reader,dc=example,dc=com
  OVERLEAF_LDAP_BIND_CREDENTIALS=GoodNewsEveryone
  OVERLEAF_LDAP_EMAIL_ATT=mail
  OVERLEAF_LDAP_FIRST_NAME_ATT=givenName
  OVERLEAF_LDAP_LAST_NAME_ATT=sn
  # OVERLEAF_LDAP_NAME_ATT=cn
  OVERLEAF_LDAP_SEARCH_ATTRIBUTES=["uid", "sn", "givenName", "mail"]

  OVERLEAF_LDAP_UPDATE_USER_DETAILS_ON_LOGIN=true

  OVERLEAF_LDAP_PLACEHOLDER='Username or email address'

  OVERLEAF_LDAP_IS_ADMIN_ATT=mail
  OVERLEAF_LDAP_IS_ADMIN_ATT_VALUE=admin@example.com

  OVERLEAF_LDAP_CONTACTS_FILTER=(gidNumber={{userProperty}})
  OVERLEAF_LDAP_CONTACTS_PROPERTY=gidNumber
  OVERLEAF_LDAP_CONTACTS_NON_LDAP_VALUE='*'
  ```
</Accordion>

## Step-by-step: goauthentik

This walks through a setup that is tested against [goauthentik](https://goauthentik.io/). The examples use the Base DN `dc=example,dc=com`; replace it with yours.

<Steps>
  <Step title="Create a bind account">
    Overleaf first logs in to the directory with an account of its own to find the user. In Authentik, open **Directory > Users**, click **New User**, choose **Internal User** and click **Next**. Enter a username, for example `ldapservice`, and click **Create**:

    <Frame caption="Authentik: create the bind account">
      <img src="https://mintcdn.com/ayakaleaf-pro/TjqwH4XzxYAkHStq/images/on-premises/ldap-authentik-user-create.png?fit=max&auto=format&n=TjqwH4XzxYAkHStq&q=85&s=bf5d9a5aefd048781339157121a9e203" alt="" width="1120" height="810" data-path="images/on-premises/ldap-authentik-user-create.png" />
    </Frame>

    Open the new user and click **Set password**. This password goes into `OVERLEAF_LDAP_BIND_CREDENTIALS`:

    <div style={{ textAlign: "center" }}>
      <Frame caption="Authentik: set the password of the bind account (test instance)">
        <img src="https://mintcdn.com/ayakaleaf-pro/TjqwH4XzxYAkHStq/images/on-premises/ldap-authentik-user-page.png?fit=max&auto=format&n=TjqwH4XzxYAkHStq&q=85&s=96aad0f919dc8e74fef5995b1dde1e78" alt="" width="207" data-path="images/on-premises/ldap-authentik-user-page.png" />
      </Frame>
    </div>

    Note the number of the user in the address bar, for example `19` in `…/#/identity/users/19`. You need it in step 3.
  </Step>

  <Step title="Create the provider and the application">
    Open **Applications > Applications** and click **New Application**. The wizard creates the application and its provider together.

    1\. Give the application a name and a slug, for example `overleaf-ldap`, and click **Next**:

    <Frame caption="Authentik: name and slug of the application">
      <img src="https://mintcdn.com/ayakaleaf-pro/TjqwH4XzxYAkHStq/images/on-premises/ldap-authentik-app.png?fit=max&auto=format&n=TjqwH4XzxYAkHStq&q=85&s=0d3e6b54e9bf552bd81d8b1d6b722701" alt="" width="1120" height="810" data-path="images/on-premises/ldap-authentik-app.png" />
    </Frame>

    2\. Choose **LDAP Provider** and click **Next**:

    <Frame caption="Authentik: choose the LDAP provider">
      <img src="https://mintcdn.com/ayakaleaf-pro/TjqwH4XzxYAkHStq/images/on-premises/ldap-authentik-type.png?fit=max&auto=format&n=TjqwH4XzxYAkHStq&q=85&s=ee63f2682051572a08c1f5bc321a1af6" alt="" width="1120" height="810" data-path="images/on-premises/ldap-authentik-type.png" />
    </Frame>

    3\. Set **Bind Mode** to **Direct binding** and **Search Mode** to **Direct querying**:

    <Frame caption="Authentik: bind and search mode of the LDAP provider">
      <img src="https://mintcdn.com/ayakaleaf-pro/TjqwH4XzxYAkHStq/images/on-premises/ldap-authentik-modes.png?fit=max&auto=format&n=TjqwH4XzxYAkHStq&q=85&s=ea147f1605e76142021d07f775d5ef53" alt="" width="1120" height="810" data-path="images/on-premises/ldap-authentik-modes.png" />
    </Frame>

    4\. Further down, set **Bind Flow** to `default-authentication-flow` and **Base DN** to your base DN, for example `dc=example,dc=com`:

    <Frame caption="Authentik: bind flow and Base DN of the LDAP provider">
      <img src="https://mintcdn.com/ayakaleaf-pro/TjqwH4XzxYAkHStq/images/on-premises/ldap-authentik-basedn.png?fit=max&auto=format&n=TjqwH4XzxYAkHStq&q=85&s=4ce418571d83294ee749d56b13c63d44" alt="" width="1120" height="810" data-path="images/on-premises/ldap-authentik-basedn.png" />
    </Frame>

    5\. Click **Next** until the last page and submit the application.
  </Step>

  <Step title="Let the bind account search the directory">
    Without this permission the bind account only sees itself, the search finds no user and every LDAP login fails.

    Open the provider, go to **Permissions** and click **Assign Role Object Permission**. As **Role**, type the number from step 1 and pick `ak-managed-role--user-<number>`, then turn on **Search full LDAP directory**:

    <Frame caption="Authentik: give the bind account the search permission (test instance)">
      <img src="https://mintcdn.com/ayakaleaf-pro/TjqwH4XzxYAkHStq/images/on-premises/ldap-authentik-assign.png?fit=max&auto=format&n=TjqwH4XzxYAkHStq&q=85&s=5af53c691ae845adcb4ede42c24fb600" alt="" width="1120" height="529" data-path="images/on-premises/ldap-authentik-assign.png" />
    </Frame>

    The role then shows a check mark under **Search full LDAP directory**:

    <Frame caption="Authentik: permissions of an LDAP provider (test instance)">
      <img src="https://mintcdn.com/ayakaleaf-pro/TjqwH4XzxYAkHStq/images/on-premises/ldap-authentik-permissions.png?fit=max&auto=format&n=TjqwH4XzxYAkHStq&q=85&s=24ae7f0dfb6d19e1b3d6584d6ccf0f0b" alt="" width="990" height="488" data-path="images/on-premises/ldap-authentik-permissions.png" />
    </Frame>
  </Step>

  <Step title="Run the LDAP outpost">
    Authentik answers LDAP through an outpost, a separate container. Open **Applications > Outposts**, create an outpost of type **LDAP** with your provider, and deploy it as Authentik describes. It listens on port 389 of the host it runs on. When it is connected, it shows a green check:

    <Frame caption="Authentik: a running LDAP outpost (test instance)">
      <img src="https://mintcdn.com/ayakaleaf-pro/TjqwH4XzxYAkHStq/images/on-premises/ldap-authentik-outposts.png?fit=max&auto=format&n=TjqwH4XzxYAkHStq&q=85&s=cd8af2f7df7b2df24d2369416509faef" alt="" width="990" height="468" data-path="images/on-premises/ldap-authentik-outposts.png" />
    </Frame>

    ```dotenv theme={null}
    OVERLEAF_LDAP_URL=ldap://ldap.example.com:389
    ```
  </Step>

  <Step title="Fill in the DNs">
    The provider page shows the Base DN and an example under **How to connect**:

    <Frame caption="Authentik: overview of an LDAP provider (test instance)">
      <img src="https://mintcdn.com/ayakaleaf-pro/TjqwH4XzxYAkHStq/images/on-premises/ldap-authentik-provider.png?fit=max&auto=format&n=TjqwH4XzxYAkHStq&q=85&s=9aa4b747f4c0f51533454a97d835c18c" alt="" width="990" height="773" data-path="images/on-premises/ldap-authentik-provider.png" />
    </Frame>

    Do not copy the example values as they are:

    * **Bind DN** shows the account you are logged in with. Use the bind account from step 1 instead: `cn=ldapservice,ou=users,<Base DN>`.
    * **Search base** shows the Base DN. Use `ou=users,<Base DN>`.

    ```dotenv theme={null}
    OVERLEAF_LDAP_BIND_DN=cn=ldapservice,ou=users,dc=example,dc=com
    OVERLEAF_LDAP_BIND_CREDENTIALS=<password of ldapservice>
    OVERLEAF_LDAP_SEARCH_BASE=ou=users,dc=example,dc=com
    OVERLEAF_LDAP_SEARCH_FILTER=(cn={{username}})
    ```

    <Warning>
      Authentik keeps a group with the name of every user under `ou=virtual-groups`. Searching the whole Base DN for `(cn=alice)` finds both `cn=alice,ou=users,…` and `cn=alice,ou=virtual-groups,…`, and Overleaf refuses a login that matches more than one entry. Keep the search base at `ou=users,<Base DN>`.
    </Warning>
  </Step>

  <Step title="Check the search">
    Before you start Overleaf, run the search it will do. It must print exactly one `dn:`:

    ```shell theme={null}
    ldapsearch -x -H ldap://ldap.example.com:389 \
      -D cn=ldapservice,ou=users,dc=example,dc=com -w '<password of ldapservice>' \
      -b ou=users,dc=example,dc=com '(cn=alice)' dn
    ```

    No `dn:` at all usually means the permission from step 3 is missing.
  </Step>

  <Step title="Map the admins (optional)">
    The groups of a user are in `memberOf`, as DNs under `ou=groups`. To make the members of the Authentik group `Admins` admins of Overleaf:

    ```dotenv theme={null}
    OVERLEAF_LDAP_IS_ADMIN_ATT=memberOf
    OVERLEAF_LDAP_IS_ADMIN_ATT_VALUE=cn=Admins,ou=groups,dc=example,dc=com
    ```

    <Warning>
      The admin flag is updated on every LDAP login. With a wrong attribute or value, every admin who logs in through LDAP loses the admin rights. Test the mapping with a second admin account first.
    </Warning>
  </Step>
</Steps>

<Accordion title="Tested variables.env for goauthentik">
  ```dotenv title="variables.env" wrap theme={null}
  EXTERNAL_AUTH=ldap
  OVERLEAF_LDAP_IDENTITY_SERVICE_NAME=Log in with Authentik
  OVERLEAF_LDAP_URL=ldap://ldap.example.com:389
  OVERLEAF_LDAP_BIND_DN=cn=ldapservice,ou=users,dc=example,dc=com
  OVERLEAF_LDAP_BIND_CREDENTIALS=<password of ldapservice>
  OVERLEAF_LDAP_SEARCH_BASE=ou=users,dc=example,dc=com
  OVERLEAF_LDAP_SEARCH_FILTER=(cn={{username}})
  OVERLEAF_LDAP_EMAIL_ATT=mail
  OVERLEAF_LDAP_NAME_ATT=name
  OVERLEAF_LDAP_IS_ADMIN_ATT=memberOf
  OVERLEAF_LDAP_IS_ADMIN_ATT_VALUE=cn=Admins,ou=groups,dc=example,dc=com
  OVERLEAF_LDAP_UPDATE_USER_DETAILS_ON_LOGIN=true
  ```
</Accordion>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.