Czy serwer może być klientem i resource serwerem jednocześnie?

Czy serwer może być klientem i resource serwerem jednocześnie?
pankeny
  • Rejestracja:prawie 7 lat
  • Ostatnio:4 miesiące
  • Lokalizacja:Kraków
  • Postów:34
0

Cześć Wszystkim,

Na pytanie z tematu wątku (myślę) znam odpowiedź, jeden serwis może być OAuth2 Clientem i Resource Serverem. Pytanie czy jest to poprawne podejście.

Trochę kontekstu.
Robię 4fun projekt template, który posłuży jako szybki bootstrap do budowy aplikacji webowych opartych na React i Spring Boot. W założenia wpisałem sobie, że taki template to prosty UI z formularzami do logowania/rejestracji za pomocą kredek jak i zewnętrznych IDP, oraz zabezpieczenie routeów po stronie frontu i endpointów po stronie backendu.

Teraz problem, który chce rozwiązać i jak sobie to wyobrażam.
Na początku stwierdziłem, że mój pojedynczy serwer będzie zarówno klientem jak i resource serverem. Nawet zacząłem iść w tym kierunku, widziałbym to tak, że na froncie pod przyciskiem logowania za pomocą zewnętrznego IDP np. Google kryje się zwykły link do backendu z konkretną końcówką. Użytkownik jest przekierowany do zewnętrznego IDP i wraca z informacją czy uwierzytelnienie się powiodło. W Spring Security przechodzimy do Success Handlera, który mi wygeneruje JWT w claimsach umieszczam token, z którym użytkownik wrócił od zewnętrznego dostawcy. Użytkownik wraca skąd przyszedł i zapisuje się JWT. Potem przy każdym strzale na backend JWT jest walidowany, myślałem, też żeby odpytać ext IDP o poprawność tokenu, z którym wrócił użytkownik, i którego zapisaliśmy w claimsach (overkill?).
Plus, minus takbym widział high-level flow.
Jednak coraz bardziej mam przekonanie, że klientem OAuth2 powinien być front, a backend jako resource server potwierdzać poprawność tokenu, z którym przychodzi użytkownik. W tym podejściu backend nie musi być dostępny dla świata, np. mogę mieć dwa kontenery, które są w jednej sieci wirtualnej ale tylko front jest wystawiony na zewnątrz. W pierwszym podejściu backend musiałby być dostępny dla świata, aby móc przekierować usera z poziomu backendu do zewnętrznego dostawcy tożsamości.

Stąd też następujące pytania:

  1. Jakie podejście jest lepsze i dlaczego? Jeżeli odpowiedzią jest "To zależy", to od czego zależy?
  2. Jeżeli Client i Resource Server na backendzie to jak zarządzać JWT? Czytałem, że token, który wydaje zewnętrzny IDP nie powinien być używany po stronie backendu, stąd też pomysł żeby generować nowy JWT a w claimsach zawrzeć ten od IDP w celu podwójnej walidacji.
  3. Jeżeli Client i Resource Server na backendzie to czy przedstawione flow jest w porządku? Może ktoś zna jakieś przykładowe repo.
  4. Jeżeli Client na Frontendzie, to czy są rzeczy, na które trzeba zwrócić uwagę z powodu, że tutaj jest to publiczny klient, który nie przechowuje żadnych secretów.

Link do repo: https://github.com/malochak/react-spring-tailwind-auth

edytowany 2x, ostatnio: Riddle
IV
  • Rejestracja:ponad 4 lata
  • Ostatnio:około miesiąc
  • Postów:30
2

Na wstępie chciałbym napisać, że musisz wejść w kod źródłowy springa aby zrozumieć jak to działa. W innym przypadku będziesz wynajdował koło na nowo, kleił kod aby się odpalał, a potem okaże się, że słabo to działa. Niestety, kiedyś zostałem wrzucony do projektu, gdzie ktoś zaimplementował własne tokeny, i nazwał je JWT, chociaż nimi nie były, takie tak security by obscurity, które kopało nas w ...

Dobra, teraz czas na konkrety:

  • W OAuth2/OpenID Connect wyróżniamy 4 role:
    • User - czyli najczęściej człowiek, fizyczny byt
    • Client - aplikacja, która w imieniu user-a wykonuje akcje, w tym przypadku aplikacja frontendowa
    • Resource server - zasób, czyli w tym przypadku aplikacja backendowa z REST Api, do której uderza client
    • Authorization server - wystawca tokenów JWT, do którego uderza resource server w celu sprawdzenia uprawnień, może nim być np. Google, Facebook, Keycloak
  • Aby aplikacja springowa działała jako resource server to dodajemy do niej zależność spring-boot-starter-oauth2-resource-server
  • Aby aplikacja springowa integrowała się z zewnętrznym authorization server to dodajemy do niej zależność spring-boot-starter-oauth2-client
  • Widzę, że w springu pojawiła się nowa zależność spring-boot-starter-oauth2-authorization-server, niestety jest to dla mnie coś nowego i jeszcze nie miałem okazji tego zbadać, proponuję Tobie przyjrzeć się temu
  • JWT powinny być podpisane, jakakolwiek zmiana zawartości tokena jest przez to niemożliwa.
  • JWT posiadają pole iss (issuer), jest to wystawca tokena, czyli authorization server, jeśli Google go wystawia, to siebie tam wpisuje, jeśli Ty wystawiasz token, to wpisujesz swój identyfikator systemu (lub odnośnik do niego).
  • Jeden resource server może być zintegrowany z wieloma authorization server-ami, to po polu iss backend rozróżnia, kto wystawił token i który authorization server ma się odpytać.
  • JWT wystawione przez authorization server wracają do resource server, tam resource server może odczytywać claims, stosować odpowiednie walidacje do uprawnień, ale same tokeny są niezmienne i przesyła je w odpowiedzi client-owi, a ten klient w kolejnych żądanich używa tego samego tokenu (nagłówek Authorization: Bearer <token>)
  • Można łączyć ze sobą różne role w jednej aplikacji, często resource server jest łączony z authorization server

