openid-0.2.1.0: src/Network/OpenID/AttributeExchange.hs
module Network.OpenID.AttributeExchange (
AXFieldTy(..),
AXFieldVal,
axName, axSpec, axTyFromName,
axEmailRequired,
axExtParams,
axExtParams',
getAxFields
) where
import Prelude()
import Prelude.Compat
import Control.Monad (guard)
import Network.OpenID.Types
import Data.Maybe (listToMaybe, mapMaybe, fromMaybe)
import Data.List (intercalate, nub, isPrefixOf)
defaultAlias :: String
defaultAlias = "ax"
extNamespace, extNamespacePrefix, extMode_fetchRequest :: String
extNamespace = "http://openid.net/srv/ax/1.0"
extNamespacePrefix = "http://openid.net/srv/ax/1."
extMode_fetchRequest = "fetch_request"
-- | Some common, useful Attribute Exchange specs.
data AXFieldTy
= AXBirthdate
| AXEmail
| AXFirstName
| AXFullName
| AXGender
| AXLanguage
| AXLastName
| AXNickname
deriving (Eq, Show, Ord, Read)
axName :: AXFieldTy -> String
axName AXBirthdate = "birthdate"
axName AXEmail = "email"
axName AXFirstName = "firstname"
axName AXFullName = "fullname"
axName AXGender = "gender"
axName AXLanguage = "language"
axName AXLastName = "lastname"
axName AXNickname = "friendly"
axSpec :: AXFieldTy -> String
axSpec AXBirthdate = "http://axschema.org/birthDate"
axSpec AXEmail = "http://axschema.org/contact/email"
axSpec AXFirstName = "http://axschema.org/namePerson/first"
axSpec AXFullName = "http://axschema.org/namePerson"
axSpec AXGender = "http://axschema.org/person/gender"
axSpec AXLanguage = "http://axschema.org/pref/language"
axSpec AXLastName = "http://axschema.org/namePerson/last"
axSpec AXNickname = "http://axschema.org/namePerson/friendly"
axTyFromName :: String -> Maybe AXFieldTy
axTyFromName "birthdate" = Just AXBirthdate
axTyFromName "email" = Just AXEmail
axTyFromName "firstname" = Just AXFirstName
axTyFromName "fullname" = Just AXFullName
axTyFromName "gender" = Just AXGender
axTyFromName "language" = Just AXLanguage
axTyFromName "lastname" = Just AXLastName
axTyFromName "friendly" = Just AXNickname
axTyFromName _ = Nothing
-- | Used to store responses.
type AXFieldVal = (AXFieldTy, String)
-- | The simplest use case is to request the user's email. This would be
-- used to replace traditional verification emails.
axEmailRequired :: Params
axEmailRequired = axExtParams [AXEmail]
-- | Use these functions to roll your own list of fields to request when
-- you send an auth request
axExtParams :: [AXFieldTy] -- ^ params we want them to send in the
-- "id_res" mode verification
-> Params
axExtParams = axExtParams' defaultAlias
-- | specify the alias as well as the list of requested fields
axExtParams' :: String -- ^ alias. it doesn't really matter
-- what this is as long as we're
-- consistent
-> [AXFieldTy] -- ^ params we require in the "id_res"
-- mode verification
-> Params
axExtParams' alias extsRequired =
[ ("openid.ns." ++ alias, extNamespace)
, ("openid." ++ alias ++ ".mode", extMode_fetchRequest)
, ("openid." ++ alias ++ ".required", formatRequiredVal extsRequired')
] ++ exts
where
exts = map (formatRequestField alias) extsRequired'
extsRequired' = nub extsRequired
formatRequestField :: String -> AXFieldTy -> (String, String)
formatRequestField alias field =
("openid." ++ alias ++ ".type." ++ axName field, axSpec field)
formatRequiredVal :: [AXFieldTy] -> String
formatRequiredVal =
intercalate "," . map axName
-- | Retrieve the requested fields from the HTTP request params. Keep
-- | in mind the spec does not require that the OpenID Provider return
-- | any of our requested fields, even on a successful verification.
getAxFields :: Params -> [AXFieldVal]
getAxFields ps =
fromMaybe [] fieldsMb
where
fieldsMb :: Maybe [AXFieldVal]
fieldsMb = getAxFields' ps <$> aliasMb
aliasMb :: Maybe String
aliasMb = listToMaybe $ mapMaybe getAxAlias ps
getAxFields' :: Params -> String -> [AXFieldVal]
getAxFields' ps alias =
mapMaybe getAxFieldTypes' ps
where
getAxFieldTypes' :: (String, String) -> Maybe AXFieldVal
getAxFieldTypes' (n,v) = do
guard (valueAliasPrefix `isPrefixOf` n)
ty <- axTyFromName $ drop (length valueAliasPrefix) n
return (ty,v)
valueAliasPrefix = "openid." ++ alias ++ ".value."
-- the server will return a response of the form
-- openid.ALIAS.value.email: "foobar"
-- openid.ALIAS.type.email: "http://axschema.org/contact/email"
-- ...
--
-- the value of the alias may vary from request to request, depending
-- on how many extensions are enabled, but we haven't seen them vary
-- the fields. for example, if we requested the email, with the field
-- alias "email" in the request then it comes back under value.email
-- and type.email. the other libraries I've seen assume this as well
-- so we'll just go with that.
getAxAlias :: (String, String) -> Maybe String
getAxAlias (n,v) = do
guard ("openid.ns." `isPrefixOf` n && extNamespacePrefix `isPrefixOf` v)
return (drop (length "openid.ns.") n)