diff --git a/include/class.auth.php b/include/class.auth.php
index 0912239241c5dfb14771ffc77530123909459d94..563a79b1908b83cc462b3e8a7442a0675c823196 100644
--- a/include/class.auth.php
+++ b/include/class.auth.php
@@ -800,6 +800,41 @@ class AuthTokenAuthentication extends UserAuthenticationBackend {
 }
 UserAuthenticationBackend::register('AuthTokenAuthentication');
 
+//Simple ticket lookup backend used to recover ticket access link.
+// We're using authentication backend so we can guard aganist brute force
+// attempts (which doesn't buy much since the link is emailed)
+class AccessLinkAuthentication extends UserAuthenticationBackend {
+    static $name = "Ticket Access Link Authentication";
+    static $id = "authlink";
+
+    function authenticate($email, $number) {
+
+        if (!($ticket = Ticket::lookupByNumber($number))
+                || !($user=User::lookup(array('emails__address' =>
+                            $email))))
+            return false;
+
+        //Ticket owner?
+        if ($ticket->getUserId() == $user->getId())
+            $user = $ticket->getOwner();
+        //Collaborator?
+        elseif (!($user = Collaborator::lookup(array('userId' =>
+                            $user->getId(), 'ticketId' =>
+                            $ticket->getId()))))
+            return false; //Bro, we don't know you!
+
+
+        return new ClientSession($user);
+    }
+
+    //We are not actually logging in the user....
+    function login($user, $bk) {
+        return true;
+    }
+
+}
+UserAuthenticationBackend::register('AccessLinkAuthentication');
+
 class osTicketClientAuthentication extends UserAuthenticationBackend {
     static $name = "Local Client Authentication";
     static $id = "client";
diff --git a/include/class.client.php b/include/class.client.php
index 105eba9cc3eb314c9cfc55544c952e4c7259d29b..14533f9bba16d7d52d3bb0274a427138e42bb381 100644
--- a/include/class.client.php
+++ b/include/class.client.php
@@ -51,9 +51,8 @@ abstract class TicketUser {
         global $ost;
 
         if (!($ticket = $this->getTicket())
-                || !($dept = $ticket->getDept())
-                || !($email = $dept->getAutoRespEmail())
-                || !($tpl = $dept->getTemplate()->getMsgTemplate('user.accesslink')))
+                || !($email = $ost->getConfig()->getDefaultEmail())
+                || !($content = Page::lookup(Page::getIdByType('access-link'))))
             return;
 
         $vars = array(
@@ -61,8 +60,13 @@ abstract class TicketUser {
             'ticket' => $this->getTicket(),
             'recipient' => $this);
 
-        $msg = $ost->replaceTemplateVariables($tpl->asArray(), $vars);
-        $email->send($this->getEmail(), $msg['subj'], $msg['body']);
+        $msg = $ost->replaceTemplateVariables(array(
+            'subj' => $content->getName(),
+            'body' => $content->getBody(),
+        ), $vars);
+
+        $email->send($this->getEmail(), Format::striptags($msg['subj']),
+            $msg['body']);
     }
 
     protected function getAuthToken($algo=1) {
diff --git a/include/client/accesslink.inc.php b/include/client/accesslink.inc.php
new file mode 100644
index 0000000000000000000000000000000000000000..f588f3249f33d3f5684a2867487e67aa3cf655a2
--- /dev/null
+++ b/include/client/accesslink.inc.php
@@ -0,0 +1,34 @@
+<?php
+if(!defined('OSTCLIENTINC')) die('Access Denied');
+
+$email=Format::input($_POST['lemail']?$_POST['lemail']:$_GET['e']);
+$ticketid=Format::input($_POST['lticket']?$_POST['lticket']:$_GET['t']);
+?>
+<h1>Check Ticket Status</h1>
+<p>Please provide us with your email address and a ticket number, and an access
+link will be emailed to you.</p>
+<form action="login.php" method="post" id="clientLogin">
+    <?php csrf_token(); ?>
+<div style="display:table-row">
+    <div style="display:table-cell;width:40%">
+    <strong><?php echo Format::htmlchars($errors['login']); ?></strong>
+    <br>
+    <div>
+        <label for="email">E-Mail Address:</label><br/>
+        <input id="email" type="text" name="lemail" size="30" value="<?php echo $email; ?>">
+    </div>
+    <div>
+        <label for="ticketno">Ticket Number:</label><br/>
+        <input id="ticketno" type="text" name="lticket" size="16" value="<?php echo $ticketid; ?>"></td>
+    </div>
+    <p>
+        <input class="btn" type="submit" value="Email Access Link">
+    </p>
+    </div>
+    <div style="display:table-cell"></div>
+</div>
+</form>
+<br>
+<p>
+If this is your first time contacting us or you've lost the ticket number, please <a href="open.php">open a new ticket</a>.
+</p>
diff --git a/include/staff/settings-access.inc.php b/include/staff/settings-access.inc.php
index 119369908b5e1265e723228486e95ca187953d97..753d8ca43098d4f144d5f09523243c099447fd6a 100644
--- a/include/staff/settings-access.inc.php
+++ b/include/staff/settings-access.inc.php
@@ -141,7 +141,8 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config)
     ?><a href="#ajax.php/content/<?php echo $content; ?>/manage"
     onclick="javascript:
         $.dialog($(this).attr('href').substr(1), 200);
-        return false;"><?php echo Format::htmlchars($title); ?></a><?php
+        return false;"><i class="icon-file-text"></i> <?php
+        echo Format::htmlchars($title); ?></a><?php
 }; ?>
         <tr><td>Password Reset Emails</td>
             <td><?php $manage_content('Staff Members', 'pwreset-staff'); ?>
