wordpress-auth-1.0.0.0: Validate Wordpress Cookies & Nonces; Build Wordpress Hashes & Salts

Safe HaskellNone
LanguageHaskell2010

Wordpress.Auth

Contents

Description

The Wordpress.Auth module is used for checking the validity of various Wordpress authentication schemes.

This is useful if you want a Haskell application to be able to serve authenticated API requests to a Wordpress site without having to devise a Wordpress-to-Haskell authentication system.

You will need some constants from your Wordpress site's wp-config.php, like the NONCE_KEY & NONCE_SALT, you could supply these via environmental variables:

 loggedInScheme <- AuthScheme
     <$> (wpConfigKey . T.pack <$> getEnv "LOGGED_IN_KEY")
     <*> (wpConfigSalt . T.pack <$> getEnv "LOGGED_IN_SALT")

Then you'll want to pull the specific cookie's text out of the Cookie header(see findCookie) & use then parseWordpressCookie to build a WPCookie. You should then use the username field of the cookie to query your Wordpress database for the User's ID(WordpressUserId) & user_pass(WordpresUserPass) fields as well as the session_tokens User Meta(SessionToken).

Equiped with these and the current time(via getPOSIXTime), you can then validate the cookie:

 passwordFragment = WordpressUserPass myUserTablesUserPassFieldValue
 sessionTokens    = decodeSessionTokens myUserMetaTablesSessionTokensMetaValue
 cookieIsValid    = validateCookie loggedInScheme currentTime cookie
                                   passwordFragment sessionTokens

If this is a REST request or a form submission, you should always validate the nonce, even for requests with no auth cookies. The nonce can be pulled out of the X-WP-Nonce header or the _wpnonce query parameter.

 nonceTick     <- wordpressNonceTick (60 * 60 * 24) currentTime
 let validNonce = validateNonce nonceScheme (Just cookie) nonceTick
                                (Just $ WordpressUserId userId)
                                "wp_rest" myNonceText
Synopsis

Request Handling

authorizeWordpressRequest :: forall m a. MonadIO m => WPAuthConfig m a -> RequestHeaders -> [QueryItem] -> m (WPAuthorization a) Source #

The is a generalized authentication verification scheme that authorizes a user if the logged_in cookie is set and valid, & verifies the wp_rest nonce action for both authorized & anonymous users.

The WPAuthConfig failure handler will be used if a Cookie is present but invalid or if the nonce is missing/invalid.

data WPAuthConfig m a Source #

Configuration data specific to your Wordpress site & Haskell application.

Constructors

WPAuthConfig 

Fields

data UserAuthData a Source #

The data needed for authentication, along with some arbitrary data that is returned on success.

Constructors

UserAuthData 

Fields

  • userData :: a

    Arbitrary data that the validation should return. E.g., if you query your users table for the ID & user_pass, you can return your whole User type so you don't have to make another database call in your handler.

  • wpUser :: WordpressUserId

    The ID field of the User.

  • wpPass :: WordpressUserPass

    The user_pass field of the User.

  • wpTokens :: [SessionToken]

    The session_tokens usermeta for the User. You can use decodeSessionTokens to parse the raw meta value.

Instances
Eq a => Eq (UserAuthData a) Source # 
Instance details

Defined in Wordpress.Auth

Show a => Show (UserAuthData a) Source # 
Instance details

Defined in Wordpress.Auth

data WPAuthorization a Source #

The result of the authorizeWordpressRequest function can be an authorized user with some additional data, or an anonymous user.

Instances
Eq a => Eq (WPAuthorization a) Source # 
Instance details

Defined in Wordpress.Auth

Show a => Show (WPAuthorization a) Source # 
Instance details

Defined in Wordpress.Auth

data WPAuthError Source #

Potential errors during authentication.

Constructors

EHeader CookieHeaderError

Header Error.

EParse CookieParseError

Parsing Error.

EValid CookieValidationError

Validation Error.

UserDataNotFound

