WS: Single Sign On

SSO webservice is the utility to allow users to have access to the entire editor offer with a single account, a user simply log-in to one of the sites of the editor eg. cms.editor.com and when he visits virtualnewspaper.editor.com or vice-versa no login is required.

Global workflow

Ssoflow.jpg

The Flow

When the user log-in on one of the sites that site stores a cookie on the client that will be valid for every host of the editor. The cookie is named vuidjson
When he lands on a site that cookie will be read and the user will be logged in whenever the cookie is correctly parsed
if the user is not saved on the server DB a Webservice will be contacted to retrieve the user informations

The login

Whenever a user logs in a cookie named vuidjson that contains a 3DES encryption JSON URLEncoded with provided username and password will be released by the server.

Example:

the JSON {"login":"username@email.com","password":"123456789"} will be moXWrm7OEpRv9uEGO0ch%2BmoDWze1f2gtfxKtRkw8SJ0Yp3kRB5mtA1Pyq4Qk1rHvo3FHXs4DTfk%3D

The resultant String will be the value of the vuidjson Cookie

Code Example

import java.security.*;
import java.security.spec.*;
import javax.crypto.spec.*;
import javax.crypto.*;
public class DesEncrypter {
 
    javax.crypto.Cipher ecipher;
    javax.crypto.Cipher dcipher;    
    // 8-byte Salt
    byte[] salt = {
        (byte)0xA9, (byte)0x9B, (byte)0xC8, (byte)0x32,
        (byte)0x56, (byte)0x35, (byte)0xE3, (byte)0x03
    };    
    // Iteration count
    int iterationCount = 19;
 
    public DesEncrypter(String passPhrase) {
        try {
            // Create the key
            KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount);
            SecretKey key = SecretKeyFactory.getInstance(
            "PBEWithMD5AndDES").generateSecret(keySpec);
            ecipher = Cipher.getInstance(key.getAlgorithm());
            dcipher = Cipher.getInstance(key.getAlgorithm());
 
            // Prepare the parameter to the ciphers
            AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
 
            // Create the ciphers
            ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
            dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
        } catch (java.security.InvalidAlgorithmParameterException e) {
        } catch (java.security.spec.InvalidKeySpecException e) {
        } catch (javax.crypto.NoSuchPaddingException e) {
        } catch (java.security.NoSuchAlgorithmException e) {
        } catch (java.security.InvalidKeyException e) {
        }
    }
 
    public String encrypt(String str) {
        if (str==null) str="";
        try {
            // Encode the string into bytes using utf-8
            byte[] utf8 = str.getBytes("UTF8");
 
            // Encrypt
            byte[] enc = ecipher.doFinal(utf8);
 
            // Encode bytes to base64 to get a string
            return new sun.misc.BASE64Encoder().encode(enc);
        } catch (javax.crypto.BadPaddingException e) {
        } catch (IllegalBlockSizeException e) {
        } catch (java.io.UnsupportedEncodingException e) {
        } catch (java.io.IOException e) {
        }
        return null;
    }
 
    public String decrypt(String str) {
        if (str==null) str="";
        try {
            // Decode base64 to get bytes
            byte[] dec = new sun.misc.BASE64Decoder().decodeBuffer(str);            
            // Decrypt
            byte[] utf8 = dcipher.doFinal(dec);           
            // Decode using utf-8
            return new String(utf8, "UTF8");
        } catch (javax.crypto.BadPaddingException e) {
        } catch (IllegalBlockSizeException e) {
        } catch (java.io.UnsupportedEncodingException e) {
        } catch (java.io.IOException e) {
        }
        return null;
    }  
}
String pk="thePrivateKey"; // The correct value is in the panel settings module
org.json.JSONObject auth=new org.json.JSONObject();
auth.put("login","username@email.com");
auth.put("password","123456789");
javax.servlet.http.Cookie loginCookie=new javax.servlet.http.Cookie("vuidjson",java.net.URLEncoder.encode(new DesEncrypter(pk).encrypt(auth.toString()),"UTF-8"));
loginCookie.setPath("/");
loginCookie.setDomain(request.getServerName().substring(request.getServerName().indexOf(".")));
loginCookie.setMaxAge(60*60*24*365);    
response.addCookie(loginCookie);

