// caso-studio
Auth Gate per AI
Registrazione e login frontend su WordPress con double opt-in, usati come cancello per un assistente AI (Voiceflow) - solo gli utenti verificati parlano col bot, che li accoglie per nome.
Progetto che ho sviluppato in agenzia, partendo da spec e piano scritti prima del codice. Niente nomi e niente codice reale: gli snippet sono riscritti e semplificati per illustrare l'approccio.
Cos'è
Un cliente voleva offrire un assistente AI (Voiceflow) solo ai propri clienti registrati. WordPress però non ha un flusso di registrazione frontend decente: wp-login.php è brutto, non fa double opt-in e non c'entra nulla col brand. Questo plugin costruisce l'intera esperienza — registrazione, verifica email, login, recupero password — e usa lo stato di autenticazione come interruttore dell'assistente: loggato e verificato → widget di chat che ti saluta per nome; anonimo → un badge che ti invita a registrarti.
Il problema
Tre vincoli: gli account dovevano essere utenti WordPress veri (per non inventare un secondo concetto di sessione), la verifica email doveva essere obbligatoria prima del primo accesso, e nulla di tutto questo doveva avvicinarsi a wp-admin — un cliente registrato non deve mai vedere la dashboard.
Come funziona
- Ruolo dedicato con la sola capability
read;wp-adminviene bloccato a priorità 1 con redirect, e l'admin bar nascosta — con via di fuga per gli account promossi a editor in seguito - Double opt-in: il token di verifica (40 caratteri) è salvato hashato con TTL di 24 ore e confrontato con
hash_equals(); al click l'utente viene attivato e loggato in automatico - Una pagina, cinque viste: registrazione, login, recupero, reset e "conferma la tua email" sono sibling nello stesso template; un JS vanilla da ~100 righe mostra la vista che il server indica nella risposta — anche negli errori
- Igiene automatica: un cron mensile elimina gli account mai verificati dopo 30 giorni
- Recupero password su primitive core (
get_password_reset_key/reset_password), sessioni invalidate dopo ogni reset, copy admin-editabile e registrato in WPML
Dettagli di cui vado fiero
La difesa dall'enumeration, fatta nell'ordine giusto. Il flusso di login verifica la password prima di rivelare che un account esiste ma non è verificato — nell'ordine inverso, chiunque potrebbe scoprire quali email sono registrate. Stessa filosofia su recupero e reinvio: risposta generica identica sia che l'email esista sia che no, con un rate limit che resta invisibile proprio grazie alla risposta uniforme.
// Prima la password, poi lo stato: l'ordine inverso rivelerebbe
// a chiunque quali email hanno un account.
if ( ! $user || ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
return $this->fail( __( 'Credenziali non valide.' ), 'login' );
}
if ( get_user_meta( $user->ID, 'account_status', true ) === 'pending' ) {
return $this->ok( __( 'Conferma la tua email per accedere.' ), 'pending' );
}
Il contesto utente arriva all'AI senza backend in mezzo. Quando un utente verificato apre una pagina, il plugin scrive un cookie first-party di un'ora con id, email e nome; il widget lo legge client-side e personalizza il saluto. Niente API custom, niente sincronizzazione di stato: il cookie è il contratto.
Hardening contro lo script-breakout. L'ID del progetto Voiceflow configurato dall'admin finisce dentro un tag <script>: viene serializzato con wp_json_encode(..., JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT), così un eventuale </script> nel valore resta inerte anche se la sanitizzazione a monte dovesse regredire un domani.
Il server guida la UI. Ogni risposta JSON — successo o errore — può indicare la prossima vista da mostrare. "Credenziali giuste ma email non confermata" porta alla vista pending senza reload e senza che il client debba conoscere la logica: il frontend è stupido apposta.
Cosa ho imparato
- L'autenticazione è fatta di ordinamenti: cosa verifichi prima di cosa riveli, cosa hashi prima di cosa salvi
- Riusare le primitive core di WordPress (auth cookie, reset key, ruoli) batte reinventarle: meno codice mio da difendere
- Una state machine guidata dal server rende il client banale e i flussi testabili