OpenID 1 support built into OpenID 2 code

This commit is contained in:
Michael Snoyman 2010-10-05 11:02:49 +02:00
parent 0da51855ec
commit 6e575cf027
3 changed files with 53 additions and 25 deletions

View File

@ -14,8 +14,10 @@
module OpenId2.Discovery ( module OpenId2.Discovery (
-- * Discovery -- * Discovery
discover discover
, Discovery (..)
) where ) where
import Debug.Trace -- FIXME
-- Friends -- Friends
import OpenId2.Types import OpenId2.Types
import OpenId2.XRDS import OpenId2.XRDS
@ -27,19 +29,24 @@ import Data.Maybe
import Network.HTTP.Enumerator import Network.HTTP.Enumerator
import qualified Data.ByteString.Lazy.UTF8 as BSLU import qualified Data.ByteString.Lazy.UTF8 as BSLU
import qualified Data.ByteString.Char8 as S8 import qualified Data.ByteString.Char8 as S8
import Control.Arrow (first) import Control.Arrow (first, (***))
import Control.Applicative ((<$>)) import Control.Applicative ((<$>))
import Control.Monad.IO.Class (MonadIO (liftIO)) import Control.Monad.IO.Class (MonadIO (liftIO))
import Control.Failure (Failure (failure)) import Control.Failure (Failure (failure))
import Control.Monad (mplus)
data Discovery = Discovery1 String (Maybe String)
| Discovery2 Provider Identifier
deriving Show
-- | Attempt to resolve an OpenID endpoint, and user identifier. -- | Attempt to resolve an OpenID endpoint, and user identifier.
discover :: (MonadIO m, Failure OpenIdException m) discover :: (MonadIO m, Failure OpenIdException m)
=> Identifier => Identifier
-> m (Provider, Identifier) -> m Discovery
discover ident@(Identifier i) = do discover ident@(Identifier i) = do
res1 <- liftIO $ discoverYADIS ident Nothing res1 <- liftIO $ discoverYADIS ident Nothing
case res1 of case res1 of
Just x -> return x Just (x, y) -> return $ Discovery2 x y
Nothing -> do Nothing -> do
res2 <- liftIO $ discoverHTML ident res2 <- liftIO $ discoverHTML ident
case res2 of case res2 of
@ -97,23 +104,29 @@ parseYADIS ident = listToMaybe . mapMaybe isOpenId . concat
-- | Attempt to discover an OpenID endpoint, from an HTML document. The result -- | 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. -- will be an endpoint on success, and the actual identifier of the user.
discoverHTML :: Identifier -> IO (Maybe (Provider,Identifier)) discoverHTML :: Identifier -> IO (Maybe Discovery)
discoverHTML ident'@(Identifier ident) = discoverHTML ident'@(Identifier ident) =
parseHTML ident' . BSLU.toString <$> simpleHttp ident parseHTML ident' . BSLU.toString <$> simpleHttp ident
-- | Parse out an OpenID endpoint and an actual identifier from an HTML -- | Parse out an OpenID endpoint and an actual identifier from an HTML
-- document. -- document.
parseHTML :: Identifier -> String -> Maybe (Provider,Identifier) parseHTML :: Identifier -> String -> Maybe Discovery
parseHTML ident = resolve parseHTML ident = resolve
. filter isOpenId . filter isOpenId
. map (dropQuotes *** dropQuotes)
. linkTags . linkTags
. htmlTags . htmlTags
where where
isOpenId (rel,_) = "openid" `isPrefixOf` rel isOpenId (rel,_) = "openid" `isPrefixOf` rel
resolve ls = do resolve1 ls = do
server <- lookup "openid.server" ls
let delegate = lookup "openid.delegate" ls
return $ Discovery1 server delegate
resolve2 ls = do
prov <- lookup "openid2.provider" ls prov <- lookup "openid2.provider" ls
let lid = maybe ident Identifier $ lookup "openid2.local_id" ls let lid = maybe ident Identifier $ lookup "openid2.local_id" ls
return (Provider prov,lid) return $ Discovery2 (Provider prov) lid
resolve ls = traceShow ls $ resolve2 ls `mplus` resolve1 ls
-- | Filter out link tags from a list of html tags. -- | Filter out link tags from a list of html tags.
@ -150,3 +163,12 @@ splitAttr xs = case break (== '=') xs of
f key p cs = case break p cs of f key p cs = case break p cs of
(_,[]) -> Nothing (_,[]) -> Nothing
(value,_:rest) -> Just ((key,value), dropWhile isSpace rest) (value,_:rest) -> Just ((key,value), dropWhile isSpace rest)
dropQuotes :: String -> String
dropQuotes s@('\'':x:y)
| last y == '\'' = x : init y
| otherwise = s
dropQuotes s@('"':x:y)
| last y == '"' = x : init y
| otherwise = s
dropQuotes s = s

