In questo periodo sto lavorando su un progetto basato su Symfony 4. Il back-end consiste di una serie di API RESTful mentre il front-end è realizzato in Vue con alcune pagine renderizzate partendo da template Twig. In brevissimo tempo si è posto il problema dell’autenticazione. Il team di back-end developer aveva l’esigenza che le API fossero stateless in accordo con le specifiche dell’architettura REST. Tuttavia il sistema di autenticazione di default di Symfony è basato sul session cookie di PHP e questo fa si che il requisito della statelessness venga meno. Inizialmente si è deciso di implementare un doppio sistema di autenticazione: uno basato sulla PHP session per i controller web e uno basato su JWT (JSON Web Tokens) mediante l’impiego del LexikJWTAuthenticationBundle per le API. In brevissimo tempo i front-end developer ci hanno informati della difficoltà di gestire un doppio sistema di autenticazione chiedendoci di porre rimedio.

Una tra le soluzioni a questo problema è l’impiego di JWT sia per le API REST che per i controller web mediante il salvataggio del JWT in un cookie. Il LexikJWTAuthenticationBundle offre infatti la possibilità di estrarre il token sia dal Authentication Header che da un cookie. E’ sufficiente un minimo di configurazione e il gioco è fatto!

In questo mio repository ho implementato un prototipo di applicazione che implementa questa soluzione fornendo l’autenticazione JWT sia per i classici controller web che per le API REST. Il file README contiene le istruzioni necessarie ad installare e avviare l’applicazione. In meno di due minuti potrai testare il funzionamento della soluzione proposta da questo articolo e verificare se fa al caso tuo qualora ti trovassi in una situazione analoga.

Il tutto si basa sulla configurazione del firewall config/packages/security.yaml e su un paio di classi: AuthenticationSuccessHandler e JWTTokenAuthenticator. Nella fattispecie nel file config/packages/security.yaml ho definito 4 firewall:

Copy to Clipboard

web_login e api_login si occupano di intercettare e gestire le richieste di autenticazione rispettivamente per la parte web e REST, secured_apisecured_web si occupano di autenticare tutte le richieste alle aree protette del sito. Il JWTTokenAuthenticator è un ottimo bundle pensato per essere utilizzato nell’implementazione API RESTful, dove il concetto di statelessness è fondamentale, tuttavia non offre supporto al normale flusso dei controller web. E’ stato quindi necessario estendere il comportamento di alcune classi di questo bundle al fine di riutilizzarne la logica di estrazione del token e di autenticazione dell’utente. A tale scopo ho implementato le due classi di cui sopra.

Nello specifico la classe JWTTokenAuthenticator estende la medesima del LexikJWTAuthenticationBundle riadattandone il comportamento al contesto dei tradizionali controller web. Il metodo supports a differenza di quanto accade nel caso delle API REST, non solleva alcuna eccezione nel caso di token scaduto o non valido, semplicemente restituisce false, delegando al framework la gestione del flusso. Il metodo start anziché di limitarsi a rispondere con un messaggio di errore, reindirizza l’utente alla pagina di login.

Copy to Clipboard

La classe AuthenticationSuccessHandler si occupa di gestire il caso in cui l’autenticazione di un utente va a buon fine. In questo caso è necessario reindirizzare l’utente all’ultima pagina visitata prima che venisse richiesto il login. La casse propaga inoltre un evento così che eventuali listener possano estenderne il comportamento.

Copy to Clipboard

Puoi visualizzare i sorgenti dell’intero progetto sul mio profilo GitHub. Con questi piccoli accorgimenti è possibile utilizzare l’autenticazione mediante JWT per tutti gli end-point dell’applicazione, sia REST che web, semplificando la vita ai front-end developer che non dovranno più preoccuparsi di gestire più modalità di autenticazione a seconda del contesto. Al tempo stesso è possibile sviluppare delle API RESTful in piena osservanza della costraint della statelessness.

Ora a te la parola: questa soluzione ti è piaciuta? Può essere migliorata? Lascia anche tu un commento!