diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,29 @@
+# authenticate changelog
+
+## 1.3.5.2
+
+* Support for aeson-2.2 [#61](https://github.com/yesodweb/authenticate/pull/61)
+
+## 1.3.5.1
+
+* Support for aeson-2.0 [#56](https://github.com/yesodweb/authenticate/pull/56)
+
+## 1.3.5
+
+* Drop tagstream-conduit dep (for GHC 8.8 support)
+
+## 1.3.4
+
+* Relaxed a bunch of type signatures
+
+## 1.3.3.2
+
+* Support for http-conduit-2.2.0 [#47](https://github.com/yesodweb/authenticate/issues/47)
+
+## 1.3.3.1
+
+* License update [#46](https://github.com/yesodweb/authenticate/issues/46)
+
+## 1.3.3
+
+Deprecated Google OpenID support
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,25 +1,20 @@
-The following license covers this documentation, and the source code, except
-where otherwise indicated.
-
-Copyright 2008, Michael Snoyman. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
+Copyright (c) 2012 Michael Snoyman, http://www.yesodweb.com/
 
-* Redistributions of source code must retain the above copyright notice, this
-  list of conditions and the following disclaimer.
+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:
 
-* Redistributions in binary form must reproduce the above copyright notice,
-  this list of conditions and the following disclaimer in the documentation
-  and/or other materials provided with the distribution.
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
 
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
-OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
-OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+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/OpenId2/Discovery.hs b/OpenId2/Discovery.hs
--- a/OpenId2/Discovery.hs
+++ b/OpenId2/Discovery.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE CPP #-}
 
 --------------------------------------------------------------------------------
 -- |
@@ -22,77 +23,95 @@
 import OpenId2.Types
 import OpenId2.XRDS
 
-import Debug.Trace
 -- Libraries
 import Data.Char
 import Data.Maybe
-import Network.HTTP.Enumerator
+import Network.HTTP.Conduit
 import qualified Data.ByteString.Char8 as S8
 import Control.Arrow (first)
 import Control.Monad.IO.Class (MonadIO (liftIO))
-import Control.Failure (Failure (failure))
-import Control.Monad (mplus, liftM)
+import Control.Monad (mplus, liftM, guard)
 import qualified Data.CaseInsensitive as CI
 import Data.Text (Text, unpack)
 import Data.Text.Lazy (toStrict)
 import qualified Data.Text as T
 import Data.Text.Lazy.Encoding (decodeUtf8With)
 import Data.Text.Encoding.Error (lenientDecode)
-import Text.HTML.TagSoup (parseTags, Tag (TagOpen))
 import Control.Applicative ((<$>), (<*>))
+import Network.HTTP.Types (status200)
+import Control.Exception (throwIO)
+import Text.HTML.DOM
+import Text.XML.Cursor
+import Text.XML (Node (..), Element (..))
+import qualified Data.Map as Map
 
 data Discovery = Discovery1 Text (Maybe Text)
                | Discovery2 Provider Identifier IdentType
     deriving Show
 
 -- | Attempt to resolve an OpenID endpoint, and user identifier.
-discover :: ( MonadIO m
-            , Failure AuthenticateException m
-            , Failure HttpException m
-            )
-         => Identifier
-         -> m Discovery
-discover ident@(Identifier i) = do
-    res1 <- discoverYADIS ident Nothing 10
+discover :: MonadIO m => Identifier -> Manager -> m Discovery
+discover ident@(Identifier i) manager = do
+    res1 <- discoverYADIS ident Nothing 10 manager
     case res1 of
         Just (x, y, z) -> return $ Discovery2 x y z
         Nothing -> do
-            res2 <- discoverHTML ident
+            res2 <- discoverHTML ident manager
             case res2 of
                 Just x -> return x
-                Nothing -> failure $ DiscoveryException $ unpack i
+                Nothing -> liftIO $ throwIO $ DiscoveryException $ unpack i
 
 -- YADIS-Based Discovery -------------------------------------------------------
 
 -- | Attempt a YADIS based discovery, given a valid identifier.  The result is
 --   an OpenID endpoint, and the actual identifier for the user.
-discoverYADIS :: ( MonadIO m
-                 , Failure HttpException m
-                 )
+discoverYADIS :: MonadIO m
               => Identifier
               -> Maybe String
               -> Int -- ^ remaining redirects
+              -> Manager
               -> m (Maybe (Provider, Identifier, IdentType))
-discoverYADIS _ _ 0 = failure TooManyRedirects
-discoverYADIS ident mb_loc redirects = do
+discoverYADIS _ _ 0 _ =
+#if MIN_VERSION_http_conduit(2, 2, 0)
+    error "discoverYADIS: Too many redirects"
+#else
+    liftIO $ throwIO $ TooManyRedirects
+#if MIN_VERSION_http_conduit(1,6,0)
+        []
+#endif
+#endif
+discoverYADIS ident mb_loc redirects manager = do
     let uri = fromMaybe (unpack $ identifier ident) mb_loc
-    req <- parseUrl uri
-    res <- liftIO $ withManager $ httpLbs req
+#if MIN_VERSION_http_conduit(2, 2, 0)
+    req <- liftIO $ parseRequest uri
+#else
+    req <- liftIO $ parseUrl uri
+#endif
+    res <- httpLbs req
+#if !MIN_VERSION_http_conduit(2, 2, 0)
+#if MIN_VERSION_http_conduit(1, 9, 0)
+        { checkStatus = \_ _ _ -> Nothing
+#else
+        { checkStatus = \_ _ -> Nothing
+#endif
+        }
+#endif
+        manager
     let mloc = fmap S8.unpack
              $ lookup "x-xrds-location"
              $ map (first $ map toLower . S8.unpack . CI.original)
              $ responseHeaders res
     let mloc' = if mloc == mb_loc then Nothing else mloc
-    case statusCode res of
-        200 ->
+    if responseStatus res == status200
+        then
           case mloc' of
-            Just loc -> discoverYADIS ident (Just loc) (redirects - 1)
+            Just loc -> discoverYADIS ident (Just loc) (redirects - 1) manager
             Nothing  -> do
               let mdoc = parseXRDS $ responseBody res
               case mdoc of
                   Just doc -> return $ parseYADIS ident doc
                   Nothing -> return Nothing
-        _ -> return Nothing
+        else return Nothing
 
 
 -- | Parse out an OpenID endpoint, and actual identifier from a YADIS xml
@@ -120,21 +139,27 @@
 
 -- | Attempt to discover an OpenID endpoint, from an HTML document.  The result
 -- will be an endpoint on success, and the actual identifier of the user.
-discoverHTML :: ( MonadIO m, Failure HttpException m)
-             => Identifier
-             -> m (Maybe Discovery)
-discoverHTML ident'@(Identifier ident) =
-    (parseHTML ident' . toStrict . decodeUtf8With lenientDecode) `liftM` simpleHttp (unpack ident)
+discoverHTML :: MonadIO m => Identifier -> Manager -> m (Maybe Discovery)
+discoverHTML ident'@(Identifier ident) manager = do
+    req <- liftIO $ parseUrlThrow $ unpack ident
+    lbs <- liftM responseBody $ httpLbs req manager
+    return $ parseHTML ident' . toStrict . decodeUtf8With lenientDecode $ lbs
 
 -- | Parse out an OpenID endpoint and an actual identifier from an HTML
 -- document.
 parseHTML :: Identifier -> Text -> Maybe Discovery
-parseHTML ident = resolve
-                . filter isOpenId
-                . mapMaybe linkTag
-                . parseTags
+parseHTML ident text0 = do
+    let doc = parseSTChunks [text0]
+        cursor = fromDocument doc
+        links = map node $ cursor $// element "link"
+        ls = do
+          NodeElement (Element "link" as _) <- links
+          Just rel <- pure $ Map.lookup "rel" as
+          Just href <- pure $ Map.lookup "href" as
+          guard $ "openid" `T.isPrefixOf` rel
+          pure (rel, href)
+    resolve ls
   where
-    isOpenId (rel, _x) = "openid" `T.isPrefixOf` rel
     resolve1 ls = do
       server <- lookup "openid.server" ls
       let delegate = lookup "openid.delegate" ls
@@ -145,10 +170,5 @@
       -- Based on OpenID 2.0 spec, section 7.3.3, HTML discovery can only
       -- result in a claimed identifier.
       return $ Discovery2 (Provider prov) lid ClaimedIdent
-    resolve ls = resolve2 ls `mplus` resolve1 ls
 
-
--- | Filter out link tags from a list of html tags.
-linkTag :: Tag Text -> Maybe (Text, Text)
-linkTag (TagOpen "link" as) = let x = (,) <$> lookup "rel" as <*> lookup "href" as in traceShow x x
-linkTag _x = Nothing
+    resolve ls = resolve2 ls `mplus` resolve1 ls
diff --git a/OpenId2/Normalization.hs b/OpenId2/Normalization.hs
--- a/OpenId2/Normalization.hs
+++ b/OpenId2/Normalization.hs
@@ -21,18 +21,19 @@
 import Control.Applicative
 import Control.Monad
 import Data.List
-import Control.Failure (Failure (..))
 import Network.URI
     ( uriToString, normalizeCase, normalizeEscape
     , normalizePathSegments, parseURI, uriPath, uriScheme, uriFragment
     )
 import Data.Text (Text, pack, unpack)
+import Control.Monad.IO.Class
+import Control.Exception (throwIO)
 
-normalize :: Failure AuthenticateException m => Text -> m Identifier
+normalize :: MonadIO m => Text -> m Identifier
 normalize ident =
     case normalizeIdentifier $ Identifier ident of
         Just i -> return i
-        Nothing -> failure $ NormalizationException $ unpack ident
+        Nothing -> liftIO $ throwIO $ NormalizationException $ unpack ident
 
 -- | Normalize an identifier, discarding XRIs.
 normalizeIdentifier :: Identifier -> Maybe Identifier
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,5 @@
+## authenticate
+
+Focus is on third-party authentication methods, such as OpenID and BrowserID.
+
+Note: Facebook support is now provided by [the fb package](http://www.stackage.org/package/fb).
diff --git a/Web/Authenticate/BrowserId.hs b/Web/Authenticate/BrowserId.hs
--- a/Web/Authenticate/BrowserId.hs
+++ b/Web/Authenticate/BrowserId.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE CPP #-}
 module Web.Authenticate.BrowserId
     ( browserIdJs
@@ -6,30 +7,38 @@
     ) where
 
 import Data.Text (Text)
-import Network.HTTP.Enumerator (parseUrl, responseBody, httpLbs, withManager, method, urlEncodedBody)
+import Network.HTTP.Conduit (parseUrlThrow, responseBody, httpLbs, Manager, method, urlEncodedBody)
+#if MIN_VERSION_aeson(2,2,0)
+import Data.Aeson (Value (Object, String))
+import Data.Aeson.Parser (json)
+#else
 import Data.Aeson (json, Value (Object, String))
+#endif
 import Data.Attoparsec.Lazy (parse, maybeResult)
-#if MIN_VERSION_aeson(0, 4, 0)
-import qualified Data.HashMap.Lazy as Map
+#if MIN_VERSION_aeson(2,0,0)
+import qualified Data.Aeson.KeyMap as Map
 #else
-import qualified Data.Map as Map
+import qualified Data.HashMap.Lazy as Map
 #endif
 import Data.Text.Encoding (encodeUtf8)
+import Control.Monad.IO.Class (MonadIO, liftIO)
 
 -- | Location of the Javascript file hosted by browserid.org
 browserIdJs :: Text
-browserIdJs = "https://browserid.org/include.js"
+browserIdJs = "https://login.persona.org/include.js"
 
-checkAssertion :: Text -- ^ audience
+checkAssertion :: MonadIO m
+               => Text -- ^ audience
                -> Text -- ^ assertion
-               -> IO (Maybe Text)
-checkAssertion audience assertion = do
-    req' <- parseUrl "https://browserid.org/verify"
+               -> Manager
+               -> m (Maybe Text)
+checkAssertion audience assertion manager = do
+    req' <- liftIO $ parseUrlThrow "https://verifier.login.persona.org/verify"
     let req = urlEncodedBody
                 [ ("audience", encodeUtf8 audience)
                 , ("assertion", encodeUtf8 assertion)
                 ] req' { method = "POST" }
-    res <- withManager $ httpLbs req
+    res <- httpLbs req manager
     let lbs = responseBody res
     return $ maybeResult (parse json lbs) >>= getEmail
   where
diff --git a/Web/Authenticate/Facebook.hs b/Web/Authenticate/Facebook.hs
deleted file mode 100644
--- a/Web/Authenticate/Facebook.hs
+++ /dev/null
@@ -1,115 +0,0 @@
-{-# LANGUAGE FlexibleContexts #-}
-{-# LANGUAGE DeriveDataTypeable #-}
-{-# LANGUAGE OverloadedStrings #-}
-module Web.Authenticate.Facebook
-    ( Facebook (..)
-    , AccessToken (..)
-    , getForwardUrlParams
-    , getForwardUrlWithState
-    , getForwardUrl
-    , getAccessToken
-    , getGraphData
-    , getGraphData_
-    , getLogoutUrl
-    ) where
-
-import Network.HTTP.Enumerator
-import Network.HTTP.Types (parseSimpleQuery)
-import Data.Aeson
-import qualified Data.ByteString.Lazy.Char8 as L8
-import Data.Data (Data)
-import Data.Typeable (Typeable)
-import Control.Exception (Exception, throwIO)
-import Data.Attoparsec.Lazy (parse, eitherResult)
-import qualified Data.ByteString.Char8 as S8
-import Data.Text (Text)
-import qualified Data.Text as T
-import qualified Data.Text.Encoding as TE
-import Blaze.ByteString.Builder (toByteString, copyByteString)
-import Blaze.ByteString.Builder.Char.Utf8 (fromText)
-import Network.HTTP.Types (renderQueryText)
-import Data.Monoid (mappend)
-import Data.ByteString (ByteString)
-import Control.Arrow ((***))
-
-data Facebook = Facebook
-    { facebookClientId :: Text
-    , facebookClientSecret :: Text
-    , facebookRedirectUri :: Text
-    }
-    deriving (Show, Eq, Read, Ord, Data, Typeable)
-
-newtype AccessToken = AccessToken { unAccessToken :: Text }
-    deriving (Show, Eq, Read, Ord, Data, Typeable)
-
-getForwardUrlParams :: Facebook -> [(Text, Text)] -> Text
-getForwardUrlParams fb params =
-    TE.decodeUtf8 $ toByteString $
-    copyByteString "https://graph.facebook.com/oauth/authorize"
-    `mappend`
-    renderQueryText True
-        (  ("client_id", Just $ facebookClientId fb)
-         : ("redirect_uri", Just $ facebookRedirectUri fb)
-         : map (id *** Just) params)
-
--- Internal function used to simplify getForwardUrl & getForwardUrlWithState
-getForwardUrlWithExtra_ :: Facebook -> [Text] -> [(Text, Text)] -> Text
-getForwardUrlWithExtra_ fb perms extra = getForwardUrlParams fb $ (if null perms
-                                                                   then id
-                                                                   else (("scope", T.intercalate "," perms):)) extra
-
-getForwardUrlWithState :: Facebook -> [Text] -> Text -> Text
-getForwardUrlWithState fb perms state = getForwardUrlWithExtra_ fb perms [("state", state)]
-
-getForwardUrl :: Facebook -> [Text] -> Text
-getForwardUrl fb perms = getForwardUrlWithExtra_ fb perms []
-
-accessTokenUrl :: Facebook -> Text -> ByteString
-accessTokenUrl fb code =
-    toByteString $
-    copyByteString "https://graph.facebook.com/oauth/access_token"
-    `mappend`
-    renderQueryText True
-        [ ("client_id", Just $ facebookClientId fb)
-        , ("redirect_uri", Just $ facebookRedirectUri fb)
-        , ("code", Just code)
-        , ("client_secret", Just $ facebookClientSecret fb)
-        ]
-
-getAccessToken :: Facebook -> Text -> IO AccessToken
-getAccessToken fb code = do
-    let url = accessTokenUrl fb code
-    b <- simpleHttp $ S8.unpack url
-    let params = parseSimpleQuery $ S8.concat $ L8.toChunks b
-    case lookup "access_token" params of
-        Just x -> return $ AccessToken $ T.pack $ S8.unpack x
-        Nothing -> error $ "Invalid facebook response: " ++ L8.unpack b
-
-graphUrl :: AccessToken -> Text -> ByteString
-graphUrl (AccessToken s) func =
-    toByteString $
-    copyByteString "https://graph.facebook.com/"
-    `mappend` fromText func
-    `mappend` renderQueryText True [("access_token", Just s)]
-
-getGraphData :: AccessToken -> Text -> IO (Either String Value)
-getGraphData at func = do
-    let url = graphUrl at func
-    b <- simpleHttp $ S8.unpack url
-    return $ eitherResult $ parse json b
-
-getGraphData_ :: AccessToken -> Text -> IO Value
-getGraphData_ a b = getGraphData a b >>= either (throwIO . InvalidJsonException) return
-
-data InvalidJsonException = InvalidJsonException String
-    deriving (Show, Typeable)
-instance Exception InvalidJsonException
-
--- | Logs out the user from their Facebook session.
-getLogoutUrl :: AccessToken
-             -> Text -- ^ URL the user should be directed to in your site domain.
-             -> Text -- ^ Logout URL in @https://www.facebook.com/@.
-getLogoutUrl (AccessToken s) next =
-    TE.decodeUtf8 $ toByteString $
-    copyByteString "https://www.facebook.com/logout.php"
-    `mappend` renderQueryText True [("next", Just next), ("access_token", Just s)]
diff --git a/Web/Authenticate/Kerberos.hs b/Web/Authenticate/Kerberos.hs
deleted file mode 100644
--- a/Web/Authenticate/Kerberos.hs
+++ /dev/null
@@ -1,72 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
--- | Module for using a kerberos authentication service.
---
--- Please note that all configuration should have been done
--- manually on the machine prior to running the code.
---
--- On linux machines the configuration might be in /etc/krb5.conf.
--- It's worth checking if the Kerberos service provider (e.g. your university)
--- already provide a complete configuration file.
---
--- Be certain that you can manually login from a shell by typing
---
--- > kinit username
---
--- If you fill in your password and the program returns no error code,
--- then your kerberos configuration is setup properly.
--- Only then can this module be of any use.
-module Web.Authenticate.Kerberos
-  ( loginKerberos
-  , KerberosAuthResult(..)
-  ) where
-
-import Data.Text (Text)
-import qualified Data.Text as T
-import Data.Maybe (fromJust)
-import Control.Monad (msum, guard)
-import System.Process (readProcessWithExitCode)
-import System.Timeout (timeout)
-import System.Exit (ExitCode(..))
-
--- | Occurreable results of a Kerberos login
-data KerberosAuthResult = Ok
-                        | NoSuchUser
-                        | WrongPassword
-                        | TimeOut
-                        | UnknownError Text
-
-instance Show KerberosAuthResult where
-  show Ok                 = "Login sucessful"
-  show NoSuchUser         = "Wrong username"
-  show WrongPassword      = "Wrong password"
-  show TimeOut            = "kinit respone timeout"
-  show (UnknownError msg) = "Unkown error: " ++ T.unpack msg
-
-
--- Given the errcode and stderr, return error-value
-interpretError :: Int -> Text -> KerberosAuthResult
-interpretError _ errmsg = fromJust . msum $
-    ["Client not found in Kerberos database while getting" --> NoSuchUser,
-     "Preauthentication failed while getting" --> WrongPassword,
-     Just $ UnknownError errmsg]
-  where
-    substr --> kError = guard (substr `T.isInfixOf` errmsg) >> Just kError
-
--- | Given the username and password, try login to Kerberos service
-loginKerberos :: Text -- ^ Username
-              -> Text -- ^ Password
-              -> IO KerberosAuthResult
-loginKerberos username password = do
-    timedFetch <- timeout (10*1000000) fetch
-    case timedFetch of
-      Just res -> return res
-      Nothing  -> return TimeOut
-  where
-    fetch :: IO KerberosAuthResult
-    fetch = do
-      (exitCode, _out, err) <- readProcessWithExitCode
-          "kinit" [T.unpack username] (T.unpack password)
-      case exitCode of
-        ExitSuccess   -> return Ok
-        ExitFailure x -> return $ interpretError x (T.pack err)
-
diff --git a/Web/Authenticate/OAuth.hs b/Web/Authenticate/OAuth.hs
deleted file mode 100644
--- a/Web/Authenticate/OAuth.hs
+++ /dev/null
@@ -1,306 +0,0 @@
-{-# LANGUAGE DeriveDataTypeable, OverloadedStrings, StandaloneDeriving #-}
-{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}
-module Web.Authenticate.OAuth
-    ( -- * Data types
-      OAuth(..), SignMethod(..), Credential(..), OAuthException(..),
-      -- * Operations for credentials
-      emptyCredential, insert, delete, inserts,
-      -- * Signature
-      signOAuth, genSign,
-      -- * Url & operation for authentication
-      authorizeUrl, getAccessToken, getTemporaryCredential,
-      getTokenCredential, getTemporaryCredentialWithScope,
-      getAccessTokenProxy, getTemporaryCredentialProxy,
-      getTokenCredentialProxy, 
-      getAccessToken', getTemporaryCredential',
-      -- * Utility Methods
-      paramEncode, addScope, addMaybeProxy
-    ) where
-import Network.HTTP.Enumerator
-import Data.Data
-import qualified Data.ByteString.Char8 as BS
-import qualified Data.ByteString.Lazy.Char8 as BSL
-import Data.Maybe
-import Control.Applicative
-import Network.HTTP.Types (parseSimpleQuery)
-import Control.Exception
-import Control.Monad
-import Data.List (sortBy)
-import System.Random
-import Data.Char
-import Data.Digest.Pure.SHA
-import Data.ByteString.Base64
-import Data.Time
-import Numeric
-import Codec.Crypto.RSA (rsassa_pkcs1_v1_5_sign, ha_SHA1, PrivateKey(..))
-import Network.HTTP.Types (Header)
-import Control.Arrow (second)
-import Blaze.ByteString.Builder (toByteString)
-import Data.Enumerator (($$), run_, Stream (..), continue)
-import Data.Monoid (mconcat)
-import Control.Monad.IO.Class (MonadIO (liftIO))
-import Data.IORef (newIORef, readIORef, atomicModifyIORef)
-import Network.HTTP.Types (renderSimpleQuery)
-
--- | Data type for OAuth client (consumer).
-data OAuth = OAuth { oauthServerName      :: String        -- ^ Service name
-                   , oauthRequestUri      :: String        -- ^ URI to request temporary credential
-                   , oauthAccessTokenUri  :: String        -- ^ Uri to obtain access token
-                   , oauthAuthorizeUri    :: String        -- ^ Uri to authorize
-                   , oauthSignatureMethod :: SignMethod    -- ^ Signature Method
-                   , oauthConsumerKey     :: BS.ByteString -- ^ Consumer key
-                   , oauthConsumerSecret  :: BS.ByteString -- ^ Consumer Secret
-                   , oauthCallback        :: Maybe BS.ByteString -- ^ Callback uri to redirect after authentication
-                   } deriving (Show, Eq, Ord, Read, Data, Typeable)
-
-
--- | Data type for signature method.
-data SignMethod = PLAINTEXT
-                | HMACSHA1
-                | RSASHA1 PrivateKey
-                  deriving (Show, Eq, Ord, Read, Data, Typeable)
-
-deriving instance Typeable PrivateKey
-deriving instance Data PrivateKey
-deriving instance Read PrivateKey
-deriving instance Ord PrivateKey
-deriving instance Eq PrivateKey
-
--- | Data type for redential.
-data Credential = Credential { unCredential :: [(BS.ByteString, BS.ByteString)] }
-                  deriving (Show, Eq, Ord, Read, Data, Typeable)
-
--- | Empty credential.
-emptyCredential :: Credential
-emptyCredential = Credential []
-
-token, tokenSecret :: Credential -> BS.ByteString
-token = fromMaybe "" . lookup "oauth_token" . unCredential
-tokenSecret = fromMaybe "" . lookup "oauth_token_secret" . unCredential
-
-data OAuthException = OAuthException String
-                      deriving (Show, Eq, Data, Typeable)
-
-instance Exception OAuthException
-
-toStrict :: BSL.ByteString -> BS.ByteString
-toStrict = BS.concat . BSL.toChunks
-
-fromStrict :: BS.ByteString -> BSL.ByteString
-fromStrict = BSL.fromChunks . return
-
--- | Get temporary credential for requesting acces token.
-getTemporaryCredential :: OAuth         -- ^ OAuth Application
-                       -> IO Credential -- ^ Temporary Credential (Request Token & Secret).
-getTemporaryCredential = getTemporaryCredential' id
-
--- | Get temporary credential for requesting access token with Scope parameter.
-getTemporaryCredentialWithScope :: BS.ByteString -- ^ Scope parameter string
-                                -> OAuth         -- ^ OAuth Application
-                                -> IO Credential -- ^ Temporay Credential (Request Token & Secret).
-getTemporaryCredentialWithScope = getTemporaryCredential' . addScope
-
-addScope :: (MonadIO m) => BS.ByteString -> Request m -> Request m
-addScope scope req | BS.null scope = req
-                   | otherwise     = urlEncodedBody [("scope", scope)] req
-
--- | Get temporary credential for requesting access token via the proxy.
-getTemporaryCredentialProxy :: Maybe Proxy   -- ^ Proxy
-                            -> OAuth         -- ^ OAuth Application
-                            -> IO Credential -- ^ Temporary Credential (Request Token & Secret).
-getTemporaryCredentialProxy p = getTemporaryCredential' $ addMaybeProxy p
-
-getTemporaryCredential' :: (Request IO -> Request IO) -- ^ Request Hook
-                        -> OAuth                      -- ^ OAuth Application
-                        -> IO Credential              -- ^ Temporary Credential (Request Token & Secret).
-getTemporaryCredential' hook oa = do
-  let req = fromJust $ parseUrl $ oauthRequestUri oa
-      crd = maybe id (insert "oauth_callback") (oauthCallback oa) $ emptyCredential
-  req' <- signOAuth oa crd $ hook (req { method = "POST" }) 
-  rsp <- withManager . httpLbs $ req'
-  if statusCode rsp == 200
-    then do
-      let dic = parseSimpleQuery . toStrict . responseBody $ rsp
-      return $ Credential dic
-    else throwIO . OAuthException $ "Gaining OAuth Temporary Credential Failed: " ++ BSL.unpack (responseBody rsp)
-
--- | URL to obtain OAuth verifier.
-authorizeUrl :: OAuth           -- ^ OAuth Application
-             -> Credential      -- ^ Temporary Credential (Request Token & Secret)
-             -> String          -- ^ URL to authorize
-authorizeUrl oa cr = oauthAuthorizeUri oa ++ BS.unpack (renderSimpleQuery True queries)
-  where queries = case oauthCallback oa of
-                    Nothing       -> [("oauth_token", token cr)]
-                    Just callback -> [("oauth_token", token cr), ("oauth_callback", callback)]
-
--- | Get Access token.
-getAccessToken, getTokenCredential
-               :: OAuth         -- ^ OAuth Application
-               -> Credential    -- ^ Temporary Credential with oauth_verifier
-               -> IO Credential -- ^ Token Credential (Access Token & Secret)
-getAccessToken = getAccessToken' id
-
--- | Get Access token via the proxy.
-getAccessTokenProxy, getTokenCredentialProxy
-               :: Maybe Proxy   -- ^ Proxy
-               -> OAuth         -- ^ OAuth Application
-               -> Credential    -- ^ Temporary Credential with oauth_verifier
-               -> IO Credential -- ^ Token Credential (Access Token & Secret)
-getAccessTokenProxy p = getAccessToken' $ addMaybeProxy p
-
-getAccessToken' :: (Request IO -> Request IO) -- ^ Request Hook
-                -> OAuth                      -- ^ OAuth Application
-                -> Credential                 -- ^ Temporary Credential with oauth_verifier
-                -> IO Credential              -- ^ Token Credential (Access Token & Secret)
-getAccessToken' hook oa cr = do
-  let req = hook (fromJust $ parseUrl $ oauthAccessTokenUri oa) { method = "POST" }
-  rsp <- withManager . httpLbs =<< signOAuth oa cr req
-  if statusCode rsp == 200
-    then do
-      let dic = parseSimpleQuery . toStrict . responseBody $ rsp
-      return $ Credential dic
-    else throwIO . OAuthException $ "Gaining OAuth Token Credential Failed: " ++ BSL.unpack (responseBody rsp)
-
-
-getTokenCredential = getAccessToken
-getTokenCredentialProxy = getAccessTokenProxy
-
-insertMap :: Eq a => a -> b -> [(a,b)] -> [(a,b)]
-insertMap key val = ((key,val):) . filter ((/=key).fst)
-
-deleteMap :: Eq a => a -> [(a,b)] -> [(a,b)]
-deleteMap k = filter ((/=k).fst)
-
--- | Insert an oauth parameter into given 'Credential'.
-insert :: BS.ByteString -- ^ Parameter Name
-       -> BS.ByteString -- ^ Value
-       -> Credential    -- ^ Credential
-       -> Credential    -- ^ Result
-insert k v = Credential . insertMap k v . unCredential
-
--- | Convenient method for inserting multiple parameters into credential.
-inserts :: [(BS.ByteString, BS.ByteString)] -> Credential -> Credential
-inserts = flip $ foldr (uncurry insert)
-
--- | Remove an oauth parameter for key from given 'Credential'.
-delete :: BS.ByteString -- ^ Parameter name
-       -> Credential    -- ^ Credential
-       -> Credential    -- ^ Result
-delete key = Credential . deleteMap key . unCredential
-
--- | Add OAuth headers & sign to 'Request'.
-signOAuth :: OAuth              -- ^ OAuth Application
-          -> Credential         -- ^ Credential
-          -> Request IO         -- ^ Original Request
-          -> IO (Request IO)    -- ^ Signed OAuth Request
-signOAuth oa crd req = do
-  crd' <- addTimeStamp =<< addNonce crd
-  let tok = injectOAuthToCred oa crd'
-  sign <- genSign oa tok req
-  return $ addAuthHeader (insert "oauth_signature" sign tok) req
-
-baseTime :: UTCTime
-baseTime = UTCTime day 0
-  where
-    day = ModifiedJulianDay 40587
-
-showSigMtd :: SignMethod -> BS.ByteString
-showSigMtd PLAINTEXT = "PLAINTEXT"
-showSigMtd HMACSHA1  = "HMAC-SHA1"
-showSigMtd (RSASHA1 _) = "RSA-SHA1"
-
-addNonce :: Credential -> IO Credential
-addNonce cred = do
-  nonce <- replicateM 10 (randomRIO ('a','z'))
-  return $ insert "oauth_nonce" (BS.pack nonce) cred
-
-addTimeStamp :: Credential -> IO Credential
-addTimeStamp cred = do
-  stamp <- floor . (`diffUTCTime` baseTime) <$> getCurrentTime :: IO Integer
-  return $ insert "oauth_timestamp" (BS.pack $ show stamp) cred
-
-injectOAuthToCred :: OAuth -> Credential -> Credential
-injectOAuthToCred oa cred =
-    inserts [ ("oauth_signature_method", showSigMtd $ oauthSignatureMethod oa)
-            , ("oauth_consumer_key", oauthConsumerKey oa)
-            , ("oauth_version", "1.0")
-            ] cred
-
-genSign :: MonadIO m => OAuth -> Credential -> Request m -> m BS.ByteString
-genSign oa tok req =
-  case oauthSignatureMethod oa of
-    HMACSHA1 -> do
-      text <- getBaseString tok req
-      let key  = BS.intercalate "&" $ map paramEncode [oauthConsumerSecret oa, tokenSecret tok]
-      return $ encode $ toStrict $ bytestringDigest $ hmacSha1 (fromStrict key) text
-    PLAINTEXT ->
-      return $ BS.intercalate "&" $ map paramEncode [oauthConsumerSecret oa, tokenSecret tok]
-    RSASHA1 pr ->
-      liftM (encode . toStrict . rsassa_pkcs1_v1_5_sign ha_SHA1 pr) (getBaseString tok req)
-
-addAuthHeader :: Credential -> Request a -> Request a
-addAuthHeader (Credential cred) req =
-  req { requestHeaders = insertMap "Authorization" (renderAuthHeader cred) $ requestHeaders req }
-
-renderAuthHeader :: [(BS.ByteString, BS.ByteString)] -> BS.ByteString
-renderAuthHeader = ("OAuth " `BS.append`). BS.intercalate "," . map (\(a,b) -> BS.concat [paramEncode a, "=\"",  paramEncode b, "\""]) . filter ((`elem` ["realm", "oauth_token", "oauth_verifier", "oauth_consumer_key", "oauth_signature_method", "oauth_timestamp", "oauth_nonce", "oauth_version", "oauth_callback", "oauth_signature"]) . fst)
-
--- | Encode a string using the percent encoding method for OAuth.
-paramEncode :: BS.ByteString -> BS.ByteString
-paramEncode = BS.concatMap escape
-  where
-    escape c | isAscii c && (isAlpha c || isDigit c || c `elem` "-._~") = BS.singleton c
-             | otherwise = let num = map toUpper $ showHex (ord c) ""
-                               oct = '%' : replicate (2 - length num) '0' ++ num
-                           in BS.pack oct
-
-getBaseString :: MonadIO m => Credential -> Request m -> m BSL.ByteString
-getBaseString tok req = do
-  let bsMtd  = BS.map toUpper $ method req
-      isHttps = secure req
-      scheme = if isHttps then "https" else "http"
-      bsPort = if (isHttps && port req /= 443) || (not isHttps && port req /= 80)
-                 then ':' `BS.cons` BS.pack (show $ port req) else ""
-      bsURI = BS.concat [scheme, "://", host req, bsPort, path req]
-      bsQuery = map (second $ fromMaybe "") $ queryString req
-  bsBodyQ <- if isBodyFormEncoded $ requestHeaders req
-                  then liftM parseSimpleQuery $ toLBS (requestBody req)
-                  else return []
-  let bsAuthParams = filter ((`elem`["oauth_consumer_key","oauth_token", "oauth_version","oauth_signature_method","oauth_timestamp", "oauth_nonce", "oauth_verifier", "oauth_version","oauth_callback"]).fst) $ unCredential tok
-      allParams = bsQuery++bsBodyQ++bsAuthParams
-      bsParams = BS.intercalate "&" $ map (\(a,b)->BS.concat[a,"=",b]) $ sortBy compareTuple
-                   $ map (\(a,b) -> (paramEncode a,paramEncode b)) allParams
-  -- parameter encoding method in OAuth is slight different from ordinary one.
-  -- So this is OK.
-  return $ BSL.intercalate "&" $ map (fromStrict.paramEncode) [bsMtd, bsURI, bsParams]
-
-toLBS :: MonadIO m => RequestBody m -> m BS.ByteString
-toLBS (RequestBodyLBS l) = return $ toStrict l
-toLBS (RequestBodyBS s) = return s
-toLBS (RequestBodyBuilder _ b) = return $ toByteString b
-toLBS (RequestBodyEnum _ enum) = do
-    i <- liftIO $ newIORef id
-    run_ $ enum $$ go i
-    liftIO $ liftM (toByteString . mconcat . ($ [])) $ readIORef i
-  where
-    go i =
-        continue go'
-      where
-        go' (Chunks []) = continue go'
-        go' (Chunks x) = do
-            liftIO (atomicModifyIORef i $ \y -> (y . (x ++), ()))
-            continue go'
-        go' EOF = return ()
-
-isBodyFormEncoded :: [Header] -> Bool
-isBodyFormEncoded = maybe False (=="application/x-www-form-urlencoded") . lookup "Content-Type"
-
-compareTuple :: (Ord a, Ord b) => (a, b) -> (a, b) -> Ordering
-compareTuple (a,b) (c,d) =
-  case compare a c of
-    LT -> LT
-    EQ -> compare b d
-    GT -> GT
-
-addMaybeProxy :: Maybe Proxy -> Request m -> Request m
-addMaybeProxy p req = req { proxy = p }
diff --git a/Web/Authenticate/OpenId.hs b/Web/Authenticate/OpenId.hs
--- a/Web/Authenticate/OpenId.hs
+++ b/Web/Authenticate/OpenId.hs
@@ -1,24 +1,32 @@
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE OverloadedStrings #-}
 module Web.Authenticate.OpenId
-    ( getForwardUrl
+    ( -- * Functions
+      getForwardUrl
     , authenticate
+    , authenticateClaimed
+      -- * Types
     , AuthenticateException (..)
     , Identifier (..)
+      -- ** Response
+    , OpenIdResponse
+    , oirOpLocal
+    , oirParams
+    , oirClaimed
     ) where
 
 import Control.Monad.IO.Class
 import OpenId2.Normalization (normalize)
 import OpenId2.Discovery (discover, Discovery (..))
-import Control.Failure (Failure (failure))
 import OpenId2.Types
 import Control.Monad (unless)
+import qualified Data.Text as T
 import Data.Text.Lazy.Encoding (decodeUtf8With)
 import Data.Text.Encoding.Error (lenientDecode)
 import Data.Text.Lazy (toStrict)
-import Network.HTTP.Enumerator
-    ( parseUrl, urlEncodedBody, responseBody, httpLbsRedirect
-    , HttpException, withManager
+import Network.HTTP.Conduit
+    ( parseUrlThrow, urlEncodedBody, responseBody, httpLbs
+    , Manager
     )
 import Control.Arrow ((***), second)
 import Data.List (unfoldr)
@@ -27,54 +35,73 @@
 import Data.Text.Encoding (encodeUtf8, decodeUtf8)
 import Blaze.ByteString.Builder (toByteString)
 import Network.HTTP.Types (renderQueryText)
-import Data.Monoid (mappend)
+import Control.Exception (throwIO)
 
 getForwardUrl
-    :: ( MonadIO m
-       , Failure AuthenticateException m
-       , Failure HttpException m
-       )
+    :: MonadIO m
     => Text -- ^ The openid the user provided.
     -> Text -- ^ The URL for this application\'s complete page.
     -> Maybe Text -- ^ Optional realm
     -> [(Text, Text)] -- ^ Additional parameters to send to the OpenID provider. These can be useful for using extensions.
+    -> Manager
     -> m Text -- ^ URL to send the user to.
-getForwardUrl openid' complete mrealm params = do
+getForwardUrl openid' complete mrealm params manager = do
     let realm = fromMaybe complete mrealm
-    disc <- normalize openid' >>= discover
-    let helper s q = return $ s `mappend` decodeUtf8 (toByteString $ renderQueryText True $ map (second Just) q)
+    claimed <- normalize $ T.strip openid'
+    disc <- discover claimed manager
+    let helper s q = return $ T.concat
+            [ s
+            , if "?" `T.isInfixOf` s then "&" else "?"
+            , decodeUtf8 (toByteString $ renderQueryText False $ map (second Just) q)
+            ]
     case disc of
         Discovery1 server mdelegate -> helper server
                 $ ("openid.mode", "checkid_setup")
-                : ("openid.identity", maybe openid' id mdelegate)
+                : ("openid.identity", maybe (identifier claimed) id mdelegate)
                 : ("openid.return_to", complete)
                 : ("openid.realm", realm)
                 : ("openid.trust_root", complete)
                 : params
         Discovery2 (Provider p) (Identifier i) itype -> do
-            let i' =
+            let (claimed', identity') =
                     case itype of
-                        ClaimedIdent -> i
-                        OPIdent -> "http://specs.openid.net/auth/2.0/identifier_select"
+                        ClaimedIdent -> (identifier claimed, i)
+                        OPIdent ->
+                            let x = "http://specs.openid.net/auth/2.0/identifier_select"
+                             in (x, x)
             helper p
                 $ ("openid.ns", "http://specs.openid.net/auth/2.0")
                 : ("openid.mode", "checkid_setup")
-                : ("openid.claimed_id", i')
-                : ("openid.identity", i')
+                : ("openid.claimed_id", claimed')
+                : ("openid.identity", identity')
                 : ("openid.return_to", complete)
                 : ("openid.realm", realm)
                 : params
 
 authenticate
-    :: ( MonadIO m
-       , Failure AuthenticateException m
-       , Failure HttpException m
-       )
+    :: MonadIO m
     => [(Text, Text)]
+    -> Manager
     -> m (Identifier, [(Text, Text)])
-authenticate params = do
+authenticate ps m = do
+    x <- authenticateClaimed ps m
+    return (oirOpLocal x, oirParams x)
+{-# DEPRECATED authenticate "Use authenticateClaimed" #-}
+
+data OpenIdResponse = OpenIdResponse
+    { oirOpLocal :: Identifier
+    , oirParams :: [(Text, Text)]
+    , oirClaimed :: Maybe Identifier
+    }
+
+authenticateClaimed
+    :: MonadIO m
+    => [(Text, Text)]
+    -> Manager
+    -> m OpenIdResponse
+authenticateClaimed params manager = do
     unless (lookup "openid.mode" params == Just "id_res")
-        $ failure $ case lookup "openid.mode" params of
+        $ liftIO $ throwIO $ case lookup "openid.mode" params of
                       Nothing -> AuthenticationException "openid.mode was not found in the params."
                       (Just m)
                             | m == "error" ->
@@ -85,21 +112,41 @@
     ident <- case lookup "openid.identity" params of
                 Just i -> return i
                 Nothing ->
-                    failure $ AuthenticationException "Missing identity"
-    disc <- normalize ident >>= discover
-    let endpoint = case disc of
-                    Discovery1 p _ -> p
-                    Discovery2 (Provider p) _ _ -> p
+                    liftIO $ throwIO $ AuthenticationException "Missing identity"
+    discOP <- normalize ident >>= flip discover manager
+
+    let endpoint d =
+            case d of
+                Discovery1 p _ -> p
+                Discovery2 (Provider p) _ _ -> p
     let params' = map (encodeUtf8 *** encodeUtf8)
                 $ ("openid.mode", "check_authentication")
                 : filter (\(k, _) -> k /= "openid.mode") params
-    req' <- parseUrl $ unpack endpoint
+    req' <- liftIO $ parseUrlThrow $ unpack $ endpoint discOP
     let req = urlEncodedBody params' req'
-    rsp <- liftIO $ withManager $ httpLbsRedirect req
+    rsp <- httpLbs req manager
     let rps = parseDirectResponse $ toStrict $ decodeUtf8With lenientDecode $ responseBody rsp
+
+    claimed <-
+        case lookup "openid.claimed_id" params of
+            Nothing -> return Nothing
+            Just claimed' -> do
+                -- need to validate that this provider can speak for the given
+                -- claimed identifier
+                claimedN <- normalize claimed'
+                discC <- discover claimedN manager
+                return $
+                    if endpoint discOP == endpoint discC
+                        then Just claimedN
+                        else Nothing
+
     case lookup "is_valid" rps of
-        Just "true" -> return (Identifier ident, rps)
-        _ -> failure $ AuthenticationException "OpenID provider did not validate"
+        Just "true" -> return OpenIdResponse
+            { oirOpLocal = Identifier ident
+            , oirParams  = rps
+            , oirClaimed = claimed
+            }
+        _ -> liftIO $ throwIO $ AuthenticationException "OpenID provider did not validate"
 
 -- | Turn a response body into a list of parameters.
 parseDirectResponse :: Text -> [(Text, Text)]
diff --git a/Web/Authenticate/OpenId/Providers.hs b/Web/Authenticate/OpenId/Providers.hs
--- a/Web/Authenticate/OpenId/Providers.hs
+++ b/Web/Authenticate/OpenId/Providers.hs
@@ -15,6 +15,7 @@
 
 google :: String
 google = "https://www.google.com/accounts/o8/id"
+{-# DEPRECATED google "Google no longer provides OpenID support" #-}
 
 yahoo :: String
 yahoo = "http://me.yahoo.com/"
diff --git a/Web/Authenticate/Rpxnow.hs b/Web/Authenticate/Rpxnow.hs
--- a/Web/Authenticate/Rpxnow.hs
+++ b/Web/Authenticate/Rpxnow.hs
@@ -1,6 +1,5 @@
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE CPP #-}
-{-# LANGUAGE PackageImports #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE DeriveDataTypeable #-}
 ---------------------------------------------------------
@@ -23,14 +22,15 @@
     ) where
 
 import Data.Aeson
-import Network.HTTP.Enumerator
-import "transformers" Control.Monad.IO.Class
-import Control.Failure
+#if MIN_VERSION_aeson(2,2,0)
+import Data.Aeson.Parser (json)
+#endif
+import Network.HTTP.Conduit
+import Control.Monad.IO.Class
 import Data.Maybe
 import Control.Monad
 import qualified Data.ByteString.Char8 as S
 import qualified Data.ByteString.Lazy.Char8 as L
-import Control.Exception (throwIO)
 import Web.Authenticate.Internal
 import Data.Data (Data)
 import Data.Typeable (Typeable)
@@ -38,12 +38,14 @@
 import qualified Data.Attoparsec.Lazy as AT
 import Data.Text (Text)
 import qualified Data.Aeson.Types
-#if MIN_VERSION_aeson(0, 4, 0)
-import qualified Data.HashMap.Lazy as Map
+#if MIN_VERSION_aeson(2,0,0)
+import qualified Data.Aeson.KeyMap as Map
+import qualified Data.Aeson.Key as Key
 #else
-import qualified Data.Map as Map
+import qualified Data.HashMap.Lazy as Map
 #endif
 import Control.Applicative ((<$>), (<*>))
+import Control.Exception (throwIO)
 
 -- | Information received from Rpxnow after a valid login.
 data Identifier = Identifier
@@ -53,20 +55,19 @@
     deriving (Eq, Ord, Read, Show, Data, Typeable)
 
 -- | Attempt to log a user in.
-authenticate :: (MonadIO m,
-                 Failure HttpException m,
-                 Failure AuthenticateException m)
+authenticate :: MonadIO m
              => String -- ^ API key given by RPXNOW.
              -> String -- ^ Token passed by client.
+             -> Manager
              -> m Identifier
-authenticate apiKey token = do
+authenticate apiKey token manager = do
     let body = L.fromChunks
             [ "apiKey="
             , S.pack apiKey
             , "&token="
             , S.pack token
             ]
-    req' <- parseUrl "https://rpxnow.com"
+    req' <- liftIO $ parseUrlThrow "https://rpxnow.com"
     let req =
             req'
                 { method = "POST"
@@ -76,10 +77,8 @@
                     ]
                 , requestBody = RequestBodyLBS body
                 }
-    res <- liftIO $ withManager $ httpLbsRedirect req
+    res <- httpLbs req manager
     let b = responseBody res
-    unless (200 <= statusCode res && statusCode res < 300) $
-        liftIO $ throwIO $ StatusCodeException (statusCode res) b
     o <- unResult $ parse json b
     --m <- fromMapping o
     let mstat = flip Data.Aeson.Types.parse o $ \v ->
@@ -88,15 +87,15 @@
                     _ -> mzero
     case mstat of
         Success "ok" -> return ()
-        Success stat -> failure $ RpxnowException $
+        Success stat -> liftIO $ throwIO $ RpxnowException $
             "Rpxnow login not accepted: " ++ stat ++ "\n" ++ L.unpack b
-        _ -> failure $ RpxnowException "Now stat value found on Rpxnow response"
+        _ -> liftIO $ throwIO $ RpxnowException "Now stat value found on Rpxnow response"
     case Data.Aeson.Types.parse parseProfile o of
         Success x -> return x
-        Error e -> failure $ RpxnowException $ "Unable to parse Rpxnow response: " ++ e
+        Error e -> liftIO $ throwIO $ RpxnowException $ "Unable to parse Rpxnow response: " ++ e
 
-unResult :: Failure AuthenticateException m => AT.Result a -> m a
-unResult = either (failure . RpxnowException) return . AT.eitherResult
+unResult :: MonadIO m => AT.Result a -> m a
+unResult = either (liftIO . throwIO . RpxnowException) return . AT.eitherResult
 
 parseProfile :: Value -> Data.Aeson.Types.Parser Identifier
 parseProfile (Object m) = do
@@ -106,6 +105,10 @@
         <*> return (mapMaybe go (Map.toList profile))
   where
     go ("identifier", _) = Nothing
-    go (k, String v) = Just (k, v)
+    go (k, String v) = Just (
+#if MIN_VERSION_aeson(2,0,0)
+        Key.toText
+#endif
+        k, v)
     go _ = Nothing
 parseProfile _ = mzero
diff --git a/authenticate.cabal b/authenticate.cabal
--- a/authenticate.cabal
+++ b/authenticate.cabal
@@ -1,56 +1,56 @@
 name:            authenticate
-version:         0.10.4
-license:         BSD3
+version:         1.3.5.2
+license:         MIT
 license-file:    LICENSE
 author:          Michael Snoyman, Hiromi Ishii, Arash Rouhani
 maintainer:      Michael Snoyman <michael@snoyman.com>
 synopsis:        Authentication methods for Haskell web applications.
-description:     Focus is on third-party authentication methods, such as OpenID,
-                 rpxnow and Facebook.
+description:     API docs and the README are available at <http://www.stackage.org/package/authenticate>.
 category:        Web
 stability:       Stable
-cabal-version:   >= 1.6
+cabal-version:   >= 1.10
 build-type:      Simple
 homepage:        http://github.com/yesodweb/authenticate
+extra-source-files: README.md ChangeLog.md
 
+flag network-uri
+  description: Get Network.URI from the network-uri package
+  default: True
+
 library
-    build-depends:   base >= 4 && < 5,
-                     aeson >= 0.3.2.11,
-                     http-enumerator >= 0.6.5.4 && < 0.8,
-                     tagsoup >= 0.12 && < 0.13,
-                     failure >= 0.0.0 && < 0.2,
-                     transformers >= 0.1 && < 0.3,
-                     bytestring >= 0.9 && < 0.10,
-                     network >= 2.2.1 && < 2.4,
-                     case-insensitive >= 0.2,
-                     RSA >= 1.0 && < 1.1,
-                     time >= 1.1,
-                     base64-bytestring >= 0.1 && < 0.2,
-                     SHA >= 1.4 && < 1.6,
-                     random >= 1.0 && < 1.1,
-                     text >= 0.5 && < 1.0,
-                     http-types >= 0.6 && < 0.7,
-                     enumerator >= 0.4.7 && < 0.5,
-                     xml-enumerator >= 0.4 && < 0.5,
-                     blaze-builder >= 0.2 && < 0.4,
-                     attoparsec >= 0.9,
-                     tls >= 0.7 && < 0.9,
-                     containers,
-                     unordered-containers,
-                     process >= 1.0.1.1 && < 1.2
+    default-language: Haskell2010
+    build-depends:   base                          >= 4.10     && < 5
+                   , aeson                         >= 0.5
+                   , attoparsec-aeson              >= 2.1
+                   , http-conduit                  >= 1.5
+                   , transformers                  >= 0.1
+                   , bytestring                    >= 0.9
+                   , case-insensitive              >= 0.2
+                   , text
+                   , http-types                    >= 0.6
+                   , xml-conduit                   >= 1.0
+                   , blaze-builder
+                   , attoparsec
+                   , containers
+                   , unordered-containers
+                   , conduit                       >= 0.5
+                   , html-conduit                  >= 1.3
+                   , resourcet
     exposed-modules: Web.Authenticate.Rpxnow,
                      Web.Authenticate.OpenId,
                      Web.Authenticate.BrowserId,
-                     Web.Authenticate.OpenId.Providers,
-                     Web.Authenticate.OAuth,
-                     Web.Authenticate.Facebook
-                     Web.Authenticate.Kerberos
+                     Web.Authenticate.OpenId.Providers
     other-modules:   Web.Authenticate.Internal,
                      OpenId2.Discovery,
                      OpenId2.Normalization,
                      OpenId2.Types,
                      OpenId2.XRDS
     ghc-options:     -Wall
+
+    if flag(network-uri)
+      build-depends: network-uri >= 2.6
+    else
+      build-depends: network < 2.6
 
 source-repository head
   type:     git
