Gefürchtetes CORS-Problem mit WebAPI und Token

Ich schwöre, das ist mir so oft passiert, dass ich tatsächlichHas CORS. Ich habe meine Anwendung soeben in zwei Teile aufgeteilt, dass einer nur die API-Seite der Dinge und der andere die clientseitigen Dinge behandelt. Ich habe das schon einmal gemacht, also wusste ich, dass ich sicherstellen musste, dass CORS aktiviert und alle zugelassen sind, also habe ich dies in @ eingerichte WebApiConfig.cs

public static void Register(HttpConfiguration config)
{

    // Enable CORS
    config.EnableCors(new EnableCorsAttribute("*", "*", "*"));

    // Web API configuration and services
    var formatters = config.Formatters;
    var jsonFormatter = formatters.JsonFormatter;
    var serializerSettings = jsonFormatter.SerializerSettings;

    // Remove XML formatting
    formatters.Remove(config.Formatters.XmlFormatter);
    jsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));

    // Configure our JSON output
    serializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
    serializerSettings.Formatting = Formatting.Indented;
    serializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
    serializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.None;

    // Configure the API route
    config.MapHttpAttributeRoutes();
    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );
}

ie Sie sehen können, aktiviert meine erste Zeile das CORS, so sollte es funktionieren. Wenn ich meine Client-Anwendung öffne und die API abfrage, funktioniert sie tatsächlich (ohne die EnableCors erhalte ich den erwarteten CORS-Fehler. Das Problem ist mein/Zeiche erhält immer noch einen CORS-Fehler. Jetzt ist mir bewusst, dass der / token-Endpunkt nicht Teil der WebAPI ist. Deshalb habe ich meinen eigenen OAuthProvider erstellt (auf den ich hinweisen muss, dass er an anderen Orten verwendet wird) und das sieht folgendermaßen aus:

public class OAuthProvider<TUser> : OAuthAuthorizationServerProvider
    where TUser : class, IUser
{
    private readonly string publicClientId;
    private readonly UserService<TUser> userService;

    public OAuthProvider(string publicClientId, UserService<TUser> userService)
    {
        if (publicClientId == null)
            throw new ArgumentNullException("publicClientId");

        if (userService == null)
            throw new ArgumentNullException("userService");

        this.publicClientId = publicClientId; 
        this.userService = userService;
    }

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });

        var user = await this.userService.FindByUserNameAsync(context.UserName, context.Password);

        if (user == null)
        {
            context.SetError("invalid_grant", "The user name or password is incorrect.");
            return;
        }

        var oAuthIdentity = this.userService.CreateIdentity(user, context.Options.AuthenticationType);
        var cookiesIdentity = this.userService.CreateIdentity(user, CookieAuthenticationDefaults.AuthenticationType);
        var properties = CreateProperties(user.UserName);
        var ticket = new AuthenticationTicket(oAuthIdentity, properties);

        context.Validated(ticket);
        context.Request.Context.Authentication.SignIn(cookiesIdentity);
    }

    public override Task TokenEndpoint(OAuthTokenEndpointContext context)
    {
        foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
            context.AdditionalResponseParameters.Add(property.Key, property.Value);

        return Task.FromResult<object>(null);
    }

    public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        // Resource owner password credentials does not provide a client ID.
        if (context.ClientId == null)
        {
            context.Validated();
        }

        return Task.FromResult<object>(null);
    }

    public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
    {
        if (context.ClientId == this.publicClientId)
        {
            var redirectUri = new Uri(context.RedirectUri);
            var expectedRootUri = new Uri(context.Request.Uri, redirectUri.PathAndQuery);

            if (expectedRootUri.AbsoluteUri == redirectUri.AbsoluteUri)
                context.Validated();
        }

        return Task.FromResult<object>(null);
    }

    public static AuthenticationProperties CreateProperties(string userName)
    {
        IDictionary<string, string> data = new Dictionary<string, string>
        {
            { "userName", userName }
        };

        return new AuthenticationProperties(data);
    }
}

Wie Sie sehen können, In der GrantResourceOwnerCredentials Methode Ich ermögliche CORS den Zugriff auf alles wieder. Dies sollte für alle Anfragen an / token funktionieren, tut es aber nicht. Wenn ich versuche, mich von meiner Client-Anwendung aus anzumelden, wird ein CORS-Fehler angezeigt. Chrome zeigt dies:

XMLHttpRequest kann nicht geladen werdenhttp: // localhost: 62605 / token. Die Antwort auf die Preflight-Anforderung besteht die Zugriffskontrollprüfung nicht: In der angeforderten Ressource ist kein Header 'Zugriffskontrolle-Zulassen-Ursprung' vorhanden. Ursprung 'http: // localhost: 50098 'ist daher nicht zugänglich. Die Antwort hatte den HTTP-Statuscode 400.

und Firefox zeigt dies:

Ursprungsübergreifende Anforderung blockiert: Dieselbe Ursprungsrichtlinie verhindert das Lesen der Remote-Ressource unterhttp: // localhost: 62605 / token. (Grund: CORS-Header 'Access-Control-Allow-Origin' fehlt). Ursprungsübergreifende Anforderung blockiert: Dieselbe Ursprungsrichtlinie verhindert das Lesen der Remote-Ressource unterhttp: // localhost: 62605 / token. (Grund: CORS-Anforderung fehlgeschlagen).

u Testzwecken habe ich mich entschlossen, Fiddler zu verwenden, um zu prüfen, ob ich noch etwas sehen kann, das mir einen Hinweis darauf gibt, was gerade passiert. Beim Versuch, mich anzumelden, zeigt FIddler den Antwortcode 400 an. Wenn ich mir die unformatierte Antwort ansehe, wird der Fehler angezeigt:

{"error":"unsupported_grant_type"}

Das ist seltsam, weil sich die Daten, die ich sende, nicht geändert haben und vor dem Split einwandfrei funktionierten. Ich entschied mich, den Composer für Fiddler zu verwenden und wiederholte, wie die POST-Anfrage aussehen sollte. Wenn ich es ausführe, funktioniert es einwandfrei und ich erhalte einen Antwortcode von 200.

Hat jemand eine Idee, warum dies passieren könnte?

Update 1

Nur als Referenz sieht die Anfrage von meiner Client-App so aus:

OPTIONS http://localhost:62605/token HTTP/1.1
Host: localhost:62605
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Access-Control-Request-Method: POST
Origin: http://localhost:50098
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.71 Safari/537.36
Access-Control-Request-Headers: accept, authorization, content-type
Accept: */*
Referer: http://localhost:50098/account/signin
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8

vom Komponisten sieht es so aus:

POST http://localhost:62605/token HTTP/1.1
User-Agent: Fiddler
Content-Type: 'application/x-www-form-urlencoded'
Host: localhost:62605
Content-Length: 67

grant_type=password&userName=foo&password=bar

Antworten auf die Frage(10)

Ihre Antwort auf die Frage