Password Authentication

Introduction

In this guide you will learn how to perform password validation programmatically via REST APIs using IBM Security Verify.

Pre-requisites

An end user can authenticate using password validation if they have an account with a password set in one of the following:

  • the IBM Security Verify Cloud Directory
  • an LDAP directory connected via a Verify Bridge instance
  • a connected Mobile Device Management system (e.g. MaaS360)

The API client used by the application must have the following permissions in IBM Security Verify:

  • Authenticate any user

The application must have acquired an Access Token using the Client Credentials flow or by initiating the Policy-based Authentication flow.

Variables

The following variables are needed for this guide:

VariableExample ValueDescription
tenant_urltenant.verify.ibm.comThe URL of your IBM Security Verify tenant.
access_tokeneWn4Z5xChc3q9B9dqbGgFlsHDh7uhAOnmNeKW5EzThe access token obtained from the token endpoint.
usernametestuserThe username of the user to be authenticated.
password-The password of the user to be authenticated.

Determine Identity Source ID

When calling the password authentication API, the ID of the identity source where the user account exists must be provided. There are a number of ways that this can be achieved which are covered in the following sections.

📘

Performance consideration

Identity Source IDs do not change once they are created. If the application will look up Identity Source IDs at runtime, this should be done once at application start rather than performing the lookup on every authentication event.

Option 1: Hard-code ID

If all of the users of the application come from a single identity source, the ID of this source can be looked up using the Verify Admin UI and then provided as part of application initialization (usually as a configuration property).

1255

Get Identity Source from Admin UI

Option 2: Present available sources to user and allow them to choose

If the password authentication endpoint is called with a GET, it will return an array of all identity sources that are available for password-based authentication:

curl -X GET "https://${tenant_url}/v1.0/authnmethods/password" -H "Authorization: Bearer ${access_token}"

The response has the following format:

{
    "total": 3,
    "password": [
        {
            "name": "Cloud Directory",
            "location": "https://verifylab.verify.ibm.com/v1.0/authnmethods/password/10a9bc20-7511-47e0-b836-f6e18e31f978",
            "id": "10a9bc20-7511-47e0-b836-f6e18e31f978",
            "type": "ibmldap"
        },
        ...
    ],
    "limit": 200,
    "count": 200,
    "page": 1
}

The identity sources are returned as an array in the password attribute. Each array member has a name and an id. The application could present the identity source names to the user and use the associated id when performing password authentication.

Option 3: Lookup identity source by name

A search filter can be specified in the query-string of the GET request to the password authentication endpoint. This can be used to lookup an identity source by name.

curl -X GET "https://${tenant_url}/v1.0/authnmethods/password?search=name%20%3D%20%22Cloud%20Directory%22" -H "Authorization: Bearer ${access_token}"
//Pre-requisites
//var axios = require('axios');
//var tenant_url = "Tenant URL";
//var access_token = "Access Token";
//var directory_name = "Cloud Directory";

var request = {
  method: 'get',
  url: 'https://' + tenant_url +
    '/v1.0/authnmethods/password?search=name = "' + directory_name + '"',
  headers: {
    'Authorization': 'Bearer ' + access_token
  }
};

axios(request).then((response) => {
  var idsource_id = response.data.password[0].id;
  console.log(idsource_id);

  //Next code here.

}).catch((error) => {
  console.log(error);
});

The response will be the same as the lookup without the filter but the password array will only contain a single member.

🚧

Identity Source Names can change

The name of an identity source is an editable field of the identity source definition. If the name of an identity source is modified, applications performing lookup by name will need to be updated.

Option 4: Implement mapping from username

If there is a programmatic way to determine the correct identity source from the username (for example a common prefix or suffix), the application could identify the identity source in this way. The identity source IDs could be hard-coded or looked up using the methods described above.

Perform Authentication

Once an application has gathered the username and password from the user, it must call the password authentication endpoint to check if it is valid.

curl -X POST "https://${tenant_url}/v1.0/authnmethods/password/${idsource_id}" -H "Authorization: Bearer ${access_token}" -H "Content-Type: application/json" --data-raw "{\"username\": \"${username}\",\"password\":\"${password}\"}"
//Pre-requisites
//var axios = require('axios');
//var tenant_url = "Tenant URL";
//var access_token = "Access Token";
//var idsource_id = "Identity Source ID";
//var username = "submitted username";
//var password = "submitted password"

var request = {
  method: 'post',
  url: 'https://' + tenant_url +
    '/v1.0/authnmethods/password/' + idsource_id,
  headers: {
    'Authorization': 'Bearer ' + access_token,
    'Content-Type': 'application/json'
  },
  data: {
    "username": username,
    "password": password
  }
};

axios(request).then((response) => {
  var user = response.data;
  console.log(JSON.stringify(user));

  //Successful authentication
  //Next code here

}).catch((error) => {
  if (error.response.status == 400
   && error.response.data) {
    var err = error.response.data;
    console.log(JSON.stringify(err));

    //Failed authentication
    //Next code here

  } else {
    console.log(error)
  }
});

If the authentication fails, a 400 status is returned. In this case the response body includes a messageId which can be used to identify the reason. A human-readable messageDescription is also returned:

{
  "messageId":"CSIBH0044E",
  "messageDescription":"The system cannot authenticate the user because the user name or password was incorrect."
}

If the authentication is successful a JSON object that contains some information about the authenticated user is returned:

{
    "groups": [
        {
            "sourceId": "10a9bc20-7511-47e0-b836-f6e18e31f978",
            "displayName": "testgroup",
            "name": "6510001VZE"
        }
    ],
    "attributes": [
        {
            "values": [
                "[email protected]"
            ],
            "name": "email"
        },
        {
            "values": [
                "Demo User"
            ],
            "name": "name"
        },
        {
            "values": [
                "User"
            ],
            "name": "familyName"
        },
        {
            "values": [
                "Demo"
            ],
            "name": "givenName"
        },
        {
            "values": [
                "testuser"
            ],
            "name": "username"
        },
        {
            "values": [
                "cloudIdentityRealm"
            ],
            "name": "realm"
        },
        {
            "values": [
                "regular"
            ],
            "name": "userCategory"
        }
    ],
    "id": "651000TFPF"
}

The application now has basic information for the authenticated user. If a full SCIM object for the user is required, this can be obtained from the SCIM endpoint using the id from the user object.

Returning a JWT assertion

If returnJwt=true query string is added to the authentication validation request, the JSON returned following successful authentication will include an assertion attribute which contains a JWT that asserts the successful authentication.

An application client can use this to run an OAuth JWT Bearer flow to obtain an Access Token for the authenticated user (as part of a Policy-based Authentication flow).

💎

Jon Harry, IBM Security