Skip to content

Commit 6bd99d6

Browse files
committed
make authenticate cookie httpOnly and add Logout api call.
1 parent 9393aba commit 6bd99d6

4 files changed

Lines changed: 27 additions & 11 deletions

File tree

src/Happstack/Authenticate/Client.hs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ import GHCJS.Foreign.Export (Export, export, derefExport)
5151
import GHCJS.Foreign.Callback (Callback, syncCallback1, OnBlocked(ContinueAsync))
5252
import GHCJS.Nullable (Nullable(..), nullableToMaybe, maybeToNullable)
5353
import GHCJS.Types (JSVal, jsval)
54-
import Happstack.Authenticate.Core (Email(..), User(..), Username(..), AuthenticateURL(AuthenticationMethods), AuthenticationMethod(..), JSONResponse(..), Status(..), jsonOptions)
54+
import Happstack.Authenticate.Core (Email(..), User(..), Username(..), AuthenticateURL(AuthenticationMethods, Logout), AuthenticationMethod(..), JSONResponse(..), Status(..), jsonOptions)
5555
import qualified Happstack.Authenticate.Core as Authenticate
5656
import Happstack.Authenticate.Password.Core(ChangePasswordData(..), UserPass(..), NewAccountData(..), ResetPasswordData(..), RequestResetPasswordData(..))
5757
import Happstack.Authenticate.Password.URL(AccountURL(Password), PasswordURL(Account, Token, PasswordRequestReset, PasswordReset),passwordAuthenticationMethod)
@@ -569,7 +569,7 @@ logoutHandler routeFn update modelTV e =
569569
"logout" ->
570570
do debugStrLn $ "logoutHandler - logout"
571571
(Just d) <- GHCJS.currentDocument
572-
clearUser modelTV
572+
clearUser routeFn modelTV
573573
_ ->
574574
do debugStrLn $ "unknown action - " ++ show action
575575
Nothing -> do debugStrLn "target is not an element"
@@ -877,8 +877,8 @@ setAuthenticateModel modelTV v =
877877
& isAdmin .~ (_uiAuthAdmin ui)
878878
updateAuthenticateModelFromToken modelTV (_uiToken ui)
879879

880-
clearUser :: TVar AuthenticateModel -> IO ()
881-
clearUser modelTV =
880+
clearUser :: (AuthenticateURL -> Text) -> TVar AuthenticateModel -> IO ()
881+
clearUser routeFn modelTV =
882882
do atomically $ modifyTVar' modelTV $ \m ->
883883
m & usernamePasswordError .~ ""
884884
& muser .~ Nothing
@@ -887,7 +887,13 @@ clearUser modelTV =
887887
ls <- getLocalStorage w
888888
removeItem ls userKey
889889
(Just d) <- GHCJS.currentDocument
890-
setCookie d ("atc=; path=/; expires=Thu, 01-Jan-70 00:00:01 GMT;" :: JSString)
890+
891+
-- We can't do this because the cookie must be httpOnly for security reasons
892+
-- setCookie d ("atc=; path=/; expires=Thu, 01-Jan-70 00:00:01 GMT;" :: JSString)
893+
-- So we have to make an API call so the server can set a new cookie
894+
xhr <- newXMLHttpRequest
895+
open xhr "POST" (routeFn Logout) True
896+
send xhr
891897
doRedraws modelTV
892898

893899
-- FIXME: what happens if this is called twice?
@@ -910,6 +916,8 @@ initHappstackAuthenticateClient baseURL sps =
910916
(Just v) -> do --FIXME: check that atc exists an has same token value
911917
setAuthenticateModel modelTV v
912918

919+
let routeFn = (\url -> baseURL <> toPathInfo url)
920+
913921
-- up-force-logout
914922
mForceLogouts <- getElementsByTagName d "up-force-logout"
915923
case mForceLogouts of
@@ -922,7 +930,7 @@ initHappstackAuthenticateClient baseURL sps =
922930
then debugStrLn "did not actually find up-force-logout"
923931
else do
924932
debugStrLn "up-force-logout"
925-
clearUser modelTV
933+
clearUser routeFn modelTV
926934