thePrivateKey can be found in the administration panel settings module

The user Land handling

On each page of each site a control should be done to verify the existance of the 'vuidjson' cookie. If it is found it is required to read its value, decrypt, and use the stored username and password to login the user. If the user is not found on the server it must be retrieved by a webservice exposed by the server that released the vuidjson cookie.

Code Example

for(Cookie co:request.getCookies()){
  if(co.getName().equals("vuidjson")){
    org.json.JSONObject auth=new org.json.JSONObject(new utilita.DesEncrypter("thePrivateKey").decrypt(java.net.URLDecoder.decode(co.getValue(),"UTF-8")));
    ResultSet rs = st.executeQuery("select * from users where email = '"+auth.getString("email")+"' and password=md5('"+auth.getString("password")+"')");
    if (rs.next()) {
     // Login the user
    } else {
     // Use the login webservice to retrieve the user
    }
  }
}

The login webservice

the URL is: 'virtualnewspaper.editor.com/sso/getUserByEmail.jsp'

Response example:

{
  customerId: 2715636,
  address: "viale ariosto 328",
  email: "username@email.com",
  status: 1,
  name: "John",
  gender: "_",
  surname: "Doe",
  orders: [
  {
    detailOrder: [
      {
        totalCredits: 12,
        detailOrderId: 1193577,
        price: 0,
        editionsConsulted: "",
        productCode: "",
        expireDate: "2015-05-12",
        idProduct: 141,
        activationDate: "2014-05-13",
        productName: "one year subscription",
        currency: "EUR"
        }
    ],
    confirmed: 1,
    date: "2014-05-13",
    orderId: 1173568
    },
    {
    detailOrder: [
      {
        totalCredits: 1,
        detailOrderId: 1193576,
        price: 0,
        editionsConsulted: "130801edition;",
        productCode: "DIGITQRTSING",
        expireDate: "2015-05-12",
        idProduct: 40,
        activationDate: "2014-05-13",
        productName: "Single Copy",
        currency: "EUR"
      }
    ],
    confirmed: 1,
    date: "2014-05-13",
    orderId: 1173567
    }
  ],
  town: "Firenze",
  password: "08cf56a1e2c7d2da70c03b861e6dd506",
  mobile: ""
}

To retrieve the user informations a call to this service will provide the user informations

private static String readAll(java.io.Reader rd) throws java.io.IOException {
  StringBuilder sb = new StringBuilder();
  int cp;
  while ((cp = rd.read()) != -1) {
    sb.append((char) cp);
  }
  return sb.toString();
}
 
public static org.json.JSONObject readJsonFromUrl(String url) throws java.io.IOException, org.json.JSONException {
  org.json.JSONObject json = null;
  try {
    java.io.InputStream is = new java.net.URL(url).openStream();
    java.io.InputStreamReader isr=new java.io.InputStreamReader(is, "UTF-8");
    java.io.BufferedReader rd = new java.io.BufferedReader(isr);
    String jsonText = readAll(rd);
    json = new org.json.JSONObject(jsonText);
    is.close(); rd.close(); isr.close();      
  } catch(Exception e) {       
  }
  return json;
}
String email = "username@email.com";
String pk = "thePrivateKey"
String authCode = md5(email+pk);
String url = "virtualnewspaper.editor.com/sso/getUserByEmail.jsp?email="+email+"&authCode="+authCode
 
org.json.JSONObject json = utilita.Utilities.readJsonFromUrl(url);
 
if (json.getInt("status")==1) { 
  // Store (or update) and login the user
}


The same webservice must be provided in order to allow VirtualNewspaper to retrieve users informations. The URL must be communicated to Virtualcom support for the platform configuration