: 4582 | 108668 | 12353

SSO between Lotus Notes Client and Domino HTTP Access 
Use this IdeaSpace to post ideas about the Notes Client.

: 54
: 56
: 2
: Notes Client / Other
: SSO, Lotus Notes, Domino server, http
: Marius Jaeger1947 09 Jan 2009
:
: / Email
In the Lotus Notes Client I can open any URL by typing it into the addressbar. Why there is no SSO between my Lotus Notes Session and the URL on a Domino Server in the same Domino Directory?



1) Peter von Stöckel2682 (09 Jan 2009)
There's a difference between the certificate used in Notes/Domino, and the certificate used by a web server. You would have to set up SSL for the Domino web server, and then request the web page as https, and it would work.
2) Kerr Rainey3860 (09 Jan 2009)
This should be pretty straight forward too. You are authenticated with the server via the notes client, so there should be a way to get a valid LTPA token if that is set up. So any request to the internal domain should just have a valid LTPA token atttached to it.
3) Roland Reddekop4313 (09 Jan 2009)
If this were possible it would integrate Quickr Places (HTTP access) with Notes. Nobody likes to login more than once.
4) Marius Jaeger1947 (14 Jan 2009)
Here is maybe a way, how IBM could develope a solution:

1. Enhance the location form in the private pernames.ntf with an additional tab, by example called Intranet.
The Intranet tab contains two fields.
A textfield for the url and a checkbox field where you can activate SSO.

2. Extend the Lotus Notes Desktop - show an icon, like the Instant Messaging icon,
when in the Intranet tab in the location document an url is configured.

3. By click on this additional icon, check the location document and if sso is enabled request the
user home server for an ltpaToken for the current Lotus Notes User.

4. Open the Web Browser with the url from the location document, enhanced by the ltpatoken value.

5. The loginUserForm in the domcfg.nsf must parse the url and if there is an ltpatoken value, do not ask
for username and password, but create a session cookie with javascript and then redirect to url to open.
5) Keith Taylor262 (27 Jan 2009)
I spoke to a client developer at lotusphere about this very issue. I explained to him that I wanted to put a toolbar button in the Notes client that would launch iNotes in the internet browser without requesting a password. My real intent is slightly more complex than that. He said the he believed there was a way to retrieve the LPTA token using Lotuscript and that, passed to a Domino web server configured for SSO, would work. However, I'm not finding this function. I think Mr. Jaeger's suggestion may be viable.
6) Marius Jaeger1947 (05 Feb 2009)
I have make a solution to create an LTPAToken with an Agent.
But the Token is not an SSO Token, so you can only login into one server.

Maybe there is a developer which have a solution to generate an SSO LTPAToken.


Here is the code you need:
Create a Database on the Server where you want to login

In this Database create the following Agents:

Agent: (createLTPAToken)
import lotus.domino.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
import java.util.Properties;

/**
* Creates a document with an LTPAToken
*/
public class JavaAgent extends AgentBase {
private LtpaToken generatedToken;
private String tokenAsString;

public void NotesMain() {

try {
Session session = getSession();
AgentContext agentContext = session.getAgentContext();
Database db = agentContext.getCurrentDatabase();

generatedToken = new LtpaToken();
generatedToken = generatedToken.generate(session.getUserName(), new Date(), new Date());

tokenAsString = generatedToken.toString();

Document doc = db.createDocument();
doc.appendItemValue("Form", "SSOLoginNotesDomino");
doc.appendItemValue("token", tokenAsString);
doc.save();
} catch(Exception e) {
e.printStackTrace();
}
}
}

