diff --git a/demo/clientside.hs b/demo/clientside.hs
--- a/demo/clientside.hs
+++ b/demo/clientside.hs
@@ -69,6 +69,9 @@
             <button onclick="#{facebookLogin perms}">
               Login
           <p>
+            <a href="@{AuthR $ facebookForceLoginR perms}">
+              Force login route (avoid this in your code when possible).
+          <p>
             <button onclick="#{facebookLogout}">
               Logout
     |]
diff --git a/src/Yesod/Auth/Facebook/ClientSide.hs b/src/Yesod/Auth/Facebook/ClientSide.hs
--- a/src/Yesod/Auth/Facebook/ClientSide.hs
+++ b/src/Yesod/Auth/Facebook/ClientSide.hs
@@ -16,6 +16,7 @@
       -- * Widgets
     , facebookJSSDK
     , facebookLogin
+    , facebookForceLoginR
     , facebookLogout
     , JavaScriptCall
 
@@ -30,18 +31,20 @@
     ) where
 
 import Control.Applicative ((<$>), (<*>))
+import Control.Monad (when)
 import Control.Monad.IO.Class (MonadIO, liftIO)
 import Control.Monad.Trans.Error (ErrorT(..), throwError)
 import Data.ByteString (ByteString)
 import Data.Monoid (mappend, mempty)
+import Data.String (fromString)
 import Data.Text (Text)
 import System.Locale (defaultTimeLocale)
+import Text.Hamlet (hamlet)
 import Text.Julius (JavascriptUrl, julius)
 import Yesod.Auth
 import Yesod.Content
-import Yesod.Handler
-import Yesod.Request
-import Yesod.Widget
+import Yesod.Core
+import qualified Control.Exception.Lifted as E
 import qualified Data.Aeson as A
 import qualified Data.Aeson.Types as A
 import qualified Data.Text as T
@@ -54,6 +57,11 @@
 -- import qualified Data.Conduit as C
 
 
+-- | Internal function.  Construct a route to our plugin.
+fbcsR :: [Text] -> Route Auth
+fbcsR = PluginR "fbcs"
+
+
 -- | Hamlet that should be spliced /right after/ the @<body>@ tag
 -- in order for Facebook's JS SDK to work.  For example:
 --
@@ -80,7 +88,7 @@
                 <*> getFbInitOpts
                 <*> maybeAuthId
   let loggedIn = maybe ("false" :: Text) (const "true") muid
-      loginRoute  = toMaster $ PluginR "fbcs" ["login"]
+      loginRoute  = toMaster $ fbcsR ["login"]
       logoutRoute = toMaster $ LogoutR
       fbInitOpts  = A.object $ map (uncurry (A..=)) fbInitOptsList
   [whamlet|
@@ -149,11 +157,32 @@
 facebookLogin [] = "FB.login(function () {})"
 facebookLogin perms =
   T.concat [ "FB.login(function () {}, {scope: '"
-           , T.intercalate "," (map FB.unPermission perms)
+           , joinPermissions perms
            , "'})"
            ]
 
 
+-- | Route that forces the user to log in.  You should avoid
+-- using this route whenever possible, using 'facebookLogin' is
+-- much better (after all, this module is for client-side
+-- authentication).  However, you may want to use it at least for
+-- 'authRoute', e.g.:
+--
+-- @
+-- instance 'Yesod' MyFoundation where
+--   ...
+--   'authRoute' _ = Just $ AuthR (facebookForceLoginR [])
+-- @
+facebookForceLoginR :: [FB.Permission] -> Route Auth
+facebookForceLoginR perms = fbcsR ["login", "go", joinPermissions perms]
+
+
+-- | Internal function.  Joins a list of 'FB.Permission'@s@ into
+-- a format that Facebook recognizes.
+joinPermissions :: [FB.Permission] -> Text
+joinPermissions = T.intercalate "," . map FB.unPermission
+
+
 -- | JavaScript function that should be called in order to logout
 -- the user.  You could splice this into a @onclick@ event, for
 -- example:
@@ -340,12 +369,54 @@
 authFacebookClientSide =
     AuthPlugin "fbcs" dispatch login
   where
+    dispatch :: YesodAuthFbClientSide master =>
+                Text -> [Text] -> GHandler Auth master ()
+    -- Login route used when successfully logging in.  Called via
+    -- AJAX by JavaScript code on 'facebookJSSDK'.
     dispatch "GET" ["login"] = do
       etoken <- getUserAccessToken
       case etoken of
         Right token -> setCreds True (createCreds token)
         Left msg -> fail msg
-    -- Anything else gives 404
+
+    -- Login routes used to forcefully require the user to login.
+    dispatch "GET" ["login", "go"] = dispatch "GET" ["login", "go", ""]
+    dispatch "GET" ["login", "go", perms] = do
+      -- Redirect the user to the server-side flow login url.
+      y  <- getYesod
+      ur <- getUrlRender
+      tm <- getRouteToMaster
+      when (redirectToReferer y) setUltDestReferer
+      let creds      = fbCredentials y
+          manager    = authHttpManager y
+          redirectTo = ur $ tm $ fbcsR ["login", "back"]
+          uncommas "" = []
+          uncommas xs = case break (== ',') xs of
+                          (x', ',':xs') -> x' : uncommas xs'
+                          (x', _)       -> [x']
+      url <- FB.runFacebookT creds manager $
+             FB.getUserAccessTokenStep1 redirectTo $
+               map fromString $ uncommas $ T.unpack perms
+      redirect url
+    dispatch "GET" ["login", "back"] = do
+      -- Instead of going on with the server-side flow, use the
+      -- client-side JS to finish the authentication.
+      tm <- getRouteToMaster
+      mr <- getMessageRender
+      fbjssdkpc <- widgetToPageContent (facebookJSSDK tm)
+      rephtml <- hamletToRepHtml $ [hamlet|
+        $doctype 5
+        <html>
+          <head>
+            <title>#{mr Msg.LoginTitle}
+            ^{pageHead fbjssdkpc}
+          <body>
+            ^{pageBody fbjssdkpc}
+        |]
+      sendResponse rephtml
+
+
+    -- Everything else gives 404
     dispatch _ _ = notFound
 
     -- Small widget for multiple login websites.
@@ -354,7 +425,7 @@
           -> GWidget sub master ()
     login _ = [whamlet|
                  <p>
-                   <a href="#{facebookLogin perms}">
+                   <a href="#" onclick="#{facebookLogin perms}">
                      _{Msg.Facebook}
               |]
       where perms = []
@@ -402,11 +473,11 @@
                 <*> parsed A..:? "user_id"
                 <*> parsed A..:? "oauth_token"
                 <*> parsed A..:? "expires") of
