简体   繁体   中英

Angular client, WebAPI server - cookie issue when userManager.GetUserAsync() method

I'm preparing WebAPI where the client (Angular) is asking via HTTP for logging in and current user.It works fine, when I'm sending POST and GET requests from Swagger (works on https://localhost:44322/swagger/index.html). I receive all necessary answers, but fun thing happens when I'm trying to do so from Angular (works on https://localhost:4200). CORS origin turned on, headers allowed, any method allowed, credentials allowed... I think I run into a cookie-related issue, because, when I open both cards (swagger and angula) in the same browser window, I'm able to do everything find, but when I separate them, swagger works, but Angular stop seeing cookies which come from the server-side.

I think I tried everything. I tried to play withCredentials paremeter in HTTP requests, I tried to parametrize CORS to allow switch on AllowCredentials(); method. Nothing worked.

So, Swagger can send requests like below.

在此处输入图像描述

I also implemented HTTP requests from Angular. Below login.component.ts

import { HttpClient } from '@angular/common/http';
import { Message } from '@angular/compiler/src/i18n/i18n_ast';
import { Component, OnInit } from '@angular/core';
import { first } from 'rxjs';
import { UserService } from '../user.service';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {

  response: any;
  currentUser = {
    firstName: "",
    lastName: ""
  };
  user: any;
  userLogin = {
    email: "",
    password: ""
  }

  firstName: string = "";
  lastName: string = "";

  constructor(private http: HttpClient, private service: UserService) { }

  ngOnInit(): void {
    this.getCurrentUser();
  }

  loginAction(): any {
    this.response = this.service.loginUser(this.userLogin);
    if(this.response){
      this.service.currentUser().subscribe((response: any) =>{
        this.currentUser.firstName = (response as any).firstName;
      });
    }
  }
  logoutAction():any{
    this.service.logoutUser();
  }

  getCurrentUser(){
    this.service.currentUser().subscribe((response: any) =>{
      this.currentUser.firstName = (response as any).firstName;
    });    
  }

}

And user.service.ts

export class UserService {

  readonly taskAPIUrl = "https://localhost:44322/api";
  
  constructor(private http: HttpClient) { }

  loginUser(userLogin :any) {
    return this.http.post("https://localhost:44322/api/UserLogin",userLogin).subscribe();
  }

  logoutUser(): any {
    return this.http.post<any>("https://localhost:44322/api/UserLogin/logout", {withCredentials: true}).subscribe();
  }

  currentUser(): any {
    return this.http.get<any>("https://localhost:44322/api/UserLogin/getCurrentUser", {withCredentials: true});
  }

Here is Startup.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ToDoListAPI.Data;
using ToDoListAPI.Models;

namespace ToDoListAPI
{
    public class Startup
    {
        private string myAllowSpecificOrigins = "_myAllowSpecificOrigins";
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {

            services.AddControllers();
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "ToDoListAPI", Version = "v1" });
            });

            services.AddDbContext<DataContext>(options =>
            {
                options.UseSqlServer(Configuration.GetConnectionString("ConnectionString"));
            });

            

            //Enable CORS
            services.AddCors(options =>
            {
                options.AddPolicy(name: myAllowSpecificOrigins,
                    builder =>
                    {
                        builder.WithOrigins("https://localhost:4200").
                        AllowAnyMethod().
                        AllowAnyHeader().
                        AllowCredentials();
                    });
            });

            services.AddIdentity<ApplicationUser, IdentityRole>(options =>
            {
                options.ClaimsIdentity.UserNameClaimType = "UserID";
            }).
                     AddEntityFrameworkStores<DataContext>().
                     AddDefaultTokenProviders();

            services.ConfigureApplicationCookie(options =>
            {
                options.Cookie.HttpOnly = false;
            });


        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "ToDoListAPI v1"));
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseCors(myAllowSpecificOrigins);

            app.UseAuthentication();

            app.UseAuthorization();            

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

UserLoginController.cs where I send HTTP requests

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
using ToDoListAPI.Models;

// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860

namespace ToDoListAPI.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class UserLoginController : ControllerBase
    {
        private readonly UserManager<ApplicationUser> _userManager;
        private readonly SignInManager<ApplicationUser> _signInManager;

        public UserLoginController(UserManager<ApplicationUser> userManager,
                                    SignInManager<ApplicationUser> signInManager)
        {
            _userManager = userManager;
            _signInManager = signInManager;
        }
        // GET: api/<UserLoginController>
        [HttpGet]
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }

        // GET api/<UserLoginController>/5
        [HttpGet("{id}")]
        public string Get(int id)
        {
            return "value";
        }

        [HttpGet]
        [Route("getCurrentUser")]
        public async Task<IActionResult> GetCurrentUser()
        {
            var user = await _userManager.GetUserAsync(User);

            if (user == null)
            {
                return Unauthorized();
            }
            return Ok(user);
        }

        // POST api/<UserLoginController>
        [HttpPost]
        public async Task<IActionResult> Login([FromBody] UserLogin 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();
        }


        // POST api/<UserLoginController>
        // in progress
        [HttpPost]
        [Route("logout")]
        public async void Logout()
        {
            await _signInManager.SignOutAsync();
        }


        // DELETE api/<UserLoginController>/5
        [HttpDelete("{id}")]
        public void Delete(int id)
        {
        }
    }
}

Please help, I think I stuck somewhere...

Here is example of UserLogin request from Swagger在此处输入图像描述

And here from angular client在此处输入图像描述

As you can see, Swagger has a lot more in the request and response stay the same. The biggest problem is when I send getCurrentUser() request.

Swagger: 在此处输入图像描述

and angular

在此处输入图像描述

Ok. For angular it should look something like this. In user.service.ts methods should return Observalbe.

For an example:

loginUser(userLogin : "here should be model class): Observable<Any> {
    return this.http.post("https://localhost:44322/api/UserLogin",userLogin).subscribe(repond => {return respond});

       return this.httpClient
      .post("https://localhost:44322/api/UserLogin",userLogin)
      .pipe(map(resposne =>{
          return resposne;
      }),
      catchError(error => {
        console.log(error);
      }));
  }

In login.component.ts login should look something like this:

loginAction() {
    this.service.loginUser(this.userLogin)
    .pipe(first())
    .subscribe( response =>{
        this.currentUser.firstName = response.firstName;
     }, error => {
        console.log(error); 
    });
  }

For GetCurrentUser in Controller file try tu parse tu yours id type instead of User this User.Identity.Name or User.Identity.Id

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM