IdentityServer4 autoryzacja i uwierzytelnienie w ASP.NET Core – Wstęp

Na przestrzeni kilku ostatnich lat aplikacje internetowe, czy mobilne ewoluowały i stały się bardziej kompleksowe, bardziej przyjazne użytkownikom i tym samym bardziej intuicyjne. Aby sprostać nowym wymaganiom dotychczasowe dobrze znane podejścia do autoryzacji i uwierzytelnienia musiały przejść znaczące zmiany. Zazwyczaj mieliśmy do czynienia z bezpośrednią autoryzacją w naszej aplikacji za pomocą LDAP czy ASP.NET Identity, Użytkownik logował się do Naszej aplikacji i korzystał z jej zasobów. Niestety w dużych firmach lub tych zwinniejszych częstym przypadkiem jest posiadanie kilku aplikacji, wielu zasobów online, do których użytkownik musiał się niezależnie logować aby móc normalnie pracować. Powodowało to konieczność posiadania kilku kont i haseł co przysparzało wielu problemów.  We najbliższych wpisach chciałbym przedstawić bardziej praktyczne podejście jakie dostarcza nam ASP.NET Core i IdentityServer 4 wraz z protokołami OAuth2.0 i OpenID Connect.

Dla lepszego zrozumienia nowego podejścia do autoryzacji i uwierzytelnienia za pomocą IdentityServer 4 najlepiej załóżmy sobie przykładowy use case z jakim najczęściej spotkamy się w pracy. A mianowicie mamy aplikację, która za pomocą interfejsu REST API udostępnia naszym użytkownikom funkcjonalność np: zarządzania kosztami i przychodami mikro-przedsiębiorców.. Dodatkowo pod opieką mamy aplikację SPA, która korzysta z tego zasobu. Wiemy, że w przyszłości dojdą dodatkowe niezależne moduły. także trzeba być elastycznym. W dalszej części będę tworzył według mnie najbardziej odpowiednie do tego casa rozwiązanie. Stworzymy moduł odpowiedzialny za autoryzację i uwierzytelnienie, użytkownik będzie logował się tylko raz aby uzyskać dostęp do naszych zasobów API. Postaram się także w trakcie pracy nad tym mini projektem przedstawić możliwe podejścia i wyjaśnić najistotniejsze kwestie. Także zaczynajmy.

Troszkę teorii na początek.

Wiem, ze przed chwilą pisałem, że czysta teoria jest niezrozumiała i wolę praktyczne podejście, ale jakieś podstawowe pojęcie powinniśmy mieć. Inaczej się nie da. Dzięki tym kilku punktom łatwiej zrozumiesz o co tu ta naprawdę chodzi. Dla osób z zerowym doświadczeniem rozjaśni to wiele kwestii – no może lekko przybliży.

 

Co to jest ta autoryzacja i uwierzytelnienie?

Często spotykany problem;) Mylimy znaczenie tych pojęć dlatego szybkie przypomnienie. Uwierzytelnienie to proces  walidacji danych personalnych naszego użytkownika. Sprawdzamy czy osoba, która się loguje faktycznie jest tą osobą za którą się podaje. Natomiast autoryzacja to proces nadawania dostępów do naszych zasobów za pomocą ról, polis itp. Sprawdzamy czy zalogowany użytkownik może uzyskać dostęp do takich czy innych zasobów naszej aplikacji. Przykładowo, w naszym przypadku będzie proste sprawdzenie czy może korzystać z modułu do zarządzania kosztami i przychodami.

Co to jest IdentityServer 4 i do czego go potrzebujemy?

A mianowicie w aplikacji chciałbym mieć tylko jedno miejsce w którym użytkownicy będą uwierzytelniani oraz miejsce, w którym będą przechowywane wszystkie zasady na których udostępniane im są zasoby naszej aplikacji. Nie chcę aby w momencie rozszerzenia funkcjonalności naszej aplikacji konieczne było dorabianie kolejnego providera danych. Użytkownik loguje się raz i ma prosty dostęp do wszystkiego. I tu z pomocą przychodzi nam IdentityServer 4.

