JS — API выборки — Не удается получить файл cookie для входа в систему из API идентификации ASP

#javascript #api #authentication #cookies #fetch

Вопрос:

Я пытаюсь получить файл cookie после успешного входа в систему, но не могу понять, как это сделать. В API идентификации ASP Net Core с использованием swagger браузер получает файл cookie, но когда я использую api выборки, я не могу получить файл cookie. Я попытался вернуть ответ return.json(); но это не работает. У меня также есть перенаправление на домашнюю страницу при успешном входе в систему, но я точно не знаю, как вернуть ответ.json(); если это необходимо.

Как API идентификации, так и JS — Cleint работают на локальном хосте.

JS — API выборки — СООБЩЕНИЕ:

 function IdentityPost(formID, postUrl) {

     
    const currForm = document.getElementById(formID); // Get the Form
    var submitBtn = currForm.elements.namedItem("triggerSubmit"); // Get the submit button of the form
    
     // Listen for Form- Submit 
    currForm.addEventListener('submit',function handler(e)
    {
        e.preventDefault(); // Prevent page reload on Submit  
        submitBtn.disabled = true; // Disable the submit button

        LoadingMsg(); // Show Loading Message

        // Get form data as string---------------------------------------------------------------
        const formData = new FormData(this); // "this" = this Form
        const searchParams = new URLSearchParams(formData); // Get the form data params  
        let formQueryString = searchParams.toString(); // Get the form data params as string

      


        // POST ----------------------------------------------------------------------------------
         fetch(identityApiUri   postUrl   formQueryString,  // #1 = API-Address, #2 = API - Controller/Mehod, #3 = form data as sring
            {    
                method: 'POST',
                credentials: 'same-origin'
                
            }).then(function (response)
               {  
                    // IF OK                       
                if (response.status == 200 || response.status == 201) // Status 201 = "Created"
                {
                       RemoveLoadingMsg();
                       SuccessMsg("Success");
                       currForm.reset();  // Reset the  form
                       submitBtn.disabled = false; // Enable Submit button
                       
                  
                       if (document.referrer.split('/')[2] === window.location.host) // Return to previous page if local 
                       {
                           history.back(); // Go back to previouse page
                       }
                       else
                       {
                           window.location.href = "/"; // RETURN TO Home
                       }
                 }
                   else // If Bad STATUS
                   {
                     return Promise.reject(response);  // Triggers Catch method
                   }
                
               }).catch(function (err) // If Exception
               {
                   RemoveLoadingMsg(); 
                   // Show Error
                       try // Because of JSON Parse and err.text()
                       {
                          err.text().then(errorMessage => {
                               var error = errorMessage.substring(1, errorMessage.length - 1); // Remove the [..] form the Msg
                               ErrorMsg(error); // Get the error and display
                          });
                       }
                       catch(e)
                       {
                           console.warn("Post Exception -  Probably No connection to hte server");
                           ErrorMsg(err   " - Server is probably offline"); // Get the error and display
                       }

                   submitBtn.disabled = false; // Enable Submit button
                   console.warn('Post Exception:', err);
               });
                    
        this.removeEventListener('submit', handler); // Remove Event Listener 
    });

}
 

ASP Net Core — Identity API — Запуск:
Я включил CORS Любого происхождения. — Не уверен, нужно ли мне включать это .allowCredentials(). Если я попытаюсь включить его, он скажет, что я не могу этого сделать .Функция AllowAnyOrigin() включена. Я получаю доступ к Api непосредственно из клиентского «Браузера».

 using Leanheat.Identity.API.DBContexts;
using Leanheat.Identity.API.Filters;
using Leanheat.Identity.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Authorization;
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;

namespace Leanheat.Identity.API
{

    public class Startup
    {
        // Startup
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }






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


