Repository navigation
Invite tokens never expire — add 24h TTL (filterable) #11
Description
Summary
Invite tokens currently have no TTL — once created, they remain valid indefinitely until revoke_invite() is called. A lost URL from months ago is still live.
Agreed in review to add a 24-hour expiry.
Where
includes/class-map-invite.php — create_invite_url(), get_post_by_token(), handle_invite_request().
Suggested fix
Store a created-at timestamp alongside the (hashed, per #10) token. On lookup in get_post_by_token() and in handle_invite_request(), reject tokens older than MAP_INVITE_TTL (default DAY_IN_SECONDS, filterable).
Sketch (assuming we land #10 first so the storage shape changes anyway):
const TOKEN_META_KEY = '_map_invite_token'; // now stores an array: [ 'hash' => ..., 'created' => int ]
public static function create_invite_url( int $post_id ): string {
$token = wp_generate_password( 32, false );
update_post_meta(
$post_id,
self::TOKEN_META_KEY,
array(
'hash' => hash( 'sha256', $token ),
'created' => time(),
)
);
return self::build_url( $token );
}
public static function get_post_by_token( string $token ): ?\WP_Post {
if ( '' === $token ) {
return null;
}
$hash = hash( 'sha256', $token );
$ttl = (int) apply_filters( 'map_invite_ttl', DAY_IN_SECONDS );
// Query by hash via a meta_query on the serialized sub-key.
// Simpler: query by post_status etc., then hash_equals + TTL check in PHP.
…
}The array meta shape plus a TTL check means lookup can no longer be a single meta_value equality query. Two realistic approaches:
- Store hash as the meta value (scalar) and keep
createdin a second meta key_map_invite_created. Preserves the simplemeta_valuelookup; requires updating both on create/revoke. - Store as serialized array (as sketched). Query posts by a lightweight hash index (either by also keeping a
_map_invite_hashscalar alongside, or by shifting to a custom table — related to the broader storage discussion in Update README with project description and Playground link #7).
Option 1 is the smaller change.
Handling expiry in handle_invite_request()
When an expired token is presented, surface the same "invalid or revoked" error message (wp_die) — don't leak whether it was expired vs never-existed vs revoked.
Optionally: delete expired tokens as a side-effect of encountering them, so they don't persist in the DB forever.
Filter
apply_filters( 'map_invite_ttl', DAY_IN_SECONDS ) so sites that want longer/shorter can override.
Tests to add
- Token older than TTL is rejected.
- Token within TTL is accepted.
- Filter can override TTL.
handle_invite_requestwith expired token shows the same error page as an invalid token.
References
Internal security review, Apr 2026. Depends on / coordinates with #10.
Activity
Security review batch: fix REST gates, invite storage, and co-author …
dd32 commentedon Apr 23, 2026
Fixed in b4a71c1 (on trunk).