401 Несанкционировано при отправке запроса ajax на веб-API

Я ломаю голову над этим уже 2 дня. Я использую WebAPI версии 2.2 и CORS. Эта настройка работает на стороне сервера, мне разрешено получать авторизованный контент из кода сервера моего веб-клиента, но я получаю несанкционированный доступ к своим ajax-вызовам.

Вот моя конфигурация:

Конфигурация веб-API

Вебапиконфигурация:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {

        // Web API configuration and services
        // Configure Web API to use only bearer token authentication.
        config.SuppressDefaultHostAuthentication();
        config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
        config.Filters.Add(new HostAuthenticationFilter(DefaultAuthenticationTypes.ApplicationCookie));

        //enable cors
        config.EnableCors();

        // Web API routes
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        config.Filters.Add(new ValidationActionFilter());
    }
}

Запуск.Auth.cs:

// Configure the db context and user manager to use a single instance per request
        app.CreatePerOwinContext(UserContext<ApplicationUser>.Create);
        app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);

        // Enable the application to use a cookie to store information for the signed in user
        // and to use a cookie to temporarily store information about a user logging in with a third party login provider
        app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                CookieHttpOnly = true,
                CookieName = "Outpour.Api.Auth"
            }
        );

        //app.UseCors(CorsOptions.AllowAll);
        //app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

        // Configure the application for OAuth based flow
        PublicClientId = "self";
        OAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/Token"),
            Provider = new ApplicationOAuthProvider(PublicClientId),
            AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
            AllowInsecureHttp = true
        };

        // Enable the application to use bearer tokens to authenticate users
        app.UseOAuthBearerTokens(OAuthOptions);

(Я пробовал каждую комбинацию app.UseCors(CorsOptions.AllowAll) и config.EnableCors())

Атрибут моего контроллера:

[Authorize]
[EnableCors("http://localhost:8080", "*", "*", SupportsCredentials = true)]
[RoutePrefix("api/videos")]
public class VideosController : ApiController...

Веб-клиент

Звонок Аякса:

$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
            options.crossDomain = {
                crossDomain: true
            };
            options.xhrFields = {
                withCredentials: true
            };
        });

        function ajaxGetVideoResolutionList() {
            var request = {
                type: "GET",
                dataType: "json",
                timeout: Outpour.ajaxTimeOut,
                url: Outpour.apiRoot + "/videos/resolutions"
            };
            $.ajax(request).done(onAjaxSuccess).fail(onAjaxError);

Создание куки:

var result = await WebApiService.Instance.AuthenticateAsync<SignInResult>(model.Email, model.Password);

            FormsAuthentication.SetAuthCookie(result.AccessToken, model.RememberMe);

            var claims = new[]
            {
                new Claim(ClaimTypes.Name, result.UserName), //Name is the default name claim type, and UserName is the one known also in Web API.
                new Claim(ClaimTypes.NameIdentifier, result.UserName) //If you want to use User.Identity.GetUserId in Web API, you need a NameIdentifier claim.
            };

            var authTicket = new AuthenticationTicket(new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie), new AuthenticationProperties
            {
                ExpiresUtc = result.Expires,
                IsPersistent = model.RememberMe,
                IssuedUtc = result.Issued,
                RedirectUri = redirectUrl
            });

            byte[] userData = DataSerializers.Ticket.Serialize(authTicket);

            byte[] protectedData = MachineKey.Protect(userData, new[] { "Microsoft.Owin.Security.Cookies.CookieAuthenticationMiddleware", DefaultAuthenticationTypes.ApplicationCookie, "v1" });

            string protectedText = TextEncodings.Base64Url.Encode(protectedData);

            Response.Cookies.Add(new HttpCookie("Outpour.Api.Auth")
            {
                HttpOnly = true,
                Expires = result.Expires.UtcDateTime,
                Value = protectedText
            });

И последнее, но не менее важное: мои заголовки.

Remote Address:127.0.0.1:8888
Request URL:http://127.0.0.1/api/videos/resolutions
Request Method:GET
Status Code:401 Unauthorized

**Request Headersview source**
Accept:application/json, text/javascript, */*; q=0.01
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Cache-Control:no-cache
Host:127.0.0.1
Origin:http://localhost:8080
Pragma:no-cache
Proxy-Connection:keep-alive
Referer:http://localhost:8080/video/upload
User-Agent:Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36

**Response Headersview source**
Access-Control-Allow-Credentials:true
Access-Control-Allow-Origin:http://localhost:8080
Cache-Control:no-cache
Content-Length:61
Content-Type:application/json; charset=utf-8
Date:Wed, 08 Oct 2014 04:01:19 GMT
Expires:-1
Pragma:no-cache
Server:Microsoft-IIS/8.0
WWW-Authenticate:Bearer
X-AspNet-Version:4.0.30319
X-Powered-By:ASP.NET

Инструменты разработчика и скрипач утверждают, что с запросом не было отправлено никаких файлов cookie.


person Dale Marshall    schedule 08.10.2014    source источник


Ответы (2)


Я полагаю, что вы смешиваете здесь аутентификацию файлов cookie и токены носителя, вы не отправляете токен доступа в заголовке авторизации с вашим запросом, поэтому вы продолжаете получать 401. Кроме того, вам нужно только разрешить CORS с помощью application.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); и удалить его еще где из вашего атрибута контроллеров даже из конфигурации.

Посмотрите мой репозиторий здесь, где я реализовал CORS, а внешний интерфейс тоже на AngularJS. он работает правильно. Вот живая демонстрация для этого репозитория, откройте инструменты разработчика и отслеживайте запросы, вы должны увидеть предварительную проверку запрос, прежде чем вы увидите свой HTTP-запрос на получение.

Если вам нужно просто защитить свой API с помощью токенов-носителей, я рекомендую вам прочитать Аутентификация на основе токенов post

person Taiseer Joudeh    schedule 21.10.2014
comment
У меня та же проблема, но я использую не WebApi в качестве хоста ресурсов, а NancyFX. Все находится, если вызывается из curl или около того, но не из приложения AngularJS. Конечная точка маркера в порядке, но если дело доходит до GET для маршрута, предоставленного Нэнси, сетевая аутентификация ASP отклоняет предварительную проверку OPTION с 401. Я понятия не имею, как это преодолеть. Кроме того, я не уверен, соответствует ли репо демо: репо использует client_id в запросе токена, демо явно нет. Не связано, а указывает на то, что обе версии разные. - person decades; 31.03.2015

Это может быть связано с тем, что ваш API не находится на пришедшем URL-адресе в качестве вызывающего приложения. Ваш URL для API: http://127.0.0.1/ (игнорируя путь к папке, что не имеет значения)

... но вы звоните с http://127.0.0.1:8888, который считается отдельным сайтом, так как порт другой. Поскольку браузер считает, что сайт другой, он не будет отправлять файл cookie.

Пробовали ли вы протестировать его со страницы, размещенной на том же URL-адресе, что и API (с тем же портом)?

Самое главное: убедитесь, что вы видите, как файл cookie отправляется в Fiddler.

Вы также можете найти более актуальную информацию о том, как заставить это работать на этот ответ

person NickG    schedule 21.10.2014