diff --git a/helios/security.py b/helios/security.py
index 73115e00bcc85f1c3062918f2b25de7c6db4f34c..7abbf329858591f01ee1e94cadd27ddc8213306d 100644
--- a/helios/security.py
+++ b/helios/security.py
@@ -7,6 +7,7 @@ Ben Adida (ben@adida.net)
 # nicely update the wrapper function
 from functools import update_wrapper
 
+from django.core.urlresolvers import reverse
 from django.core.exceptions import *
 from django.conf import settings
 
@@ -72,17 +73,17 @@ def election_view(**checks):
   def election_view_decorator(func):
     def election_view_wrapper(request, election_uuid=None, *args, **kw):
       election = get_election_by_uuid(election_uuid)
-    
+
       # do checks
       do_election_checks(election, checks)
 
       # if private election, only logged in voters
       if election.private_p and not checks.get('allow_logins',False):
-        from views import get_voter, get_user
+        from views import get_voter, get_user, password_voter_login
         user = get_user(request)
         if not user_can_admin_election(user, election) and not get_voter(request, user, election):
           # FIXME: should be a nice redirect
-          raise PermissionDenied()
+          return HttpResponseRedirect(reverse(password_voter_login, args=[election.uuid]))
     
       return func(request, election, *args, **kw)
 
diff --git a/helios/templates/password_voter_login.html b/helios/templates/password_voter_login.html
new file mode 100644
index 0000000000000000000000000000000000000000..dd92a32597317b242722e457ed8951afdc15d32d
--- /dev/null
+++ b/helios/templates/password_voter_login.html
@@ -0,0 +1,12 @@
+{% extends TEMPLATE_BASE %}
+
+{% block title %}Log In to View Election{% endblock %}
+{% block content %}
+<h2>Private Election - Please Log In</h2>
+
+<p>
+This election, with fingerprint <tt>{{election.hash}}</tt>, is private, and can only be viewed by eligible voters.
+</p>
+
+{% include "_castconfirm_password.html" %}
+{% endblock %}
diff --git a/helios/tests.py b/helios/tests.py
index 760c8deb84a3b7a8f2f726779fdfa03512406ba2..70bdabece16d2fc5fc05d424a2bd9a8660dece0c 100644
--- a/helios/tests.py
+++ b/helios/tests.py
@@ -492,7 +492,7 @@ class ElectionBlackboxTests(TestCase):
         # return the voter username and password to vote
         return election_id, username, password
 
-    def _cast_ballot(self, election_id, username, password):
+    def _cast_ballot(self, election_id, username, password, need_login=True):
         # vote by preparing a ballot via the server-side encryption
         response = self.client.post("/helios/elections/%s/encrypt-ballot" % election_id, {
                 'answers_json': utils.to_json([[1]])})
@@ -507,12 +507,15 @@ class ElectionBlackboxTests(TestCase):
                 'encrypted_vote': encrypted_vote})
         self.assertRedirects(response, "%s/helios/elections/%s/cast_confirm" % (settings.SECURE_URL_HOST, election_id))        
 
-        # log in
-        response = self.client.post("/helios/elections/%s/password_voter_login" % election_id, {
-                'voter_id' : username,
-                'password' : password
-                })
-        self.assertRedirects(response, "/helios/elections/%s/cast_confirm" % election_id)
+        if need_login:
+            response = self.client.post("/helios/elections/%s/password_voter_login" % election_id, {
+                    'voter_id' : username,
+                    'password' : password
+                    })
+            self.assertRedirects(response, "/helios/elections/%s/cast_confirm" % election_id)
+        else:
+            response = self.client.get("/helios/elections/%s/cast_confirm" % election_id)
+            self.assertContains(response, "I am ")
 
         # confirm the vote
         response = self.client.post("/helios/elections/%s/cast_confirm" % election_id, {
@@ -576,5 +579,5 @@ class ElectionBlackboxTests(TestCase):
                 })
         self.assertRedirects(response, "/helios/elections/%s/view" % election_id)
 
-        self._cast_ballot(election_id, username, password)
+        self._cast_ballot(election_id, username, password, need_login = False)
         self._do_tally(election_id)
diff --git a/helios/views.py b/helios/views.py
index e2b377650e0ae681418ee1f9530f50e22952c57f..fbad7baa47420763a8d8301f0955c2f1ad7aa28b 100644
--- a/helios/views.py
+++ b/helios/views.py
@@ -499,11 +499,16 @@ def one_election_cast(request, election):
 
   return HttpResponseRedirect("%s%s" % (settings.SECURE_URL_HOST, reverse(one_election_cast_confirm, args=[election.uuid])))
 
-@election_view(frozen=True, allow_logins=True)
+@election_view(allow_logins=True)
 def password_voter_login(request, election):
   """
   This is used to log in as a voter for a particular election
   """
+
+  if request.method == "GET":
+    password_login_form = forms.VoterPasswordForm()
+    return render_template(request, 'password_voter_login', {'election': election, 'password_login_form': password_login_form})
+
   password_login_form = forms.VoterPasswordForm(request.POST)
 
   # redirect base depending on whether this is a private election