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 .py" .. include:: src/linotpauth.py The json response is the same as described by the curl_ request. 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. .. include:: src/linotpauth.cs The json response is the same as described by the curl_ request. Java integration ~~~~~~~~~~~~~~~~ The following code is built using IntelliJ IDEA CE with gradle. File -> New Project -> Gradle -> Java (hook). .. image:: images/java_gradle_java.png Insert your GroupId (com.example.sample) and ArtifactId (linotp-auth). .. image:: images/java_groupid.png Hook "Use auto-import" and "Create directories for empty content roots automatically". .. image:: 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: .. code:: 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" .. image:: images/java_ide_run.png .. image:: images/java_ide_run_execute.png .. include:: src/linotpauth.java The json response is the same as described by the curl_ request. Java Script integration for server side implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Get nodejs from (https://nodejs.org/en/) and install the request dependency via npm command. .. code:: npm install request .. include:: src/linotpauth.js Save the code above (i.e. linotpauth.js) and run it with: .. code:: node linotpauth.js The json response is the same as described by the curl_ request. 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 https://github.com/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 .. code-block:: c 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 .. code-block:: c 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`` .. code-block:: c 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; } .. highlight:: guess PHP integration ~~~~~~~~~~~~~~~~ .. highlight:: guess For usual web applications or portals that might be written in PHP you could use code like 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; } ?> .. highlight:: guess .. [#curl] http://curl.haxx.se