IdentityServer 4 jest open source-ową implementacją protokołów OAuth2.0 i OpenID dla aplikacji opartych o .NET Core i .NET. Dzięki niemu nie musimy implementować całej masy rzeczy wymaganych przez powyższe protokoły, tylko korzystamy już z gotowych funkcjonalności. W dalszej części bardziej szczegółowo to opiszę. Na ten moment trzeba wiedzieć, że IdentityServer 4 zapewnia nam kompleksowo autoryzację użytkowników, aplikacji do naszych zasobów. Ważne aby także wiedzieć, że IdentityServer 4 nie zapewnia nam uwierzytelnienia! Do tego wykorzystamy mechanizm dostarczony nam przez framework ASP.NET Core Identity. Dzięki tym dwóm bezpłatnym narzędziom, bez zbędnego wysiłku do naszej dyspozycji oddane zostają gotowe usługi do logowania, rejestracji, obsługi konta użytkownika, muti-factor authentication, czy też obsługa SMS-ów. Zupełnie za free:) Troszkę haseł rzuciłem OAuth2.0, OpenID, a więc co to jest?

Czym jest OAuth2.0?

Czy kiedykolwiek logowałeś się do konta Google? Dodawałeś posta na Instagrama? A może kiedykolwiek sherowałeś linka na swojej tablicy na facebook? Te kilka przykładów to właśnie wykorzystanie OAuth2.0  Ogólnikowo rzecz biorąc protokół ten pozwala na niezawodną i bezpieczną wymianę informacji pomiędzy aplikacjami.

Najważniejsze przypadki użycia OAuth2.0 to:

  • Pozwala użytkownikom zalogować się do naszej aplikacji za pomocą innego konta. Często widzimy, że niektóre aplikacji pozwalają logować się nam za pomocą naszego konta np. na Facebook lub Google. Tą funkcjonalność zapewnia nam właśnie oAuth2.0. Profesjonalnie nazwane to jest „Federated identity”
  • Pozwala jednej usłudze na dostęp do zasobów innej usługi. Przykładowo jedna z aplikacji może poprosić o udostępnienie jej twoich zdjęć na Google drive lub na publikowanie postów w twoim imieniu na Facebook. Tą operację z kolei nazywamy „Delegated authority”.

Federated identity: jest to koncepcja, która umożliwia jednemu usługodawcy (w naszym przypadku to nasza aplikacja) na uwierzytelnienie użytkownika za pomocą innego usługodawcy (np: Google). Dzięki temu użytkownik ma tylko jedno konto oraz miejsce gdzie są przechowywane jego dane. Nie musi zakładać konta u każdego z osobna.

Delegated authority: jest to koncepcja, która daje możliwość uzyskania dostępu do zasobów użytkownika przez usługę lub inną aplikację

Dla lepszego zobrazowania kilka przykładów z życia:

  • Możesz zalogować się na StackOverflow za pomocą konta Google 😉
  • Linkedin często proponuje Ci dodanie swoich kontaktów z Google
  • Możesz publikować tweety z aplikacji mobilnej Twittera

OAuth 2.0 role

Poniżej lista roli jakie odgrywają poszczególni uczestniczy w procesie autoryzacji pomiędzy sobą:

  • Właściciel zasobów (resource owner) – jest to osoba będąca właścicielem zasobu. W większości przypadków można powiedzieć, że jest to końcowy użytkownik,
  • Serwer autoryzacyjny (Authorization server) – Serwer, który wydaje tokeny dostępu dla klienta. Jest to także jednostka, która uwierzytelnia właściciela zasobu i przyznaje autoryzację,
  • Klient (Client) – Aplikacja, która chce uzyskać dostęp do zastrzeżonych zasobów,
  • Serwer zasobów (resource server) – Serwer przechowujący zasoby właściciela zasobów,

Podstawowy proces:

Poniżej opis jak wygląda ogólny proces uwierzytelnienia i autoryzacji:

  1. Klient żąda autoryzacji od właściciela zasobów. Może to zrobić na dwa sposoby, poprzez bezpośrednie przekazanie przez właściciela zasobów, lub poprzez przekierowanie do serwera autoryzacyjnego z linkiem zwrotnym,
  2. Klient otrzymuje upoważnienie reprezentujące właściciela zasobu. OAuth2.0 zapewnia 4 różne zezwolenia. Typ upoważnienie zależy od metody używanej przez klienta do żądania autoryzacji oraz typów obsługiwanych przez serwer autoryzacyjny
  3. Klient używa otrzymanego upoważnienie i żąda tokena dostępu przez punkt końcowy na serwerze autoryzacji
  4. Serwer autoryzacyjny, uwierzytelnia klienta i waliduje upoważnienie. Jeżeli jest ok przekazuje token dostępu
  5. Klient wykorzystuje token do pobierania danych
  6. Serwer zasobów waliduje token i jeżeli jest ok zwraca zasoby

