Authenticating Your APIs

One of DreamFactory’s most popular features is the wide-ranging authentication support. While API Key-based authentication is suffice for many DreamFactory-powered applications, developers often require a higher degree of security through user-specific authentication. In some cases Basic HTTP authentication will get the job done, however many enterprises require more sophisticated and flexible approaches largely because of the growing adoption of Single Sign On (SSO)-based solutions such as Active Directory and LDAP, and use of third-party identity providers and solutions such as AWS Cognito, Auth0, and Okta.

You’ll be pleased to know DreamFactory supports all of these options through a comprehensive set of authentication connectors. These connectors include Active Directory, LDAP, OAuth through well-known identity providers such as Facebook, GitHub, and Twitter, OpenID Connect, and SAML 2.0. In this chapter we’ll walk you through all of the different authentication integration options at your disposal!

Authentication Fundamentals

All DreamFactory APIs are private by default, requiring at minimum an API key for authentication purposes. The API key is associated with a role-based access control (RBAC) which determines what actions the client responsible for supplying the API key can undertake with regards to the API. For instance, it’s possible to create a read-only RBAC which ensures the client can’t access the API’s insertion, modification, or deletion endpoints if they exist. If you’re interested in protecting a database-backed API, you could limit access to a specific table, view, or stored procedure.

Further, DreamFactory supports both anonymous and user-based authentication. The former pertains to the provision of solely an API key, meaning DreamFactory won’t possess any additional information regarding the user responsible for issuing API calls through the client. However in many cases you’ll want to identify the connecting user by requiring authentication via an authentication provider such as Active Directory, LDAP, or Okta. In fact, DreamFactory supports these providers and more, including:

  • Basic Authentication
  • Active Directory
  • LDAP
  • OpenID Connect
  • OAuth, including support for providers such as Facebook and GitHub
  • SAML 2.0

The Authentication Process

Regardless of whether the desired authentication approach is anonymous or user-based, you’ll always supply an API key. This API key is passed along with the request via the X-DreamFactory-Api-Key header. DreamFactory will confirm the key exists (all API keys are listed under the administration console’s Apps tab), and then review the associated RBAC to confirm the request method and URI are permissible according to the RBAC definition.

When user-based authentication is used, DreamFactory will additionally expect a JSON Web Token (JWT) be passed along via the X-DreamFactory-Session-Token header. This JWT is generated by DreamFactory following a successful authentication against the authentication service provider. The following diagram outlines the authentication flow when using a third-party authentication provider such as Active Directory:

DreamFactory Authentication Phase

Once successfully authenticated, DreamFactory will generate the JWT and return it to the client. This JWT should then be submitted along with each subsequent request. DreamFactory will check the token’s validity and signature, examine the associated user’s assigned RBAC (role-based access controls can be assigned on a per user-basis via the user’s Roles tab), and if everything checks out the API call will be processed. The following diagram outlines this process:

DreamFactory Query Phase

Authenticating with Basic HTTP Authentication

[Section forthcoming real soon]

GET /api/v2/mysql/_table/employees HTTP/1.1 Host: demo.dreamfactory.com User-Agent: insomnia/6.6.0 Authorization: Basic d2pAd2pnaWxtb3JlLmNvbTpqYXNvbjEyMw== Accept: /

Authenticating with Azure Active Directory OAuth

<?php

$queryString = $_SERVER['QUERY_STRING'];

# Create a connection
$url = 'https://demo.dreamfactory.com/api/v2/user/session?oauth_callback=true&' . $queryString;
$ch = curl_init($url);

# Setting our options
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

# Get the response
$response = curl_exec($ch);
curl_close($ch);

var_dump(json_decode($response));

Authenticating with OpenID Connect

OpenID affords users the convenience of using an existing account for signing into different websites. Not only does this eliminate the need to juggle multiple passwords, but OpenID also gives users greater control over what personal information is shared with websites that support OpenID. OpenID has been widely adopted since its inception in 2005, with companies such as Google, Microsoft, and Facebook offering OpenID provider services. Additionally, several OpenID libraries are available for integrating with these providers. Commercial editions of DreamFactory (versions 2.7 and newer) also support OpenID, allowing you to use OpenID-based authentication in conjunction with your APIs.

Configuring OpenID Connect

