Pobieranie danych aktualnie zalogowanego użytkownika - .NET 6.0

0

Hej,

Kilka dobrych godzin kopałem w internecie , próbowałem różne rozwiązania i niestety bez skutku. Problem wygląda tak, że chce pobrać dane dla aktualnie zalogowanego użytkownika (jego obiekt lub przynajmniej id, aby pobrać sobie obiekt). Niestety przy każdym możliwym rozwiązaniu dostaję null, nie mam już pomysłów co robić, a chciałbym rozwiązać ten problem. Jakieś pomysły? Wersja .NET 6.0, jak w tytule.
Liczę, że wspólnie ogarniemy problem. :)
Kod:

Program.cs

using KursAspNetBackend.Database;
using KursAspNetBackend.Domain.Entities;
using KursAspNetBackend.Database.Repositories;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using KursAspNetBackend.Domain.Interfaces.Identity;



var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllersWithViews();

builder.Services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(builder.Configuration.GetConnectionString("Default")));

builder.Services.Configure<IdentityOptions>(options => options.ClaimsIdentity.UserIdClaimType = ClaimTypes.NameIdentifier);

//builder.Services.AddHttpContextAccessor();
///builder.Services.AddTransient<IPrincipal>(provider => provider.GetService<IHttpContextAccessor>().HttpContext.User);
//builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

builder.Services.AddHttpContextAccessor();

builder.Services.AddIdentity<ApplicationUser, IdentityRole>(config =>
{
    config.SignIn.RequireConfirmedEmail = true;
}).AddEntityFrameworkStores<ApplicationDbContext>()
  .AddDefaultTokenProviders();

// Adding Transients
builder.Services.AddTransient<IMessagesRepository, MessagesRepository>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
}

app.UseAuthentication();
//app.UseIdentityServer();
app.UseAuthorization();

app.UseStaticFiles();
app.UseRouting();

app.UseCors(x => x
    .AllowAnyMethod()
    .AllowAnyHeader()
    .SetIsOriginAllowed(origin => true) 
    .AllowCredentials()); // allow credentials

app.MapControllerRoute(
    name: "default",
    pattern: "{controller}/{action=Index}/{id?}");

app.MapFallbackToFile("index.html"); ;

using (var scope = app.Services.CreateScope())
{
    var dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
    dbContext.Database.EnsureCreated();
}



//app.Services.GetRequiredService<ApplicationDbContext>().Database.EnsureCreated();

app.Run();

AccountController.cs

using KursAspNetBackend.Domain.Entities;
using KursAspNetBackend.Domain.Dtos;
using KursAspNetBackend.Domain.Entities;
using KursAspNetBackend.Domain.Dtos;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;

namespace KursAspNetBackend.Controllers
{
    [Route("account/")]
    public class AccountController : ControllerBase
    {
        private readonly UserManager<ApplicationUser> _userManager;
        private readonly SignInManager<ApplicationUser> _signInManager;
        private readonly IHttpContextAccessor _httpContextAccessor;
        public AccountController(UserManager<ApplicationUser> userManager, 
                                SignInManager<ApplicationUser> signInManager,
                                IHttpContextAccessor httpContextAccessor)
        {
            _userManager = userManager;
            _signInManager = signInManager;
            _httpContextAccessor = httpContextAccessor;
        }

        [HttpGet]
        [Route("getCurrentUser")]
        public async Task<IActionResult> GetCurrentUser()
        {
            var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
            var user = await _userManager.GetUserAsync(_httpContextAccessor.HttpContext.User);
            
            if(user == null)
            {
                return Unauthorized();
            }

            return Ok(user);
        }

        [HttpPost]
        [Route("register")]
        public async Task<IActionResult> Register([FromBody] UserRegisterDto userRegisterDto)
        {
            var newUser = new ApplicationUser
            {
                Email = userRegisterDto.Email,
                UserName = userRegisterDto.Email,
                FirstName = userRegisterDto.FirstName,
                LastName = userRegisterDto.LastName,
                Address = "",
            };

            var result = await _userManager.CreateAsync(newUser, userRegisterDto.Password);
            
            if (result.Succeeded)
            {
                var token = await _userManager.GenerateEmailConfirmationTokenAsync(newUser);

                await _userManager.ConfirmEmailAsync(newUser, token);

                return Ok();
            }
            else
            {
                foreach (IdentityError error in result.Errors)
                    Console.WriteLine($"Oops! {error.Description} ({error.Code}");
            }
            return NotFound();
        }

