diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/UserPersistence.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/UserPersistence.java
index 380961d8e65420b6822e7f689e6d39829bd6bcdd..1b369f709fc643f55e6713dfe80fcee4cdc38cbe 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/UserPersistence.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/UserPersistence.java
@@ -120,6 +120,22 @@ public interface UserPersistence<T extends Principal>
         throws UserNotFoundException, TransientException, 
                AccessControlException;
     
+    /**
+     * Attempt to login the specified user.
+     *
+     * @param userID The userID.
+     * @param password The password.
+     *
+     * @return Boolean
+     * 
+     * @throws UserNotFoundException when the user is not found.
+     * @throws TransientException If an temporary, unexpected problem occurred.
+     * @throws AccessControlException If the operation is not permitted.
+     */
+    Boolean loginUser(String userID, String password)
+            throws UserNotFoundException, TransientException, 
+            AccessControlException;
+   
     /**
      * Updated the user specified by User.
      *
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAO.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAO.java
index b133948eb0ef553f34d805512682f339d7b4cce4..5e0b281b83bf3451473c1aae836f2abb3857497d 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAO.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAO.java
@@ -177,11 +177,11 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO
      *
      * @param username username to verify.
      * @param password password to verify.
-     * @return User
+     * @return Boolean
      * @throws TransientException
      * @throws UserNotFoundException
      */
-    public User<T> loginUser(final String username, final String password)
+    public Boolean loginUser(final String username, final String password)
         throws TransientException, UserNotFoundException
     {
         try
@@ -191,7 +191,7 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO
 
             if (bindResult != null && bindResult.getResultCode() == ResultCode.SUCCESS)
             {
-                return getUser((T) new HttpPrincipal(username));
+                return Boolean.TRUE;
             }
             else
             {
@@ -200,7 +200,16 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO
         }
         catch (LDAPException e)
         {
-            logger.debug("addUser Exception: " + e, e);
+            logger.debug("loginUser Exception: " + e, e);
+
+            if (e.getResultCode() == ResultCode.INVALID_CREDENTIALS)
+            {
+                throw new AccessControlException("Invalid password");
+            }
+            else if (e.getResultCode() == ResultCode.NO_SUCH_OBJECT)
+            {
+                throw new AccessControlException("Invalid username");
+            }
 
             throw new RuntimeException("Unexpected LDAP exception", e);
         }
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserPersistence.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserPersistence.java
index af7662460e342b8ae107af471b1b57bf0aa8d019..05aedbac0b4c6e4eaae1f4cfbd1e09b7ebcf568f 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserPersistence.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserPersistence.java
@@ -175,7 +175,36 @@ public class LdapUserPersistence<T extends Principal>
             }
         }
     }
-        
+
+    /**
+     * Get the user specified by userID.
+     *
+     * @param userID The userID.
+     *
+     * @return Boolean.
+     * 
+     * @throws UserNotFoundException when the user is not found.
+     * @throws TransientException If an temporary, unexpected problem occurred.
+     * @throws AccessControlException If the operation is not permitted.
+     */
+    public Boolean loginUser(String userID, String password)
+            throws UserNotFoundException, TransientException, AccessControlException
+    {
+        LdapUserDAO<T> userDAO = null;
+        try
+        {
+            userDAO = new LdapUserDAO<T>(this.config);
+            return userDAO.loginUser(userID, password);
+        }
+        finally
+        {
+            if (userDAO != null)
+            {
+                userDAO.close();
+            }
+        }
+    }
+       
     /**
      * Updated the user specified by User.
      *
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/LoginServlet.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/LoginServlet.java
new file mode 100755
index 0000000000000000000000000000000000000000..927031daa00b3c59c117b859cbc4d156fe16520e
--- /dev/null
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/LoginServlet.java
@@ -0,0 +1,159 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac.server.web.users;
+
+import java.io.IOException;
+import java.security.AccessControlException;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.log4j.Logger;
+
+import ca.nrc.cadc.ac.server.ldap.LdapUserPersistence;
+import ca.nrc.cadc.auth.HttpPrincipal;
+import ca.nrc.cadc.auth.SSOCookieManager;
+import ca.nrc.cadc.log.ServletLogInfo;
+import ca.nrc.cadc.util.StringUtil;
+
+@SuppressWarnings("serial")
+public class LoginServlet extends HttpServlet
+{
+    private static final Logger log = Logger.getLogger(LoginServlet.class);
+    private static final String CONTENT_TYPE = "text/plain";
+    /**
+     * Attempt to login for userid/password.
+     */
+	@SuppressWarnings("rawtypes")
+	public void doPost(HttpServletRequest request, HttpServletResponse response)
+        throws IOException
+    {
+        long start = System.currentTimeMillis();
+        ServletLogInfo logInfo = new ServletLogInfo(request);
+        try
+        {
+            log.info(logInfo.start());
+            String userID = request.getParameter("userid");
+            String password = request.getParameter("password");
+            if (StringUtil.hasText(userID))
+            {
+                if (StringUtil.hasText(password))
+                {
+                	if (new LdapUserPersistence().loginUser(userID, password))
+                	{
+	            	    String token = new SSOCookieManager().generate(new HttpPrincipal(userID));
+	            	    response.setContentType(CONTENT_TYPE);
+	            	    response.setContentLength(token.length());
+	            	    response.getWriter().write(token);
+                	}
+                }
+                else
+                {
+                	throw new IllegalArgumentException("Missing password");
+                }
+            }
+            else
+            {
+            	throw new IllegalArgumentException("Missing userid");
+            }
+        }
+        catch (IllegalArgumentException e)
+        {
+            log.debug(e.getMessage(), e);
+            logInfo.setMessage(e.getMessage());
+    	    response.setContentType(CONTENT_TYPE);
+            response.getWriter().write(e.getMessage());
+            response.setStatus(400);
+        }
+        catch (AccessControlException e)
+        {
+            log.debug(e.getMessage(), e);
+            logInfo.setMessage(e.getMessage());
+    	    response.setContentType(CONTENT_TYPE);
+            response.getWriter().write(e.getMessage());
+            response.setStatus(401);
+        }
+        catch (Throwable t)
+        {
+            String message = "Internal Server Error: " + t.getMessage();
+            log.error(message, t);
+            logInfo.setSuccess(false);
+            logInfo.setMessage(message);
+    	    response.setContentType(CONTENT_TYPE);
+            response.getWriter().write(message);
+            response.setStatus(500);
+        }
+        finally
+        {
+            logInfo.setElapsedTime(System.currentTimeMillis() - start);
+            log.info(logInfo.end());
+        }
+    }
+}