To configure DreamFactory’s OpenID connector, you’ll first need to identify an OpenID provider. This provider manages the credentials your users will use for authentication purposes. For the purposes of this tutorial we’ll use Google’s OpenID implementation. If you want to follow along with this specific example you’ll first need to login to Google’s API Console to create a set of OAuth2 credentials. After logging in, use the search field at the top of the screen to search for OAuth. In the dropdown that appears choose Credentials (see below screenshot).

Configuring DreamFactory with OpenID Connect

Next, click on the Create credentials dropdown and select OAuth client ID:

Create OAuth credentials

Next you’ll be prompted to configure your consent screen. This is the screen the user sees when initiating the authentication process. Click Configure consent screen, and you’ll be prompted to add or confirm the following items:

  • Application type: Will this OpenID integration be used solely for users of your organization, or could users outside of your organization also use it to authenticate?
  • Application name: The name of the application associated with OpenID integration.
  • Application logo: You can optionally upload your organization or project logo for presentation on the consent screen.
  • Support email: An organizational e-mail address which the user could contact with questions and issues.
  • Scopes for Google APIs: This settings determines what data your application will be able to access on behalf of the authenticated user. We’ll use the default scopes for this example (email, profile, and openid).
  • Privacy policy URL: Self-explanatory
  • Terms of service URL: Self-explanatory

After saving these changes, you’ll be prompted for two final pieces of information:

  • The application type: You can select between Web application, Android, Chrome App, iOS, or Other. What you choose here won’t affect DreamFactory’s behavior, so be sure to choose the type most suitable to your specific application.
  • Restrictions: This oddly-named field asks you to supply an authorized JavaScript origin URL and/or an authorized redirect URI. The redirect URI is crucial here because it is the destination where Google will send the authorization code following successful authentication. This code must be intercepted by your application and forwarded on to DreamFactory to complete the process and generate the session token (JWT). If you don’t yet understand exactly how this will work, I suggest just reading on and returning to this configuration screen after seeing an example later in this section.

After saving your changes, you’re ready to configure DreamFactory’s OpenID Connect connector!

Configuring DreamFactory

DreamFactory’s authentication connectors are found in the same location as the standard API connectors. To view them, login to your DreamFactory instance and navigate to the Services tab. Choose Create, and in the dropdown that appears, select OAuth and finally OpenID Connect. You’ll be presented with the following initial configuration screen:

Configuring OpenID as a DreamFactory Service

  • Name: The name will form part of your API URL, so you’ll want to use a lowercase string with no spaces or special characters. Further, you’ll want to typically choose something which allows you to easily identify the API’s purpose. For instance for your Google-backed OpenID Connect authentication API you might choose a name such as google or openid. Keep in mind a lowercased, alphanumeric name is required.
  • Label: The label is used for referential purposes within the administration interface and system-related API responses. You can use something less terse here, such as “Google OpenID API”.
  • Description: Like the label, the description is used for referential purposes within the administration interface and system-related API responses.
  • Active: This determines whether the API is active. By default it is set to active however if you’re not yet ready to begin using the API or would like to later temporarily disable it, just return to this screen and toggle the checkbox.

