VOOT Provider


Simple implementation of the VOOT 2.0 protocol written in PHP.

It only implements the /user/{userid}/groups API call with Basic Authentication as frontend for an LDAP server.


You can quickly test the service like this:

    $ composer install
    $ cp config/config.php.example config/config.php

Modify the config/config.php (see Configuration below) file for your LDAP backend.

Run the service using PHP's built-in web server:

    $ php -S localhost:8080 -t web/



In order to query the API a authUser and authPass need to be provided. Make sure you generate a secure (random) password for authPass, e.g.:

    $ dd if=/dev/urandom count=1 bs=32 | base64


Assuming the following LDAP configuration:

    'ldapUri' => 'ldaps://',
    'bindDn' => 'uid=vpn,cn=sysaccounts,cn=etc,dc=example,dc=org',
    'bindPass' => 's3cr3t',
    'baseDn' => 'cn=accounts,dc=example,dc=org',
    'searchFilterTemplate' => 'uid={{UID}}',
    'membershipAttribute' => 'memberOf',

This can be translated to the following ldapsearch command:

    $ ldapsearch \
        -H ldaps:// \
        -D "uid=vpn,cn=sysaccounts,cn=etc,dc=example,dc=org" \
        -w s3cr3t \
        -b "cn=accounts,dc=example,dc=org" \
        uid=<UID TO SEARCH FOR> \

Based on a working ldapsearch for your LDAP server you should be able to figure out the configuration. The {{UID}} in the searchFilterTemplate will be dynamically replaced by the queried user ID as part of the API call.


You can apply filters to the output of your LDAP server, e.g. when querying memberOf you'll typically get a "DN" of the groups you are a member of, and not the "CN" which is what you want.

The filter firstElementOfDn will take the first value of the DN, so if your DN is cn=developers,cn=groups,cn=accounts,dc=m,dc=tuxed,dc=net then this filter will return developers.

If you are using e.g. the eduPersonEntitlement attribute, the lastUrnPart filter will strip everything except the value after the last :, e.g. will return baz.

In the configuration file you can specify these filters as part of the filterList section. Enable the ones you want to use:

    'filterList' => [
        // remove all entries that do not start with provided substring
        //'dropNotStartsWith' => ['urn:a:b:c:'],
        // when querying LDAP groups, get (first) CN
        //'firstElementOfDn' => [],
        // use only the last part of the URN
        //'lastUrnPart' => [],
        // replace all non-alphanumeric strings to underscores (_)
        //'alphaNum' => [],


Send some HTTP requests to it:

    $ curl -u foo:bar http://localhost:8080/user/fkooman/groups

Replacing fkooman with a valid user ID in your LDAP. This should result in a list of group memberships according to the VOOT 2.0 protocol, a list with objects that contain members id and displayName, e.g.:

        {"id": "foo", "displayName": "foo"},
        {"id": "bar", "displayName": "bar"}

If the user does NOT exist (anymore), a 404 (Not Found) HTTP status code is returned. If the user does not have the required attribute, an empty list, [] is returned.