diff --git a/include/ajax.content.php b/include/ajax.content.php
index e77f45d23014d2176b24500d31e26c1ab263133f..e97e96a5c3d1d894f5d7cacfad702c97ced25cf6 100644
--- a/include/ajax.content.php
+++ b/include/ajax.content.php
@@ -36,46 +36,48 @@ class ContentAjaxAPI extends AjaxController {
 
     function ticket_variables() {
 
-        $content='<div style="width:600px;">
-                    <h2>Ticket Variables</h2>
-                    Please note that non-base variables depends on the context of use.
-                    <br>
-                    <table width="100%" border="0" cellspacing=1 cellpadding=2>
-                        <tr><td width="50%" valign="top"><b>Base Variables</b></td><td><b>Other Variables</b></td></tr>
-                        <tr>
-                            <td width="50%" valign="top">
-                                <table width="100%" border="0" cellspacing=1 cellpadding=1>
-                                    <tr><td width="100">%id</td><td>Ticket ID (internal ID)</td></tr>
-                                    <tr><td>%ticket</td><td>Ticket number (external ID)</td></tr>
-                                    <tr><td>%email</td><td>Email address</td></tr>
-                                    <tr><td>%name</td><td>Full name</td></tr>
-                                    <tr><td>%subject</td><td>Subject</td></tr>
-                                    <tr><td>%topic</td><td>Help topic (web only)</td></tr>
-                                    <tr><td>%phone</td><td>Phone number | ext</td></tr>
-                                    <tr><td>%status</td><td>Status</td></tr>
-                                    <tr><td>%priority</td><td>Priority</td></tr>
-                                    <tr><td>%dept</td><td>Department</td></tr>
-                                    <tr><td>%assigned</td><td>Assigned staff or team (if any)</td></tr>
-                                    <tr><td>%createdate</td><td>Date created</td></tr>
-                                    <tr><td>%duedate</td><td>Due date</td></tr>
-                                    <tr><td>%closedate</td><td>Date closed</td></tr>
-                                </table>
-                            </td>
-                            <td valign="top">
-                                <table width="100%" border="0" cellspacing=1 cellpadding=1>
-                                    <tr><td width="100">%message</td><td>Message (incoming)</td></tr>
-                                    <tr><td>%response</td><td>Response (outgoing)</td></tr>
-                                    <tr><td>%note</td><td>Internal/transfer note</td></tr>
-                                    <tr><td>%staff</td><td>Staff\'s name (alert/notices)</td></tr>
-                                    <tr><td>%assignee</td><td>Assigned staff</td></tr>
-                                    <tr><td>%assigner</td><td>Staff assigning the ticket</td></tr>
-                                    <tr><td>%url</td><td>osTicket\'s base url (FQDN)</td></tr>
-
-                                </table>
-                            </td>
-                        </tr>
+        $content='
+<div style="width:600px;">
+    <h2>Ticket Variables</h2>
+    Please note that non-base variables depends on the context of use.
+    <br/>
+    <table width="100%" border="0" cellspacing=1 cellpadding=2>
+        <tr><td width="50%" valign="top"><b>Base Variables</b></td><td><b>Other Variables</b></td></tr>
+        <tr>
+            <td width="50%" valign="top">
+                <table width="100%" border="0" cellspacing=1 cellpadding=1>
+                    <tr><td width="100">%id</td><td>Ticket ID (internal ID)</td></tr>
+                    <tr><td>%ticket</td><td>Ticket number (external ID)</td></tr>
+                    <tr><td>%email</td><td>Email address</td></tr>
+                    <tr><td>%name</td><td>Full name</td></tr>
+                    <tr><td>%subject</td><td>Subject</td></tr>
+                    <tr><td>%topic</td><td>Help topic (web only)</td></tr>
+                    <tr><td>%phone</td><td>Phone number | ext</td></tr>
+                    <tr><td>%status</td><td>Status</td></tr>
+                    <tr><td>%priority</td><td>Priority</td></tr>
+                    <tr><td>%dept</td><td>Department</td></tr>
+                    <tr><td>%assigned</td><td>Assigned staff or team (if any)</td></tr>
+                    <tr><td>%createdate</td><td>Date created</td></tr>
+                    <tr><td>%duedate</td><td>Due date</td></tr>
+                    <tr><td>%closedate</td><td>Date closed</td></tr>
+                </table>
+            </td>
+            <td valign="top">
+                <table width="100%" border="0" cellspacing=1 cellpadding=1>
+                    <tr><td width="100">%message</td><td>Message (incoming)</td></tr>
+                    <tr><td>%response</td><td>Response (outgoing)</td></tr>
+                    <tr><td>%note</td><td>Internal/transfer note</td></tr>
+                    <tr><td>%staff</td><td>Staff\'s name (alert/notices)</td></tr>
+                    <tr><td>%assignee</td><td>Assigned staff</td></tr>
+                    <tr><td>%assigner</td><td>Staff assigning the ticket</td></tr>
+                    <tr><td>%url</td><td>osTicket\'s base url (FQDN)</td></tr>
+                    <tr><td>%auth</td><td>Client authentication token</td></tr>
+                    <tr><td>%clientlink</td><td>Client auto-login link</td></tr>
                 </table>
-            </div>';
+            </td>
+        </tr>
+    </table>
+</div>';
 
         return $content;
     }
diff --git a/include/class.client.php b/include/class.client.php
index 8786c0e19cdb9719a86a556b3ce26795ac5ce62f..5c5f3035ee6b485d6862950b35924f00e0f90b97 100644
--- a/include/class.client.php
+++ b/include/class.client.php
@@ -133,5 +133,73 @@ class Client {
     function lookupByEmail($email) {
         return (($id=self::getLastTicketIdByEmail($email)))?self::lookup($id, $email):null;
     }
+
+    /* static */ function tryLogin($ticketID, $email, $auth=null) {
+        global $ost;
+        $cfg = $ost->getConfig();
+
+        # Only consider auth token for GET requests, and for GET requests,
+        # REQUIRE the auth token
+        $auto_login = $_SERVER['REQUEST_METHOD'] == 'GET';
+
+        //Check time for last max failed login attempt strike.
+        $loginmsg='Invalid login';
+        # XXX: SECURITY: Max attempts is enforced client-side via the PHP
+        #      session cookie.
+        if($_SESSION['_client']['laststrike']) {
+            if((time()-$_SESSION['_client']['laststrike'])<$cfg->getClientLoginTimeout()) {
+                $loginmsg='Excessive failed login attempts';
+                $errors['err']='You\'ve reached maximum failed login attempts allowed. Try again later or <a href="open.php">open a new ticket</a>';
+            }else{ //Timeout is over.
+                //Reset the counter for next round of attempts after the timeout.
+                $_SESSION['_client']['laststrike']=null;
+                $_SESSION['_client']['strikes']=0;
+            }
+        }
+        //See if we can fetch local ticket id associated with the ID given
+        if(!$errors && is_numeric($ticketID) && Validator::is_email($email) && ($ticket=Ticket::lookupByExtId($ticketID))) {
+            //At this point we know the ticket is valid.
+            //TODO: 1) Check how old the ticket is...3 months max?? 2) Must be the latest 5 tickets?? 
+            //Check the email given.
+            # Require auth token for automatic logins
+            if (!$auto_login || $auth === $ticket->getAuthToken()) {
+                if($ticket->getId() && strcasecmp($ticket->getEmail(),$email)==0){
+                    //valid match...create session goodies for the client.
+                    $user = new ClientSession($email,$ticket->getId());
+                    $_SESSION['_client']=array(); //clear.
+                    $_SESSION['_client']['userID']   =$ticket->getEmail(); //Email
+                    $_SESSION['_client']['key']      =$ticket->getExtId(); //Ticket ID --acts as password when used with email. See above.
+                    $_SESSION['_client']['token']    =$user->getSessionToken();
+                    $_SESSION['TZ_OFFSET']=$cfg->getTZoffset();
+                    $_SESSION['TZ_DST']=$cfg->observeDaylightSaving();
+                    //Log login info...
+                    $msg=sprintf("%s/%s logged in [%s]",$ticket->getEmail(),$ticket->getExtId(),$_SERVER['REMOTE_ADDR']);
+                    $ost->logDebug('User login', $msg);
+                    //Redirect tickets.php
+                    session_write_close();
+                    session_regenerate_id();
+                    @header("Location: tickets.php?id=".$ticket->getExtId());
+                    require_once('tickets.php'); //Just incase. of header already sent error.
+                    exit;
+                }
+            }
+        }
+        //If we get to this point we know the login failed.
+        $_SESSION['_client']['strikes']+=1;
+        if(!$errors && $_SESSION['_client']['strikes']>$cfg->getClientMaxLogins()) {
+            $loginmsg='Access Denied';
+            $errors['err']='Forgot your login info? Please <a href="open.php">open a new ticket</a>.';
+            $_SESSION['_client']['laststrike']=time();
+            $alert='Excessive login attempts by a client?'."\n".
+                    'Email: '.$_POST['lemail']."\n".'Ticket#: '.$_POST['lticket']."\n".
+                    'IP: '.$_SERVER['REMOTE_ADDR']."\n".'Time:'.date('M j, Y, g:i a T')."\n\n".
+                    'Attempts #'.$_SESSION['_client']['strikes'];
+            $ost->logError('Excessive login attempts (client)', $alert, ($cfg->alertONLoginError()));
+        }elseif($_SESSION['_client']['strikes']%2==0){ //Log every other failed login attempt as a warning.
+            $alert='Email: '.$_POST['lemail']."\n".'Ticket #: '.$_POST['lticket']."\n".'IP: '.$_SERVER['REMOTE_ADDR'].
+                   "\n".'TIME: '.date('M j, Y, g:i a T')."\n\n".'Attempts #'.$_SESSION['_client']['strikes'];
+            $ost->logWarning('Failed login attempt (client)', $alert);
+        }
+    }
 }
 ?>
diff --git a/include/class.ticket.php b/include/class.ticket.php
index 84a36d2a6273954d87504fa4c0bbcda025714da3..8b9581d532a1e94c8076d3eca0b82bcfe88d7d0e 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -210,6 +210,11 @@ class Ticket{
         return $this->email;
     }
 
+    function getAuthToken() {
+        # XXX: Support variable email address (for CCs)
+        return md5($this->getId() . $this->getEmail() . SECRET_SALT);
+    }
+
     function getName(){
         return $this->fullname;
     }
@@ -973,7 +978,6 @@ class Ticket{
                 $email->send($this->getEmail(),$subj,$body);
             }
         }
