BrowserID lazy load

This commit is contained in:
Michael Snoyman 2013-04-21 13:55:06 +03:00
parent ba9e40c177
commit 4cde171285
2 changed files with 69 additions and 36 deletions

View File

@ -1,10 +1,14 @@
{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE RecordWildCards #-}
module Yesod.Auth.BrowserId module Yesod.Auth.BrowserId
( authBrowserId ( authBrowserId
, authBrowserIdAudience
, createOnClick , createOnClick
, def
, BrowserIdSettings
, bisAudience
, bisLazyLoad
) where ) where
import Yesod.Auth import Yesod.Auth
@ -14,12 +18,13 @@ import Yesod.Core
import Text.Hamlet (hamlet) import Text.Hamlet (hamlet)
import qualified Data.Text as T import qualified Data.Text as T
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import Control.Monad (when) import Control.Monad (when, unless)
import Control.Exception (throwIO) import Control.Exception (throwIO)
import Text.Julius (julius, rawJS) import Text.Julius (julius, rawJS)
import Network.URI (uriPath, parseURI) import Network.URI (uriPath, parseURI)
import Data.FileEmbed (embedFile) import Data.FileEmbed (embedFile)
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
import Data.Default
pid :: Text pid :: Text
pid = "browserid" pid = "browserid"
@ -27,29 +32,42 @@ pid = "browserid"
complete :: Route Auth complete :: Route Auth
complete = PluginR pid [] complete = PluginR pid []
-- | Log into browser ID with an audience value determined from the 'approot'. -- | A settings type for various configuration options relevant to BrowserID.
authBrowserId :: YesodAuth m => AuthPlugin m --
authBrowserId = helper Nothing -- See: <http://www.yesodweb.com/book/settings-types>
--
-- Since 1.2.0
data BrowserIdSettings = BrowserIdSettings
{ bisAudience :: Maybe Text
-- ^ BrowserID audience value. If @Nothing@, will be extracted based on the
-- approot.
--
-- Default: @Nothing@
--
-- Since 1.2.0
, bisLazyLoad :: Bool
-- ^ Use asynchronous Javascript loading for the BrowserID JS file.
--
-- Default: @True@.
--
-- Since 1.2.0
}
-- | Log into browser ID with the given audience value. Note that this must be instance Default BrowserIdSettings where
-- your actual hostname, or login will fail. def = BrowserIdSettings
authBrowserIdAudience { bisAudience = Nothing
:: YesodAuth m , bisLazyLoad = True
=> Text -- ^ audience }
-> AuthPlugin m
authBrowserIdAudience = helper . Just
helper :: YesodAuth m authBrowserId :: YesodAuth m => BrowserIdSettings -> AuthPlugin m
=> Maybe Text -- ^ audience authBrowserId bis@BrowserIdSettings {..} = AuthPlugin
-> AuthPlugin m
helper maudience = AuthPlugin
{ apName = pid { apName = pid
, apDispatch = \m ps -> , apDispatch = \m ps ->
case (m, ps) of case (m, ps) of
("GET", [assertion]) -> do ("GET", [assertion]) -> do
master <- lift getYesod master <- lift getYesod
audience <- audience <-
case maudience of case bisAudience of
Just a -> return a Just a -> return a
Nothing -> do Nothing -> do
r <- getUrlRender r <- getUrlRender
@ -69,12 +87,10 @@ helper maudience = AuthPlugin
(_, []) -> badMethod (_, []) -> badMethod
_ -> notFound _ -> notFound
, apLogin = \toMaster -> do , apLogin = \toMaster -> do
onclick <- createOnClick toMaster onclick <- createOnClick bis toMaster
autologin <- fmap (== Just "true") $ lookupGetParam "autologin" autologin <- fmap (== Just "true") $ lookupGetParam "autologin"
when autologin $ toWidget [julius| when autologin $ toWidget [julius|#{rawJS onclick}();|]
#{rawJS onclick}();
|]
toWidget [hamlet| toWidget [hamlet|
$newline never $newline never
@ -89,27 +105,43 @@ $newline never
-- | Generates a function to handle on-click events, and returns that function -- | Generates a function to handle on-click events, and returns that function
-- name. -- name.
createOnClick :: (Route Auth -> Route master) -> WidgetT master IO Text createOnClick :: BrowserIdSettings
createOnClick toMaster = do -> (Route Auth -> Route master)
addScriptRemote browserIdJs -> WidgetT master IO Text
createOnClick BrowserIdSettings {..} toMaster = do
unless bisLazyLoad $ addScriptRemote browserIdJs
onclick <- newIdent onclick <- newIdent
render <- getUrlRender render <- getUrlRender
let login = toJSON $ getPath $ render $ toMaster LoginR let login = toJSON $ getPath $ render (toMaster LoginR)
toWidget [julius| toWidget [julius|
function #{rawJS onclick}() { function #{rawJS onclick}() {
navigator.id.watch({ if (navigator.id) {
onlogin: function (assertion) { navigator.id.watch({
if (assertion) { onlogin: function (assertion) {
document.location = "@{toMaster complete}" + "/" + assertion; if (assertion) {
} document.location = "@{toMaster complete}/" + assertion;
}, }
onlogout: function () {} },
}); onlogout: function () {}
navigator.id.request({ });
returnTo: #{login} + "?autologin=true" navigator.id.request({
}); returnTo: #{login} + "?autologin=true"
});
}
else {
alert("Loading, please try again");
}
} }
|] |]
when bisLazyLoad $ toWidget [julius|
(function(){
var bid = document.createElement("script");
bid.async = true;
bid.src = #{toJSON browserIdJs};
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(bid, s);
})();
|]
autologin <- fmap (== Just "true") $ lookupGetParam "autologin" autologin <- fmap (== Just "true") $ lookupGetParam "autologin"
when autologin $ toWidget [julius|#{rawJS onclick}();|] when autologin $ toWidget [julius|#{rawJS onclick}();|]

View File

@ -45,6 +45,7 @@ library
, http-types , http-types
, file-embed , file-embed
, email-validate >= 1.0 , email-validate >= 1.0
, data-default
exposed-modules: Yesod.Auth exposed-modules: Yesod.Auth
Yesod.Auth.BrowserId Yesod.Auth.BrowserId