            // Log in - DbContext
            services.AddDbContextPool<LeanheatIdentityApiContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("IdentityContextConnection")));


            // UnitOfWork - Filter
            services.AddScoped<UnitOfWorkFilter>(); 
            services.AddControllers(config => { config.Filters.AddService<UnitOfWorkFilter>(); });  // UnitOfWork for all Controllers


            // CORS - Allow calling the API from WebBrowsers
            services.AddCors();

            // Log In
            services.AddIdentity<ApplicationUser, IdentityRole>(options =>
            {
                // Password settings
                options.Password.RequireDigit = false;
                options.Password.RequireLowercase = false;
                options.Password.RequireNonAlphanumeric = false;
                options.Password.RequireUppercase = false;
                options.Password.RequiredLength = 6;
                options.Password.RequiredUniqueChars = 1;

            }).AddEntityFrameworkStores<LeanheatIdentityApiContext>().AddDefaultTokenProviders(); // AddDefaultTokenProviders is used for the Update Log In Password etc.







            // Log In
            // Make all Controllers protected by default so only Authorized Users can accsess them, for Anonymouse Users use [AlloAnonymouse] over the controllers.
            services.AddMvc(options => {
                var policy = new AuthorizationPolicyBuilder()
                  .RequireAuthenticatedUser()
                  .Build();
                options.Filters.Add(new AuthorizeFilter(policy));

            }).AddXmlSerializerFormatters();

            //services.AddControllers();



           


            // Swagger
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "Leanheat.Identity.API", Version = "v1" });
            });
        }









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


         

            app.UseHsts(); // Allow HTTPS
            app.UseHttpsRedirection();
            app.UseRouting();


            // CORS - Allow calling the API from WebBrowsers
            app.UseCors(x => x
                .AllowAnyMethod()
                .AllowAnyHeader()
                .AllowAnyOrigin()
                .SetIsOriginAllowed(origin => true));// allow any origin  
                



            // Log In
            app.UseAuthentication(); // UseAuthentication SHOULD ALWAYS BE BEFORE Authorization
            app.UseAuthorization();


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

Метод входа в систему в API идентификации:

  // Log In ===================================================================================
    [HttpPost]
    [Route("LogIn")]
    [AllowAnonymous]
    public async Task<IActionResult> LogIn(string email, string password, bool rememberMe)
    {
        if(email != null amp;amp; password !=null)
        {
            var result = await signInManager.PasswordSignInAsync(email, password, rememberMe, false);
            if (result.Succeeded) // If Login Ok
            {
                return new JsonResult(result);
            }
            return StatusCode(401, "[n "Invalid Log In" n]");  // If Erors return errors 
        }
        return StatusCode(401, "[n "Email or Password cant be empty" n]");
    }
 

Используя swagger, я могу получить файл cookie в браузере:
Img1

РЕДАКТИРОВАТЬ — Почти работает: я добавил в API идентификации в startup.cs это:

 services.AddCors(options =>
        {
            options.AddDefaultPolicy(builder =>
                builder.SetIsOriginAllowed(_ => true)
                .AllowAnyMethod()
                .AllowAnyHeader()
                .AllowCredentials());
        });
 