Dzięki IdentityServer 4 i ASP.NET Core nie będziemy musieli implementować tego wszystkiego sami.

Rodzaje klientów:

  • Confidential clients – klient, który jest w stanie zapewnić bezpieczeństwo naszym danym personalnym w naszym przypadku to może być aplikacja ASP.NET Core
  • Public clients – klient, który nie jest zdolny do zapewnienia bezpieczeństwa naszym danym uwierzytelniającym np: aplikacja SPA na angularze

Authorization Grants:

OAuth2.0 udostępnia nam cztery podstawowe typy procesów, które klient może wykorzystać w celu uzyskania upoważnienia do pobrania tokenu dostępowego.

Authorization Code:

Proces poprzez kod autoryzujący jest oparty na przekierowaniach. Co oznacza, że serwer autoryzacji służy jako pośrednik między klientem a właścicielem zasobu. W tym procesie klient przekierowuje właściciela zasobów do serwera autoryzacji za pośrednictwem user-agent.. Po otrzymaniu zgody właściciela zasobu zostaje przekierowany do klienta  z kodem autoryzacyjnym także przez user-agent-a

response_type = code

Authorization Code

  1. Właściciel zasobów jest przekierowany poprzez user-agent do serwera autoryzacyjnego. W kliencie za pomocą którego się łączymy zawarte są zmienne konfiguracyjne jak : identyfikator klienta, żądany zakres (scope), stan lokalny, link przekierowujący do którego serwer autoryzacji przekieruje klienta po przyznaniu lub odmowie dostępu.
  2. Serwer autoryzacji uwierzytelnia właściciela zasobu za pośrednictwem user-agenta. Właściciel zasobu następnie przyznaje lub odrzuca żądanie dostępu klienta do zasobów za pośrednictwem strony na której wyświetlone są zasoby (W przypadku googla jesteśmy pytani np: o dostęp klienta do naszego profilu, zdjęć itp).
  3. W przypadku, gdy właściciel zasobu udziela dostępu, serwer autoryzacji przekierowuje user-agenta z powrotem przy użyciu linka przekierowującego podanego wcześniej w parametrze zapytania: redirect_uri. Identyfikator URI przekierowania zawiera w parametrze kod autoryzacji  i stan podany przez klienta w pierwszym kroku.
  4. Klient żąda tokena dostępu z punktu końcowego serwera autoryzacji, dołączając kod autoryzacji otrzymany w poprzednim kroku. Klient uwierzytelnia się również za pomocą serwera autoryzacji. W celu zweryfikowania żądanie zawiera również identyfikator URI przekierowania używany do uzyskania kodu autoryzacji.
  5. Serwer autoryzacji uwierzytelnia klienta, weryfikuje kod autoryzacji i zapewnia, że odebrany link przekierowujący  jest zgodny z linkiem przekierowującym użytym do przekierowania klienta w trzecim kroku. Jeśli jest prawidłowy, serwer autoryzacji odpowiada tokenem dostępu i opcjonalnie tokenem odświeżania

Authorization Code zapewnia bardzo wysoki poziom bezpieczeństwa, ponieważ

  1. poświadczenia właściciela zasobu nigdy nie są ujawniane klientowi,
  2. jest to proces oparty na przekierowaniu,
  3. klient uwierzytelnia się na serwerze zasobów
  4. token dostępu jest przesyłane bezpośrednio do klienta.
Implicit Grant:

Jest uproszczoną wersją Authorization Code, w której klientowi wydaje się token dostępu bezpośrednio za pośrednictwem autoryzacji właściciela, zamiast wydawania nowego żądania przy użyciu kodu autoryzacji.

response type = token

