Many corporations may have an intranet or extranet portal that includes links to third-party services that may need user authentication. Administrators often want to leverage an existing portal authorization system to grant single sign-on (SSO) experiences to third-party, web-based applications.


There are several enterprise-level standards for federated identity, notably Security Assertion Markup Language (SAML) and OpenID, that can be used to provide an SSO. We recommend and prefer that the SAML version 2.0 web browser SSO profile be used to implement SSOs for the Widen Collective; however, many organizations may have not yet adopted a federated identity solution.


This article details a method for one-way authentication from any system that implements individual authenticated users. If the portal tracks only groups or roles of users (e.g., users authenticate using a generic group name, like sales or exec), this method cannot be used.


The method described has been designed primarily for ease of implementation and follows several best-practice data security policies.


Overview

The simple one-way portal login implementation depends on the ability of the portal software to generate HTML form markup on the server. The method described cannot be used to generate markup via client-side JavaScript. The implementation process is accomplished by the following:

  1. The portal software collects user information and builds an HTML form containing hidden fields.
  2. A specific set of values, including a secret key, are concatenated and its MD5 hash value is calculated. The hash value is added to the form as the signature field.
  3. The HTML form is submitted via the POST method to the Collective.
  4. The Collective verifies the request by recreating the signature value and comparing it to the signature value sent in the request. If all checks are passed, the user is logged in to the Collective.


Date fields

The table below includes field names that are automatically recognized by the Collective.


Form field nameDescription of value
guid*
Unique identifier value for the user. This value determines user uniqueness in the Collective.


We recommend that an MD5 hash value that is unique to the user, combined with some static text, be used. For example, if the unique identifier for a user is “87,” set the GUID value to the MD5 hash result of the text “87:UniqueGuidForWiden” which is “899f28f95410f995e628c237f5384814.”


If a unique identifier is not available for the user, the user’s email address may be a suitable substitute.
timestamp*
Timestamp that the form was generated. This value is required to be part of the request signature to prevent “replay” forgeries.


RFC1123 format is required.


Widen will reject any requests that were generated 30 minutes ahead or behind the current time.
email*
Email address
signature*
See Calculating the Signature Value
firstname
Given name. Defaults to unknown.
lastname
Given name. Defaults to unknown.
title
Job position
company
Name of company
streetaddress
Address delivery street address
 city
Address city 
 state
Address state or province 
 zip
Address ZIP or postal code 
 roles
Multiple role values must be separated by commas 
 redirectionUrl
Relative URL to redirect to after authentication 
 *Required field 

Data security

All communications will be transmitted over a secure HTTP connection (TLS, aka SSL). Because the transmission is secured at the network layer, a signature value scheme is specified to ensure that form field values were not modified after your server generated the markup.


RFC-1123 date format

The timestamp value requires RFC-1123 date format. The table below provides details about the RFC-1123 format for Java, .NET and PHP programming languages.


Language
Code to generate RFC-1123 pattern date 
Java
US); df.setTimeZone(TimeZone.getTimeZone(“GMT”));
String value = df.format(new Date());
.NET
http://msdn.microsoft.com/enus/library/az4se3k1.aspx#RFC1123
PHP

gmdate(‘D, d M Y H:i:s \G\M\T’)


http://php.net/manual/en/function.gmdate.php

Calculating the signature value

A signature value is required to authenticate that the login request was produced by a trusted server. The value is generated by the server by taking the MD5 hash value of all the values included in the login page, sorted alphabetically by key name and appending a shared secret key at the end of the string. A shared secret key will be issued by Widen.


Field name
Field value 
guid
195d881a58bee9064bec2c133ab436ac
firstname
Benjamin
lastname
Franklin
email
postmaster@usa.gov
timestamp
Fri, 7 Aug 2015, 17:06: GMT
roles
US Region, France Region

The following is an example concatenated string of all form values plus the shared secret key of 3aa9fed216c64d42930552435aa70e01:


postmaster@usa.govBenjamin195d881a58bee9064bec2c133ab436acFranklinUS Region, France RegionThu,18 Jul 2014 17:06:20 GMT3aa9fed216c64d42930552435aa70e01


The following is the MD5 hash signature value of concatenated string: 7ae44530190c84e30de839406c4b4698


MD5 hash function

An MD5 hash is the cryptographic result of a one-way encryption algorithm expressed in the ASCII hex format. A hash cannot be “decoded” to the original value from which it was calculated; however, the same input will always produce the same result. Therefore, it’s useful as a signature method to verify that text was not tampered with in transit between servers.


The MD5 hash of the text “Hello World!” is “ed076287532e86365e841e92bfc50d8c”.


The table below provides MD5 has function details for Java, .NET and PHP programming languages.


Language
MD5 hash function
Java
String md5Hash(String text) throws NoSuchAlgorithmException
{
MessageDigest m = MessageDigest.getInstance(“MD5”);
BigInteger i = new BigInteger(1, m.digest(text.getBytes()));
return String.format(“%1$032x”, i);
}
.NET
http://msdn.microsoft.com/enus/library/system.security.cryptography.
md5.aspx 
PHP
http://php.net/manual/en/function.md5.php

HTML form example

Each data element should be passed as a form value. The same timestamp value should be used both in calculating the signature and the timestamp form value. The order the fields in the form is irrelevant. 