With these fields completed, click the Config tab to finish configuration. On this screen you’ll be presented with a number of fields, including:

  • Default Role: DreamFactory can automatically assign a default role (learn more about roles here) to a user following successful login. You can identify that role here. If you want to more selectively grant roles, see the Role per App field, introduced below.
  • Discovery Document Endpoint: If your identity provider offers a discovery document endpoint, adding it here will be the fastest way to configure your OpenID Connect connector. This is because doing so will automatically configure the rest of the fields, requiring you to only additionally supply the client ID, client secret, and redirection URL.
  • Authorization Endpoint: This endpoint authorizes access to a protected resource such as the resource owner’s identity. It will be invoked following the resource owner’s successful login and authorization for the requester to access said protected resource.
  • Token Endpoint: The token endpoint is contacted by the client after receiving an authorization code from the authorization endpoint. The client passes this authorization code to the token endpoint where if validated, tokens are returned to the client.
  • User Info Endpoint: This endpoint can be contacted by the client for reason of retrieving information about the logged-in user’s claims (name, email, etc.).
  • Validate ID Token: By checking this field, DreamFactory will validate the ID token by performing tasks such as checking that the encryption algorithm used to encrypt the token matches that specified by the OpenID provider, validating the token signature, and validating the token claims.
  • JWKS URI: This identifies the JSON Web Key Set (JWKS) URI. The JWKS contains the set of public keys used for JWT verification. For instance Google defines this URI as https://www.googleapis.com/oauth2/v3/certs.
  • Scopes: Scopes identify the level of restricted access requested by the client. For instance this might be some user profile information such as the name and e-mail address, or it might be access to an otherwise private service such as the user’s Google Calendar. Using this field you’ll define your scopes in comma-delimited format, such as openid,email,profile.
  • Client ID: Along with the client secret (introduced next), the client ID forms one part of the credentials pair used by the client to interact with the identity provider. You’ll obtain the client ID when creating a developer’s account with the desired identity provider.
  • Client Secret: The client secret is used in conjunction with the client ID to authenticate with the identity provider. You’ll receive this secret along with the client ID when creating a developer’s account with the identity provider.
  • Redirect URL: Perhaps more than any other, the OpenID redirect URL causes considerable confusion amongst developers when creating an OpenID flow. This is because a bit of additional coding within the application is required in order to complete the OpenID flow. Upon successful authentication and authorization on behalf of the identity provider, this URL will be contacted with a set of parameters that the URL’s script must then forward on to DreamFactory. DreamFactory will contact the identity provider one last time to verify the parameters, and then return a session token (JWT) to the script that initiated the forwarding. Without this additional sequence it would not be possible for your custom application to obtain the JWT! Don’t worry though, later in this section we provide an example script demonstrating this process.
  • Role per App: If assigning a blanket role through the Default Role setting is not desired, you can instead use this setting to assign roles on a per application basis.

After configuring these settings, press Save to persist the changes. Next we’ll complete the configuration process by creating a script responsible for completing the OAuth callback and generating the session token (JWT)

The OpenID Authentication Process

Recall when configuring Google’s OpenID settings you added the redirection URI:

OpenID Redirect URL

This endpoint is responsible for intercepting the OAuth callback parameters which need to be forwarded onto DreamFactory in order to generate the session token. The following example PHP script does exactly this, and then returns the JSON object containing the JWT and other user profile data such as the name and e-mail address. Here’s the script:

<?php

$queryString = $_SERVER['QUERY_STRING'];

# Create a connection
$url = 'https://example.com/api/v2/user/session?oauth_callback=true&' . $queryString;
$ch = curl_init($url);

# Setting our options
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

# Get the response
$response = curl_exec($ch);
curl_close($ch);

$json = json_decode($response);

var_dump($json);

And here is the formatted JSON output:

object(stdClass)#1 (12) {
    ["session_token"]=> string(353)
    "JWT_TOKEN_HERE"
    ["id"]=> int(9)
    ["name"]=> string(13) "Jason Gilmore"
    ["first_name"]=> string(5) "Jason"
    ["last_name"]=> string(7) "Gilmore"
    ["email"]=> string(37) "[email protected]"
    ["is_sys_admin"]=> bool(false)
    ["last_login_date"]=> string(19) "2019-07-10 15:04:45"
    ["host"]=> string(20) "dreamfactory-demo-30"
    ["oauth_token"]=> string(129) "OAUTH_TOKEN_HERE"
    ["id_token"]=> string(1245) "ID_TOKEN_HERE"
}

You’ll add a script like this to your application in order to retrieve the JWT (defined within the session_token attribute) and subsequently pass that JWT along with future API requests. So now that all of the pieces to the puzzle are in place, what does the authentication workflow look like? Let’s walk through the entire process.

To create the authentication link, you’ll use this URL:

https://YOUR_DREAMFACTORY_SERVER.com/api/v2/user/session?service=YOUR_SERVICE_NAME

Of course you’ll need to replace YOUR_DREAMFACTORY_SERVER with your DreamFactory server’s domain name, and YOUR_SERVICE_NAME with the name of the OpenID service you created inside DreamFactory.

Step #2. Login Using the Designated Identity Provider

Once the user clicks on this link he will be redirected to the authentication form, which when using Google OpenID looks like this:

Google Account SSO

After entering your e-mail address and password, the user will next be prompted to confirm permission for the client to access a specified set of resources:

Google SSO Permissions Confirmation

Step #3. DreamFactory Generates the Session Key

Once the user clicks Allow, the OpenID provider will return the authorization information to the redirect URL. At this point the script associated with the redirect URL will forward that information on to DreamFactory (see above script), and DreamFactory will return the session token to the script, at which point your application can persist it and include it with subsequent requests.

Authenticating with Okta