Implicity Grant

  1. Klient inicjuje proces i kieruje użytkownika  przez user agent do serwera autoryzacyjnego. Żądanie zawiera identyfikator klienta, zakres, stan lokalny, który zostaje  zachowany, oraz link przekierowujący, do którego serwer autoryzacji odeśle użytkownika klienta po przyznaniu dostępu.
  2. Serwer autoryzacji uwierzytelnia właściciela zasobu za pośrednictwem klienta. Właściciel zasobu następnie przyznaje lub odrzuca żądanie dostępu klienta, zwykle za pośrednictwem strony z żądanymi dostępami do zasobów
  3. W przypadku, gdy właściciel zasobu udziela dostępu, serwer autoryzacji kieruje go z powrotem do user agenta za pomocą linka przekwaterowującego. Jednocześnie do fragmentu URL doklejany jest token dostępu.
  4. User agent postępuje zgodnie z instrukcjami przekierowania i wysyła żądanie do zasobu klienta hostowanego w Internecie (strona internetowa). Zazwyczaj jest to strona HTML ze skryptem do wyodrębnienia tokena z linka
  5. Strona internetowa wykonuje skrypt i wyodrębnia token dostępu i zwraca do user agenta
  6. User agent ostatecznie przekazuje token dostępu do klienta

Implicit Grant: jest zoptymalizowany pod klientów publicznych, którzy zazwyczaj działają w przeglądarce jak pełne aplikacje Javascript. Nie ma osobnego żądania otrzymania tokena dostępu, co czyni go nieco bardziej responsywnym i wydajnym dla takiego rodzaju klientów. Z drugiej strony nie obejmuje uwierzytelniania klienta, a token dostępu jest widoczny bezpośrednio w linku.

Resource Owner Password Credentials:

grant_type = password

Resource Owner Password Credentials

Jest bardzo uproszczonym, bezkierunkowym procesem, w którym właściciel zasobów podaje klientowi swoją nazwę użytkownika i hasło, a sam klient używa ich do bezpośredniego pobrania tokena dostępu z serwera autoryzacji.

  1. Właściciel zasobu podaje klientowi swoją nazwę użytkownika i hasło.
  2. Klient żąda tokena dostępu od serwera autoryzacji, podając poświadczenia podane przez właściciela zasobu. Podczas żądania klient uwierzytelnia się na serwerze autoryzacji.
  3. Serwer autoryzacji uwierzytelnia klienta i sprawdza poświadczenia właściciela zasobu. Jeśli wszystkie są prawidłowe, wydaje token dostępu.

Resource Owner Password Credentials jest odpowiedni tylko dla zaufanych klientów oraz gdy inne typy nie są dostępne (np. Nie można używać tego typu dla klienta przeglądarkowego).

Client Credentials Grant:

jest uproszczonym typem, który działa całkowicie bez właściciela zasobu (można powiedzieć, że to klient JEST właścicielem zasobu).

grant_type = client_credentials

Client Credentials Grant

  1. Klient uwierzytelnia się na serwerze autoryzacji i żąda tokena dostępu.
  2. Serwer autoryzacji uwierzytelnia klienta i jeśli jest ok, wydaje token dostępu

Jest powszechnie używany, gdzie klient działa we własnym imieniu. Bardzo częstym przypadkiem użycia jest komunikacja między wewnętrznymi mikro-usługami. Klient MUSI być także klientem poufnym.

Typy tokenów:

W poprzednich akapitach wspomniałem o dwóch rodzajach tokenów jak „token dostępu” (access token)  i „token odświerzający” (refresh token). Token odświeżający może być zwrócony tylko przez proces typu Authorization Code i  Resource Owner Password Credentials. Pewnie się zastanawiasz, jaka jest pomiędzy nimi różnica:

  • Token dostępu służy do uzyskiwania dostępu do chronionych zasobów i reprezentuje autoryzację wydaną klientowi. Zastępuje różne konstrukcje autoryzacji (np. Nazwę użytkownika i hasło) pojedynczym tokenem zrozumiałym dla serwera zasobów.
  • Token odświeżania, który jest również wysyłany do klienta przez serwer autoryzacji, służy do uzyskania nowego tokena dostępu, gdy bieżący token stanie się nieważny lub wygaśnie. Jeśli serwer autoryzacji wyda token odświeżania, jest on uwzględniany podczas wydawania tokena dostępu. Token odświeżania może być używany tylko przez serwer autoryzacji.

