.NET Core WebAPI - pobieranie informacji o użytkowniku zapisanych w tokenie (Claims)

0

Witam.
Tworze API, które pewne informacje ma dynamiczne i wpisuje je w tokenie (claims) podczas "logowania". Problem polega na tym, że do każdego endpointa muszę wyciągnąć te informacje.

var database = JsonConvert.DeserializeObject<AppUser>(User.Claims.FirstOrDefault(x => x.Type == "user").Value).Database;
var result = await _documentService.Get(database, type, filter);

Zaznaczam, że to jest "prototyp". Jestem w fazie produkcji i pewnie kwestie (jak działają) próbuje poukładać.

Czy do _documentService.Get(); powinienem wrzucić cały User? Czy tylko Claims? Czy może jest jakaś ładniejsza opcja? Na tę chwilę jest to tylko baza danych ale takich informacji będzie więcej z czasem i chciałbym to zrobić logiczniej, bo w ten sposób jest "brzydko". Zaznaczam, że taki zabieg jest przy każdym endpoincie.

PS.
Pisałem też, w tym wątku, że chciałbym zrobić niestandardową walidację tokenu na podstawie obiektu sesji z Comarch Optima. To też się będzie wiązać z tym co będzie w Claims.

0

Nie wiem co tam dalej z tym robisz ale może metoda _documentService.Get() mogła by przyjmować dictionary<klucz, wartość>

1

Czy dobrze rozumiem że przechowujesz cały, zserializowany obiekt User jako claim? Jeśli tak to chyba nie jest to najlepszy pomysł. Claimy powinny zawierać informacje o użytkowniku, a nie samego użytkownika.

Czy do _documentService.Get(); powinienem wrzucić cały User? Czy tylko Claims? Czy może jest jakaś ładniejsza opcja?

Tak jak wspomniał szydlak- to zależy co robisz. Zawsze możesz to jakoś wyabstrahować tak żeby serwis nie brał całego Usera (jeśli nie chcesz tego robić), ale jednocześnie nie przyjmował claimów (bądź co bądź jest to pewien detal implementacyjny).

Ja bym podpiął jakieś middleware które wyciągało by token z aktualnego requesta (zakładając że ta logika wykonuje się w kontekście requestu HTTP, dla konkretnego użytkownika) i wstrzykiwał to w takiej wyabstrahowanej postaci.

0

Czy dobrze rozumiem że przechowujesz cały, zserializowany obiekt User jako claim?

Tak. Chcesz powiedzieć, że lepiej jest odpytać bazę danych o potrzebne informacje niż zwyczajnie zdeserializować jsona do obiektu?

Claimy powinny zawierać informacje o użytkowniku, a nie samego użytkownika.

Co to są informacje o użytkowniku? Czy email, nazwa bazy, nazwa firmy to już użytkownik czy informacje o nim?

Ja bym podpiął jakieś middleware które wyciągało by token z aktualnego requesta (zakładając że ta logika wykonuje się w kontekście requestu HTTP, dla konkretnego użytkownika) i wstrzykiwał to w takiej wyabstrahowanej postaci.

Rzucisz jakim przykładem gdzie ten middleware miałby użycie? Myślałem właśnie o jakiś serwisie pośredniczącym, który by taką walidację przeprowadzał i zwracał odpowiednie informacje. Tylko jak to zrobić w konstruktorze, skoro token i informacje o użytkowniku wpadają dopiero na endpoint'cie?

private ClaimsPrincipal _appUser;

public ExampleController()
{
    _appUser = User;
}
3

Tak. Chcesz powiedzieć, że lepiej jest odpytać bazę danych o potrzebne informacje niż zwyczajnie zdeserializować jsona do obiektu?

Przede wszystkim chodzi o to żeby zawierać w tokenie niezbędne minimum. Koniec końców jest to coś co stale przesyłamy w requestach. Ponadto warto się zastanowić czy faktycznie potrzebujesz całego użytkownika, ze wszystkimi właściwościami? Bardzo często tak nie jest, i potrzebujemy jedynie mały podzbiór informacji. A jeśli potrzebujesz całego użytkownika to tak- ja bym go wyciągał z bazy danych, a w tokenie przesyłał jego ID.

Co to są informacje o użytkowniku? Czy email, nazwa bazy, nazwa firmy to już użytkownik czy informacje o nim?

Chodziło mi właśnie o to że każdy taki rodzaj informacji (np. adres email, imię, nazwa firmy) to powinien być oddzielny claim.

Rzucisz jakim przykładem gdzie ten middleware miałby użycie? Myślałem właśnie o jakiś serwisie pośredniczącym, który by taką walidację przeprowadzał i zwracał odpowiednie informacje. Tylko jak to zrobić w konstruktorze, skoro token i informacje o użytkowniku wpadają dopiero na endpoint'cie?

Trochę poleciałem z tym middleware bo zakładając że używasz standardową autoryzację Identity to nawet middleware nie jest potrzebne. W jednym z projektów ja użyłem czegoś takiego:

    public interface IClaimsPrincipalAccessor
    {
        ClaimsPrincipal Principal { get; }
    }

    internal class ClaimsPrincipalAccessor : IClaimsPrincipalAccessor
    {
        private readonly IHttpContextAccessor _httpContextAccessor;

        public ClaimsPrincipalAccessor(IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
        }

        public ClaimsPrincipal Principal => _httpContextAccessor.HttpContext.User;
    }
```
Oczywiście to nadal jest dosyć techniczne bo konsument tego serwisu będzie musiał operować bezpośrednio na claimach, więc zależnie od tego jak wydzieloną masz warstwę biznesową/domenową możesz chcieć wyabstrahować to jeszcze bardziej i wystawić właściwości bezpośrednio z informacjami o obecnym użytkowniku, np.

``````csharp
    public interface ICurrentUser
    {
        string Email { get; }
        string CompanyName { get; }
    }
```
0

Podoba mi się to ale mam jedno zastrzeżenie, które jest lepiej ogarnięte jeśli wpakuje cały obiekt do claims. Owszem, później trzeba to deserializować, więcej roboty, ale jeśli wciśniesz cały obiekt w postaci json do claims to poradzi sobie z nullami. Logowanie do systemu opiera się o kod operatora (comarch optima) i taki użytkownik nie musi mieć adresu email, a claims nulla nie przyjmie. Czyli trzeba będzie ifować. Jest to wartę zachodu?

0

To już zależy od Ciebie. Z tym ifowaniem bym jednak nie przesadzał bo jak nie będziesz miał claima to będzie właśnie null (np. przy użyciu FirstOrDefault). Pamietaj również że jeśli używasz tego tokena w komunikacji sieciowej czy po prostu po stronie klienta to jest to również pewna dodatkowa kwestia bezpieczeństwa- claimy można parsować a więc ktoś mógłby mieć wgląd w to jak wygląda Twój model użytkownika. No chyba że Ty używasz zaszyfrowane tokeny.

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