Application Passwords Hooks

This document covers the action hooks available in the WP_Application_Passwords class for extending and monitoring application password functionality.

Actions

wp_create_application_password

Fires when a new application password is created.

do_action( 
    'wp_create_application_password', 
    int $user_id, 
    array $new_item, 
    string $new_password, 
    array $args 
);

Since: 5.6.0
Updated: 6.8.0 — Password now hashed with wp_fast_hash() instead of phpass

Parameters:

Name Type Description
$user_id int The user ID
$new_item array The created password details
$new_password string The plain text password
$args array Original arguments passed to creation

$new_item array:

Key Type Description
uuid string Unique identifier
app_id string Application-provided UUID (may be empty)
name string Human-readable name
password string One-way hash of the password
created int Unix timestamp
last_used null Always null on creation
last_ip null Always null on creation

Example — Send notification email:

add_action( 'wp_create_application_password', function( $user_id, $item, $password, $args ) {
    $user = get_userdata( $user_id );
    
    wp_mail(
        $user->user_email,
        'New Application Password Created',
        sprintf(
            'A new application password "%s" was created for your account on %s.',
            $item['name'],
            get_bloginfo( 'name' )
        )
    );
}, 10, 4 );

Example — Log creation event:

add_action( 'wp_create_application_password', function( $user_id, $item, $password, $args ) {
    error_log( sprintf(
        'Application password created: user=%d, name=%s, uuid=%s',
        $user_id,
        $item['name'],
        $item['uuid']
    ) );
}, 10, 4 );

Example — Sync to external system:

add_action( 'wp_create_application_password', function( $user_id, $item, $password, $args ) {
    // Store app_id mapping for external service
    if ( ! empty( $item['app_id'] ) ) {
        update_user_meta( 
            $user_id, 
            '_external_app_' . $item['app_id'], 
            $item['uuid'] 
        );
    }
}, 10, 4 );

wp_update_application_password

Fires when an application password is updated.

do_action( 
    'wp_update_application_password', 
    int $user_id, 
    array $item, 
    array $update 
);

Since: 5.6.0
Updated: 6.8.0 — Password may be hashed with either wp_fast_hash() (new) or phpass (existing)

Parameters:

Name Type Description
$user_id int The user ID
$item array The updated password details
$update array The fields that were requested to change

$item array:

Key Type Description
uuid string Unique identifier
app_id string Application-provided UUID
name string Human-readable name (may be updated)
password string One-way hash (unchanged)
created int Unix timestamp (unchanged)
last_used int|null Last use timestamp
last_ip string|null Last use IP address

Example — Audit logging:

add_action( 'wp_update_application_password', function( $user_id, $item, $update ) {
    if ( isset( $update['name'] ) ) {
        error_log( sprintf(
            'Application password renamed: user=%d, uuid=%s, new_name=%s',
            $user_id,
            $item['uuid'],
            $item['name']
        ) );
    }
}, 10, 3 );

Note: Currently only the name field can be updated. This action fires even if the new name matches the old name (no actual change).


wp_delete_application_password

Fires when an application password is deleted.

do_action( 
    'wp_delete_application_password', 
    int $user_id, 
    array $item 
);

Since: 5.6.0

Parameters:

Name Type Description
$user_id int The user ID
$item array The deleted password details

$item array:

Key Type Description
uuid string Unique identifier
app_id string Application-provided UUID
name string Human-readable name
password string One-way hash
created int Unix timestamp
last_used int|null Last use timestamp
last_ip string|null Last use IP address

Example — Security notification:

add_action( 'wp_delete_application_password', function( $user_id, $item ) {
    $user = get_userdata( $user_id );
    
    wp_mail(
        $user->user_email,
        'Application Password Revoked',
        sprintf(
            'The application password "%s" has been revoked from your account.',
            $item['name']
        )
    );
}, 10, 2 );

Example — Clean up external references:

add_action( 'wp_delete_application_password', function( $user_id, $item ) {
    // Remove any external mappings
    if ( ! empty( $item['app_id'] ) ) {
        delete_user_meta( $user_id, '_external_app_' . $item['app_id'] );
    }
}, 10, 2 );

Example — Track deletion for security audit:

add_action( 'wp_delete_application_password', function( $user_id, $item ) {
    // Log with usage statistics
    error_log( sprintf(
        'Application password deleted: user=%d, name=%s, created=%s, last_used=%s, last_ip=%s',
        $user_id,
        $item['name'],
        date( 'Y-m-d H:i:s', $item['created'] ),
        $item['last_used'] ? date( 'Y-m-d H:i:s', $item['last_used'] ) : 'never',
        $item['last_ip'] ?? 'unknown'
    ) );
}, 10, 2 );

Note: When delete_all_application_passwords() is called, this action fires once for each password deleted.


Common Patterns

Limit Application Passwords Per User

add_action( 'wp_create_application_password', function( $user_id ) {
    $passwords = WP_Application_Passwords::get_user_application_passwords( $user_id );
    $max_allowed = 10;
    
    if ( count( $passwords ) > $max_allowed ) {
        // Delete oldest passwords beyond limit
        usort( $passwords, function( $a, $b ) {
            return $a['created'] - $b['created'];
        } );
        
        while ( count( $passwords ) > $max_allowed ) {
            $oldest = array_shift( $passwords );
            WP_Application_Passwords::delete_application_password( $user_id, $oldest['uuid'] );
        }
    }
}, 20, 1 );

Require Approval for New Passwords

// Store pending passwords
add_action( 'wp_create_application_password', function( $user_id, $item ) {
    // Mark as pending
    update_user_meta( 
        $user_id, 
        '_pending_app_password_' . $item['uuid'], 
        true 
    );
    
    // Notify admin
    wp_mail(
        get_option( 'admin_email' ),
        'Application Password Needs Approval',
        sprintf(
            'User %d created application password "%s". Review and approve.',
            $user_id,
            $item['name']
        )
    );
}, 10, 2 );

Sync Deletions Across Multisite

add_action( 'wp_delete_application_password', function( $user_id, $item ) {
    if ( is_multisite() ) {
        // Log to main site for centralized audit
        switch_to_blog( get_main_site_id() );
        
        // Your logging logic here
        
        restore_current_blog();
    }
}, 10, 2 );

Related Filters

While not part of WP_Application_Passwords class, these filters affect application password behavior:

wp_is_application_passwords_available

Controls whether application passwords feature is available site-wide.

add_filter( 'wp_is_application_passwords_available', '__return_false' );

wp_is_application_passwords_available_for_user

Controls availability for a specific user.

add_filter( 'wp_is_application_passwords_available_for_user', function( $available, $user ) {
    // Only allow for administrators
    return user_can( $user, 'manage_options' );
}, 10, 2 );

application_password_is_api_request

Filters whether the current request is an API request that should allow application password authentication.

add_filter( 'application_password_is_api_request', function( $is_api_request ) {
    // Custom logic to determine if this is an API request
    return $is_api_request;
} );