diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,3 @@
+# 0.1.0.0
+
+* Initial release
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,7 @@
+Copyright 2020 Anton Gushcha
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,53 @@
+# reflex-monad-auth
+
+The package provides utilities to build in authorization in reflex application in
+agnostic way to the concrete authorization scheme.
+
+Features:
+- Split application into two contexts: authorized and not authorized. Provides
+  helpers to dynamically switch between both.
+- Access to authorization specific state in authorized part of FRP network.
+
+# How to use
+
+See (example)[./example/Main.hs] and run it with `cabal new-run -f examples`.
+
+First, define you own authorization type:
+
+``` haskell
+data JWTAuth = JWTAuth {
+    authToken :: !Text
+  , authRole  :: !Role
+  } deriving (Eq)
+```
+
+Use it in `info` type hole in `AuthT` monad transformer:
+``` haskell
+runAuthJWT :: (Reflex t, TriggerEvent t m, MonadIO m) => AuthT JWTAuth t m a -> m a
+runAuthJWT = runAuth
+
+main :: IO ()
+main = mainWidgetWithCss css $ runAuthJWT frontend
+```
+
+Now you can use the main tool of the package `liftAuth`:
+
+``` haskell
+frontend :: (HasAuth t m, PerformEvent t m, TriggerEvent t m, MonadHold t m, DomBuilder t m, PostBuild t m, MonadIO m, MonadIO (Performable m), AuthInfo t m ~ JWTAuth) => m ()
+frontend = void $ liftAuth notlogged logged
+  where
+    notlogged = do
+      pressE <- buttonClass "outline" "Login"
+      widgetHold_ (pure ()) $ text "Logging in..." <$ pressE
+      signE <- delay 1 pressE
+      loginE <- requestLoginFromServer signE -- Here you widget that ask server for token
+      void $ signin loginE
+    logged = do
+      pressE <- buttonClass "outline" "Logout"
+      text "We are authorized!"
+      void $ signout pressE
+```
+
+# Hacking
+
+To enter shell with all dependencies for GHC, use `./ghc.sh` script. Or use `./ghcjs.sh` for GHCJS.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/example/Main.hs b/example/Main.hs
new file mode 100644
--- /dev/null
+++ b/example/Main.hs
@@ -0,0 +1,59 @@
+{-# LANGUAGE OverloadedLists #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE UndecidableInstances #-}
+module Main where
+
+import Control.Monad.IO.Class
+import Data.Functor
+import Data.Map.Strict (Map)
+import Data.Text (Text)
+import Reflex.Auth
+import Reflex.Dom
+import Text.RawString.QQ
+
+runAuthBool :: (Reflex t, TriggerEvent t m, MonadIO m) => AuthT Bool t m a -> m a
+runAuthBool = runAuth
+
+deriving instance DomBuilder t m => DomBuilder t (AuthT info t m)
+
+main :: IO ()
+main = mainWidgetWithCss css $ runAuthBool frontend
+  where
+    css = [r|
+      .outline {
+        border-width: 1px;
+        border-radius: 10px;
+        min-width: 100px;
+        min-height: 50px;
+        margin-right: 30px;
+        color: black;
+        background-color: white;
+        border-color: black;
+        font-size: 14pt;
+      }
+    |]
+
+frontend :: (HasAuth t m, PerformEvent t m, TriggerEvent t m, MonadHold t m, DomBuilder t m, PostBuild t m, MonadIO m, MonadIO (Performable m), AuthInfo t m ~ Bool) => m ()
+frontend = void $ liftAuth notlogged logged
+  where
+    notlogged = do
+      pressE <- buttonClass "outline" "Login"
+      widgetHold_ (pure ()) $ text "Logging in..." <$ pressE
+      signE <- delay 1 pressE
+      void $ signin (True <$ signE)
+    logged = do
+      pressE <- buttonClass "outline" "Logout"
+      text "We are authorized!"
+      void $ signout pressE
+
+buttonClass :: (DomBuilder t m, PostBuild t m)
+  => Dynamic t Text -> Dynamic t Text -> m (Event t ())
+buttonClass classValD lbl = mkButton "button" [("onclick", "return false;")] classValD $ dynText lbl
+
+mkButton :: (DomBuilder t m, PostBuild t m) => Text -> Map Text Text -> Dynamic t Text -> m a -> m (Event t a)
+mkButton eltp attrs classValD ma = do
+  let classesD = do
+        classVal <- classValD
+        pure $ attrs <> [("class", classVal)]
+  (e, a) <- elDynAttr' eltp classesD ma
+  return $ a <$ domEvent Click e
diff --git a/reflex-monad-auth.cabal b/reflex-monad-auth.cabal
new file mode 100644
--- /dev/null
+++ b/reflex-monad-auth.cabal
@@ -0,0 +1,102 @@
+name:                reflex-monad-auth
+version:             0.1.0.0
+synopsis:            Utilities to split reflex app to authorized and not authorized contexts
+description:         See README.md
+license:             MIT
+license-file:        LICENSE
+copyright:           2020 Anton Gushcha
+category:            Web
+build-type:          Simple
+cabal-version:       >=1.10
+extra-source-files:
+  README.md
+  CHANGELOG.md
+
+flag examples
+  default: False
+  manual: True
+  description: Build examples executables.
+
+library
+  hs-source-dirs:      src
+  exposed-modules:
+    Reflex.Auth
+    Reflex.Auth.Class
+    Reflex.Auth.Trans
+  build-depends:
+      base                    >= 4.7      && < 4.15
+    , jsaddle                 >= 0.9      && < 0.10
+    , mtl                     >= 2.1      && < 2.3
+    , reflex                  >= 0.4      && < 0.9
+    , reflex-external-ref     >= 1.0      && < 1.1
+  default-language:    Haskell2010
+  default-extensions:
+    BangPatterns
+    ConstraintKinds
+    CPP
+    DataKinds
+    DeriveDataTypeable
+    DeriveGeneric
+    FlexibleContexts
+    FlexibleInstances
+    FunctionalDependencies
+    GADTs
+    GeneralizedNewtypeDeriving
+    LambdaCase
+    OverloadedStrings
+    RankNTypes
+    RecordWildCards
+    RecursiveDo
+    ScopedTypeVariables
+    StandaloneDeriving
+    TemplateHaskell
+    TupleSections
+    TypeApplications
+    TypeFamilies
+    TypeOperators
+    UndecidableInstances
+
+executable reflex-monad-auth-example
+  hs-source-dirs: example
+  if flag(examples)
+    buildable: True
+  else
+    buildable: False
+  main-is:             Main.hs
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      base
+    , containers
+    , raw-strings-qq
+    , reflex
+    , reflex-dom
+    , reflex-monad-auth
+    , text
+
+  default-language:    Haskell2010
+  default-extensions:
+    BangPatterns
+    ConstraintKinds
+    DataKinds
+    DeriveDataTypeable
+    DeriveGeneric
+    FlexibleContexts
+    FlexibleInstances
+    FunctionalDependencies
+    GADTs
+    GeneralizedNewtypeDeriving
+    LambdaCase
+    OverloadedStrings
+    RankNTypes
+    RecordWildCards
+    ScopedTypeVariables
+    StandaloneDeriving
+    TemplateHaskell
+    TupleSections
+    TypeApplications
+    TypeFamilies
+    TypeOperators
+
+source-repository head
+  type: git
+  location: https://github.com/ncrashed/reflex-monad-auth
diff --git a/src/Reflex/Auth.hs b/src/Reflex/Auth.hs
new file mode 100644
--- /dev/null
+++ b/src/Reflex/Auth.hs
@@ -0,0 +1,7 @@
+module Reflex.Auth(
+    module Reflex.Auth.Class
+  , module Reflex.Auth.Trans
+  ) where
+
+import Reflex.Auth.Class
+import Reflex.Auth.Trans
diff --git a/src/Reflex/Auth/Class.hs b/src/Reflex/Auth/Class.hs
new file mode 100644
--- /dev/null
+++ b/src/Reflex/Auth/Class.hs
@@ -0,0 +1,67 @@
+module Reflex.Auth.Class(
+    HasAuth(..)
+  , AuthedEnv
+  , getAuthInfoMay
+  , getLogged
+  , signout
+  , signin
+  ) where
+
+import Control.Monad.Fix
+import Control.Monad.IO.Class
+import Control.Monad.Reader
+import Data.Maybe
+import Reflex
+import Reflex.ExternalRef
+
+-- | Environment that holds changing authorization information
+type AuthedEnv t m = Dynamic t (AuthInfo t m)
+
+-- | Monad for actions that can be authorizaed or not authorized.
+class (Eq (AuthInfo t m), Reflex t) => HasAuth t m | m -> t where
+  -- | Monad specific information about authorization
+  type AuthInfo t m :: *
+
+  -- | Get reference from context that tracks current state of authorization
+  getAuthInfoRef :: m (ExternalRef t (Maybe (AuthInfo t m)))
+
+  -- | A typical way to perform one action when user logged and perform another
+  -- FRP network part when the user is authorized.
+  liftAuth :: m a -- ^ Which widget to render when not authorized
+    -> ReaderT (AuthedEnv t m) m a -- ^ Which widget to render when authorized
+    -> m (Dynamic t a)
+
+-- | Get dynamic that is 'Nothing' when is not authorized and 'Just' when we have authorization.
+getAuthInfoMay :: (HasAuth t m, MonadHold t m, MonadFix m, MonadIO m) => m (Dynamic t (Maybe (AuthInfo t m)))
+getAuthInfoMay = holdUniqDyn =<< externalRefDynamic =<< getAuthInfoRef
+
+-- | Return dynamic that indicates whether we in authorized or in authorized state.
+getLogged :: (HasAuth t m, MonadHold t m, MonadFix m, MonadIO m) => m (Dynamic t Bool)
+getLogged = fmap isJust <$> getAuthInfoMay
+
+-- | When input event is fired, the auth info inside is nullified and all `liftAuth` widgets
+-- are switched back to not authorized states.
+signout :: (HasAuth t m, PerformEvent t m, MonadIO m, MonadIO (Performable m)) => Event t () -> m (Event t ())
+signout e = do
+  ref <- getAuthInfoRef
+  performEvent $ ffor e $ const $ writeExternalRef ref Nothing
+
+-- | When input event is fired all `liftAuth` widgets switches to authorized states.
+signin :: (HasAuth t m, PerformEvent t m, MonadIO m, MonadIO (Performable m)) => Event t (AuthInfo t m) -> m (Event t (AuthInfo t m))
+signin e = do
+  ref <- getAuthInfoRef
+  performEvent $ ffor e $ \ai -> writeExternalRef ref (Just ai) >> pure ai
+
+instance {-# OVERLAPPABLE #-} (HasAuth t m, Monad m) => HasAuth t (ReaderT e m) where
+  type AuthInfo t (ReaderT e m) = AuthInfo t m
+
+  getAuthInfoRef = lift getAuthInfoRef
+  {-# INLINE getAuthInfoRef #-}
+
+  liftAuth unauthed authed = do
+    e <- ask
+    let authed' = do
+          ae <- ask
+          lift $ runReaderT (runReaderT authed ae) e
+    lift $ liftAuth (runReaderT unauthed e) authed'
+  {-# INLINE liftAuth #-}
diff --git a/src/Reflex/Auth/Trans.hs b/src/Reflex/Auth/Trans.hs
new file mode 100644
--- /dev/null
+++ b/src/Reflex/Auth/Trans.hs
@@ -0,0 +1,112 @@
+module Reflex.Auth.Trans(
+    AuthEnv
+  , AuthT(..)
+  , newAuthEnv
+  , runAuthT
+  , runAuth
+  , module Reflex.Auth.Class
+  ) where
+
+import Control.Monad.Fix
+import Control.Monad.Reader
+import Control.Monad.State.Strict
+import Language.Javascript.JSaddle.Types
+import GHC.Generics
+import Reflex
+import Reflex.Auth.Class
+import Reflex.ExternalRef
+import Reflex.Network
+
+-- | Environment for `AuthT`
+type AuthEnv t info = ExternalRef t (Maybe info)
+
+-- | Allocate new authorization environment that is not logged by default
+newAuthEnv :: (Reflex t, TriggerEvent t m, MonadIO m) => m (AuthEnv t info)
+newAuthEnv = newExternalRef Nothing
+
+-- | Monad that implements 'HasAuth' with simple reader inside
+newtype AuthT info t m a = AuthT { unAuthT :: ReaderT (AuthEnv t info) m a }
+  deriving (Functor, Applicative, Monad, Generic, MonadFix)
+
+deriving instance PostBuild t m => PostBuild t (AuthT info t m)
+deriving instance NotReady t m => NotReady t (AuthT info t m)
+deriving instance PerformEvent t m => PerformEvent t (AuthT info t m)
+deriving instance TriggerEvent t m => TriggerEvent t (AuthT info t m)
+deriving instance MonadHold t m => MonadHold t (AuthT info t m)
+deriving instance MonadSample t m => MonadSample t (AuthT info t m)
+deriving instance MonadIO m => MonadIO (AuthT info t m)
+#ifndef ghcjs_HOST_OS
+deriving instance MonadJSM m => MonadJSM (AuthT info t m)
+#endif
+deriving instance (Group q, Additive q, Query q, Eq q, MonadQuery t q m, Monad m) => MonadQuery t q (AuthT info t m)
+deriving instance (Monoid w, DynamicWriter t w m) => DynamicWriter t w (AuthT info t m)
+deriving instance (Monoid w, BehaviorWriter t w m) => BehaviorWriter t w (AuthT info t m)
+deriving instance (Semigroup w, EventWriter t w m) => EventWriter t w (AuthT info t m)
+deriving instance (Requester t m) => Requester t (AuthT info t m)
+
+instance MonadTrans (AuthT info t) where
+  lift = AuthT . lift
+  {-# INLINABLE lift #-}
+
+instance MonadReader e m => MonadReader e (AuthT info t m) where
+  ask = lift ask
+  {-# INLINABLE ask #-}
+  local f (AuthT ma) = AuthT $ do
+    r <- ask
+    lift $ local f $ runReaderT ma r
+  {-# INLINABLE local #-}
+
+instance MonadState s m => MonadState s (AuthT info t m) where
+  get = lift get
+  {-# INLINABLE get #-}
+  put = lift . put
+  {-# INLINABLE put #-}
+
+instance Adjustable t m => Adjustable t (AuthT info t m) where
+  runWithReplace a0 a' = do
+    r <- AuthT ask
+    lift $ runWithReplace (runAuthT a0 r) $ fmap (`runAuthT` r) a'
+  {-# INLINABLE runWithReplace #-}
+  traverseIntMapWithKeyWithAdjust f dm0 dm' = do
+    r <- AuthT ask
+    lift $ traverseIntMapWithKeyWithAdjust (\k v -> runAuthT (f k v) r) dm0 dm'
+  {-# INLINABLE traverseIntMapWithKeyWithAdjust #-}
+  traverseDMapWithKeyWithAdjust f dm0 dm' = do
+    r <- AuthT ask
+    lift $ traverseDMapWithKeyWithAdjust (\k v -> runAuthT (f k v) r) dm0 dm'
+  {-# INLINABLE traverseDMapWithKeyWithAdjust #-}
+  traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = do
+    r <- AuthT ask
+    lift $ traverseDMapWithKeyWithAdjustWithMove (\k v -> runAuthT (f k v) r) dm0 dm'
+  {-# INLINABLE traverseDMapWithKeyWithAdjustWithMove #-}
+
+-- | Execute widget with authorization logic with given environment.
+runAuthT :: AuthT info t m a -> AuthEnv t info -> m a
+runAuthT (AuthT ma) e = runReaderT ma e
+{-# INLINEABLE runAuthT #-}
+
+-- | Simplified version of `runAuthT`
+runAuth :: (Reflex t, TriggerEvent t m, MonadIO m) => AuthT info t m a -> m a
+runAuth ma = do
+  re <- newAuthEnv
+  runAuthT ma re
+{-# INLINABLE runAuth #-}
+
+instance (Eq info, Reflex t, MonadIO m, MonadHold t m, MonadFix m, Adjustable t m) => HasAuth t (AuthT info t m) where
+  type AuthInfo t (AuthT info t m) = info
+
+  getAuthInfoRef = AuthT ask
+  {-# INLINE getAuthInfoRef #-}
+
+  liftAuth unauth authed = do
+    ref <- AuthT ask
+    ai0 <- readExternalRef ref
+    aimd <- holdUniqDyn =<< externalRefDynamic ref
+    aid <- fmap fromJust <$> improvingMaybe aimd
+    let
+      mauthed = runReaderT authed aid
+      m = maybe unauth (const mauthed)
+    networkHold (m ai0) $ m <$> updated aimd
+    where
+      fromJust Nothing = error "liftAuth: impossible, forced Nothing in authed dynamic"
+      fromJust (Just a) = a
