• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Nietypowe logowanie do API (JWT, API Platform)

Object Storage Arubacloud
0 głosów
498 wizyt
pytanie zadane 5 czerwca 2019 w PHP przez Assasz Nałogowiec (30,460 p.)
Witam,

Aktualnie piszę aplikację w API Platform, gdzie posiadam dwie encje: User i Client. User jest użytkownikiem uprawnionym do korzystania z API, a Client jest zwyczajnym klientem serwisu (który może się zarejestrować i logować). Każdy Client jest powiązany z Userem. Autoryzację usera mam zrobioną przy użyciu JWT, a teraz muszę zrobić logowanie klienta. No i nie wiem, jak się za to zabrać.

Myślałem o tym, aby po udanym logowaniu klienta zapisywać w tokenie dodatkowo jego id, a następnie zwracać nowy token do użytkownika. Nie wiem jednak, czy to dobry pomysł, ani jak coś takiego zrobić (nie znalazłem żadnych informacji na ten temat).

Proszę o jakieś łopatologiczne rozwiązanie tego problemu, nie pogardzę również linkami.
komentarz 5 czerwca 2019 przez NowyUrzydgownig Mądrala (5,090 p.)
Nie lepiej nadać ci po prostu uprawnienia dla Usera, zamiast tworzyć nowy byt w swoim systemie? Rozumiem, że masz problem, bo nie wiesz jak rozpoznać czy to klient czy to user? Stwórz sobie po prostu osobny endpoint dla klienta i usera do logowania.
komentarz 5 czerwca 2019 przez Assasz Nałogowiec (30,460 p.)
Tak został zaprojektowany system i nie mam na to wpływu, ja tylko piszę do tego API. Mam osobne endpointy - standardowy /login_check dla usera i /clients/login dla klienta. Nie wiem po prostu, co mam właściwie zrobić w /clients/login, jak powinno wyglądać takie logowanie. /login_check zwraca token dla loginu i hasła usera, dopiero mając ten token można wywołać akcję /clients/login.
komentarz 5 czerwca 2019 przez NowyUrzydgownig Mądrala (5,090 p.)
Jeżeli nie wiesz jak odpytać zewnętrzne api, aby się zalogować - to nie jest nic uniwersalnego. Musisz poszukać w dokumentacji API z którego korzystasz.
komentarz 5 czerwca 2019 przez Assasz Nałogowiec (30,460 p.)

Nie korzystam z żadnego zewnętrznego API, ten proces logowania to właśnie to, co mam do zrobienia. 