class LtpaToken {
private byte[] creation;
private Date creationDate;
private byte[] digest;
private byte[] expires;
private Date expiresDate;
private byte[] hash;
private byte[] header;
private Log log;
private String ltpaToken;
private Properties properties = null;
private byte[] rawToken;
private byte[] user;

/**
* Cookie name.
*/
public final static String COOKIE_NAME = "LtpaToken";
private final static String COOKIE_DOMAIN = ".blabla.bla.net";
private final static String DOMINO_SECRET = "WXYzWxWXYZx1yz9wxYZWx+9wxyz=";

/**
* Constructor for the LtpaToken object
*/
public LtpaToken() {
init();
}

/**
* Constructor for the LtpaToken object
*
* @param token Description of the Parameter
*/
public LtpaToken( String token ) {
init();
ltpaToken = token;
rawToken = Base64.decode( token.toCharArray() );
user = new byte[( rawToken.length ) - 40];
for ( int i = 0; i < 4; i++ ) {
header = rawToken;
}
for ( int i = 4; i < 12; i++ ) {
creation[i - 4] = rawToken;
}
for ( int i = 12; i < 20; i++ ) {
expires[i - 12] = rawToken;
}
for ( int i = 20; i < ( rawToken.length - 20 ) ; i++ ) {
user[i - 20] = rawToken;
}
for ( int i = ( rawToken.length - 20 ); i < rawToken.length; i++ ) {
digest[i - ( rawToken.length - 20 )] = rawToken;
}
creationDate = new Date( Long.parseLong( new String( creation ), 16 ) * 1000 );
expiresDate = new Date( Long.parseLong( new String( expires ), 16 ) * 1000 );
}

/**
* Description of the Method
*/
private void init() {
creation = new byte[8];
digest = new byte[20];
expires = new byte[8];
hash = new byte[20];
header = new byte[4];
}

/**
* Creates a new SHA-1 <code>MessageDigest</code> instance.
*
* @return The instance.
*/
private MessageDigest getDigest() {
try {
return MessageDigest.getInstance( "SHA-1" );
} catch ( NoSuchAlgorithmException nsae ) {
}
return null;
}

/**
* Generates a new LtpaToken w ith given parameters.
*
* @param canonicalUser User name in canonical form. e.g. 'CN=Robert Kelly/OU=MIS/O=EBIMED'.
* @param tokenCreation Token creation date.
* @param tokenExpires Token expiration date.
* @return The generated token.
*/
public static LtpaToken generate( String canonicalUser, Date tokenCreation, Date tokenExpires ) {
LtpaToken ltpa = new LtpaToken();

Calendar calendar = Calendar.getInstance();
MessageDigest md = ltpa.getDigest();

ltpa.header = new byte[]{0, 1, 2, 3};
ltpa.user = canonicalUser.getBytes();

byte[] token = null;
calendar.setTime( tokenCreation );

ltpa.creation = Long.toHexString( calendar.getTime().getTime() / 1000 ).toUpperCase().getBytes();
calendar.setTime( tokenExpires );
calendar.add(Calendar.HOUR,8);

ltpa.expires = Long.toHexString( calendar.getTime().getTime() / 1000 ).toUpperCase().getBytes();
ltpa.user = canonicalUser.getBytes();

token = concatenate( token, ltpa.header );
token = concatenate( token, ltpa.creation );
token = concatenate( token, ltpa.expires );
token = concatenate( token, ltpa.user );
md.update( token );
ltpa.digest = md.digest( Base64.decode( DOMINO_SECRET.toCharArray() ) );
token = concatenate( token, ltpa.digest );

return new LtpaToken( new String( Base64.encode( token ) ) );
}

/**
* Helper method to concatenate a byte array.
*
* @param a Byte array a.
* @param b Byte array b.
* @return a + b.
*/
private static byte[] concatenate( byte[] a, byte[] b ) {
if ( a == null ) {
return b;
} else {
byte[] bytes = new byte[a.length + b.length];

System.arraycopy( a, 0, bytes, 0, a.length );
System.arraycopy( b, 0, bytes, a.length, b.length );
return bytes;
}
}

/**
* String representation of LtpaToken object.
*
* @return Returns token String suitable for cookie value.
*/
public String toString() {
return ltpaToken;
}
}

