Docs / Reference

Hooks and filters

The actions and filters FluentAuth exposes for developers, grouped by what they control.

All documentation

FluentAuth is built on WordPress hooks and exposes its own. All are prefixed fluent_auth/, with one exception noted below. This page lists the ones meant for other code to use; the settings screens cover everything else.

SINCE 3.0

Rows marked 3.0 were added in FluentAuth 3.0.0. Everything else has been available for longer.

Login decisions

FilterArgumentsPurpose
can_user_login$allowed, $user, $providerRefuse a login. $provider is google, github or facebook, and empty for the designed login page
login_security_enabled$enabledTurn the attempt limit off, for a staging site with automated logins. 3.0
account_attempt_limit$limit, $userFailures against one account, across addresses, before a challenge is required. Default 3 × the per-IP limit. 3.0
user_ip$ipOverride the resolved visitor address
trusted_proxies$proxiesAdd trusted proxies in code. 3.0
cloudflare_ip_ranges$rangesReplace the built-in Cloudflare ranges. 3.0
low_level_user_roles$roles, $raw_rolesRoles kept out of wp-admin

can_user_login runs on the paths that set the session themselves, which is where the normal login chain cannot be used. The ordinary login form and magic links do not reach it; refuse those with WordPress’s own authenticate filter. A WP_Error return shows its own message on a provider login. Everywhere else only the truthiness is read, so return false and the visitor sees the generic refusal.

// Only allow administrators in through a social provider during a maintenance window.
add_filter('fluent_auth/can_user_login', function ($allowed, $user, $provider) {
    if (get_option('site_maintenance') && !user_can($user, 'manage_options')) {
        return new WP_Error('maintenance', 'The site is under maintenance. Try again later.');
    }
    return $allowed;
}, 10, 3);

Two-factor

HookTypePurpose
2fa_challenge_requiredfilter $required, $userRequire a second factor for a user whose role does not. It cannot waive one: an enrolled method, or a role that requires one, is resolved before this is consulted. 3.0
2fa_methodsfilter $methodsRegister another method. Each entry is a BaseTwoFaMethod instance. 3.0
enforce_2fa_on_auth_cookiefilter $enforce, $user_idExempt a programmatic login from the second factor. 3.0
ask_to_set_up_totpfilter $ask, $userShow or hide the post-login enrolment nudge. 3.0
2fa_code_request_limit, 2fa_code_request_timingfilter $value, $userRate limit for emailed codes. Both default to the login attempt-limit settings. 3.0
totp_enabledfilter $enabled, $userOffer the authenticator app to this user, or not. 3.0
totp_activated, totp_disabledaction $user_idEnrolment lifecycle. 3.0
recovery_codes_generatedaction $user_id, $countA fresh set of recovery codes was issued. 3.0
recovery_code_usedaction $user_id, $remainingOne was spent. $remaining is what is left. 3.0
secret_key_materialfilter $materialSupply the encryption key from somewhere other than wp-config.php. 3.0
fls_send_2fa_codeaction $data, $user, $auto_login_urlA code was issued; send it by another channel too. The code is $data['two_fa_code']

fls_send_2fa_code carries no fluent_auth/ prefix, for compatibility with code written against 1.x. It fires even when the plugin’s own email was held back by the rate limit above.

Passkeys and WebAuthn

Everything runs on your own server; there is no service to configure. These are for sites that need to change how the ceremony is framed, usually a subdomain or a headless front end. All added in 3.0.

HookTypePurpose
passkey_enabledfilter $enabled, $userOffer passkeys to this user, or not
passkey_allow_without_fallbackfilter $allow, $userLet a user hold a passkey as their only factor, with no recovery codes behind it. Off by default for good reason; see Passkeys
webauthn_rp_idfilter $hostThe relying-party ID. The registrable domain, so a passkey made on www. still answers on the bare host
webauthn_rp_namefilter $nameThe site name shown in the browser prompt
webauthn_allowed_originsfilter $originsOrigins an assertion may come from. Add one for a headless front end
webauthn_user_verificationfilter $requirementDefault required, so the device asks for a biometric or PIN
webauthn_creation_options, webauthn_request_optionsfilter $options, $userThe raw options handed to the browser
passkey_registeredaction $user_id, $credential_idA passkey was enrolled
passkey_removed, passkeys_clearedaction $user_id[, $id]One removed, or all of them
passkey_verifiedaction $user, $credentialA sign-in was proved with a passkey
passkey_verification_failed, passkey_registration_failedaction $user, $messageThe ceremony failed
webauthn_sign_count_reusedaction $credential, $presented, $storedThe authenticator’s counter did not advance, which can mean a cloned credential

Magic login

HookTypePurpose
magic_login_can_usefilter $can, $userPer-user availability
will_disable_magic_formfilter $disableHide the form on wp-login
default_token_validityfilter $minutes, $userLink lifetime; default 10
login_token_by_user_idfilter '', $user_id, $minutesReturns a single-use token, not a URL
login_token_by_user_emailfilter '', $email, $minutesSame, by address

Both token filters return an empty string when magic login is off or the user does not exist. The token goes in the fls_al query argument:

// A sign-in link valid for 15 minutes.
$token = apply_filters('fluent_auth/login_token_by_user_id', '', $user_id, 15);
$link  = $token ? add_query_arg('fls_al', $token, site_url('index.php')) : '';