The getUserData function returned Nothing.

NoNonce

The Request has no X-WP-Nonce header.

InvalidNonce

The nonce couldn't be validated.

Instances
Eq WPAuthError Source # 
Instance details

Defined in Wordpress.Auth

Show WPAuthError Source # 
Instance details

Defined in Wordpress.Auth

data CookieName Source #

The name of a Wordpress authentication cookie. Wordpress's frontend uses CookieNameWithMD5 "wordpress_logged_in_" "<your-site-url>" by default.

Constructors

CustomCookieName Text

A constant name for the cookie.

CookieNameWithMD5 Text Text

A cookie name with some text to hash & append. E.g., Wordpress's logged_in auth scheme uses wordpress_logged_in_ suffixed with the MD5 hash of the siteurl option.

Instances
Eq CookieName Source # 
Instance details

Defined in Wordpress.Auth

Show CookieName Source # 
Instance details

Defined in Wordpress.Auth

cookieName :: CookieName -> Text Source #

Build the name of an authentication cookie from a CookieName, hashing the suffix if present.

findCookie :: CookieName -> RequestHeaders -> Either CookieHeaderError Text Source #

Try to find & decode a Cookie in the headers with the given name.

data CookieHeaderError Source #

Potential errors while searching for a specific cookie in the request headers.

Constructors

NoCookieHeader

The Request has no Cookie header.

NoCookieMatches

No Cookie matched the expected CookieName.

findNonce :: RequestHeaders -> [QueryItem] -> Maybe Text Source #

Try to find & decode a Nonce in either the X-WP-Nonce header or the _wpnonce query parameter.

Cookies

data WPCookie Source #

This represents a Cookie set by a Wordpress authentication scheme (auth, auth_sec, & logged_in).

Constructors

WPCookie 

Fields

Instances
Eq WPCookie Source # 
Instance details

Defined in Wordpress.Auth

Show WPCookie Source # 
Instance details

Defined in Wordpress.Auth

newtype CookieToken Source #

A User's Wordpress Session Token from an auth cookie.

Constructors

CookieToken 

Fields

Instances
Eq CookieToken Source # 
Instance details

Defined in Wordpress.Auth

Show CookieToken Source # 
Instance details

Defined in Wordpress.Auth

parseWordpressCookie :: Text -> Either CookieParseError WPCookie Source #

Parse a WPCookie from the body text of an auth, auth_sec, or logged_in cookie.

data CookieParseError Source #

Potential errors we may encounter while parsing a WPCookie.

Constructors

MalformedCookie

The cookie did not have 4 fields separated by `|` characters.

InvalidExpiration

The expiration field of the cookie is not an Integer.

validateCookie Source #

Arguments

:: AuthScheme

They _KEY & _SALT constants for a WP auth scheme.

-> POSIXTime

The current time.

-> WPCookie

The cookie to validate.

-> WordpressUserPass

The user_pass field for the cookie's user.

-> [SessionToken]

The session_tokens meta for the cookie's user.

-> Either CookieValidationError () 

Validate a Wordpress Authentication Cookie by verifying that the hash & token in the cookie are valid and the expiration time is in the future.

newtype WordpressUserPass Source #

The user_pass field from the users table of a Wordpress site.

Constructors

WordpressUserPass 

data CookieValidationError Source #

Potential validation errors for a WPCookie.

Constructors

CookieExpired

The expiration time of the cookie is in the past.

InvalidHash

The hmac hash in the cookie doesn't match the calculated hash.

InvalidToken

The token in the cookie is not valid or expired.

validateCookieHash :: AuthScheme -> WPCookie -> WordpressUserPass -> Bool Source #

Determine if a WPCookie's hash matches the hashed password & token.

A secret is generated by hashing the user, password, expiration, & token. This secret is then used to hash the user, expiration, & token. The resulting hash should match the hmac hash in the WPCookie.

Session Tokens

data SessionToken Source #

