NET 6 WebAPI - powiązanie tokena z użytkownikiem

0

Witam.
Potrzebuje stworzyć mały serwer licencji do oprogramowania okienkowego. Stanęło na API, które będzie przechowywać odpowiednie informację, z którym to aplikacja okienkowa będzie się komunikować i podejmować odpowiednie decyzje.

Oprócz jakichś zmyślnych "kluczy", chciałbym też generować token, który byłby powiązany z danym klientem i jego licencją. Na tej podstawie również może działać wygaśnięcie licencji, ponieważ ważności tokena może o tym dodatkowo decydować.

Czy jedynym wyjściem jest wpisanie tokenów do bazy?

ID VAT_NUMBER EMAIL TOKEN EXPIRES_AT

Domyślam się, że wtedy też trzeba będzie zrobić jakiś middleware do atrybutu [Authorize], albo własny [CustomAuthorize], który to poprawnie zweryfikuje i zwróci odpowiedni response code.

Ogólnie to chciałbym, aby token nie był "uniwersalny", że dochodzi do niestandardowej autoryzacji. Żeby inny user nie mógł użyć tokena drugiego.

1

Dosyć to abstrakcyjne więc ciężko coś jasno odpowiedzieć. Masz na myśli używanie swojego własnego tokena? Rozważałeś użycie JWT? Wtedy nie będziesz musiał wpisywać tego do bazy, bo wszelkie potrzebne informacje możesz przekazać w formie claimów.

0

Najprostsze rozwiązanie: - W przypadku autoryzacji tokenem, przekazujesz ten jako login słowo np: "<Tokien>" zaś sam tokien przekazujesz jako hasło.
Więc tak jakby zostaje zalogowany użytkownik "<Tokien>".
W zasadzie jedyna zmiana w poleceniu SQL sprawdzającym czy można się zalogować.

0

Tak, JWT. Tylko czy takie dane nie są zbyt "otwarte"? Takie JWT można edytować za pomocą jwt.io/. Czy już za daleko z tym idę i nikt nie jest na tyle mądry? Robienie jakichś hashy na podstawie sprzętu chyba już jest staroświeckie. Wiem, że licencjonowanie "online" jest najlepsze, ponieważ niczego nie muszę hardcodować, a żaden cwaniak mi tego nie skopiuje i nie zacznie używać bez mojej wiedzy/zgody.

Stąd też dodatkowa weryfikacja za pomocą tokena. Czyli, aby oprogramowanie klientowi działało to potrzebuje mieć numer nip w bazie. W naszym przypadku jest on wpisany w Comarch Optima i ten sam nip byłby u nas w bazie API, email do powiadomień, ze subskrypcja się kończy no i token, który dodatkowo zabezpieczyłby sparowane oprogramowanie.

1
AdamWox napisał(a):

Tak, JWT. Tylko czy takie dane nie są zbyt "otwarte"? Takie JWT można edytować za pomocą jwt.io/.

No możesz edytować ale nic Ci to nie da xD

0

Skoro widzisz to jako dodatkowe zabezpieczenie to wszystko to nie ma żadnego sensu, tylko utrudnisz logowanie.

4

Tokeny JWT można edytować, ale wtedy ich sygnatura nie będzie się zgadzać. Innymi słowy, weryfikując token sprawdzasz czy został on przez Ciebie podpisany, i niezmieniony. Token podpisuje się za pomocą sekretu, i sprawdza za pomocą tegoż sekretu (synchroniczny token) lub publicznego klucza (niesynchroniczny token). Oczywiście jeśli weryfikujesz za pomocą teog samego sekretu, to musi się to odbyć tylko przez serwer który ten sekret posiada i z nikim się nie dzieli. W przypadku kluczy publicznych sprawa jest prostsza, bo będziesz mógł go sprawdzić przez jakiegokolwiek klienta, nawet po stronie Twojej aplikacji okienkowej.

Ze względu na to że token nie jest szyfrowany, to ważne aby nie przechowywać w claimach żadnych sekretów/poufnych informacji.

EDIT: Inna sprawa żę JWT nie rozwiązuje wszystkich problemów, jeśli np. zależy Ci na odwoływaniu tokenów przed ich wygaśnięciem. Wtedy będziesz musiał mieć jakiś stan sesji na serwerze.