Mam zaimplementowaną autoryzację usera przy użyciu JWT (https://api-platform.com/docs/core/jwt/), stąd posiadam endpoint /login_check, który zwraca token po podaniu loginu i hasła usera. Ten token jest wykorzystywany do wywoływania innych endpointów. 

Teraz chcę zrobić logowanie klienta, w tym celu przygotowałem endpoint /clients/login. Mój problem polega na tym, że nie wiem, jak takie logowanie zrobić. Czy powinienem np. pobrać aktualny token usera, przeprowadzić autentykację (sprawdzenie poprawności hasła klienta itp.), a następnie dokleić do tego tokena ID klienta i zwrócić nowy token? Aktualny payload tokena to coś takiego:

{
  "iat": 1559743194,
  "exp": 1559746794,
  "roles": [
    "ROLE_ADMIN"
  ],
  "username": "bileter" // to jest login Usera
}

Myślałem, aby zrobić coś takiego (chociaż swoją drogą też nie wiem, jak coś takiego dodać):

{
  "iat": 1559743194,
  "exp": 1559746794,
  "roles": [
    "ROLE_ADMIN"
  ],
  "username": "bileter"
  "idClient": 123
}

 

komentarz 6 czerwca 2019 przez NowyUrzydgownig Mądrala (5,090 p.)
Powinieneś mieć w takim razie jeden token - generowany tą samą metodą i trzymać po prostu role, albo jakąś własną wartość.
komentarz 6 czerwca 2019 przez Assasz Nałogowiec (30,460 p.)

A jak mogę dodać jakąś wartość do tokena z poziomu innego endpointa? Tutaj mam listę dostępnych eventów, ale żaden z nich nie pasuje do mojej sytuacji, co najwyżej chciałbym wywołać konkretny event dla mojej metody. Próbowałem też wstrzyknąć TokenStorage, ale tam nie ma chyba takiej możliwości.

Czyli klent wysyła postem na /clients/login swój adres email i hasło, ja sprawdzam poprawność hasła itp., a następnie dodaje do tokena te idClient i zwracam nowy token. Tak bym to widział mniej więcej.

komentarz 6 czerwca 2019 przez NowyUrzydgownig Mądrala (5,090 p.)
Nie wiem jak jest zbudowana aplikacja z którą pracujesz, więc sam musisz zdecydować jak to zrobić. Rozumiem, że teraz problemem jest wywoływanie odpowiednich eventów i coś w stylu rejestracji tokena w api z którym pracujesz, za którą ty już nie odpowiadasz.
komentarz 6 czerwca 2019 przez Assasz Nałogowiec (30,460 p.)

To może pokażę, co mam:

// serwis odpowiedzialny za autentykację

// ...

$jwt = $this->jwtManager->create($client->getUser());

$authContent = $this->authenticationSuccessHandler->handleAuthenticationSuccess($client->getUser(), $jwt)->getContent();
$token = json_decode($authContent, true)['token'];

return $token;

// jwtManager->create wywołuje event JWT_CREATED

// JWTCreatedListener

public function onJWTCreated(JWTCreatedEvent $event)
{
    $user = $event->getUser();

    if (!$user instanceof UserInterface) {
        return;
    }

    $payload = $event->getData();
    $payload['idClient'] = 'test'; // tutaj chciałbym dodać idClient, ale nie wiem, jak mogę przekazać klienta do tej metody

    $event->setData($payload);
}

 

komentarz 6 czerwca 2019 przez NowyUrzydgownig Mądrala (5,090 p.)
Możesz skorzystać z mechanizmu cache'owania. Tzn. ustaw w cache'u wartość jaką chcesz, a w metodzie onJWTCreated po prostu pobierz ją z cache. Czas życia tej danej w pamięci powinien być krótki np. 5s. Jeżeli nie używacie obecnie cache'a a ty nie możesz go dodać do projektu, to możesz w ostateczności zedytować event, aby nie zwracał on User'a a clienta, którego chcesz.
komentarz 6 czerwca 2019 przez Assasz Nałogowiec (30,460 p.)

W sumie to znalazłem inne rozwiązanie, chyba lepsze. Wykorzystując własnego UserProvidera najpierw pobieram instancję User, a przy logowaniu klienta instancję Client (który od teraz implementuje interfejs UserInterface). 

    public function loadUserByUsername($username)
    {
        if (filter_var($username, FILTER_VALIDATE_EMAIL)) {
            $user = $this->entityManager->getRepository(Client::class)->findOneBy(['email' => $username]);
        } else {
            $user = $this->entityManager->getRepository(User::class)->findOneBy(['login' => $username]);
        }

        if (!$user instanceof UserInterface) {
            throw new UsernameNotFoundException();
        }

        return $user;
    }

Nie wiem jeszcze, czy to ujdzie, ale póki co jest ok. W każdym razie dzięki za czas i podpowiedzi. 

2 odpowiedzi

0 głosów
odpowiedź 5 czerwca 2019 przez mrspock1 Mądrala (6,420 p.)
Z tego co pamiętam, ID użytkownika oraz fakt że jest zalogowany (zmienna logiczna) to są osobne zmienne niż zmienna sesji. Zmienna sesji ma powiązać część serwerową aplikacji z częścią kliencką i jest zapisywana na serwerze i na stronie w przeglądarce klienta. Jak przeglądarka wysyła zapytanie do serwera, to serwer wyszukuje po otrzymanej z przeglądarki  zmiennej sesyjnej część serwerową aplikacji i wtedy sprawdza potrzebne informacje takie jak zalogowanie. Nie wiem czy o to chodzi. Zmienne sesyjne raczej są generowane automatycznie przez bibliotekę gdy zaczynasz sesję i z nimi nie trzeba nic więcej robić. Sprawę trochę komplikuje to, że można jeszcze zapisać ją w cookies zamiast w ukrytym polu strony, a użytkownik który nie ma włączonego cookies powinien wciąż mieć możliwość używania programu przez zmienną sesji w ukrytym polu. Można też zmienne sesji podać w adresie strony ale to brzydko wygląda ja za adresem ciągnie się trzydzieści dziwnych znaków.
0 głosów
odpowiedź 5 czerwca 2019 przez Ehlert Ekspert (212,670 p.)
Po pierwsze podejrzewam że źle zamodelowałeś dane. User to user i powinien być jako jeden model (+/- dziedziczenie). Do tego korzysta się z

https://symfony.com/doc/current/security/guard_authentication.html
komentarz 5 czerwca 2019 przez Assasz Nałogowiec (30,460 p.)
Niestety nie ja modelowałem dane. Przygotowuję API dla dość archaicznego systemu i tam jest podział na dwa byty: User i Client. User to użytkownik uprawniony do wykonywania różnych działań, reprezentujący jakąś firmę i on posiada API key, a Client to zwyczajny użytkownik serwisu, powiązany z Userem. Do Usera mam podpiętą autentykację JWT (pisałem o tym wyżej w komentarzach) i nie wiem, jak połączyć z tym klienta (proces logowania do serwisu).

Podobne pytania

0 głosów
1 odpowiedź 441 wizyt
pytanie zadane 30 czerwca 2018 w JavaScript przez mi-20 Stary wyjadacz (13,190 p.)
+2 głosów
2 odpowiedzi 199 wizyt
pytanie zadane 29 grudnia 2023 w JavaScript przez NediCC Nowicjusz (140 p.)
0 głosów
1 odpowiedź 689 wizyt
pytanie zadane 8 lipca 2018 w JavaScript przez mi-20 Stary wyjadacz (13,190 p.)

92,568 zapytań

141,424 odpowiedzi

319,634 komentarzy

61,956 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...