{- | This module presents a Servant 'AuthHandler' that validates
a @LOGGED_IN@ Wordpress Cookie & the @"wp_rest"@ Nonce.

You'll need to build a 'WPAuthConfig' for your application to pass to
the 'wpAuthHandler' function. The config defines some specifics about your
Wordpress site, as well as functions to pull a User's authentication data
& to handle authentication failures.

You must define the 'Servant.Server.Experimental.Auth.AuthServerData' type
instance yourself:

> type instance "AuthServerData" ("AuthProtect" \"wp\") = WPAuthorization (Entity User)

For more information, be sure to check out the
<https://haskell-servant.readthedocs.io/en/stable/tutorial/Authentication.html#generalized-authentication Generalized Authentication>
section of the servant tutorial.

If you want to build your own custom 'AuthHandler', check out the
"Wordpress.Auth" module.

-}
module Servant.Auth.Wordpress
    (
    -- * Auth Handlers
      wpAuthHandler
    , wpAuthorizedOnlyHandler
    , WPAuthorization(..)
    -- * Configs
    , WPAuthConfig(..)
    , CookieName(..)
    , AuthScheme(..)
    , WordpressKey
    , wpConfigKey
    , WordpressSalt
    , wpConfigSalt
    , UserAuthData(..)
    , WordpressUserId(..)
    , WordpressUserPass(..)
    , SessionToken(..)
    , decodeSessionTokens
    -- * Errors
    , WPAuthError(..)
    , CookieHeaderError(..)
    , CookieParseError(..)
    , CookieValidationError(..)
    )
where

import           Network.Wai                    ( Request
                                                , requestHeaders
                                                , queryString
                                                )
import           Servant                        ( Handler )
import           Servant.Server.Experimental.Auth
                                                ( AuthHandler
                                                , mkAuthHandler
                                                )
import           Wordpress.Auth                 ( WPAuthConfig(..)
                                                , UserAuthData(..)
                                                , WPAuthorization(..)
                                                , authorizeWordpressRequest
                                                , WPAuthError(..)
                                                , AuthScheme(..)
                                                , CookieName(..)
                                                , CookieHeaderError(..)
                                                , CookieParseError(..)
                                                , CookieValidationError(..)
                                                , WordpressUserId(..)
                                                , WordpressUserPass(..)
                                                , SessionToken(..)
                                                , decodeSessionTokens
                                                , WordpressKey
                                                , wpConfigKey
                                                , WordpressSalt
                                                , wpConfigSalt
                                                , findCookie
                                                )


-- | A Servant Authentication Handler that valiates a @logged_in@ Cookie
-- & a @wp_rest@ Nonce.
wpAuthHandler
    :: WPAuthConfig Handler a -> AuthHandler Request (WPAuthorization a)
wpAuthHandler = mkAuthHandler . handler

-- | This is similar to 'wpAuthHandler' but it allows you to throw an error
-- for anonymous users with valid nonces - restricting handlers to only
-- logged in users.
wpAuthorizedOnlyHandler
    :: WPAuthConfig Handler a
    -> (WPAuthError -> Handler a)
    -> AuthHandler Request a
wpAuthorizedOnlyHandler cfg authFailure = mkAuthHandler $ \req -> do
    result <- handler cfg req
    case result of
        WPAuthorizedUser uData -> return uData
        WPAnonymousUser        -> do
            name <- getCookieName cfg
            authFailure
                $ either EHeader (const $ EHeader NoCookieMatches)
                $ findCookie name (requestHeaders req)


handler :: WPAuthConfig Handler a -> Request -> Handler (WPAuthorization a)
handler cfg req =
    authorizeWordpressRequest cfg (requestHeaders req) (queryString req)