Okta is a powerful and popular identity management solution used by thousands of businesses across the globe. Many developers wish to integrate Okta into their application authentication infrastructure, and DreamFactory offers a straightforward solution for doing so. In this tutorial we’ll guide you through the configuration process.

Configuring OKTA

Begin by creating an Okta account at https://www.okta.com if you haven’t already done so. Once logged-in, open the Admin section:

Configuring DreamFactory with OKTA

Next, you’ll add a new application:

Adding a new OKTA Application

Be sure to select SAML 2.0:

Select SAML 2.0

Next, we’ll configure the application:

Configure OKTA Application

Open Setup instructions, making sure you don’t close the tab containing these instructions as we’ll return to them later:

Setup OKTA Instructions

Configuring DreamFactory

Next, we’ll configure DreamFactory to support the new OKTA application. Begin by signing into DreamFactory as an administrator, and then navigate to the Roles section and configure a role for the users who will sign in via Okta SSO. Here’s an example of a role defining access to all APIs (not typical but nonetheless illustrative):

Configuring OKTA as a DreamFactory Service

With the role defined, navigate to the Apps tab and create a new API key which will be associated with this role:

Creating API Key for OKTA

Creating the SAML 2.0 Service

With the role and API key defined, it’s time to create the SAML 2.0 service that will connect your Okta application to DreamFactory. Navigate to Services > Create, choose SSO, and finally SAML 2.0:

Creating a DreamFactory SAML 2.0 Service

Begin by configuring the Info tab:

Configuring SAML 2.0 information

Next, configure the Config tab, filling in the fields with the information found in Okta’s Setup instructions page:

Configuring SAML 2.0 Config

Save these changes, and navigate to the API Docs tab. Here you can see new Okta endpoints:

Check Documentation for SAML 2.0 Service

Adding Okta Users to the DreamFactory Application

With your Okta application created and DreamFactory configured, return to Okta, and in the Admin app navigate to the Application page:

Adding an OKTA user to DreamFactory

Select our DreamFactory application in the list:

Select DreamFactory from OKTA List

Assign this application to the People and Groups who will use it:

Assign DreamFactory Application to People and Groups

Go to the General tab and click the Edit button:

Edit OKTA Application

Change Single sign on URL and Audience URI (SP Entity ID) to the values presented in DreamFactory’s Okta API documentation, and then save the changes:

Change SSO and URI to Values in DreamFactory API Documentation

Application configuration

We’re almost done! Now we can sign in via Okta by going to the service’s /sso endpoint. In our example application we assign Sign in with OKTA button to this endpoint. Clicking this button, DreamFactory can return the X-DreamFactory-Session-Token, which we have to use for comunication with DreamFactory:

Returning the DreamFactory Session Token with OKTA

But how does DreamFactory know where send the token? We have to configure our Relay State for this purpose. Open the Services tab and select your OKTA SSO service. Navigate to the Config tab and update the Relay State field URL which will contain the token returned from DreamFactory. Our example site hosted on http://127.0.0.1:5500 will pass token to the /hello.html page:

Update DreamFactory Relay State Field

DreamFactory will replace the _token_ with a real X-DreamFactory-Session-Token. You might then use JavaScript to persist this token to local storage, or use server-side languages to do the same using cookies:

Persist Session Token to Local Storage

Now we can communicate with DreamFactory by including X-DreamFactory-Session-Token and X-DreamFactory-API-Key in the request header:

Include Token and Api Key in Request Header

Don’t forget add your application to the CORS interface via Config > CORS. Our example CORS configuration allows any requests to all DreamFactory endpoints with any headers. You can configure it to be more secure:

CORS Configuration for OKTA Application

Debugging SAML

You can use a browser extension to view SAML messages as they are passed from client to the authentication service provider.

Managing User Profiles

The Users tab offers a convenient interface for managing user profiles, however it only provides a window for essential information such as e-mail address, password, and phone number. You can however extend the profile by adding custom user attributes. How exactly this is accomplished will depend upon whether the user is authenticated or unauthenticated.

The former would apply when the user is perhaps logged into a profile manager and actively maintaining his own profile via a web form, for instance. The latter would apply in cases where an administrator was editing profiles using an administrative interface, or perhaps a script was bulk updating user information. In this section we’ll show you how to update user profiles to suit both situations.

Adding Custom Attributes to Authenticated Users

