Autentificare cu JWT si httponly cookies in React

Dezvolt un api in Symfony 4 si pentru front-end am ales React. Dupa multa documentare pe Google m-am hotarat sa folosesc JWT tokens si httponly cookies. Autentificarea functioneaza in felul urmator:

  • se incarca aplicatia, verific daca am state-ul in local storage;
  • daca nu-l am, atunci redirectionez utilizatorul la pagina de login;
  • daca il am, verific daca proprietatea autentificat din state este true (ca sa pot pastra un utilizator autentificat si daca reincarca pagina); in caz afirmativ redirectionez utilizatorul in pagina de administrare, in caz contrar il redirectionez la login;
  • dupa ce utilizatorul se autentifica cu succes, stochez in state numele de utilizator si setez proprietatea autentificat la true; de asemenea la fiecare actualizare de state scriu in local storage state-ul;
  • la dezautentificarea utilizatorului, elimin din local storage state-ul;

Ce parere aveti, este corect procesul urmat? Pot dormi linistit noaptea ca nu voi avea vizitatori nedoriti cu acces neautorizat?

Am ales httponly cookies pentru ca nu pot fi accesate din Javascript si se trimit automat la fiecare solicitare a browser-ului. De asemenea api-ul pentru backend si front-end-ul se afla pe acelasi domeniu.

Uite aici o solutie mai buna, zic eu:

Etapa 1 - Front-end:

  • La deschiderea aplicatiei faci un request catre back-end, un request GET fara niciun query. Sa spunem ca ruta este /who-am-i

Etapa 2 Back-end:

  • Cand primesti un request pe ruta /who-am-i, iei din request cookie-ul si il decodezi extragand id-ul user-ului din el. Apoi iei user-ul din baza de date si introci informatiile necesare despre el catre front-end, de exemplu nume sau alte date(dar nu si id-ul). Daca nu o sa gasesti niciun user atunci o sa returnezi undefined sau false.

Etapa 3 Front-end:

  • Cand primesti raspunsul de la back-end verifici: 1. Este undefined sau fals, daca da atunci il duci pe pagina de login. 2. Daca user-ul exista atunci salvezi in state-ul aplicatiei informatiile primite de la back-end.

UPDATE: Si ca sa blochezi user-ul sa intre pe paginile in care nu are autorizatie, atunci pe back-end pui un middleware pe fiecare ruta pe care vrei sa o securizezi, un middleware in care faci din nou Etapa 2

Cookie-urile este bine sa fie secure de asemenea, nu doar httponly, că sa forțezi sa meargă doar pe https.

Legat de etapa 1, la ce te referi prin request GET fara nici un query? Din etapa 2 inteleg ca fac un query pentru ca trimit id-ul user-ului in cookie.

Am incercat sa fac un request catre back-end la deschiderea aplicatiei React dar m-am lovit de problema urmatoare: componenta se desena in pagina de doua ori. Prima data cand se facea request-ul si a doua oara cand procesam raspunsul si actualizam state-ul. Prima afisare nu dura mult, dar era sesizabila.

La varianta cu middleware pe back-end nu m-am gandit.

Solutia propusa de mine nu este sigura?

Da, de acord.

nu ma pot lauda ca am folosit jwt-ul prea mult, da de ce ai nevoie de cookie-uri pt autentificare daca vrei sa folosesti jwt? practic ala il iei odata (la login) si dupa aia il trimiti in fiecare request in header.

Dupa ce il iau la login, trebuie sa il memorez cumva si folosesc un cookie. Sau cum pot sa il trimit la fiecare request? Si pentru mine este prima data cand folosesc.

il tii in storage. sau ii faci refresh la fiecare request (oricum trebuie sa fii atent cand expira si sa-i faci refresh)

Acel cookie trebuie sa fie httpOnly si secured, adica il poti controla doar din back-end, nu poti si nici nu trebuie sa il atingi cu codul de front-end.

Am facut un gist cu niste exemple de la un proiect al meu. Codul este scris in node.js dar principiul este acelasi. Toate fisierele sunt din back-end mai putin primul cel care are numele frontend.

Iti recomand sa te uiti peste ele in ordinea aceasta:

  1. signup.js - in acesta parte creezi contul + salvezi token-ul in cookie-urile din browser (linia 34)
  2. login.js - aici faci din nou ultima parte, cea cu setarea token-ului
  3. Frontend.js - codul asta se executa cand se porneste aplicatia: faci un request si apoi in functie de raspuns randezi continutul dorit(ai spus ca folosesti React.js, deci o sa fie la fel ca aici - adica faci client side rendering)
  4. middleware.js + whoIam.js - aici preiei toate request-urile si iei token-ul din acestea
  5. In final un exemplu cum securizezi lucrurile pe back-end ( securityLayer.js, si updateUser.js)

Aici este link-ul: JWT EX

P.S: Daca ai intrebari nu ezita.

1 Like

Multumesc pentru link, ma uit peste cod si revin daca am intrebari.

2 Likes

In final am refacut autentificarea intr-o varianta mai simpla si am lasat in seama Symfony autentificarea, astfel:

In backend am urmatoarele rute: autentificare/inregistrare/admin/logout care servesc un singur template in care se incarca aplicatia React.
Din cele 4 rute, doar ‘admin’ este protejata. La fiecare accesare fara autentificare, utilizatorul este redirectionat (de backend) catre pagina de login, altfel ramane pe ruta ‘admin’.

In React nu cred ca mai am nevoie sa protejez rutele, in componenta cea mai ‘inalta’ am urmatorul ‘switch’:

<Switch>
	<Route exact path="/admin" component={Dashboard} />
	<Route exact path="/autentificare" component={Login}/>
	<Route exact path="/inregistrare" component={Register} />
	<Route component={NoMatchView} />
</Switch>

La accesarea rutei ‘logout’ din pagina protejata se face redirectionare din backend catre pagina de autentificare dupa ce utilizatorul a fost delogat.

1 Like