OpenID Connect:

Opisując OAuth2.0 powiedziałem wcześniej, że jego celem jest wydanie tokenu dostępu do chronionych zasobów. Innymi słowy OAuth2.0 zapewnia nam za pomocą OAuth2.0 tylko autoryzację. Nie dostarcza nam uwierzytelnienia. Rzeczywisty użytkownik nigdy nie jest uwierzytelniany bezpośrednio w kliencie. Tokeny dostępowe udostępniają nam tylko pseoudo-uwierzytelnienie bez wpływu na tożsamość. Pseudo-uwierzytelnienie nie zapewnia informacji o tym, kiedy, gdzie i jak nastąpiło uwierzytelnienie. I właśnie w tym miejscu do gry wchodzi OpenID.

OpenID connect to tzw. warstwa tożsamości w protokole OAuth2.0. Umożliwia klientom weryfikację tożsamości użytkownika końcowego na podstawie uwierzytelnienia wykonywanego przez serwer autoryzacji. Uzyskuje podstawowe informacje o profilu użytkownika w sposób podobny do REST. Wykorzystuje oświadczenia do przekazywania informacji o użytkowniku i rozszerza protokół OAuth.

Ponizej podstawowi aktorzy w OpenID connect:

  • End-User: Odpowiednik użytkownika, w OAuth2.0 nazywamy go właścicielem zasobów
  • Relying Party: w OAuth2.0 jest to aplikacja kliencka. Wymaga do działania uwierzytelnionego End-Usera i jego claimsów
  • Identity Provider: w OAuth2.0 jest to serwer autoryzacyjny który uwierzytelnia End-Usera i przekazuje jego claimsy,
  • Identity Token: jest to JWT zawierający claimsy użytkownika, które to posiadają informacje o uwierzytelnieniu.

Ponieważ OpenID Connect jest oparty na OAuth 2.0, wykorzystuje tylko niektóre procesy OAuth 2.0. W rzeczywistości OpenID Connect może śledzić proces autoryzacji, Implicit i Hybrid, który jest kombinacją dwóch poprzednich. Procesy są dokładnie takie same, z tą różnicą, że identyfikator jest wydawany razem z kluczem dostępu. To, czy proces jest czystym OAuth 2.0, czy OpenID Connect, zależy od obecności w żądaniu autoryzacji zakresu „openid”.

Token tożsamości & JWT

Token tożsamości zawiera informacje o uwierzytelnieniu i jest zwracany jako JSON, którego wszyscy znają jako JWT czyli JSON Web Token. JWT jest to otwarty standard prezentowania claims-ów, które można bezpiecznie przenosić pomiędzy aplikacjami. Bezpiecznie, to znaczy, że są podpisane cyfrowo, że dane są zaufane i że nie ma żadnych zmian w trakcie przesyłania pomiędzy aplikacjami. JWT mogą być wysyłane poprzez URL żądania POST lub nagłówka HTTP. Są sprawdzane lokalnie przez serwery zasobów przy użyciu klucza serwera autoryzacji. Ważne aby pamiętać o tym, że token jest wysyłany z serwera autoryzacji i normalnie, gdy zostanie wysłany do serwera zasobów, będzie musiał odesłać go z powrotem do serwera autoryzacji w celu weryfikacji!

Claimsy i zakresy:

Ostatnio często wspominałem o jakiś claims-ach, teraz przyszedł czas na szybkie wyjaśnienie. Claimsy są to informacje w postaci klucz-wartość na temat naszego użytkownika. Zakresy służą do pobierania całych zestawów claims-ów. Podstawowym i obligatoryjnym zakresem jest właśnie „OpenId”. W próżniejszych częściach zauważysz, że każde requesty wysyłane w procesie będą zawierały właśnie ten zakres. Standardowe zakresy możesz znaleźć pod tym linkiem OpenId claims

I tyle z teorii. W następnej części zabieram się za implementację.

Poniżej kilka linków,które mogą być pomocne:

  • Dokumentacja oAuth2.0 – link
  • OpenID Connect – link
  • IdentityServer 4 – link

 

No comment yet, add your voice below!


Add a Comment

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *