Skip to content

Credential validation at Login time

In our Extension Recipes we have briefly shown how to validate credentials at Login time using this example:

getApi().checkSecurePassword(session, clearPass, encryptedPass);

It is important to keep in mind that login credentials are always encrypted even with SSL turned off, and this snippet shows how to validate those credentials against an original (non-encrypted) password stored somewhere (database, web service, file etc...).

Most common use case

Keeping the original passwords unencrypted is clearly not good practice these days (or ever) and therefore the previous example would only work ok in low security or testing environments.

In a production system credentials are typically stored as hashes or hash+salt and we need a different way of checking them against what the client sends. In particular we wouldn't be able to validate the password against the stored hash if the client already sends it encrypted.

For starters we need SSL/TLS encryption to be active by deploying a valid certificate and activating encryption at the Zone level.

Here's the recommended flow:

  • from client side send the password in "clear" using a custom field (it will be protected by SSL anyways)
  • in your Extension Login handler read the password from the same field
  • apply the necessary hashing steps you would normally use prior to storing it in the database
  • match the result with what's stored in the database

Code example:

var params = new SFSObject();
params.PutShortString("pwd", "MyPassword123");

sfs.Send(new LoginRequest(userName, null, zoneName, params));

We do not provide the password as the 2nd argument of the LoginRequest, instead we pack it in the params object and send it out.

public class LoginEventHandler extends BaseServerEventHandler
{
@Override
public void handleServerEvent(ISFSEvent event) throws SFSException
{
        String name = (String) event.getParameter(SFSEventParam.LOGIN_NAME);
        ISFSObject params = (ISFSObject) event.getParameter(SFSEventParam.LOGIN_IN_DATA);

        String password = params.getShortString("pwd");

        if (password != null && !password.isEmpty())
        {
            // Load original password for this user
            String originalPassword = getPassFromDatabase(name);

            // Apply hashing, salting etc...

            if (!password.equals(originalPassword))
            {
                // Create the error code to send to the client
                SFSErrorData errData = new SFSErrorData(SFSErrorCode.LOGIN_BAD_PASSWORD);
                errData.addParameter(name);

                // Fire a Login exception
                throw new SFSLoginException("Login error!", errData);   
            }

            // Login success... 
        }

        // Can't login without a password
        else
        {
            SFSErrorData errData = new SFSErrorData(SFSErrorCode.LOGIN_GUEST_NOT_ALLOWED);
            errData.addParameter(name);

            throw new SFSLoginException("Login error!", errData);
        }
    }
}

On the server side we extract the "clear" password in the same way and proceed with checking that it's not empty and that it matches the hashed string stored in our database.

When throwing an SFSLoginException we also need to pass an error code from the SFSErrorCode enum, this in turn will be sent back to the client to provide extra feedback about what went wrong.