View File

@ -10,9 +10,10 @@ import Data.List (intercalate)
qsUrl :: String -> [(String, String)] -> String qsUrl :: String -> [(String, String)] -> String
qsUrl s [] = s qsUrl s [] = s
qsUrl url pairs = qsUrl url pairs =
url ++ "?" ++ intercalate "&" (map qsPair pairs) url ++ delim : intercalate "&" (map qsPair pairs)
where where
qsPair (x, y) = qsEncode x ++ '=' : qsEncode y qsPair (x, y) = qsEncode x ++ '=' : qsEncode y
delim = if '?' `elem` url then '&' else '?'
qsEncode :: String -> String qsEncode :: String -> String
qsEncode = qsEncode =

View File

@ -7,7 +7,7 @@ module Web.Authenticate.OpenId2
import Control.Monad.IO.Class import Control.Monad.IO.Class
import OpenId2.Normalization (normalize) import OpenId2.Normalization (normalize)
import OpenId2.Discovery (discover) import OpenId2.Discovery (discover, Discovery (..))
import Control.Failure (Failure (failure)) import Control.Failure (Failure (failure))
import OpenId2.Types (OpenIdException (..), Identifier (Identifier), import OpenId2.Types (OpenIdException (..), Identifier (Identifier),
Provider (Provider)) Provider (Provider))
@ -19,20 +19,29 @@ import Network.HTTP.Enumerator
(parseUrl, urlEncodedBody, responseBody, httpLbsRedirect) (parseUrl, urlEncodedBody, responseBody, httpLbsRedirect)
import Control.Arrow ((***)) import Control.Arrow ((***))
import Data.List (unfoldr) import Data.List (unfoldr)
import Data.Maybe (fromMaybe)
getForwardUrl :: (MonadIO m, Failure OpenIdException m) getForwardUrl :: (MonadIO m, Failure OpenIdException m)
=> String -- ^ The openid the user provided. => String -- ^ The openid the user provided.
-> String -- ^ The URL for this application\'s complete page. -> String -- ^ The URL for this application\'s complete page.
-> m String -- ^ URL to send the user to. -> m String -- ^ URL to send the user to.
getForwardUrl openid' complete = do getForwardUrl openid' complete = do
(Provider p, Identifier i) <- normalize openid' >>= discover disc <- normalize openid' >>= discover
return $ qsUrl p case disc of
[ ("openid.ns", "http://specs.openid.net/auth/2.0") Discovery1 server mdelegate ->
, ("openid.mode", "checkid_setup") return $ qsUrl server
, ("openid.claimed_id", i) [ ("openid.mode", "checkid_setup")
, ("openid.identity", i) , ("openid.identity", fromMaybe openid' mdelegate)
, ("openid.return_to", complete) , ("openid.return_to", complete)
] ]
Discovery2 (Provider p) (Identifier i) ->
return $ qsUrl p
[ ("openid.ns", "http://specs.openid.net/auth/2.0")
, ("openid.mode", "checkid_setup")
, ("openid.claimed_id", i)
, ("openid.identity", i)
, ("openid.return_to", complete)
]
authenticate :: (MonadIO m, Failure OpenIdException m) authenticate :: (MonadIO m, Failure OpenIdException m)
=> [(String, String)] => [(String, String)]
@ -44,14 +53,10 @@ authenticate params = do
Just i -> return i Just i -> return i
Nothing -> Nothing ->
failure $ AuthenticationException "Missing identity" failure $ AuthenticationException "Missing identity"
endpoint <- disc <- normalize ident >>= discover
case lookup "openid.op_endpoint" params of let endpoint = case disc of
Just e -> return e Discovery1 p _ -> p
Nothing -> Discovery2 (Provider p) _ -> p
failure $ AuthenticationException "Missing op_endpoint"
(Provider p, Identifier i) <- normalize ident >>= discover
unless (endpoint == p) $
failure $ AuthenticationException "endpoint does not match discovery"
let params' = map (BSU.fromString *** BSU.fromString) let params' = map (BSU.fromString *** BSU.fromString)
$ ("openid.mode", "check_authentication") $ ("openid.mode", "check_authentication")
: filter (\(k, _) -> k /= "openid.mode") params : filter (\(k, _) -> k /= "openid.mode") params