Podsumowując próbujesz osiągnąć coś hybrydowego, chcesz użyć zewnętrznego authorization server (Google) z jego użytkownikami, a także rejestrować własnych użytkowników. W drugim przypadku chcesz utworzyć własny authorization server. Po udanej rejestracji użytkownika powinieneś stworzyć własny token z odpowiednią wartością pola iss oraz go podpisać własnym kluczem. Dobrym pomysłem będzie przyjrzenie się klasie JwtIssuerAuthenticationManagerResolver oraz poszukanie artykułów i filmików, gdzie prezentacje prowadzi autor implementacji OAuth2 w springu Josh Cummings.
Możesz też wystawić własny authorization server, np. Keycloak i zintegrować się z nim. Keycloak posiada także także swoje REST api, za pomocą którego możesz utworzyć nowych userów zamiast zapisywać ich we własnej bazie danych.

pankeny
  • Rejestracja:prawie 7 lat
  • Ostatnio:4 miesiące
  • Lokalizacja:Kraków
  • Postów:34
0
ivo napisał(a):

Podsumowując próbujesz osiągnąć coś hybrydowego, chcesz użyć zewnętrznego authorization server (Google) z jego użytkownikami, a także rejestrować własnych użytkowników. W drugim przypadku chcesz utworzyć własny authorization server. Po udanej rejestracji użytkownika powinieneś stworzyć własny token z odpowiednią wartością pola iss oraz go podpisać własnym kluczem. Dobrym pomysłem będzie przyjrzenie się klasie JwtIssuerAuthenticationManagerResolver oraz poszukanie artykułów i filmików, gdzie prezentacje prowadzi autor implementacji OAuth2 w springu Josh Cummings.
Możesz też wystawić własny authorization server, np. Keycloak i zintegrować się z nim. Keycloak posiada także także swoje REST api, za pomocą którego możesz utworzyć nowych userów zamiast zapisywać ich we własnej bazie danych.

Dziękuje za odpowiedź. Pytaniem pozostaje dalej, czy user ma być przekierowany do zewnętrznego IDP (Authorization Servera) z poziomu frontu, czy backendu?
Jeżeli mam użytkownika, który się uwierzytelnia z pomocą loginu i hasła to wiadomo, że uderza na backend w celu zweryfikowania poprawności tej pary. Jeżeli mamy użytkownika, który rejestrował się z pomocą Google jako zewnętrznego IDP, to czy klientem jest tutaj front, czy backend? Skąd użytkownik będzie przekierowany, żeby Auth Server (w tym przypadku Google) wystawił ten token, który będzie weryfikowany z poziomu backendu (Resource Servera)

IV
  • Rejestracja:ponad 4 lata
  • Ostatnio:około miesiąc
  • Postów:30
1

W Twoim przypadku w kontekście oauth2 Twój frontend zawsze jest klientem, w kontekście relacji pomiędzy usługami (czyli poza kontekstem oauth2) Twój backend jest klientem dla serwisu Google. Stąd też nie należy mieszać tych dwóch znaczenia słowa klient, bo to zależy od kontekstu.
Dawno nie bawiłem się oauth2, więc mogę w czymś się mylić, ale postaram się odpowiedzieć:
Czy przekierowanie do authorization server powinno być z poziomu backendu czy frontendu? To zależy jak ustawisz w authorization server. O ile pamiętam jeśli konfigurujemy Keycloak jako authorization server to można było wybrać rodzaj klienta (klient w znaczeniu usługa, która łączy się z serwisem). Jeden rodzaj to public, a drugi to confidential. Pierwszy służy do frontendów renderowanych po stronie przeglądarki (Angular, React), a drugi do usług backendowych. W pierwszym flow przekierowań jest zarządzanych przez kod frontendu, a w drugim przez kod backendu.
O ile pamiętam ze względu na bezpieczeństwo preferowana jest druga opcja, gdzie należy oprogramować tak logikę na frontendzie, aby obsługiwała przekierowania zwracane przez backend. Wtedy authorization server będzie komunikował się z Twoim backendem, a backend będzie zarządzał flow w kontekscie oauth2. A od strony backendu prawdopodobnie nic nie trzeba bedzie oprogramowywać, wystarczy odpowiednia konfiguracja w application.yml.
Proponuję Tobie poszerzyć wiedzę teoretyczną nt. oauth2 oraz openid connect. Mimo że to są dwa różne terminy często są zamiennie używane, w zależności kto opisuje dane zagadnienie.
Kolejna sprawa to taka, że jeśli piszesz sobie własny template aplikacji to głupio byłoby, abyś zrobił coś co potem będziesz powielał w swoich aplikacjach, a będzie to zrobione w sposób niezgodny z ogólnymi zasadami bezpieczeństwa. Dlatego podstawy teoretyczne są tutaj bardzo potrzebne. Głupio będzie np. jak napiszesz aplikację frontendową w Reakcie i wkompilujesz w nią swój client-id i client-secret. A takie rzeczy zdarzają się, jak ludzie nie rozróżniają typu klienta w Keycloak pomiędzy public a confidential.

pankeny
ma to sens, dzięki!
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)