927935
-- add login form handlers
928936
let attachLogin inline oldNode =
@@ -933,8 +941,8 @@ initHappstackAuthenticateClient baseURL sps =
933941
(Just inputUsername) <- getElementByNameAttr newElement "username"
934942
(Just inputPassword) <- getElementByNameAttr newElement "password"
935943
update =<< (atomically $ readTVar modelTV)
936-
addEventListener newNode (ev @Submit) (loginHandler (\url -> baseURL <> toPathInfo url) inputUsername inputPassword update modelTV) False
937-
addEventListener newNode (ev @Click) (logoutHandler (\url -> baseURL <> toPathInfo url) update modelTV) False
944+
addEventListener newNode (ev @Submit) (loginHandler routeFn inputUsername inputPassword update modelTV) False
945+
addEventListener newNode (ev @Click) (logoutHandler routeFn update modelTV) False
938946
pure update
939947
-- block login form
940948
mUpLogins <- getElementsByTagName d "up-login"

src/Happstack/Authenticate/Core.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,8 @@ data AuthenticateURL
345345
= -- Users (Maybe UserId)
346346
AuthenticationMethods (Maybe (AuthenticationMethod, [Text]))
347347
| HappstackAuthenticateClient
348+
| Logout
349+
-- | AmAuthenticated
348350
deriving (Eq, Ord, Read, Show, Data, Typeable, Generic)
349351

350352
makeBoomerangs ''AuthenticateURL
@@ -355,6 +357,8 @@ authenticateURL =
355357
( -- "users" </> ( rUsers . rMaybe userId )
356358
"authentication-methods" </> ( rAuthenticationMethods . rMaybe authenticationMethod)
357359
<> "happstack-authenticate-client" . rHappstackAuthenticateClient
360+
<> "logout" . rLogout
361+
-- <> "am-authenticated" . rAmAuthenticated
358362
)
359363
where
360364
userId = rUserId . integer

src/Happstack/Authenticate/Handlers.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import Data.Time (UTCTime, addUTCTime, diffUTCTime, getCur
3232
import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds, posixSecondsToUTCTime)
3333
import Data.UserId (UserId(..), rUserId, succUserId, unUserId)
3434
import Happstack.Authenticate.Core
35-
import Happstack.Server (Cookie(secure), CookieLife(Session, MaxAge), Happstack, ServerPartT, Request(rqSecure), Response, addCookie, askRq, expireCookie, getHeaderM, lookCookie, lookCookieValue, mkCookie, notFound, toResponseBS)
35+
import Happstack.Server (Cookie(httpOnly, sameSite, secure), CookieLife(Session, MaxAge), Happstack, SameSite(SameSiteStrict), ServerPartT, Request(rqSecure), Response, addCookie, askRq, expireCookie, getHeaderM, lookCookie, lookCookieValue, mkCookie, notFound, toResponseBS)
3636
import GHC.Generics (Generic)
3737
import Prelude hiding ((.), id, exp)
3838
import System.IO (IOMode(ReadMode), withFile)
@@ -457,7 +457,7 @@ addTokenCookie :: (Happstack m) =>
457457
addTokenCookie authenticateState authenticateConfig user =
458458
do token <- issueToken authenticateState authenticateConfig user
459459
s <- rqSecure <$> askRq -- FIXME: this isn't that accurate in the face of proxies
460-
addCookie (MaxAge (60*60*24*30)) ((mkCookie authCookieName (Text.unpack token)) { secure = s })
460+
addCookie (MaxAge (60*60*24*30)) ((mkCookie authCookieName (Text.unpack token)) { sameSite = SameSiteStrict, secure = s, httpOnly = True })
461461
-- addCookie (MaxAge 60) ((mkCookie authCookieName (Text.unpack token)) { secure = s })
462462
return token
463463

src/Happstack/Authenticate/Route.hs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import Data.UserId (UserId)
1616
import HSP.JMacro (IntegerSupply(..))
1717
import Happstack.Authenticate.Core
1818
import Happstack.Authenticate.Handlers
19-
import Happstack.Server (internalServerError, notFound, ok, Response, ServerPartT, ToMessage(toResponse))
19+
import Happstack.Server (internalServerError, notFound, ok, method, Method(POST), Response, ServerPartT, ToMessage(toResponse))
2020
import Happstack.Server.FileServe (serveFile, asContentType)
2121
import Happstack.Server.JMacro ()
2222
import Language.Javascript.JMacro (JStat)
@@ -44,6 +44,10 @@ route authenticationHandlers authenticateConfigTV url =
4444
case _happstackAuthenticateClientPath ac of
4545
Nothing -> internalServerError $ toResponse "path to happstack-authenticate-client not configured"
4646
(Just p) -> serveFile (asContentType "text/javascript") p
47+
Logout ->
48+
do method [POST]
49+
deleteTokenCookie
50+
ok $ toResponse ()
4751

4852
------------------------------------------------------------------------------
4953
-- initAuthenticate

0 commit comments

Comments
 (0)