-
     }
 
     function onAssign($note, $alert=true) {
@@ -1102,7 +1106,8 @@ class Ticket{
 
 
         $search = array('/%id/','/%ticket/','/%email/','/%name/','/%subject/','/%topic/','/%phone/','/%status/','/%priority/',
-                        '/%dept/','/%assigned_staff/','/%createdate/','/%duedate/','/%closedate/','/%url/');
+                        '/%dept/','/%assigned_staff/','/%createdate/','/%duedate/','/%closedate/','/%url/',
+                        '/%auth/', '/%clientlink/');
         $replace = array($this->getId(),
                          $this->getExtId(),
                          $this->getEmail(),
@@ -1117,13 +1122,15 @@ class Ticket{
                          Format::db_daydatetime($this->getCreateDate()),
                          Format::db_daydatetime($this->getDueDate()),
                          Format::db_daydatetime($this->getCloseDate()),
-                         $cfg->getBaseUrl());
-        return preg_replace($search,$replace,$text);
+                         $cfg->getBaseUrl(),
+                         $this->getAuthToken(),
+                         '%url/view.php?t=%ticket&e=%email&a=%auth');
+        while ($text != ($T = preg_replace($search,$replace,$text))) {
+            $text = $T;
+        }
+        return $text;
     }
 