0

Ok, teraz rozumiem. Czyli generalnie rzecz biorąc ten token nie jest mi potrzebny do niczego, oraz mogę zrezygnować z samego [Authorize] w API, ponieważ wtedy jeden i ten sam token mógłby działać u każdego z klientów? Nikt, ręcznie, nie będzie mógł tego tokena wygenerować, tylko ja, osobnym oprogramowaniem dostępnym u mnie na serwerze. Token generowałby się w momencie dopisywania nowego klienta lub podczas odświeżania w razie wygaśnięcia licencji.

EDIT
Jedyne co, to własną weryfikacje NIP, Email i innych zmyślnych głupot.

0

Raczej tokien ma krótki żywot (przeważnie) ewentualnie jego czas życia określony przez samego użytkownika.

0

Ja mogę zdefiniować w API jaki żywot ma mieć token.

1

Tak, chociaż tak jak napisałem w edycji posta, wygasanie licencji wymagałoby dodatkowej obsługi po Twojej stronie. Token ma czas ważności, i raz wygenerowany z takim czasem ważności nie może zostać zmieniony. Więc bez podjęcia odpowiednich kroków, nawet jeśli w Twoim systemie już by nie było takiej licencji, techniczne token nadal byłby ważny.

Ja mogę zdefiniować w API jaki żywot ma mieć token.

A co jeśli okaże się że musisz anulować licencję wcześniej?

0

W tabeli miałby kolumnę ExpiresAt, która by przyjmowała datę wygaśnięcia licencji. Jeśli token byłby dalej ważny to nie przejdzie dalsza weryfikacja. To samo tyczyłoby się anulacji licencji.

1

Robisz na tej aplikacji zwykłe logowanie, po logowaniu możesz token zwrócić i refresh token, jeśli ktoś będzie używał dwóch tokenów to jak jeden wygaśnie, użyje refresh tokena, drugi ukradnięty token zostanie użyty to też wymusi refresh tokena przez co wykryjesz drugie użycie tokena refresh i możesz odciąć użytkownika bo ktoś mu się włamał.

Ale jak użytkownik kupił licencję to i tak musi mieć pełne prawo do produktu więc przy zalogowaniu musi mieć zawsze możliwość odzyskać dostęp.

Token JWT działa na zasadzie synchroniczny hmac-sha256, co w skrócie za pomocą hasła skomplikowanego klucza randomowego, wykonujesz podpis na danych jego login/id, exp/expires i tu podajesz jak długo ma być żywy token.
Token dajesz użytkownikowi.

Jak użytkownik wyśle token z zapytaniem do api, to serwer sprawdza czy podpis się zgadza za pomocą hmac-sha256 potem sprawdza expires czy nie wygasł i jak jest wszystko git to daje dostęp.

0
AdamWox napisał(a):

W tabeli miałby kolumnę ExpiresAt, która by przyjmowała datę wygaśnięcia licencji. Jeśli token byłby dalej ważny to nie przejdzie dalsza weryfikacja. To samo tyczyłoby się anulacji licencji.

To oznacza że każda weryfikacja będzie się musiała odbywać poprzez komunikację z serwerem (wspomniana wcześniej przeze mnie stanowość). Niekoniecznie to coś złego- po prostu coś z czego należy sobie zdawać sprawę. W takim przypadku faktycznie równie dobrze mógłbyś się obejść bez JWT, i stworzyć jakiś własny, uproszczony token.

Tylko mam wrażenie że zatoczyłeś koło, bo przecież w pierwszym poście sugerowałeś że chcesz uniknąć trzymania czegoś w bazie.

0

Bardziej mnie martwiło bezpieczeństwo trzymania tokenów w bazie, stąd pytanie czy to jedyne wyjście. A, że takiego typu licencjonowanie i zabezpieczenia robię pierwszy raz to chciałbym obrać jakiś jeden kierunek na przyszłość i tylko dopisywać licencje do odpowiedniego oprogramowania.

PS.
Wątpię, aby ktoś z naszym oprogramowaniem chciał się bawić w nielegalne kopiowanie. Szefy się uparły, że ma być, bo myślą, że ten program jest złotem i każdy będzie je chciał 😅

0