class Base64 {
private static char[] map1 = new char[64];
static {
int i=0;
for (char c='A'; c<='Z'; c++) map1[i++] = c;
for (char c='a'; c<='z'; c++) map1[i++] = c;
for (char c='0'; c<='9'; c++) map1[i++] = c;
map1[i++] = '+'; map1[i++] = '/';
}

// Mapping table from Base64 characters to 6-bit nibbles.
private static byte[] map2 = new byte[128];
static {
for (int i=0; i<map2.length; i++) map2 = -1;
for (int i=0; i<64; i++) map2[map1] = (byte)i;
}

/**

* Encodes a string into Base64 format.
* No blanks or line breaks are inserted.
* @param s a String to be encoded.
* @return A String with the Base64 encoded data.
*/
public static String encodeString (String s) {
return new String(encode(s.getBytes()));
}

/**
* Encodes a byte array into Base64 format.
* No blanks or line breaks are inserted.
* @param in an array containing the data bytes to be encoded.
* @return A character array with the Base64 encoded data.
*/
public static char[] encode (byte[] in) {
return encode(in,in.length);
}

/**
* Encodes a byte array into Base64 format.
* No blanks or line breaks are inserted.
* @param in an array containing the data bytes to be encoded.
* @param iLen number of bytes to process in <code>in</code>.
* @return A character array with the Base64 encoded data.
*/
public static char[] encode (byte[] in, int iLen) {
int oDataLen = (iLen*4+2)/3; // output length without padding
int oLen = ((iLen+2)/3)*4; // output length including padding
char[] out = new char[oLen];
int ip = 0;
int op = 0;
while (ip < iLen) {
int i0 = in[ip++] & 0xff;
int i1 = ip < iLen ? in[ip++] & 0xff : 0;
int i2 = ip < iLen ? in[ip++] & 0xff : 0;
int o0 = i0 >>> 2;
int o1 = ((i0 & 3) << 4) | (i1 >>> 4);
int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6);
int o3 = i2 & 0x3F;
out[op++] = map1[o0];
out[op++] = map1[o1];
out[op] = op < oDataLen ? map1[o2] : '='; op++;
out[op] = op < oDataLen ? map1[o3] : '='; op++;
}
return out;
}

/**
* Decodes a string from Base64 format.
* @param s a Base64 String to be decoded.
* @return A String containing the decoded data.
* @throws IllegalArgumentException if the input is not valid Base64 encoded data.
*/
public static String decodeString (String s) {
return new String(decode(s));
}

/**
* Decodes a byte array from Base64 format.
* @param s a Base64 String to be decoded.
* @return An array containing the decoded data bytes.
* @throws IllegalArgumentException if the input is not valid Base64 encoded data.
*/
public static byte[] decode (String s) {
return decode(s.toCharArray());
}

/**
* Decodes a byte array from Base64 format.
* No blanks or line breaks are allowed within the Base64 encoded data.
* @param in a character array containing the Base64 encoded data.
* @return An array containing the decoded data bytes.
* @throws IllegalArgumentException if the input is not valid Base64 encoded data.
*/
public static byte[] decode (char[] in) {
int iLen = in.length;
if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4.");

while (iLen > 0 && in[iLen-1] == '=') iLen--;

int oLen = (iLen*3) / 4;
byte[] out = new byte[oLen];
int ip = 0;
int op = 0;
while (ip < iLen) {
int i0 = in[ip++];
int i1 = in[ip++];
int i2 = ip < iLen ? in[ip++] : 'A';
int i3 = ip < iLen ? in[ip++] : 'A';
if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127)
throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
int b0 = map2[i0];
int b1 = map2[i1];
int b2 = map2[i2];
int b3 = map2[i3];
if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0)
throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
int o0 = ( b0 <<2) | (b1>>>4);
int o1 = ((b1 & 0xf)<<4) | (b2>>>2);
int o2 = ((b2 & 3)<<6) | b3;
out[op++] = (byte)o0;
if (op<oLen) out[op++] = (byte)o1;
if (op<oLen) out[op++] = (byte)o2;
}
return out;
}

// Dummy constructor.
private Base64() {}
}

Agent: (openIntranetByID)
Here is the code to open the document

Dim w As New NotesUIWorkspace
Dim s As New NotesSession
Dim db As NotesDatabase
Dim view As NotesView
Dim doc As NotesDocument

Set db = s.CurrentDatabase
Set view = db.GetView("(docByName)")

Print "Intranet will be open .."
Sleep(1)
Print "Intranet will be open ...."
Call view.Refresh()
Set doc = view.GetDocumentByKey(s.UserName, True)
If doc Is Nothing Then
Print "Intranet will be open ....."
Sleep(1)
Print "Intranet will be open ......."
Call view.Refresh()
Set doc = view.GetDocumentByKey(s.UserName, True)

If doc Is Nothing Then
Print "Intranet will be open ........"
Sleep(1)
Print "Intranet will be open .........."
Call view.Refresh()
Set doc = view.GetDocumentByKey(s.UserName, True)
End If
End If