If the user is authenticated and managing his own profile, you’ll use the POST /api/v2/user/custom endpoint, passing along the user’s session key and a payload containing the custom attributes. For instance if the user wanted to update his office building and number, then the following payload would be sent to the aforementioned endpoint:

{
    "resource": [{
        "name": "Building",
        "value": "Folsby"
    },
    {
        "name": "Room",
        "value": "456"
    }
    ]
}

Note how the payload itemizes each attribute using a name and value pair. Also, don’t forget to additionally send along the user’s session token using the X-DreamFactory-Session-Token header.

For more information about

Adding Custom Attributes to Unauthenticated Users

If you want to administratively modify an unauthenticated user’s custom attributes, you’ll use the PUT /api/v2/system/user/{ID} endpoint and additionally supply the related parameter. Here is an example URI:

`/api/v2/system/user/7?related=user_custom_by_user_id`

As with authenticated users, you’ll pass along a payload that looks like this:

{
    "resource": [{
        "name": "Building",
        "value": "Folsby"
    },
    {
        "name": "Room",
        "value": "456"
    }
    ]
}

For more information about managing custom user attributes, check out this wiki page.

LDAP Authentication with DreamFactory

Setting up LDAP-based authentication for your users into your DreamFactory workflow is a simple process, and even with LDAPS requires little configuration on the client (DreamFactory) side. In the following section we will guide you through setting up this process. If you would like to create an LDAP server to test, then we have also provided a tutorial below to setup a basic directory with two users.

Testing Your LDAP Connection

Before actually creating an LDAP service, the best way to test that DreamFactory is able to connect to your LDAP server is by creating the following script from within your DreamFactory environment.

vim connection.php

<?php

$connection = ldap_connect('<YourLDAPURI>');
        ldap_set_option($connection, LDAP_OPT_PROTOCOL_VERSION, 3);
        ldap_set_option($connection, LDAP_OPT_REFERRALS, 0);

$search = ldap_search($connection, '<YourBaseDN>', '(uid=<someUser>)');

$result = ldap_get_entries($connection, $search);
   if (isset($result[0]['dn'])) {
     echo $result[0]['dn'];
    }

The LDAP uri will be along the lines of ldap://host:port(if necessary) The BaseDN will be, for example, dc=practice,dc=net The uid can be any user you wish to test with.

Save and run php connection.php and, if succesful, you will get a return looking like:

`uid=tomo,ou=Employee,dc=practice,dc=net`

This shows that DreamFactory can see your LDAP server, and we will be able to configure our service.

Configuring LDAP

To configure LDAP, login to your DreamFactory instance using an administrator account and click on the Services tab:

Configuring LDAP with DreamFactory

On the left side of the interface you’ll see the Create button. Click this button to begin. You’ll be presented with a single dropdown form control titled Select Service Type. You’ll often use this dropdown to generate new APIs as well as configuring authenetication options. For now, lets navigate to LDAP and then Standard LDAP

Creating an LDAP Service

After selecting Standard LDAP, you’ll be presented with the following form:

DreamFactory LDAP Form

Let’s review these fields:

  • Name: The name will form part of the API URL, so use a lowercase string with no spaces or special characters. Further, you’ll want to typically choose something which alloways you to easily identify its purpose. For your LDAP authentication you might choose a name such as ldap, users, or developers. Lowercasing is a requirement.
  • Label: The label is used for referential purposes within the administration interface, and will also be used when selecting the authentication type when logging in (more on this later). Something less terse is ok here, such as “LDAP User Login”.
  • Description: Like the label, the description is used for referential purposes within the administration interface and system-related API responses.

After completing these fields, click on the Config tab localted at the top of the interface. You’ll be presented with the following form:

DreamFactory LDAP Config Screen

There are not too many fields here, so lets go through them:

  • Host: The directory server’s host address. This may be an IP address or domain name. Enter the port number here as well.
  • Default Role: DreamFactory can automatically assign a default role (see more here) to a user following successful login. You can identify that role here.
  • Role per App: If assigning a blanket role through the Default Role setting is not desired, you can instead use this setting to assign roles on a per application basis.
  • Base DN: i.e the starting point wher eyour LDAP server searches for users. For example dc=example,dc=com
  • Account Suffix: Usually the same as your Base DN, e.g. @example.com

After completion, press the save button to generate. After a moment you’ll see a pop up message indicating Service Saved Succesfully. Congratulations!

Now log out of DreamFactory, and you will notice that the login page now has a “Services” dropdown. The service will correspond to the label you assigned when creating the LDAP service.

