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:
Variable | Example Value | Description |
---|---|---|
tenant_url | tenant.verify.ibm.com | The URL of your IBM Security Verify tenant. |
access_token | eWn4Z5xChc3q9B9dqbGgFlsHDh7uhAOnmNeKW5Ez | The access token obtained from the token endpoint. |
username | testuser | The 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).
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
Updated about 1 year ago