One approach would be to encrypt the user's username/password and write them to a persistent cookie. So, in a custom login page controller, you would have code like
// Technically, '^' is permitted in an email address, but most systems disallow it
String data = username + '^' + password;
// Base64 encoded key is stored in a custom setting
EncryptionSettings__c settings = EncryptionSettings__c.getOrgDefaults();
Blob key = null;
if (settings.key__c == null) {
// No key yet - create a random one and save it
key = Crypto.generateAesKey(256);
settings.key__c = EncodingUtil.base64Encode(key);
insert settings;
} else {
key = EncodingUtil.base64Decode(settings.key__c);
}
String base64blob = EncodingUtil.base64Encode(Crypto.encryptWithManagedIV('AES256', key, Blob.valueOf(data)));
// Set cookieName, maxAge appropriately
ApexPages.currentPage().setCookies(new Cookie[]{new Cookie(cookieName,base64blob,null,maxAge,false)});
In an action method, specified in the action
attribute for the page, you could have something like:
if (ApexPages.currentPage().getCookies().get(cookieName) == null) {
// No cookie
return null;
}
String base64blob = ApexPages.currentPage().getCookies().get(cookieName).getValue();
EncryptionSettings__c settings = EncryptionSettings__c.getOrgDefaults();
if (settings.key__c == null) {
throw new MyAppException('Cannot decrypt without a key!');
}
Blob key = EncodingUtil.base64Decode(settings.key__c);
String data = Crypto.decryptWithManagedIV('AES256', key, EncodingUtil.base64Decode(data)).toString();
String parts = data.split('^');
String username = parts[0];
String password = parts[1];
return Site.login(username, password, null); // a PageReference
NOTE - YOU SHOULD CAREFULLY REVIEW THE SECURITY OF THIS APPROACH. THE USER'S CREDENTIALS ARE NOT STORED ANYWHERE EXCEPT ON THEIR OWN MACHINE, ENCRYPTED WITH A KEY THAT RESIDES IN SALESFORCE. THIS LOOKS SECURE TO ME, BUT IT'S CONCEIVABLE THAT THERE MAY BE A SECURITY HOLE SOMEWHERE.