DreamFactory Login Page with LDAP Option

Select your new ldap authentication method, and you will be able to login with a username (uid) and userPassword.

If you log out, and log back in as the administrator, you will now notice that in the Users Tab, the user you signed in with over ldap has been added.

DreamFactory LDAP User Created

Configuring LDAPS

For LDAPS, the process is much the same as described above, however you will need to go into your DreamFactory server, and make the following change / addition to /etc/ldap/ldap.conf

TLS_REQCERT allow

(you can also use TLS_REQCERT never)

If you are using a client certificate, then make sure the TLS_CACERT option is pointing to the right file also. (You can also use TLS_CACERTDIR to point to a directory rather than a specific file). Remember to also run sudo update-ca-certificates after installing your certificate.

API Authentication Using LDAP

You can make the following API call for your ldap service:

POST https://your-url/api/v2/user/session?service={ldap_service_name}

and in the body, as JSON:

{
    "username": "uid",
    "password": "userPassword"
}

An example response would be:

{
    "session_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJmNDdmNTcxN2ZlNzFiYjg0YWQ3ZDg4ZDBjYjEzMmI5NCIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3QvYXBpL3YyL3VzZXIvc2Vzc2lvbiIsImlhdCI6MTYyMjcwODcwMCwiZXhwIjoxNjIyNzk1MTAwLCJuYmYiOjE2MjI3MDg3MDAsImp0aSI6IkhKWnsfgafgafeghaTFRvVmRzUlAiLCJ1c2VyX2lkIjo5LCJmb3JldmVyIjpmYWxzZX0.Fz6IJolnuuQ0i8bT0HJZm1eALrtmmi6my4mewg2TG78",
    "session_id": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJmNDdmNTcxN2ZlNzFiYjg0YWQ3ZDg4ZDBjYjEzMmI5NCIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3QvYXBpL3YyL3VzZXIvc2VsfgafhgsImlhdCI6MTYyMjcwODcwMCwiZXhwIjoxNjIyNzk1MTAwLCJuYmYiOjE2MjI3MDg3MDAsImp0aSI6IkhKWndPRVNBTFRvVmRzUlAiLCJ1c2VyX2lkIjo5LCJmb3JldmVyIjpmYWxzZX0.Fz6IJolnuuQ0i8bT0HJZm1eALrtmmi6my4mewg2TG78",
    "id": 9,
    "name": "Tomo Norman",
    "first_name": "Tomo",
    "last_name": "norman",
    "email": "[email protected]",
    "is_sys_admin": false,
    "last_login_date": "2021-06-03 08:25:00",
    "host": "ef362f431a16",
    "role": "ldapuser",
    "role_id": 12,
    "groupMembership": [],
    "is_root_admin": false
}

Troubleshooting LDAPS

Client Side

The only client side configuration change you will need to make (as mentioned previously) is having TLS_REQCERT allow in your ldap.conf file, and if you are using a client certificate, double checking the TLS_CACERT option is pointing to the right file, and that you have run sudo update-ca-certificates.

Server Side

As you are no doubt aware, configuring LDAPS can be somewhat of a nightmare. Given everyone’s setup is different, it can be difficult to offer any advice serverside, however we have found the following settings to work:

  • Check the certificate permissions and ownership (should be given to openldap):
sudo chgrp openldap /etc/ldap/<certficatefile>_key.pem
sudo chmod 0640 /etc/ldap/<certificatefile>_key.pem
  • Add the following olc configurations:
dn: cn=config
add: olcTLSCipherSuite
olcTLSCipherSuite: NORMAL
-
add: olcTLSCRLCheck
olcTLSCRLCheck: none
-
add: olcTLSVerifyClient
olcTLSVerifyClient: never
  • Remember to add ldaps:/// to your /etc/default.slapd file. The SLAPD_SERVICES line should look like the following:
SLAPD_SERVICES="ldap:/// ldapi:/// ldaps:///"

Creating an LDAP Server

If you would like to have a try at creating an LDAP server, or just want something simple to test with, we have prepared the following tutorial to help setup a local LDAP server. The following has been made with Ubuntu as the OS.

  1. In your server, first run sudo apt update and sudo apt upgrade to make sure everything is up to date.

  2. Now we can start the configuration process. sudo apt install slapd ldap-utils. This will then ask you to create your administrator password. Once you have done so run sudo dpkg-reconfigure slapd and follow the below instructions:

    • Omit OpenLDAP Server Configuration? -> No
    • DNS Domain Name: E.g. practice.net
    • Organization Name: E.g. Practice
    • Password -> The same that you previoulsy created
    • Database Backend -> use MDB
    • Database removed when slapd is purged? -> No
    • Move Old Database? -> Yes

    You can check everything has installed correctly by running sudo tree /etc/ldap/slapd.d. You should get something back looking like this:

    /etc/ldap/slapd.d
    ├── cn=config
    │   ├── cn=module{0}.ldif
    │   ├── cn=schema
    │   │   ├── cn={0}core.ldif
    │   │   ├── cn={1}cosine.ldif
    │   │   ├── cn={2}nis.ldif
    │   │   └── cn={3}inetorgperson.ldif
    │   ├── cn=schema.ldif
    │   ├── olcBackend={0}mdb.ldif
    │   ├── olcDatabase={0}config.ldif
    │   ├── olcDatabase={-1}frontend.ldif
    │   └── olcDatabase={1}mdb.ldif
    └── cn=config.ldif
    
    2 directories, 11 files
    

Adding Entries

Lets create a couple of entries. We will need a distinguished name, object class, the organizational unit (attributes associated with the class), and the entries themselves. Whenever we add (or modify), we need to create a file in the .ldif format.

Create a file sudo vim add_entries.ldif and add the following (edit as you wish):

dn: ou=Employee, dc=practice, dc=net
objectClass: organizationalUnit
ou: Employee

dn: ou=Groups, dc=practice, dc=net
objectClass: organizationalUnit
ou:Groups

dn: cn=developers, ou=Groups, dc=practice, dc=net
objectClass: posixGroup
cn: developers
gidNumber: 5000

dn: uid=tomo, ou=Employee, dc=practice, dc=net
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: tomo
sn: norman
givenName: Tomo
cn: Tomo Norman
displayName: Tomo Norman
uidNumber: 10000
gidNumber: 5000
userPassword: tomo123
homeDirectory: /home/tomo

dn: uid=alex, ou=Employee, dc=practice, dc=net
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: alex
sn: Vieira
givenName: Alex
cn: Alex Vieira
displayName: Alex Vieira
uidNumber: 20000
gidNumber: 5000
userPassword: alex123
homeDirectory: /home/alex

Notice we have created two organizational units; Employee, and Groups. We’ve then created a “Developers” group, and two employees who belong to the Employee organizational unit, and who are both “Developers” (through gidNumber). To add our new group and employees:

ldapadd -x -D cn=admin,dc=practice,dc=net -W -f add_entries.ldif

It will prompt you for your password, and then return the following:

adding new entry "ou=Employee, dc=practice, dc=net"

adding new entry "ou=Groups, dc=practice, dc=net"

adding new entry "cn=developers, ou=Groups, dc=practice, dc=net"

adding new entry "uid=tomo, ou=Employee, dc=practice, dc=net"

adding new entry "uid=alex, ou=Employee, dc=practice, dc=net"

We can confirm these entries are added by running ldapsearch -x -LLL -b dc=practice,dc=net.

And you should be good to go! You can now create an LDAP authentication service in DreamFactory using the guide above.

Setting up LDAPS

