5.1.2. Example for authentication integration#
Adding LinOTP authentication to your existing source code is quite simple. In this chapter we describe how to authenticate users against LinOTP in the programming languages:
Python
C#
Java
Java Script (server side)
C
PHP
First of all, you will need a working LinOTP. If you don’t have a running LinOTP yet, check https://linotp.org/doc/latest/part-installation/server-installation/index.html to get the installation done. After your LinOTP is installed login to the management Web UI http(s)://LINOTPSERVER/manage and add a UserIDResolver and a Realm. (https://linotp.org/doc/latest/part-management/quickstart.html)
Python integration#
To create a stand alone Python example save the following code as a Python file and run it; i.e. “python3 <file_name>.py”
import requests
import json
def main():
# prepare hostname and user which want to login
hostname = "linotpexample.com"
controller = "validate"
action = "check"
url = "https://" + hostname + "/" + \
controller + "/" + action
post_params = {"user": "testuser",
"pass": "123456",
"realm": "realmname"}
# do request
response = requests.post(url, data=post_params)
# parse response as json
json_response = json.loads(response.text)
authenticated = json_response[u"result"][u"value"]
if authenticated:
print("authenticated successful")
else:
print("not authenticated")
print("json response: {}".format(json_response))
if __name__ == '__main__':
# execute only if run as the entry point into the program
main()
The json response is the same as described in Authentication via “validate” controller with “check” action.
C# integration#
To test the following code you can create a new C# (console) project add the following code. This code will work on Microsoft Visual Studio as well as Xamarin.
If you run it you will see the “authenticated” or “not authenticated” text in the console.
using System;
using System.Net;
using System.Collections.Specialized;
using Newtonsoft.Json;
namespace authlinotp
{
class MainClass
{
public static void Main(string[] args)
{
string hostname = "linotpexample.com";
string controller = "validate";
string action = "check";
string URI = "https://" + hostname + "/" + controller + "/" + action;
var postParameters = new
NameValueCollection{{ "user", "username"},
{ "pass", "123456"},
{ "realm", "realmname"}};
byte[] response;
using (WebClient client = new WebClient())
{
response = client.UploadValues(URI, postParameters);
}
dynamic jsonResp = JsonConvert.DeserializeObject(System.Text.Encoding.UTF8.GetString(response));
bool authenticated = jsonResp.result.value;
if (authenticated)
{
Console.WriteLine("authenticated");
}
else
{
Console.WriteLine("not authenticated");
}
Console.WriteLine(System.Text.Encoding.UTF8.GetString(response));
}
}
}
The json response is the same as described in Authentication via “validate” controller with “check” action.
Java integration#
The following code is built using IntelliJ IDEA CE with gradle.
File -> New Project -> Gradle -> Java (hook).
Insert your GroupId (com.example.sample) and ArtifactId (linotp-auth).
Hook “Use auto-import” and “Create directories for empty content roots automatically”.
Create a new java class by clicking right on src/main/java and copy the following code into this file. .. image:: images/java_ide_add_class.png To your “build.grade” file add:
dependencies {
compile 'org.apache.httpcomponents:httpclient:4.5'
compile group: 'org.glassfish', name: 'javax.json', version: '1.0.4'
}
Then run your code via the IDE click on the green triangle and click “Run”
package com.example.linotpauth;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonReader;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
public class LinOTPAuth {
public static void main(String[] args) throws Exception {
String hostname = "linotpexample.com";
String controller = "validate";
String action = "check";
String requestURL = "https://" + hostname + "/" + controller + "/" + action;
// Create HTTP request via ssl port (https) and pass post parameters
CloseableHttpClient httpclient = HttpClients.createDefault();
try {
HttpPost httpPost = new HttpPost(requestURL);
List <NameValuePair> nvps = new ArrayList <NameValuePair>();
nvps.add(new BasicNameValuePair("user", "username"));
nvps.add(new BasicNameValuePair("pass", "password"));
nvps.add(new BasicNameValuePair("realm", "realmname"));
httpPost.setEntity(new UrlEncodedFormEntity(nvps));
CloseableHttpResponse response = httpclient.execute(httpPost);
try {
HttpEntity entity = response.getEntity();
String s_response = EntityUtils.toString(entity);
JsonReader reader = Json.createReader(new StringReader(s_response));
JsonObject j_response = reader.readObject();
//parse json response for result value
JsonObject j_result = j_response.getJsonObject("result");
Boolean authenticated = j_result.getBoolean("value", false);
if (authenticated) {
System.out.println("authenticated");
}
else {
System.out.println("not authenticated");
}
System.out.println(s_response);
// consume will release the entity
EntityUtils.consume(entity);
} finally {
response.close();
}
} finally {
httpclient.close();
}
}
}
The json response is the same as described in Authentication via “validate” controller with “check” action.
Java Script integration for server side implementation#
Get nodejs from (https://nodejs.org/en/) and install the request dependency via npm command.
npm install request
var request = require('request');
var hostname = "linotpexample.com"
var controller = "validate"
var action = "check"
var reqeustUrl = "https://" + hostname + "/" + controller + "/" + action;
var postParameters = {
user: "username",
pass: "123456",
realm: "realmname"
};
request.post(reqeustUrl, {form: postParameters}, function callback(err, response, body) {
if(err) {
return console.error('request failed', err);
}
var data = JSON.parse(body);
if(data.result.value === true) {
console.log("authenticated")
}
else {
console.log("not authenticated")
}
console.log(body)
});
Save the code above (i.e. linotpauth.js) and run it with:
node linotpauth.js
The json response is the same as described in Authentication via “validate” controller with “check” action.
C integration#
For usual binary programs written in C, you could take a look at the pam_linotp implementation which can be obtained from our GitHub repository LinOTP/LinOTP. The pam_linotp.c file is located in auth_modules/src/libpam-linotp/src/
This creates the URL, that needs to be sent
char * createUrl(CURL *curl_handle,
const char * validateurl,
const char * realm, const char * resConf,
const char * user, const char * password)
{
char * url = NULL;
int size = 300;
int nchars = 0;
// escape user and password
char *escPassword;
char *escUser;
escPassword = curl_easy_escape(curl_handle, password, 0);
escUser = curl_easy_escape(curl_handle, user, 0);
char *escRealm = curl_easy_escape(curl_handle, realm, 0);
char *escResConf = curl_easy_escape(curl_handle, resConf, 0);
if (escPassword == NULL || escUser == NULL)
{
log_error("ERROR: failed to escape user or password");
goto cleanup;
}
url = (char*) malloc (size);
if (url == NULL)
{
log_error("ERROR: could allocate size for url");
goto cleanup;
}
// allocate the memory for url string
memset(url,'\0',size);
nchars = snprintf( url, size-1, "%s?user=%s&pass=%s&realm=%s&resConf=%s",
validateurl,
escUser, escPassword,
escRealm, escResConf );
if (nchars >= size-1)
{
// reallocate
size = nchars +1;
url = (char*) myrealloc (url, size);
if (url == NULL)
{
log_error("ERROR: failed to alloc space for url + user and password");
goto cleanup;
}
memset(url,'\0',size);
snprintf(url, size-1, "%s?user=%s&pass=%s&realm=%s&resConf=%s", validateurl,
escUser, escPassword,
escRealm, escResConf );
}
cleanup:
return url;
}
The authentication itself could look like this
int linotp_auth(const char *user, const char *password, const char *validateurl,
const char *realm, const char *resConf,
int nosslhostnameverify, int nosslcertverify, int debug)
{
CURL *curl_handle = NULL;
int returnValue = PAM_AUTH_ERR;
char *url = NULL;
CURLcode all_status = 0;
char errorBuffer[CURL_ERROR_SIZE];
struct MemoryStruct chunk;
chunk.memory = NULL; /* we expect realloc(NULL, size) to work */
chunk.size = 0; /* no data at this point */
curl_global_init(CURL_GLOBAL_ALL);
curl_handle = curl_easy_init();
if (curl_handle == NULL)
{
log_error("ERROR: could not get curl_handle!");
returnValue = PAM_AUTH_ERR;
goto cleanup;
}
curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, errorBuffer);
url = createUrl(curl_handle, validateurl, realm, resConf, user, password);
if (url == NULL)
{
log_error("ERROR: could allocate size for url");
goto cleanup;
}
if (debug)
{
log_debug("DEBUG: connecting to url:%s",url);
}
all_status = sendRequest(curl_handle, url,
(void *)&chunk, nosslhostnameverify, nosslcertverify);
/* set default return*/
returnValue = PAM_AUTH_ERR;
if (all_status != 0)
{
log_error("ERROR: Error talking to linotpd server at %s: %s", url, errorBuffer);
returnValue = PAM_AUTH_ERR;
goto cleanup;
}
if(chunk.memory == NULL)
{
log_error("ERROR: No response returned for %s: %s", url, errorBuffer);
returnValue = PAM_AUTH_ERR;
goto cleanup;
}
if (strcmp(chunk.memory, LINOTPD_REJECT) == 0)
{
log_warning("WARNING: user '%s' rejected", user);
returnValue = PAM_AUTH_ERR;
goto cleanup;
}
if (strcmp(chunk.memory, LINOTPD_FAIL ) == 0)
{
log_warning("WARNING: authentication for '%s' failed", user);
returnValue = PAM_AUTH_ERR;
goto cleanup;
}
if (strcmp( chunk.memory, LINOTPD_OK ) == 0)
{
log_info("INFO: user '%s' authenticated successfully\n", user);
returnValue = PAM_SUCCESS;
goto cleanup;
}
// default
{
log_error("ERROR: An error occurred for '%s' on '%s'\n", user, validateurl);
returnValue = PAM_AUTH_ERR;
goto cleanup;
}
cleanup:
if (chunk.memory!= NULL)
{
free(chunk.memory);
}
if (url != NULL)
{
free(url);
}
/* we're done with libcurl, so clean it up
cleanup also takes care of escPassword and escUser
*/
curl_global_cleanup();
if (debug)
{
log_debug("linotp_auth exited normally.");
}
return (returnValue);
}
In this example we defined a helper function sendRequest
int sendRequest(CURL *curl_handle, char * url,
struct MemoryStruct * chunk,
int nosslhostnameverify, int nosslcertverify)
{
int all_status = 0;
int status = 0;
all_status = 0;
status = curl_easy_setopt(curl_handle, CURLOPT_URL, url);
all_status += status;
status = curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
all_status += status;
status = curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, chunk);
all_status += status;
status = curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
all_status += status;
if ( nosslhostnameverify )
status = curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
else
status = curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 2L);
all_status += status;
if ( nosslcertverify )
status = curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
else
status = curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 1L);
all_status += status;
status = curl_easy_perform(curl_handle);
all_status += status;
curl_easy_cleanup(curl_handle);
return all_status;
}
PHP integration#
For usual web applications or portals that might be written in PHP you could use code like this:
<?php
public function linotp_auth($user="", $pass="", $realm="") {
$ret=false;
try {
$server = $this->server;
$REQUEST="https://$server/validate/check?pass=$pass&user=$user";
if (""!=$realm)
$REQUEST="$REQUEST&realm=$realm";
if (!function_exists("curl_init"))
die("cURL extension is not installed!");
$ch=curl_init($REQUEST);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $this->verify_peer);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $this->verify_host);
$r=curl_exec($ch);
curl_close($ch);
$Object = json_decode($r);
if (true == $Object->{'result'}->{'status'} )
if (true == $Object->{'result'}->{'value'} )
$ret=true;
} catch (Exception $e) {
print "Error in receiving response from LinOTP server: $e";
}
return $ret;
}
?>