OpenId uses Text

This commit is contained in:
Michael Snoyman 2011-04-07 23:04:34 +03:00
parent 72281c7fa0
commit 705528277c
4 changed files with 42 additions and 32 deletions

View File

@ -33,6 +33,7 @@ import Control.Monad.IO.Class (MonadIO (liftIO))
import Control.Failure (Failure (failure)) import Control.Failure (Failure (failure))
import Control.Monad (mplus, liftM) import Control.Monad (mplus, liftM)
import qualified Data.CaseInsensitive as CI import qualified Data.CaseInsensitive as CI
import Data.Text (Text, pack, unpack)
data Discovery = Discovery1 String (Maybe String) data Discovery = Discovery1 String (Maybe String)
| Discovery2 Provider Identifier IdentType | Discovery2 Provider Identifier IdentType
@ -53,7 +54,7 @@ discover ident@(Identifier i) = do
res2 <- discoverHTML ident res2 <- discoverHTML ident
case res2 of case res2 of
Just x -> return x Just x -> return x
Nothing -> failure $ DiscoveryException i Nothing -> failure $ DiscoveryException $ unpack i
-- YADIS-Based Discovery ------------------------------------------------------- -- YADIS-Based Discovery -------------------------------------------------------
@ -68,7 +69,7 @@ discoverYADIS :: ( MonadIO m
-> m (Maybe (Provider, Identifier, IdentType)) -> m (Maybe (Provider, Identifier, IdentType))
discoverYADIS _ _ 0 = failure TooManyRedirects discoverYADIS _ _ 0 = failure TooManyRedirects
discoverYADIS ident mb_loc redirects = do discoverYADIS ident mb_loc redirects = do
let uri = fromMaybe (identifier ident) mb_loc let uri = fromMaybe (unpack $ identifier ident) mb_loc
req <- parseUrl uri req <- parseUrl uri
res <- liftIO $ withManager $ httpLbs req res <- liftIO $ withManager $ httpLbs req
let mloc = fmap S8.unpack let mloc = fmap S8.unpack
@ -95,7 +96,7 @@ parseYADIS ident = listToMaybe . mapMaybe isOpenId . concat
where where
isOpenId svc = do isOpenId svc = do
let tys = serviceTypes svc let tys = serviceTypes svc
localId = maybe ident Identifier $ listToMaybe $ serviceLocalIDs svc localId = maybe ident (Identifier . pack) $ listToMaybe $ serviceLocalIDs svc
f (x,y) | x `elem` tys = Just y f (x,y) | x `elem` tys = Just y
| otherwise = Nothing | otherwise = Nothing
(lid, itype) <- listToMaybe $ mapMaybe f (lid, itype) <- listToMaybe $ mapMaybe f
@ -117,7 +118,7 @@ discoverHTML :: ( MonadIO m, Failure HttpException m)
=> Identifier => Identifier
-> m (Maybe Discovery) -> m (Maybe Discovery)
discoverHTML ident'@(Identifier ident) = discoverHTML ident'@(Identifier ident) =
(parseHTML ident' . BSLU.toString) `liftM` simpleHttp ident (parseHTML ident' . BSLU.toString) `liftM` simpleHttp (unpack 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.
@ -135,7 +136,7 @@ parseHTML ident = resolve
return $ Discovery1 server delegate return $ Discovery1 server delegate
resolve2 ls = do 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 . pack) $ lookup "openid2.local_id" ls
-- Based on OpenID 2.0 spec, section 7.3.3, HTML discovery can only -- Based on OpenID 2.0 spec, section 7.3.3, HTML discovery can only
-- result in a claimed identifier. -- result in a claimed identifier.
return $ Discovery2 (Provider prov) lid ClaimedIdent return $ Discovery2 (Provider prov) lid ClaimedIdent

View File

@ -26,12 +26,13 @@ import Network.URI
( uriToString, normalizeCase, normalizeEscape ( uriToString, normalizeCase, normalizeEscape
, normalizePathSegments, parseURI, uriPath, uriScheme, uriFragment , normalizePathSegments, parseURI, uriPath, uriScheme, uriFragment
) )
import Data.Text (Text, pack, unpack)
normalize :: Failure AuthenticateException m => String -> m Identifier normalize :: Failure AuthenticateException m => Text -> m Identifier
normalize ident = normalize ident =
case normalizeIdentifier $ Identifier ident of case normalizeIdentifier $ Identifier ident of
Just i -> return i Just i -> return i
Nothing -> failure $ NormalizationException ident Nothing -> failure $ NormalizationException $ unpack ident
-- | Normalize an identifier, discarding XRIs. -- | Normalize an identifier, discarding XRIs.
normalizeIdentifier :: Identifier -> Maybe Identifier normalizeIdentifier :: Identifier -> Maybe Identifier
@ -42,12 +43,13 @@ normalizeIdentifier = normalizeIdentifier' (const Nothing)
-- normalize an XRI. -- normalize an XRI.
normalizeIdentifier' :: (String -> Maybe String) -> Identifier normalizeIdentifier' :: (String -> Maybe String) -> Identifier
-> Maybe Identifier -> Maybe Identifier
normalizeIdentifier' xri (Identifier str) normalizeIdentifier' xri (Identifier str')
| null str = Nothing | null str = Nothing
| "xri://" `isPrefixOf` str = Identifier `fmap` xri str | "xri://" `isPrefixOf` str = (Identifier . pack) `fmap` xri str
| head str `elem` "=@+$!" = Identifier `fmap` xri str | head str `elem` "=@+$!" = (Identifier . pack) `fmap` xri str
| otherwise = fmt `fmap` (url >>= norm) | otherwise = fmt `fmap` (url >>= norm)
where where
str = unpack str'
url = parseURI str <|> parseURI ("http://" ++ str) url = parseURI str <|> parseURI ("http://" ++ str)
norm uri = validScheme >> return u norm uri = validScheme >> return u
@ -59,6 +61,7 @@ normalizeIdentifier' xri (Identifier str)
| otherwise = uriPath uri | otherwise = uriPath uri
fmt u = Identifier fmt u = Identifier
$ pack
$ normalizePathSegments $ normalizePathSegments
$ normalizeEscape $ normalizeEscape
$ normalizeCase $ normalizeCase

View File

@ -21,12 +21,13 @@ module OpenId2.Types (
import Data.Data (Data) import Data.Data (Data)
import Data.Typeable (Typeable) import Data.Typeable (Typeable)
import Web.Authenticate.Internal import Web.Authenticate.Internal
import Data.Text (Text)
-- | An OpenID provider. -- | An OpenID provider.
newtype Provider = Provider { providerURI :: String } deriving (Eq,Show) newtype Provider = Provider { providerURI :: String } deriving (Eq,Show)
-- | A valid OpenID identifier. -- | A valid OpenID identifier.
newtype Identifier = Identifier { identifier :: String } newtype Identifier = Identifier { identifier :: Text }
deriving (Eq, Ord, Show, Read, Data, Typeable) deriving (Eq, Ord, Show, Read, Data, Typeable)
data IdentType = OPIdent | ClaimedIdent data IdentType = OPIdent | ClaimedIdent

View File

@ -1,4 +1,5 @@
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
module Web.Authenticate.OpenId module Web.Authenticate.OpenId
( getForwardUrl ( getForwardUrl
, authenticate , authenticate
@ -22,25 +23,28 @@ import Network.HTTP.Enumerator
import Control.Arrow ((***)) import Control.Arrow ((***))
import Data.List (unfoldr) import Data.List (unfoldr)
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import Data.Text (Text, pack, unpack)
import Data.Text.Encoding (encodeUtf8)
getForwardUrl getForwardUrl
:: ( MonadIO m :: ( MonadIO m
, Failure AuthenticateException m , Failure AuthenticateException m
, Failure HttpException m , Failure HttpException m
) )
=> String -- ^ The openid the user provided. => Text -- ^ The openid the user provided.
-> String -- ^ The URL for this application\'s complete page. -> Text -- ^ The URL for this application\'s complete page.
-> Maybe String -- ^ Optional realm -> Maybe Text -- ^ Optional realm
-> [(String, String)] -- ^ Additional parameters to send to the OpenID provider. These can be useful for using extensions. -> [(Text, Text)] -- ^ Additional parameters to send to the OpenID provider. These can be useful for using extensions.
-> m String -- ^ URL to send the user to. -> m Text -- ^ URL to send the user to.
getForwardUrl openid' complete mrealm params = do getForwardUrl openid' complete mrealm params = do
let realm = fromMaybe complete mrealm let realm = fromMaybe complete mrealm
disc <- normalize openid' >>= discover disc <- normalize openid' >>= discover
case disc of case disc of
Discovery1 server mdelegate -> Discovery1 server mdelegate ->
return $ qsUrl server return $ pack $ qsUrl server
$ map (unpack *** unpack) -- FIXME
$ ("openid.mode", "checkid_setup") $ ("openid.mode", "checkid_setup")
: ("openid.identity", fromMaybe openid' mdelegate) : ("openid.identity", maybe openid' pack mdelegate)
: ("openid.return_to", complete) : ("openid.return_to", complete)
: ("openid.realm", realm) : ("openid.realm", realm)
: ("openid.trust_root", complete) : ("openid.trust_root", complete)
@ -50,22 +54,22 @@ getForwardUrl openid' complete mrealm params = do
case itype of case itype of
ClaimedIdent -> i ClaimedIdent -> i
OPIdent -> "http://specs.openid.net/auth/2.0/identifier_select" OPIdent -> "http://specs.openid.net/auth/2.0/identifier_select"
return $ qsUrl p return $ pack $ qsUrl p
$ ("openid.ns", "http://specs.openid.net/auth/2.0") $ ("openid.ns", "http://specs.openid.net/auth/2.0")
: ("openid.mode", "checkid_setup") : ("openid.mode", "checkid_setup")
: ("openid.claimed_id", i') : ("openid.claimed_id", unpack i')
: ("openid.identity", i') : ("openid.identity", unpack i')
: ("openid.return_to", complete) : ("openid.return_to", unpack complete)
: ("openid.realm", realm) : ("openid.realm", unpack realm)
: params : map (unpack *** unpack) params
authenticate authenticate
:: ( MonadIO m :: ( MonadIO m
, Failure AuthenticateException m , Failure AuthenticateException m
, Failure HttpException m , Failure HttpException m
) )
=> [(String, String)] => [(Text, Text)]
-> m (Identifier, [(String, String)]) -> m (Identifier, [(Text, Text)])
authenticate params = do authenticate params = do
unless (lookup "openid.mode" params == Just "id_res") unless (lookup "openid.mode" params == Just "id_res")
$ failure $ case lookup "openid.mode" params of $ failure $ case lookup "openid.mode" params of
@ -74,8 +78,8 @@ authenticate params = do
| m == "error" -> | m == "error" ->
case lookup "openid.error" params of case lookup "openid.error" params of
Nothing -> AuthenticationException "An error occurred, but no error message was provided." Nothing -> AuthenticationException "An error occurred, but no error message was provided."
(Just e) -> AuthenticationException e (Just e) -> AuthenticationException $ unpack e
| otherwise -> AuthenticationException $ "mode is " ++ m ++ " but we were expecting id_res." | otherwise -> AuthenticationException $ "mode is " ++ unpack m ++ " but we were expecting id_res."
ident <- case lookup "openid.identity" params of ident <- case lookup "openid.identity" params of
Just i -> return i Just i -> return i
Nothing -> Nothing ->
@ -84,20 +88,21 @@ authenticate params = do
let endpoint = case disc of let endpoint = case disc of
Discovery1 p _ -> p Discovery1 p _ -> p
Discovery2 (Provider p) _ _ -> p Discovery2 (Provider p) _ _ -> p
let params' = map (BSU.fromString *** BSU.fromString) let params' = map (encodeUtf8 *** encodeUtf8)
$ ("openid.mode", "check_authentication") $ ("openid.mode", "check_authentication")
: filter (\(k, _) -> k /= "openid.mode") params : filter (\(k, _) -> k /= "openid.mode") params
req' <- parseUrl endpoint req' <- parseUrl endpoint
let req = urlEncodedBody params' req' let req = urlEncodedBody params' req'
rsp <- liftIO $ withManager $ httpLbsRedirect req rsp <- liftIO $ withManager $ httpLbsRedirect req
let rps = parseDirectResponse $ BSLU.toString $ responseBody rsp let rps = parseDirectResponse $ pack $ BSLU.toString $ responseBody rsp -- FIXME
case lookup "is_valid" rps of case lookup "is_valid" rps of
Just "true" -> return (Identifier ident, rps) Just "true" -> return (Identifier ident, rps)
_ -> failure $ AuthenticationException "OpenID provider did not validate" _ -> failure $ AuthenticationException "OpenID provider did not validate"
-- | Turn a response body into a list of parameters. -- | Turn a response body into a list of parameters.
parseDirectResponse :: String -> [(String, String)] parseDirectResponse :: Text -> [(Text, Text)]
parseDirectResponse = unfoldr step parseDirectResponse =
map (pack *** pack) . unfoldr step . unpack
where where
step [] = Nothing step [] = Nothing
step str = case split (== '\n') str of step str = case split (== '\n') str of