The tables the plugin creates
Five tables appear in your database when Pro activates. What each one holds, how long it holds it, and which ones carry a secret you should treat as one.
- Advanced
- 7 min read
- Applies to 2.0
Why you are reading this
You opened phpMyAdmin, saw five tables you did not create, and want to know what they are. That is the whole audience for this page.
The free plugin creates none. Everything below belongs to Pro, and every name is
prefixed with your installation's table prefix — wp_ in a default install, so
wp_attrua_login_attempts and so on. The prefix is written {prefix} here.
Four are created on activation by the installer; the fifth arrives with a migration the first time social sign-in is set up.
The five
| Table | Holds | Emptied by |
|---|---|---|
{prefix}attrua_login_attempts | failed sign-in counters, per identifier and method | the throttling window, then a daily purge |
{prefix}attrua_user_sessions | nothing — see below | — |
{prefix}attrua_totp_secrets | one authenticator secret and its backup codes per user | the user turning 2FA off |
{prefix}attrua_magic_links | one-time sign-in tokens, hashed | expiry and single use, then a daily purge |
{prefix}attrua_social_connections | the link between a WordPress account and an OAuth provider account | disconnecting the provider |
What each one is for
attrua_login_attempts
One row per identifier and method, not per attempt: a UNIQUE KEY on
(identifier, method) means a repeated failure updates failed_attempts and
last_attempt rather than adding a row. So this table stays small, and its size
does not tell you how much has been attempted.
It keeps ip_address and user_agent alongside. That makes it personal data
under most privacy regimes, and it is the table to look at when someone asks
what you retain about failed sign-ins.
attrua_user_sessions
This table holds nothing, and is no longer created.
Its columns — session_token, ip_address, user_agent — describe something
the plugin does not do. Sessions are held in transients, which is what the move
away from PHP sessions in 2.0 settled on. Nothing has ever written a row here
or read one.
It is not created on activation. If an earlier version of the plugin left one on your site, it is empty and harmless; it is not dropped out from under an upgrade, and removing the plugin removes it.
If you found it in phpMyAdmin and concluded that session tokens were being stored in the clear — a reasonable reading of those column names — they were not. There was nothing in there at all.
attrua_totp_secrets
This table holds authentication secrets. secret_key is the shared secret
an authenticator app derives its six-digit codes from, and backup_codes holds
the recovery codes.
secret_key is encrypted (AES-256-GCM) and backup_codes are hashed the way
passwords are, so a dump on its own is not immediately usable. It is still the
most sensitive table the plugin has: treat a copy of it the way you would treat
a password file, and keep it out of staging copies, support tickets and shared
backups.
By default the encryption key is derived from this site's WordPress salts. That
is sound while the salts live in wp-config.php, where a database dump cannot
reach them — but on an installation that has moved them into the options table,
the lock and the key are back in the same box.
To keep the key independent of the salts, define it in wp-config.php:
define( 'ATTRUA_ENCRYPTION_KEY', '…a long random string…' );
Existing enrolments keep working and are re-encrypted as they are used. The Security screen tells you which of the two cases you are in.
One row per user, enforced by a UNIQUE KEY on user_id. last_used_at is
what tells you whether an enrolment is real or abandoned.
attrua_magic_links
Tokens are stored as token_hash, not in the clear — a leaked copy of this
table does not let anyone sign in, because the link that was emailed cannot be
reconstructed from its hash.
Two columns do the security work: expires_at and used_at. A link is refused
once either has passed, which is what makes it single-use.
Spent and expired rows are deleted by the daily purge, so the table does not grow without bound.
attrua_social_connections
Created by a migration rather than the installer, the first time social sign-in is configured. One row per (user, provider) pair, holding the provider's own user id, and the display name and avatar it returned.
access_token, refresh_token and token_expires_at exist in the schema and
are never written — they have been NULL on every installation since the
table was created. Social sign-in stores identity and nothing more; the plugin
does not call provider APIs on a member's behalf afterwards, so it has no use
for a token.
Their schema comments used to read "Encrypted OAuth access token", which described no code that has ever existed. If you have quoted that line in a security questionnaire, it is worth correcting.
What happens on uninstall
Deactivating the plugin changes nothing: the tables stay, scheduled tasks stop, and switching it back on picks up where it left off.
Deleting it — the separate, confirmed action on the plugins screen — drops all six tables.
Deleting the plugin destroys every member's authenticator enrolment along with the sign-in history. Deactivate first if you only mean to stop it running, and take a backup before deleting if the enrolments matter.
All six go, attrua_totp_secrets included — so authenticator secrets do not
linger in the database of a site whose plugin has been deleted, where nothing
would remain that knew what they were.
Two names you may find in older notes
{prefix}attrua_pro_submissions and {prefix}attrua_webhook_logs appear in the
source tree but never in a database. They belong to two form layers that are
excluded from the built package by exclude-from-classmap in composer.json,
so the code that would create them is not shipped. If you found those names in
an old note or an issue, that is why the tables are not there.
Something missing or out of date? Tell support.