Mi się wydaje, że nie jest potrzebny żaden JWT.
Pytanie jest takie jak chcesz, żeby to działało po zarejestrowaniu. Czy przy każdym uruchomieniu ma walidować ważność licencji?

Kiedyś siedziałem w nauce jazdy. Kupowałem program do egzaminu wewnętrznego. Opierało się to na podobnym rozwiązaniu. Tzn Uruchamiales program i wymagał podania klucza licencyjnego, Po wklepaniu klucza leciał request do serwera licencyjnego. Wracała odpowiedź że ważdny to tego i tego dnia. Potem czy offline czy online program działał. Ale jak np usunąłem program i ponownie zainstalowałem to nie mogłem użyć drugi raz tej licencji tylko był telefon do firmy o odblokowanie w celu ponownej rejestracji.

0

Nie bardzo mogę to w ten sposób zrobić, ponieważ to są dwa programy, jeden jest usługą windows, który ogarnia całą logikę. Okno jest tylko do konfiguracji tej usługi. Zapytanie o ważność licencji leciałoby przy każdym wywołaniu zadania. Oprogramowanie bazuje na danych z internetu - Allegro, Baselinker, IdoSell, więc brak internetu nie popsuję licencjonowania, ponieważ brak połączenia do serwera licencji będzie najmniejszym problemem jeśli żadne dane nie zostaną przetworzone. Teraz teoretycznie wszystko zależy od instalatora - jeśli ktoś przeinstaluje program na tej samej maszynie to konfiguracja zostanie i licencja będzie aktywna. Jeśli zainstaluje na innym komputerze to trzeba będzie nową licencję, albo istniejącą zamienić na nowym sprzęcie.

0

Zastanawia mnie jeszcze jedno. Biorąc przykład Allegro:

  1. Token jest powiązany z danym kontem użytkownika
  2. Jeśli pobiorę zamówienie używając tokenu z innego konta to dostaje NotFound

Jak taki system łączy to wszystko? Czy to nie jest przypadkiem to samo co ja chciałem uzyskać?

0

Ja bym zrobił to najprościej jak się da z użyciem JWT:

  1. Dodajesz do dbContext tabelę:
public class Subscription
    {
        public Guid Id { get; set; }
        public Guid ApplicationUserId { get; set; }
        public DateTime? ExpiryTime { get; set; }
    }
  1. W akcji API dodajesz [Authorize(AuthenticationSchemes = "Bearer")], nie zapominając o AddJwtBearer()

  2. Na podstawie zalogowanego użytkownika pobierasz licencje, które posiada i tyle w temacie. :)

var currentUser = await _userManager.GetUserAsync(User);
var activeSubsriptions = await _subscriptionManager.GetAllActiveForUser(currentUser.Id);
0

Po pierwsze nie używam EF, ale to akurat ma najmniejsze znaczenie. Po drugie, nikt się nigdzie nie loguje w takim sensie, że nie ma do tego żadnego frontu. API w tym przypadku jest transparentne A atrybut [Authorize] zadziała dla każdego kto ma token. Ja chce żeby API rzuciło dane tylko tego użytkownika, którego jest token.

USER 1 => token XYZ => nip 123
USER 2 => token ABC => nip 456

Jeśli odpytam API podając nip 456 ale używając tokena XYZ, to powinno zwrócić 404

1

Nieistotne jakiej bazy używasz. Daję Tobie przykład dla Jeśli odpytam API podając nip 456 ale używając tokena XYZ, to powinno zwrócić 404

public class Subscription
    {
        public Guid Id { get; set; }
        public Guid ApplicationUserId { get; set; }
        public string NIP { get; set; }
        public DateTime? ExpiryTime { get; set; }
    }
 [Authorize(AuthenticationSchemes = "Bearer")]
 public async Task<IActionResult> Example([FromQuery]ExampleInput input)
 {
    var currentUser = await _userManager.GetUserAsync(User);
    var activeSubsriptions = await _subscriptionManager.GetAllActiveForUser(currentUser.Id, input.NIP);
    if(activeSubsriptions == null)
      return NotFound();

Po prostu stwórz ten token ręcznie i wczytaj dynamicznie do aplikacji

1 użytkowników online, w tym zalogowanych: 0, gości: 1