И в почтовом коде Js:

  // POST ----------------------------------------------------------------------------------
         fetch(identityApiUri   postUrl   formQueryString,  // #1 = API-Address, #2 = API - Controller/Mehod, #3 = form data as sring
            {    
                method: 'POST',
                mode: 'no-cors',
                headers: {
                    'Access-Control-Allow-Origin': '*'
                },
                credentials: 'include'
 

И теперь я получаю файл cookie, но я также получаю исключение:
Сервер возвращает код состояния 200, и теперь я могу получить файл cookie, но я получаю исключение в методе fetch api post.

img2

Хорошо: ложь — но я получаю файл cookie, и сервер возвращает статус 200. img4

Ответ №1:

Вы звоните fetch() из другого источника, чем api, верно? Если это так, то это звучит как простая проблема CORS.

По умолчанию CORS не включает учетные данные, такие как файлы cookie. Вы должны зарегистрироваться, установив режим учетных данных на стороне клиента и Access-Control-Allow-Credentials заголовок на стороне сервера.

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials

С fetch() помощью api режим учетных данных устанавливается с credentials: 'include' помощью опции. Что касается серверной стороны, я не знаком с ASP, но похоже, что он предоставляет какой-то удобный метод для установки соответствующего заголовка.

Как вы намекаете в своем посте, когда в Access-Control-Allow-Credentials заголовке установлено значение true , * значение, означающее любое происхождение, на самом деле не может использоваться в Access-Control-Allow-Origin заголовке, поэтому вам нужно будет четко указать, какое происхождение вы хотите разрешить, т. Е. Происхождение вашего клиентского приложения, происхождение определяется как комбинация протокола, домена и порта.

Комментарии:

1. Я не уверен, правильно ли я понимаю CORS. Я хочу использовать API идентификации непосредственно из клиента, которым является приложение SPA. Но если API хочет, чтобы я сказал, какое происхождение, я не могу знать, потому что клиентская сторона может быть доступна любому. Должен ли я использовать Клиентский сервер в качестве «посредника» для передачи данных в API идентификации и указания их источника? Но если я это сделаю, не возникнет ли у меня та же проблема «CORS» с клиентским сервером — потому что все, кто может получить доступ к Клиенту, могут отправлять данные, и это опять же любое происхождение?

2. «Но если API хочет, чтобы я сказал, какое происхождение, я не могу знать, потому что Клиентская сторона может быть доступна любому». Тот факт, что любой может получить доступ к клиентскому приложению, не влияет на источник клиентского приложения, который всегда будет одним и тем же, независимо от того, как определяется комбинацией протокола, домена и порта. Имеет ли это смысл? Или вы на самом деле хотите сказать, что несколько клиентских приложений, доступных в разных источниках, могут захотеть получить доступ к api?

3. Хорошо, теперь я вроде как понимаю. Итак, вы имеете в виду, что мне просто нужно добавить адрес клиентского приложения в CORS в API идентификации, чтобы разрешить это происхождение.

4. Да, настройте свой сервер так, чтобы он возвращал значение источника вашего клиентского приложения в качестве значения Access-Control-Allow-Origin заголовка.

5. Примечание. Я только что понял, что мой первоначальный ответ был немного усечен, просто отредактировал его. Эта часть: «Что касается серверной части, я не знаком с ASP, но похоже, что он предоставляет какой-то удобный метод для установки соответствующего заголовка».

Ответ №2:

Теперь это работает благодаря @IAmDranged
img1

Метод JS — Fetch Api — Post:

  fetch(identityApiUri   postUrl   formQueryString,  // #1 = API-Address, #2 = API - Controller/Mehod, #3 = form data as sring
        {    
            method: 'POST',
            mode: 'cors',
            //headers: {
            //    'Access-Control-Allow-Origin': 'https://localhost',
            //},
            credentials: 'include'
            
        }).then(function (response)
           {    ...........................
 

API идентификации Asp Net core — Startup.cs:

  // Configure ===========================================================================================
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
              
            app.UseRouting();

            // CORS  
            app.UseCors(x => x
                .AllowAnyMethod()
                .AllowAnyHeader()
                .AllowCredentials()
                .WithOrigins("https://localhost:44351")); // Allow only this origin
                //.SetIsOriginAllowed(origin => true));// Allow any origin  
.................................................
 

Так что решение было:

  1. Добавить в метод JS — выборки:
     mode: 'cors',
    credentials: 'include'
     
  2. Добавить в Asp net Core — startup.cs

пользователи приложений(x => x

                     .AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowCredentials()
                    .WithOrigins("https://localhost:44351")); // Allow only this origin
                    //.SetIsOriginAllowed(origin => true));// Allow any origin 
 

.WithOrigins("https://localhost:44351")); Позволяет только клиенту использовать api, а если я использую .SetIsOriginAllowed(origin => true)); без WithOrigins части — это позволяет всем.