A User Session's Token. These can be found in the usermeta Wordpress table for rows where meta_key="session_token".

You'll probably want to use decodeSessionTokens to parse the tables's meta_value instead of constructing them yourself.

Instances
Eq SessionToken Source # 
Instance details

Defined in Wordpress.Auth

Show SessionToken Source # 
Instance details

Defined in Wordpress.Auth

decodeSessionTokens :: Text -> [SessionToken] Source #

Decode a serialized PHP array containing a User's Session Tokens. These are usually stored as the session_tokens usermeta.

It may be an associative array of tokens to expiration times, or tokens to an associative array of sub-fields:

array(
  'some-random-hex-text' => 192836504,
  // ...
);
array(
  'deadbeef ' => array(
    'expiration' => 9001,
    // ...
  ),
);

validateSessionToken Source #

Arguments

:: POSIXTime

The current time

-> CookieToken

The session token from a WPCookie

-> [SessionToken]

A list of the User's session tokens

-> Bool 

Determine if the SHA256 hash of the token matches one of the unexpired session tokens.

Nonces

newtype NonceTick Source #

The tick number of a Wordpress site - required for Nonce verification.

Constructors

NonceTick 

Fields

Instances
Eq NonceTick Source # 
Instance details

Defined in Wordpress.Auth

Show NonceTick Source # 
Instance details

Defined in Wordpress.Auth

wordpressNonceTick Source #

Arguments

:: NominalDiffTime

The nonce lifetime. Wordpress's default is 1 day.

-> POSIXTime

The current time.

-> NonceTick 

A port of the wp_nonce_tick function. Calculates the nonce tick number, where each nonce has a lifetime of two ticks.

validateNonce Source #

Arguments

:: AuthScheme

The Wordpress site's nonce scheme constants - NONCE_KEY & NONCE_SALT.

-> Maybe CookieToken

A token from the logged_in cookie.

-> NonceTick

The current tick number.

-> Maybe WordpressUserId

The ID of the currently logged in User.

-> Text

The action of the nonce (e.g., "wp_rest" for API requests).

-> Text

The nonce to verify.

-> Bool 

Determine if the tick-dependent hash of the CookieToken matches the hash of the current or previous tick.

newtype WordpressUserId Source #

The ID field from the users table of a Wordpress site.

Constructors

WordpressUserId 

Hashing / Salting

wordpressHash :: AuthScheme -> Text -> Text Source #

A port of the wp_hash function. This performs an hmac hash on some text using a secret derived from the authentication scheme's key & salt constants.

wordpressSalt :: AuthScheme -> Text Source #

A port of the wp_salt function. Builds a secret key for a hashing function using the auth scheme's key & salt.

data AuthScheme Source #

This represents one of the $schemes that Wordpress's cookie/nonce functions use to salt their hashes.

The built-in Wordpress schemes are auth/auth_sec for HTTP/HTTPS requests to wp-admin, logged_in for authenticated front-end requests, & nonce for form submissions & API requests.

The secret keys & salts are constants found in your wp-config.php file, defined as LOGGED_IN_SALT, LOGGED_IN_KEY, etc.

Instances
Eq AuthScheme Source # 
Instance details

Defined in Wordpress.Auth

Show AuthScheme Source # 
Instance details

Defined in Wordpress.Auth

data WordpressKey Source #

An auth scheme's _KEY constant, usually defined in your Wordpress site's wp-config.php. E.g., LOGGED_IN_KEY

Instances
Eq WordpressKey Source # 
Instance details

Defined in Wordpress.Auth

Show WordpressKey Source # 
Instance details

Defined in Wordpress.Auth

data WordpressSalt Source #

An auth scheme's _SALT constant, usually defined in your Wordpress site's wp-config.php. E.g., LOGGED_IN_SALT

wpConfigKey :: Text -> WordpressKey Source #

Build the _KEY value for an authentiation scheme.

wpConfigSalt :: Text -> WordpressSalt Source #

Build the _SALT value for an authentiation scheme.