If Not doc Is Nothing Then
Call w.URLOpen( "{ Link } & doc.UniversalID )
Print "Intranet will be open for " & s.CommonUserName & "."
Else
Print "Error while opening Intranet"
End If



In the PostOpen Event of the Database start the Agents (createLTPAToken) and (openIntranetByID), then close the database.

Create a Form SSOLoginNotesDomino in the Database.
In the HTML-Head insert the formula: @SetHTTPHeader("Set-Cookie"; "LtpaToken=" + token)

In the onLoad Event insert
var ltpatoken = getcookie("LtpaToken");
eraseCookie("LtpaToken");
createCookie("LtpaToken", ltpatoken );

document.location.replace("{ Link }


In the JS-Header insert:
function getcookie(c_name) {

if (document.cookie.length>0) {
c_start=document.cookie.indexOf(c_name + "=");

if (c_start!=-1) {
c_start=c_start + c_name.length+1;
c_end=document.cookie.indexOf(";",c_start);

if (c_end==-1) c_end=document.cookie.length;
return unescape(document.cookie.substring(c_start,c_end));
}
}
return "";
}


function eraseCookie(name) {
createCookie(name,"",-1);
}


function createCookie(name,value) {
document.cookie = name+"="+value+"; path=/";
}


Insert a view into the database, called (login)
The first column is sorted and has the formula @Text(@DocumentUniqueID)

Insert a view into the database, called (docByName)
The first columns is sorted and has the formula @Name([Canonicalize];@Author)


Change the COOKIE_DOMAIN and the DOMINO_SECRET values in the (createLTPAToken) for your needs.


Whe you open the database in the lotus notes client:
the agent (createLTPAToken) generates a document with an LTPAToken,
the agent (openIntranetByID) starts the web browser and opens the document with the LTPAToken,
the javascript in the form change the token to root path and redirect to the intranet homepage.


7) Marius Jaeger1947 (20 Feb 2009)
Now I have found why my LtpaToken not support SSO.

Change the createCookie(..) function from

document.cookie=name+"="+value+"; path=/";

to

document.cookie=name+"="+value+"; path=/; domain=.blabla.bla.net";

The domain is the same as in COOKIE_DOMAIN in the (createLTPAToken) Agent.
8) Michael Morban10 (09 Mar 2011)
If i try to compile the agent "createLTPAToken" i get some erros on these lines:

creation[i - 4] = rawToken;

etc...

any ideas how to fix the errors?

greetings

Michael
9) Marius Jaeger1947 (28 Mar 2011)
Sorry, no ideas. I don't get errors.
10) Lars Berntrop-Bos113 (24 Jun 2015)
for removal of errors:
header = rawToken; => header = rawToken;
creation[i - 4] = rawToken; => creation[i - 4] = rawToken;
expires[i - 12] = rawToken; => expires[i - 12] = rawToken;
user[i - 20] = rawToken; = > user[i - 20] = rawToken;
digest[i - ( rawToken.length - 20 )] = rawToken; => digest[i - ( rawToken.length - 20 )] = rawToken;
for (int i=0; i<map2.length; i++) map2 = -1; => for (int i=0; i<map2.length; i++) map2 = -1;
for (int i=0; i<64; i++) map2[map1] = (byte)i; => for (int i=0; i<64; i++) map2[map1] = (byte)i;

the LotusScript also suffers from autotranslations (I broke up the http part to stop it:
Call w.URLOpen( "{ Link } & doc.UniversalID ) =>
Call w.URLOpen( {http" + "://" + "hp.blabla.bla.net/test/SSOToken.nsf/(login)/" & doc.UniversalID )

and in the javascript:
document.location.replace("{ Link } =>
document.location.replace("http" + "://" + "hp.blabla.bla.net/Homepage.nsf");

note that when patching the code to make LtpaToken support SSO, the domain strings in the java and javascript code need to match.










:
:




Welcome to IdeaJam


You can run IdeaJam™ in your company. It's easy to install, setup and customize. Your employees, partners and customers will immediately see results.

Use IdeaJam to:

  • Collect ideas from employees
  • Solicit feedback and suggestions from employees and customers
  • Run innovation contests and competitions
  • Validate concepts
  • Use the power of "crowd-sourcing" to rank ideas and allow the best ideas to rise to the top

IdeaJam™ works with:

  • IBM Connections
  • IBM Lotus Quickr
  • Blogs and Wikis
  • Websphere Portal
  • Microsoft Sharepoint
  • and other applications.

IdeaJam has an extensive set of widgets and API's that allow you to extend and integrate IdeaJam™ with other applications.

Learn more about IdeaJam >>






IdeaJam developed by

Elguji Software Logo