If you already have your own ssl certificate for your domain then you should use that. For the purposes of this excercise we will create our own self-signed certificate in order to get LDAPS up and running:

  1. First lets get the tools required to create our ssl certificates:
    sudo apt install gnutls-bin ssl-cert
    
  2. Create a private key:
    `sudo certtool --generate-privkey --bits 4096 --outfile /etc/ssl/private/mycakey.pem`.
    
  3. Now we will create a template file for our self-signed certificate. sudo vim /etc/ssl/ca.info and add the following (edit to your liking):
    cn = Practice
    ca
    cert_signing_key
    expiration_days = 3650
    
  4. Create the self signed certificate:
    \sudo certtool --generate-self-signed \
    --load-privkey /etc/ssl/private/mycakey.pem \
    --template /etc/ssl/ca.info \
    --outfile /usr/local/share/ca-certificates/mycacert.crt
    

    and run sudo update-ca-certficates and you should get a return stating a certificate has been added to the list of trusted CAs. Something like the following:

    Updating certificates in /etc/ssl/certs...
    1 added, 0 removed; done.
    Running hooks in /etc/ca-certificates/update.d...
    done.
    
  5. We need to then create a private key for the server. We can do so by running:
    sudo certtool --generate-privkey \
    --bits 2048 \
    --outfile /etc/ldap/practice_slapd_key.pem
    

    (Change the filename of the key to match your domain).

  6. Moving on, we will create a template for our server certificate (again, change filenames and common names to match your domain). sudo vim /etc/ssl/localhost.info and add the following:
    organization = Practice
    cn = localhost         
    tls_www_server
    encryption_key
    signing_key
    expiration_days = 365
    

    and then create the server certificate using the just created template:

    sudo certtool --generate-certificate \
    --load-privkey /etc/ldap/localhost_slapd_key.pem \
    --load-ca-certificate /etc/ssl/certs/mycacert.pem \
    --load-ca-privkey /etc/ssl/private/mycakey.pem \
    --template /etc/ssl/localhost.info \
    --outfile /etc/ldap/localhost_slapd_cert.pem
    
  7. We need to adjust our permissions and ownership so that openldap can read our key:
    sudo chgrp openldap /etc/ldap/localhost_slapd_key.pem
    sudo chmod 0640 /etc/ldap/localhost_slapd_key.pem
    
  8. Almost there! Now we just need to tell slapd about our TLS configugration. Create a file sudo vim certinfo.ldif and point everything to the right place.
    dn: cn=config
    add: olcTLSCACertificateFile
    olcTLSCACertificateFile: /etc/ssl/certs/mycacert.pem
    -
    add: olcTLSCertificateFile
    olcTLSCertificateFile: /etc/ldap/localhost_slapd_cert.pem
    -
    add: olcTLSCertificateKeyFile
    olcTLSCertificateKeyFile: /etc/ldap/localhost_slapd_key.pem
    -
    add: olcTLSCipherSuite
    olcTLSCipherSuite: NORMAL
    -
    add: olcTLSCRLCheck
    olcTLSCRLCheck: none
    -
    add: olcTLSVerifyClient
    olcTLSVerifyClient: never
    

    and then update: sudo ldapmodify -Y EXTERNAL -H ldapi:/// -f certinfo.ldif

  9. Finally we just need to go to our slapd config file located at /etc/default/slapd and add ldaps to it. The SLAPD_Service line should end up looking like the below:
    SLAPD_SERVICES="ldap:/// ldapi:/// ldaps:///"
    

    Restarting slapd with sudo systemctl restart slapd will start our ldaps connection. If you run sudo lsof -i -P -n | grep LISTEN you should now see port 636 listening. The output will be similar to this:

    sshd       1269            root    3u  IPv4  19533      0t0  TCP *:22 (LISTEN)
    sshd       1269            root    4u  IPv6  19544      0t0  TCP *:22 (LISTEN)
    systemd-r  2836 systemd-resolve   13u  IPv4  26004      0t0  TCP 127.0.0.53:53 (LISTEN)
    slapd     20316        openldap    8u  IPv4  55269      0t0  TCP *:389 (LISTEN)
    slapd     20316        openldap    9u  IPv6  55270      0t0  TCP *:389 (LISTEN)
    slapd     20316        openldap   11u  IPv4  55274      0t0  TCP *:636 (LISTEN)
    slapd     20316        openldap   12u  IPv6  55275      0t0  TCP *:636 (LISTEN)
    

And thats it! You will now have an LDAP connection over port 389, and an LDAPS connection over port 636, ready to be hooked up to your client(s).

Debugging LDAP and Active Directory

You can use the following PHP script to determine whether your host, base DN, and credentials are correct:

<?php

$host = "HOSTNAME";
$baseDN = "BASEDN";
$username = "USERNAME";
$password = "PASSWORD";

$connection = ldap_connect($host) or die("Could not connect to LDAP server.");

ldap_set_option($connection, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($connection, LDAP_OPT_REFERRALS, 0);

$search = ldap_search($connection, $baseDN, '(uid=' . $username . ')');
$result = ldap_get_entries($connection, $search);

if (isset($result[0]['dn'])) {
    print_r($result);
    $userDN = $result[0]['dn'];
    echo "USER DN: " . $userDN . "\n";
} else {
    echo "USER DN NOT FOUND";
}

$auth = ldap_bind($connection, $userDN, $password);

if ($auth) {
    echo "LDAP bind successful.";
} else {
    echo "LDAP bind failed.";
}


Last modified July 13, 2022: Add alt tags (29549d4)