Signup and forms

HookTypePurpose
registration_form_fieldsfilter $fieldsAdd, remove or reorder signup fields
signup_default_rolefilter $role, $form_dataRole for new accounts through the forms
user_rolefilter $roleRole for an account created by a social sign-up
signup_enabledfilter $enabledOverride the WordPress “anyone can register” check for FluentAuth forms
signup_form_datafilter $dataSanitised submission before the account is created
before_signup_validation, after_signup_validationaction $form_dataAround validation
before_creating_useraction $form_dataBefore wp_insert_user()
after_creating_useraction $user_id, $dataAfter it
auto_login_after_signupfilter $auto, $userSign the new user in immediately, or not
verify_signup_emailfilter $verify, $form_dataRequire email verification for this signup
signup_verification_email_bodyfilter $body, $code, $form_dataThe verification email
signup_complete_responsefilter $response, $userThe message and redirect after signup
signup_policy_urlfilter $urlThe terms link under the form
auth_shortcode_defaultsfilter $defaultsDefault attributes for the shortcodes
login_form_argsfilter $argsArguments passed to wp_login_form()
already_logged_in_messagefilter $htmlWhat a signed-in visitor sees instead of a form. Return an empty string to render nothing
validate_password_lengthfilter $checkReturn false to drop the built-in six-character minimum and apply your own
reset_password_formfilter $fieldsThe reset form
reset_password_messagefilter $message, $user, $linkThe reset email
reset_password_mail_subjectfilter $subjectIts subject
extra_login_page_wrap_css_classfilter $classesClasses on the designed login page. End the string with a space; the layout class is appended directly to it

Redirects

HookTypePurpose
login_redirect_urlfilter $url, $user, $requestDestination after a login through the shortcode forms. Every other route uses WordPress’s own login_redirect
validated_redirectfilter $url, $requested, $fallbackAllow a destination the same-site check refused. Only fires when the check changed the URL
respect_front_login_urlfilter $respectWhether a front-end form’s page counts as the origin
social_redirect_tofilter $urlWhere a social login lands

Login lifecycle

ActionArgumentsFires
before_logging_in_user$user_idBefore the session is issued by a signup auto-login
after_logging_in_user$user_idAfter it
user_login_success$userA login was recorded
login_attempts_checked$userAfter the limit check, before the second factor
login_media_labels (filter)$labelsRename the methods in the log. 3.0

The first two fire only for the auto-login that follows a signup through the shortcode forms. For every login by any route, hook user_login_success, or WordPress’s own wp_login.

Social login

HookTypePurpose
social/rendering_button_{provider}action $buttonFires just before a provider’s button is printed. Echo your own markup here; the button itself is still printed afterwards
is_google_one_tap_enabledfilterListened on, not fired. apply_filters('fluent_auth/is_google_one_tap_enabled', false) tells you whether One-Tap is on
init_google_popup_authactionListened on, not fired. do_action('fluent_auth/init_google_popup_auth', ['type' => 'inline', 'delay' => 2]) loads One-Tap on a page of your own

The last two are the reverse of the usual arrangement: FluentAuth registers callbacks on them so that other code can call One-Tap rather than reach into the handler class.

Security checks and scanning

All added in 3.0.

HookTypePurpose
security_checksfilter $checksAdd checks to the findings screen
recommended_settingsfilter $settingsWhat “Apply recommended” applies
guessable_usernamesfilter $namesThe list the admin-username check uses
dormant_admin_daysfilter $daysIdle days before an administrator is dormant; default 365
backup_file_patternsfilter $patternsWhat counts as a backup file in the web root
is_local_sitefilter $is_local, $host, $environmentWhether to treat this install as local, which stands down the HTTPS finding
integrity_scan_budget, baseline_scan_budgetfilter $secondsTime per cron run; both default to 20
integrity_plugin_targets, integrity_theme_targetsfilter $slugsWhat the scanner covers
integrity_extension_ignore_patternsfilter $patternsPaths to skip inside extensions
baseline_extensionsfilter $extsFile types the baseline hashes
uploads_probe_ttlfilter $seconds, $resultHow long the “PHP in uploads” result is cached
report_extension_inventoryfilter $inventoryThe plugin and theme list sent with a scan report
alerts_api_urlfilter $urlThe scanning service endpoint
wp_config_pathfilter $pathWhere wp-config.php lives, for an unusual layout

Emails

HookTypePurpose
fluentcrm_auth/email_smartcodesfilter $codesThe smartcodes offered in the system-email editor. Applied to the user group and again to the site group
smartcode_fallbackfilter $raw_code, $userValue for an unknown smartcode. $raw_code is the tag as written
wp_system_email_headactionExtra <head> content in the email template. Echo it; no arguments

Admin

HookTypePurpose
app_permissionfilter $capCapability needed to open FluentAuth; default manage_options
fluent_security/app_varsfilter $varsData passed to the admin app
onboarding_stepsfilter $stepsAdd or remove setup-wizard steps. 3.0
remote_auth_response_datafilter $dataWhat a remote auth server returns about a user

Cron events

fluent_auth_daily_tasks (log retention, digests, scheduled scans) and fluent_auth_hourly_tasks (hourly scans). Since 3.0, fluent_auth_recovery_resets runs the batched password resets.