-      Right (Just code, _, _, _) -> lift $ do
+      Right (Just code, _, _, _) -> do
         -- We have to exchange the code for the access token.
-        moldCode <- lookupSession sessionCode
+        moldCode <- lift $ lookupSession sessionCode
         case moldCode of
-          Just code' | code == TE.encodeUtf8 code' -> do
+          Just code' | code == TE.encodeUtf8 code' -> lift $ do
             -- We have a cached token for this code.
             Just userId  <- lookupSession sessionUserId
             Just data_   <- lookupSession sessionToken
@@ -416,10 +487,16 @@
                                         (read $ T.unpack exptime)
           _ -> do
             -- Get access token from Facebook.
-            token <- FB.runFacebookT creds manager $
+            let fbErrorMsg :: FB.FacebookException -> String
+                fbErrorMsg exc = "getUserAccessToken: getUserAccessTokenStep2 " ++
+                                 "failed with " ++ show exc
+            token <- ErrorT $
+                     fmap (either (Left . fbErrorMsg) Right) $
+                     E.try $
+                     FB.runFacebookT creds manager $
                      FB.getUserAccessTokenStep2 "" [("code", code)]
             case token of
-              FB.UserAccessToken userId data_ exptime -> do
+              FB.UserAccessToken userId data_ exptime -> lift $ do
                 -- Save it for later.
                 setSession sessionCode    (TE.decodeUtf8 code)
                 setSession sessionUserId  (TE.decodeUtf8 userId)
@@ -429,9 +506,9 @@
       Right (_, Just uid, Just oauth_token, Just expires) ->
         return $ FB.UserAccessToken uid oauth_token (toUTCTime expires)
       Right (Nothing, _, _, _) ->
-        throwError "no user_id nor code on signed request"
+        throwError "getUserAccessToken: no user_id nor code on signed request"
       Left msg ->
-        throwError ("never here (" ++ show msg ++ ")")
+        throwError ("getUserAccessToken: never here (" ++ show msg ++ ")")
   where
     toErrorT :: Functor m => String -> m (Maybe a) -> ErrorT String m a
     toErrorT msg = ErrorT . fmap (maybe (Left ("getUserAccessToken: " ++ msg)) Right)
diff --git a/yesod-auth-fb.cabal b/yesod-auth-fb.cabal
--- a/yesod-auth-fb.cabal
+++ b/yesod-auth-fb.cabal
@@ -1,5 +1,5 @@
 Name:                yesod-auth-fb
-Version:             1.0.3
+Version:             1.0.4
 Synopsis:            Authentication backend for Yesod using Facebook.
 Homepage:            https://github.com/meteficha/yesod-auth-fb
 License:             BSD3
@@ -43,8 +43,10 @@
   hs-source-dirs: src
 
   Build-depends:   base         >= 4.3     && < 5
+                 , lifted-base  == 0.1.*
                  , yesod-core   >= 1.0     && < 1.1
                  , yesod-auth   >= 1.0     && < 1.1
+                 , hamlet
                  , shakespeare-js
                  , wai
                  , http-conduit