        [HttpPost]
        [Route("login")]
        public async Task<IActionResult> Login([FromBody] UserLoginDto userLoginDto)
        {
            var foundUser = await _userManager.FindByEmailAsync(userLoginDto.Email);
            
            if(foundUser == null)
            {
                return NotFound();
            }

            var result = await  _signInManager.PasswordSignInAsync(foundUser, userLoginDto.Password, true, false);

            if (result.Succeeded)
            {
               
                return Ok();
            }

            return NotFound();
        }
    }
}

launchSettings.json

{
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:12761",
      "sslPort": 0
    }
  },
  "profiles": {
    "KursAspNetBackend": {
      "commandName": "Project",
      "launchBrowser": true,
      "applicationUrl": "http://localhost:5054",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development",
        "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
      }
    },
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development",
        "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
      }
    }
  }
}

2
  1. Brak authorization attribute
  2. Po logowaniu nie generujesz tokena
  3. Jak front czy cokolwiek nie wysyła tokena to skąd masz mieć dane zalogowanego usera?
0
szydlak napisał(a):
  1. Brak authorization attribute
  2. Po logowaniu nie generujesz tokena
  3. Jak front czy cokolwiek nie wysyła tokena to skąd masz mieć dane zalogowanego usera?

Świetnie, tak myślałem, że coś przeoczyłem niestety. :) Wieczorem po pracy ogarnę te opcje i sprawdzę.
Co do authorization attribute, mogę prosić o jakiś przykład? Chodzi o oznaczenia przed metodą, np "[AuthAttribute("Index", "Admin")]"?

3
szydlak napisał(a):
  1. Po logowaniu nie generujesz tokena

To akurat zaleta. Po co mu w takiej apce jakieś bieda-JWT?
Jak to API tylko na potrzeby jego frontu to JWT nic nie wnosi.

  1. Jak front czy cokolwiek nie wysyła tokena to skąd masz mieć dane zalogowanego usera?

Np. z podpisanego ciasteczka :) Tak chyba domyślnie działa ASP.NET Core Identity po wywołaniu SignInManager.SignInAsync :P

0
[some_ONE napisał(a)]

Np. z podpisanego ciasteczka :) Tak chyba domyślnie działa ASP.NET Core Identity po wywołaniu SignInManager.SignInAsync :P

Masz racje. Ale, popraw mnie jeśli sie myle, powinnac byc w startupie wywylana metoda addCookie().
Dla mnie jwt w przypadku api jest bardzie praktyczne

0
some_ONE napisał(a):
szydlak napisał(a):
  1. Po logowaniu nie generujesz tokena

To akurat zaleta. Po co mu w takiej apce jakieś bieda-JWT?
Jak to API tylko na potrzeby jego frontu to JWT nic nie wnosi.

  1. Jak front czy cokolwiek nie wysyła tokena to skąd masz mieć dane zalogowanego usera?

Np. z podpisanego ciasteczka :) Tak chyba domyślnie działa ASP.NET Core Identity po wywołaniu SignInManager.SignInAsync :P

Co do ciasteczka - tak prowadzący w kursie w sumie tłumaczył z tego co pamiętam. To tak aby uzupełnić swoją wiedzę na ten temat - myślisz, że po wywołaniu metody o której wspomnial @szydlak czyli addCookie(), użytkownik powinien zostać zapisany w ciastku? Czy są może jeszcze jakieś inne potencjalne problemy? :)

0
szydlak napisał(a):

Masz racje. Ale, popraw mnie jeśli sie myle, powinnac byc w startupie wywylana metoda addCookie().

A tego to nie pamiętam :) Dawno nie używałem już tego Identity.

Dla mnie jwt w przypadku api jest bardzie praktyczne

W przypadku implementacji OAuth2 i udostępniania takiego API klientom zewnętrznym i owszem. Ale wtedy to albo piszemy same API i naszego frontu nie ma, a jak mamy swój front to on i tak najczęściej będzie korzystał z API wewnętrznego rozwijanego w zupełnie innym tempie niż to publiczne.

W przypadku, gdy to API jest tylko na potrzeby naszego frontu to według mnie JWT to przerost formy nad treścią i nic sensownego nie wnoszą.

0
some_ONE napisał(a):
szydlak napisał(a):

Masz racje. Ale, popraw mnie jeśli sie myle, powinnac byc w startupie wywylana metoda addCookie().

A tego to nie pamiętam :) Dawno nie używałem już tego Identity.

Dla mnie jwt w przypadku api jest bardzie praktyczne

W przypadku implementacji OAuth2 i udostępniania takiego API klientom zewnętrznym i owszem. Ale wtedy to albo piszemy same API i naszego frontu nie ma, a jak mamy swój front to on i tak najczęściej będzie korzystał z API wewnętrznego rozwijanego w zupełnie innym tempie niż to publiczne.

W przypadku, gdy to API jest tylko na potrzeby naszego frontu to według mnie JWT to przerost formy nad treścią i nic sensownego nie wnoszą.