The following is an HTML form example.


<form name=”widenssologin”

action=”https://{client}.widencollective.com/{Client}SSO.html”

method=”post”>

<input type=”hidden” name=”signature” value=”7ae44530190c84e30de839406c4b4698” />

<input type=”hidden” name=”guid” value=”195d881a58bee9064bec2c133ab436ac” />

<input type=”hidden” name=”roles” value=”US Region, France Region” />

<input type=”hidden” name=”firstname” value=”Benjamin” />

<input type=”hidden” name=”lastname” value=”Franklin” />

<input type=”hidden” name=”email” value=”postmaster@usa.gov” />

<input type=”hidden” name=”timestamp” value=”Fri, 18 Jul 2014 17:06:20 GMT” />

</form>


To automatically submit this form on page load, include the following JavaScript in the <body> element:

onload=”document.forms.widenssoform.submit()”


Java Signature Implementation Example

In Java, TreeMap sorts pairs alphabetically by their key value. In other languages, you may have to manually sort the keys so they are appended in alphabetical order.


The following is a Java signature example.


Map<String, String> values = new TreeMap<String, String>();

values.put(“guid”, “195d881a58bee9064bec2c133ab436ac”);

values.put(“firstname”, “Benjamin”);

values.put(“lastname”, “Franklin”);

values.put(“email”, “postmaster@usa.gov”);

values.put(“timestamp”, “Fri, 18 Jul 2014 17:06:20 GMT”);

values.put(“roles”, “US Region, France Region”);


StringBuilder signatureValue = new StringBuilder();

for (String key : values.keySet())

{

signatureValue.append(values.get(key));

}

String secretKey = “3aa9fed216c64d42930552435aa70e01”;

signatureValue.append(secretKey);


System.out.println(“Signature value is: “ + signatureValue.toString());

System.out.println(“MD5 Hash is: “ + md5Hash(signatureValue.toString()));

>> result

The signature value is postmaster@usa.govBenjamin195d881a58bee9064bec2c133ab436acFranklinUS Region,

France Region Fri, 18 Jul 2014 17:06:20 GMT3aa9fed216c64d42930552435aa70e01.


The MD5 hash is 7ae44530190c84e30de839406c4b4698.


Full Java example

The following is a full Java example.


%@ page import=”java.util.*, java.security.*, java.text.*, java.math.*” %>


<html>

<head>

<title>Simple One-way Portal Example</title>

</head>

<body>


<h3>Sending you to the Widen Collective. Please wait...</h3>


In production add this JavaScript to the <b>body</b> element:

<tt>onload=”document.forms.widenssoform.submit()”</tt>

<br/><br/>


<%!

String widenSharedSecretKey()

{

return “3aa9fed216c64d42930552435aa70e01”;

}

//in production use a database connection

Map<String, String> getUserValues()

{

Map<String, String> values = new HashMap<String, String>();

values.put(“guid”, md5Hash(“123” + “:UniqueGuidForWiden”));

values.put(“firstname”, “Benjamin”);

values.put(“lastname”, “Franklin”);

values.put(“email”, “postmaster@usa.gov”);

values.put(“timestamp”, nowInRfc1123Format());

values.put(“roles”, “US Region, France Region”);

System.out.println(“User values are: “ + values.toString());

return values;

}


String nowInRfc1123Format()

{

DateFormat df = new SimpleDateFormat(“EEE, dd MMM yyyy HH:mm:ss z”, Locale.US);

df.setTimeZone(TimeZone.getTimeZone(“GMT”));

return df.format(new Date());

}

String md5Hash(String text)

{

try

{

MessageDigest m = MessageDigest.getInstance(“MD5”);

BigInteger i = new BigInteger(1, m.digest(text.getBytes()));

return String.format(“%1$032x”, i);

catch (Exception e)

{

throw new RuntimeException(e);

}

}


String signatureForRequest(Map<String, String> map)

{

Map<String, String> sortedMap = new TreeMap<String, String>(map);

StringBuilder signatureValue = new StringBuilder();

for (String key : sortedMap.keySet())

{

signatureValue.append(sortedMap.get(key));

}

signatureValue.append(widenSharedSecretKey());

String signatureResult = md5Hash(signatureValue.toString());

System.out.println(“Signature is ‘” + signatureResult + “’ for ‘” + signatureValue + “’”);


return signatureResult;

}

%>


<%

 Map<String, String> userValues = getUserValues();

%>


<form name=”widenssoform” action=”https://localhost.yden.us:8443/WidenDevSSOLogin.html”

method=”post”>

<input type=”hidden” name=”signature” value=”<%= signatureForRequest(userValues) %>” />

<input type=”hidden” name=”guid” value=”<%= userValues.get(“guid”) %>” />

<input type=”hidden” name=”roles” value=”<%= userValues.get(“roles”) %>” />

<input type=”hidden” name=”firstname” value=”<%= userValues.get(“firstname”) %>” />

<input type=”hidden” name=”lastname” value=”<%= userValues.get(“lastname”) %>” />

<input type=”hidden” name=”email” value=”<%= userValues.get(“email”) %>” />

<input type=”hidden” name=”timestamp” value=”<%= userValues.get(“timestamp”) %>” />

<input type=”submit” value=”submit login request” />

</form>


</body>

</html>