[Devlog] Tydzień drugi. Łzy rozpaczy i wielka radocha. 403 Bad credentials, CORS, taka sytuacja.

Cześć. Post z opóźnieniem, bo powinien być w piątek, ale walczyłem do ostatniego dnia sprintu, żeby chociaż tą jedną rzecz zrobić. Zajęło mi to ponad tydzień i się udało. Budowa api w Symfony miała być prosta i przyjemna, ale pojawiły się problemy. O co chodzi? Czytaj dalej.

W ostatnim poście pisałem o procesie planowania i o zalążku, dosłownie, mojej aplikacji. Ten tydzień zszedł mi na jednym dużym problemie. Przez praktycznie cały tydzień zadawałem sobie pytanie „dlaczego do nie działa?” i wszelkie jego formy, również ze wstawkami z łaciny.

Najgorsze było to, że Stack Overflow milczał, a na dwa napisane posty na forum i na fejsie odpowiedzi były ubogie i tak naprawdę nikt nie wiedział o co chodzi i czemu nie działa.

Fakt jest taki, że roboczo spędziłem nad tym może z 8h, ale jednak jako, że nie pracuję nad tym projektem pełnoetatowo, wyszedł tydzień. Do rzeczy jednak.

Wszystko rozchodziło się o nagłówki CORS (na początku) a potem o JSON’a.

Co to jest CORS?

CORS to w uproszczeniu mówiąc dodatkowe nagłówki, które są wysyłane przez serwer, wyrażając zgodę na połączenie między dwiema różnymi domenami. I tak mój backend stoi na localhost:8000 a aplikacja Vue na localhost:8080, to tak naprawdę dwie różne domeny. I teraz kiedy z Vue wysyłam żądanie logowania występował błąd – 500 Bad Request z wiadomością „No Access-Control-Allow-Origin header is present on the requested resource. […]”. To właśnie świadczy o tym, że mój serwer backendowy odrzuca żądanie, bo nie akceptuje takich z innej domeny niż jego własna. Jest to rzecz jasna podyktowane względami bezpieczeństwa. Ten problem w Symfony można rozwiązać instalując np. NelmioCorsBundle, który pobieramy za pomocą Composera. Potem tworzymy plik nelmio_cors.yaml (w przypadku Symfony 4) w katalogu config/packages i wypełniamy w sposób podobny do mojego:

Dzięki temu serwer zwraca nagłówek Access-Control-Allow-Origin: * czyli akceptuje żądania ze wszystkich domen. Jeśli chcemy to ograniczyć, w linii allow_origin: [‚*’] zmieniamy zawartość tablicy na akceptowane przez nas domeny.

Okej. CORS załatwiony i przecież to takie proste. No fakt, tę sprawę rozwiązałem w powiedzmy 20 minut, bo też miałem delikatne problemy, ale to moje gapiostwo. Kolejny problem był z danymi do logowania.

403 Bad credentials

Taki błąd otrzymujemy w momencie, gdy przekazujemy błędne dane logowania. Niby spoko, pożądane działanie. Problem tylko, że podawałem poprawne dane! I to właśnie nad tym siedziałem tyle czasu, bo to było dziwne. Dopiero po pewnym czasie okazało się, że Vue przesyła te dane w formie JSON’a Symfony samo w sobie nie radzi sobie z nim. Serwer oczekiwał po prostu konkretnych dwóch parametrów: username i password.

Pogłówkałem, zasięgnąłem drobnej rady i problem w końcu rozwiązałem. Otóż utworzyłem listener, który nasłuchiwał na zdarzenie OnKernelRequest, sprawdzałem, czy zawartość żądania jest w formie JSON i jeśli tak, to podmieniałem request na dane oczekiwane przez moduł Security mojego webservice. Przykładowa zawartość takiego listenera:

Jak widać sprawdzam jeszcze czy żądanie dotyczy ścieżki logowania, nie ma potrzeby aby przerabiać każdy request. No jeszcze trzeba do services.yaml dodać coś takiego:

Od tej chwili po żądaniu do endpoint’a /api/login z moimi danymi, otrzymywałem token co było celem.

Autoryzacja za pomocą JWT – JSON Web Token

Wspomniałem o tokenie. Teraz w skrócie opiszę jak użyć JWT w Vue. Ogólnie jest to bardzo proste – kiedy odpowiedź serwera zawiera token (czyli logowanie przebiegło poprawnie), zapisujemy go do LocalStorage. Teraz za każdym razem, kiedy wysyłamy jakieś żądanie do serwera musimy dokleić nagłówek „Authorization” z naszym tokenem. Można tak robić ręcznie do każdego requesta, ale możemy też globalnie doklejać ów nagłówek. W tym celu w main.js dołączamy taki kod:

Kod jest bardzo prosty – przechwytujemy request przed jego wysłaniem i jeżeli istnieje token, to go do nagłówka go „Authorization” go wklejamy i wysyłamy. Następnie sprawdzamy odpowiedź i jeżeli jej kod to 400, 401, 402 albo 403 to robimy przekierowanie do strony z logowaniem, bo te kody oznaczają brak zalogowania.

Ogólnie jest jeszcze kwestia stanu globalnego aplikacji – czy user jest zalogowany czy nie, tutaj użyłem Vuex czyli implementacji Redux, ale o tym może innym razem.

Repozytoria są zaaktualizowane:

https://github.com/webkonstruktor/saver-frontend

https://github.com/webkonstruktor/saver-backend

Do następnego!

BTW. Zapomniałem odnowić certyfikat i przez parę dni był problem z dostaniem się na bloga za co przepraszam 😉