-
-
-
     function markUnAnswered() {
         return (!$this->isAnswered() || $this->setAnsweredState(0));
     }
@@ -1135,7 +1142,6 @@ class Ticket{
     function markOverdue($whine=true) {
         
         global $cfg;
-
         
         if($this->isOverdue())
             return true;
@@ -1294,15 +1300,12 @@ class Ticket{
        
         if(!$this->getId()) return 0;
 
-
-            
         //Strip quoted reply...on emailed replies
         if(!strcasecmp($source, 'Email') 
                 && $cfg->stripQuotedReply() 
                 && ($tag=$cfg->getReplySeparator()) && strpos($message, $tag))
             list($message)=split($tag, $message);
 
-
         # XXX: Refuse auto-response messages? (via email) XXX: No - but kill our auto-responder.
 
         $sql='INSERT INTO '.TICKET_THREAD_TABLE.' SET created=NOW()'
diff --git a/login.php b/login.php
index 823c48a030d494f2b6ae4f06bbbcf35c8de8918d..f35693fd4083401a1aea72125e8cb1cc116710c7 100644
--- a/login.php
+++ b/login.php
@@ -20,68 +20,9 @@ define('OSTCLIENTINC',TRUE); //make includes happy
 
 require_once(INCLUDE_DIR.'class.client.php');
 require_once(INCLUDE_DIR.'class.ticket.php');
-//We are ready baby
-$loginmsg='Authentication Required';
-if($_POST && (!empty($_POST['lemail']) && !empty($_POST['lticket']))):
-    $loginmsg='Authentication Required';
-    $email=trim($_POST['lemail']);
-    $ticketID=trim($_POST['lticket']);
-    //$_SESSION['_client']=array(); #Uncomment to disable login strikes.
-    
-    //Check time for last max failed login attempt strike.
-    $loginmsg='Invalid login';
-    if($_SESSION['_client']['laststrike']) {
-        if((time()-$_SESSION['_client']['laststrike'])<$cfg->getClientLoginTimeout()) {
-            $loginmsg='Excessive failed login attempts';
-            $errors['err']='You\'ve reached maximum failed login attempts allowed. Try again later or <a href="open.php">open a new ticket</a>';
-        }else{ //Timeout is over.
-            //Reset the counter for next round of attempts after the timeout.
-            $_SESSION['_client']['laststrike']=null;
-            $_SESSION['_client']['strikes']=0;
-        }
-    }
-    //See if we can fetch local ticket id associated with the ID given
-    if(!$errors && is_numeric($ticketID) && Validator::is_email($email) && ($ticket=Ticket::lookupByExtId($ticketID))) {
-        //At this point we know the ticket is valid.
-        //TODO: 1) Check how old the ticket is...3 months max?? 2) Must be the latest 5 tickets?? 
-        //Check the email given.
-        if($ticket->getId() && strcasecmp($ticket->getEmail(),$email)==0){
-            //valid match...create session goodies for the client.
-            $user = new ClientSession($email,$ticket->getId());
-            $_SESSION['_client']=array(); //clear.
-            $_SESSION['_client']['userID']   =$ticket->getEmail(); //Email
-            $_SESSION['_client']['key']      =$ticket->getExtId(); //Ticket ID --acts as password when used with email. See above.
-            $_SESSION['_client']['token']    =$user->getSessionToken();
-            $_SESSION['TZ_OFFSET']=$cfg->getTZoffset();
-            $_SESSION['TZ_DST']=$cfg->observeDaylightSaving();
-            //Log login info...
-            $msg=sprintf("%s/%s logged in [%s]",$ticket->getEmail(),$ticket->getExtId(),$_SERVER['REMOTE_ADDR']);
-            $ost->logDebug('User login', $msg);
-            //Redirect tickets.php
-            session_write_close();
-            session_regenerate_id();
-            @header("Location: tickets.php?id=".$ticket->getExtId());
-            require_once('tickets.php'); //Just incase. of header already sent error.
-            exit;
-        }
-    }
-    //If we get to this point we know the login failed.
-    $_SESSION['_client']['strikes']+=1;
-    if(!$errors && $_SESSION['_client']['strikes']>$cfg->getClientMaxLogins()) {
-        $loginmsg='Access Denied';
-        $errors['err']='Forgot your login info? Please <a href="open.php">open a new ticket</a>.';
-        $_SESSION['_client']['laststrike']=time();
-        $alert='Excessive login attempts by a client?'."\n".
-                'Email: '.$_POST['lemail']."\n".'Ticket#: '.$_POST['lticket']."\n".
-                'IP: '.$_SERVER['REMOTE_ADDR']."\n".'Time:'.date('M j, Y, g:i a T')."\n\n".
-                'Attempts #'.$_SESSION['_client']['strikes'];
-        $ost->logError('Excessive login attempts (client)', $alert, ($cfg->alertONLoginError()));
-    }elseif($_SESSION['_client']['strikes']%2==0){ //Log every other failed login attempt as a warning.
-        $alert='Email: '.$_POST['lemail']."\n".'Ticket #: '.$_POST['lticket']."\n".'IP: '.$_SERVER['REMOTE_ADDR'].
-               "\n".'TIME: '.date('M j, Y, g:i a T')."\n\n".'Attempts #'.$_SESSION['_client']['strikes'];
-        $ost->logWarning('Failed login attempt (client)', $alert);
-    }
-endif;
+
+if ($_POST) ClientSession::tryLogin($_POST['lticket'], $_POST['lemail']);
+else ClientSession::tryLogin($_GET['t'], $_GET['e'], $_GET['a']);
 
 $nav = new UserNav();
 $nav->setActiveNav('status');