I have two projects one is web api and second is Angular app. I made simple "To do" app, and added Authorize attribute over ToDoController so I can restrict each user can see only their to dos.
Problem which I encountered is when I want to get all to dos per user and inspect call in browser console I see every my request is redirected to Identity/Account/Login even after I successful logged in. I got this message:
Http failure response for https://localhost:7141/Identity/Account/Login?ReturnUrl=/api/to-do
But when I send request via postman everything works fine, but first I need to login via postman and after that if I send get request to localhost:7141/api/to-do I can fetch all to dos for logged user. Why this doesn't work if I go via web app through web browser?
ToDoController.cs
[ApiController]
[Route("api/to-do")]
[Authorize]
public class ToDoController : ControllerBase
{
private readonly ToDoService _toDoService;
private readonly UserManager<ApplicationUser> _userManager;
public ToDoController(ToDoService toDoService, UserManager<ApplicationUser> userManager)
{
_toDoService = toDoService;
_userManager = userManager;
}
[HttpGet()]
public async Task<IActionResult> GetAll()
{
int.TryParse(_userManager.GetUserId(HttpContext.User), out int _userId);
return Ok(await _toDoService.GetToDos(_userId));
}
}
Login method in AccountController
[HttpPost("login")]
public async Task<IActionResult> Login([FromBody] LoginModel loginModel)
{
var appUser = await _userManager.FindByEmailAsync(loginModel.Email);
if (appUser == null) return BadRequest("User not registered!");
var result = await _signInManager.PasswordSignInAsync(appUser.UserName, loginModel.Password, false, false);
if (result.Succeeded)
{
return Ok(new UserModel { Id = appUser.Id, Email = appUser.Email });
}
return BadRequest("Invalid login details!");
}
Program.cs (WebApi)
...
builder.Services.AddDefaultIdentity<ApplicationUser>()
.AddEntityFrameworkStores<ToDoAngularDbContext>();
builder.Services.AddAuthentication();
builder.Services.AddScoped<ToDoRepository>();
builder.Services.AddScoped<ToDoService>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseCors(options => options.WithOrigins("https://localhost:44426")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
...
HomeComponent where on ngonInit I go to user to dos.
export class HomeComponent implements OnInit {
public toDos: ToDoModel[] | null = [];
private apiUrl: string = this.constants.WEB_API_BASE_URL
this.constants.API_TO_DO_ENDPOINT;
constructor(private http: HttpClient, public authService: AuthenticationService,
private snackBar: MatSnackBar, private constants: Constants) {}
ngOnInit(): void {
const headers: HttpHeaders = new HttpHeaders();
headers.set('Content-Type', 'application/json');
this.http.get<ToDoModel[]>(this.apiUrl, { headers: headers, observe: 'response'
}).subscribe(result => {
if (result) {
if (result.status == HttpStatusCode.Ok) {
this.toDos = result.body;
}
}
},
error => console.error(error));
}
}
CodePudding user response:
After struggling almost whole day with this, with HTTP toolkit application I manage to watch request which I send with my app and postman. Postman request was sent with cookies while my Angular app didn't send cookies.
So I figured I need to set one of angular request properties to true in this case "withCredentials: true".
After I add this in my request everything works fine.
ngOnInit(): void {
const headers: HttpHeaders = new HttpHeaders();
headers.set('Content-Type', 'application/json');
this.http.get<ToDoModel[]>(this.apiUrl, { headers: headers, observe: 'response', withCredentials: true }).subscribe(result => {
if (result) {
if (result.status == HttpStatusCode.Ok) {
this.toDos = result.body;
}
}
},
error => console.error(error));
}
Also if you use SignInManager to login on your site, you need set withCredential to true in this case too, so you can get cookies from web api.
this.http.post<UserModel>(this.apiUrl '/login', loginModel, { headers: headers, observe: 'response', withCredentials: true })