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).

../../_images/java_gradle_java.png

Insert your GroupId (com.example.sample) and ArtifactId (linotp-auth).

../../_images/java_groupid.png

Hook “Use auto-import” and “Create directories for empty content roots automatically”.

../../_images/java_use_autoimport.png

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”

../../_images/java_ide_run.png ../../_images/java_ide_run_execute.png
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;
}
?>