diff --git a/login.php b/login.php
index 7e7e35cd6b0a5769bb8e006f9f95ad306fb0e34c..d543cc9ea11330447f7454244013566df577c2db 100644
--- a/login.php
+++ b/login.php
@@ -24,8 +24,12 @@ define('OSTCLIENTINC',TRUE); //make includes happy
 require_once(INCLUDE_DIR.'class.client.php');
 require_once(INCLUDE_DIR.'class.ticket.php');
 
-$inc = 'login.inc.php';
-if ($_POST) {
+if ($cfg->getClientRegistrationMode() == 'disabled')
+    $inc = 'accesslink.inc.php';
+else
+    $inc = 'login.inc.php';
+
+if ($_POST && isset($_POST['luser'])) {
     if (!$_POST['luser'])
         $errors['err'] = 'Valid username or email address is required';
     elseif (($user = UserAuthenticationBackend::process($_POST['luser'],
@@ -35,6 +39,21 @@ if ($_POST) {
         $errors['err'] = 'Invalid email or ticket number - try again!';
     }
 }
+elseif ($_POST && isset($_POST['lticket'])) {
+    if (!Validator::is_email($_POST['lemail']))
+        $errors['err'] = 'Valid email address and ticket number required';
+    elseif (($user = UserAuthenticationBackend::process($_POST['lemail'],
+            $_POST['lticket'], $errors))) {
+        // We're using authentication backend so we can guard aganist brute
+        // force attempts (which doesn't buy much since the link is emailed)
+        $user->sendAccessLink();
+        $msg = sprintf("%s - access link sent to your email!",
+            $user->getName()->getFirst());
+        $_POST = null;
+    } elseif(!$errors['err']) {
+        $errors['err'] = 'Invalid email or ticket number - try again!';
+    }
+}
 
 if (!$nav) {
     $nav = new UserNav();
diff --git a/scp/css/scp.css b/scp/css/scp.css
index ea1a1dddeec181a5c63271437c39f00925068394..4500c2dfbeb0a04c7820a08775f148bcef77c5be 100644
--- a/scp/css/scp.css
+++ b/scp/css/scp.css
@@ -12,6 +12,10 @@ a {
     text-decoration:none;
 }
 
+.form_table a:hover {
+    text-decoration: underline;
+}
+
 .centered {
     text-align:center;
 }
@@ -544,6 +548,7 @@ a.print {
 
 .form_table td {
     border-bottom:1px solid #ddd;
+    height: 20px;
 }
 
 table.fixed {