Jak coś mała aktualizacja - dodałem do serwisu w program.cs obsługę ciasteczek i dalej nie działa :/ Ba, udało nawet mi się znaleźć przechowywanie tokenu użytkownika w ciasteczku w dokumentacji .net 6.0, zrobiłem zgodnie z instrukcją i dalej nie działa nic, a nic. Macie jakieś pomysły? W ostateczności użyję do autentykacji JWT, ale szkoda byłoby wyłożyć się na czymś, co w teorii jest proste i działa... :/

1

Coś mieszasz, odpaliłem twój kod z SQLitem i działa w takiej konfiguracji jak masz (bez żadnych dodatkowych Authorize czy ręcznego konfigurowania ciasteczek).
Login zwraca ciasteczko, które następnie musisz przekazać przy wywoływaniu getCurrentUser. Może jakoś dziwnie to hostujesz (różne domeny itp.) i po prostu ciasteczko nie jest przekazywane do serwera?

0
some_ONE napisał(a):

Coś mieszasz, odpaliłem twój kod z SQLitem i działa w takiej konfiguracji jak masz (bez żadnych dodatkowych Authorize czy ręcznego konfigurowania ciasteczek).
Login zwraca ciasteczko, które następnie musisz przekazać przy wywoływaniu getCurrentUser. Może jakoś dziwnie to hostujesz (różne domeny itp.) i po prostu ciasteczko nie jest przekazywane do serwera?

Hmm dziwne, ja to na razie robię wszystko na localu, może po prostu dzisiaj wieczorem spróbuję stworzyć nowy projekt, przepnę tylko moduły stworzone (Domains, Database, etc.) i spróbuję na świeżym projekcie ten sam kod odpalić. Będę się kierował tym co napisałeś i mam nadzieję, że w końcu to ogarnę, bo to jest taka prosta rzecz, ale tak ważna, że masakra. No i to że ciasteczko się nie przekazuje, to jestem niemalże pewien, jak breakpointy ustawiłem i sprawdzałem, to wszystko jest okej (nawet przy customowych Claimsach) do czasu zakończenia metody logowania, zmiany podstrony i wywołania z drugiej podstrony metody getCurrentUser - wtedy okazuje się, że ciastko jest puste, tak jakby totalnie nie zostało zapisane, albo "zgubiło się". Dzięki za rady, spróbuję co napisałem i dam znać :D

0

Otwórz przeglądarkę w private mode i sprawdz czy działa

0
1a2b3c4d5e napisał(a):

Otwórz przeglądarkę w private mode i sprawdz czy działa

Niestety nie działą, a już testowałem różne opcje, nawet stworzyłem projekty odrebne biblioteki klas (Domain, Database, Services) w .net 6.0.6
Nie mam totalnei pomysłu co jest nie tak, dramat. Już kilka razy robiłem wszystko zgodnie z tutorialami, zawsze cookie puste...

0
some_ONE napisał(a):

Coś mieszasz, odpaliłem twój kod z SQLitem i działa w takiej konfiguracji jak masz (bez żadnych dodatkowych Authorize czy ręcznego konfigurowania ciasteczek).
Login zwraca ciasteczko, które następnie musisz przekazać przy wywoływaniu getCurrentUser. Może jakoś dziwnie to hostujesz (różne domeny itp.) i po prostu ciasteczko nie jest przekazywane do serwera?

Mogę poprosić o podrzucenie tego projektu? Byłbym bardzo wdzięczny, zobaczę czy u mnie zadziała. Mega dziwna sprawa, totalnie nie działa to, nawet tak jak wyżej pisałem, zmieniłęm .Net standard 2.1 dla bibliotek klas, na .net 6.0.6, próbowąłem tworzyć nowe projekty, nic ciagle null zwraca

0

Ciekawostka - sprawdziłem Postmanem - wysłałem userLoginDto w formacie json, zalogowało, dostałem dwa ciastka i co ciekawe, po wysłaniu requesta proszacego o aktualnego usera, otrzymałem uwaga - moje zalogowane konto. Ktoś wie, co mozę mieć na to wpływ?

0

Dobra rozwiązane, w Angularze trzeba było dodać dodatkowe parametry, przede wszystkim withCredentials: true. Podrzucam poniżej kod, gdyby ktos kiedys miał problem i szukał rozwiązania :)

ngOnInit(): void {
    this.http.get("https://localhost:44372/" + "account" + "/getCurrentUser", { observe: 'response', withCredentials: true }).subscribe(response => {
      this.firstName = (response.body as any).firstName;
      this.lastName = (response.body as any).lastName;
      console.log(response.body);
    },
      error => {
      });
  }

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