2015-10-21 5 views
5

Ich schwöre, das ist so oft passiert, dass ich tatsächlich hasse CORS. Ich habe gerade meine Anwendung in zwei aufgeteilt, so dass man nur die API-Seite der Dinge behandelt und die andere behandelt die clientseitigen Sachen. Ich habe das schon mal gemacht, so dass ich wusste, dass ich sicher CORS zu machen brauchte aktiviert und erlaubte alle, so dass ich dies bis in WebApiConfig.csGefürchtete CORS Problem mit WebAPI und Token

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 } 
    ); 
} 

Wie Sie sehen können, meine erste Linie Aktiviert das CORS, so sollte es funktionieren. Wenn ich meine Client-Anwendung öffnen und die API abfragen, funktioniert es tatsächlich (ohne die EnableCors bekomme ich den erwarteten CORS-Fehler. Das Problem ist mein /Token bekommt immer noch einen CORS-Fehler. Jetzt bin ich mir bewusst, dass/Token Endpunkt ist nicht Teil der WebAPI, so dass ich meinen eigenen OAuthProvider (was muß mich darauf hinweisen in anderen Orten ganz gut verwendet wird) und das sieht wie folgt 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 den GrantResourceOwnerCredentials Methode Ich erlaube CORS Zugriff auf alles wieder. Dies sollte für alle Anfragen an/Token funktionieren, aber es nicht. Wenn ich versuche, mich von meinem Client anzumelden a Anwendung bekomme ich einen CORS-Fehler. Chrome zeigt dies:

XMLHttpRequest cannot load http://localhost:62605/token . Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin ' http://localhost:50098 ' is therefore not allowed access. The response had HTTP status code 400.

und Firefox zeigt dies:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:62605/token . (Reason: CORS header 'Access-Control-Allow-Origin' missing). Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:62605/token . (Reason: CORS request failed).

Zu Testzwecken habe ich beschlossen, Fiedler zu verwenden, um zu sehen, ob ich etwas sehen konnte, anderes, das mir einen Anhaltspunkt geben könnte, was es passiert. Wenn ich versuche, um sich einzuloggen, zeigt Fiddler einen Antwortcode 400, und wenn ich an der rohen Antwort aussehen kann ich den Fehler sehen:

{"error":"unsupported_grant_type"} 

die seltsam ist, weil die Daten, die ich hat sende nicht geändert und arbeitete gut vor dem Split. Ich entschied mich, den Composer auf fiddler zu verwenden und replizierte, wie die POST-Anfrage aussehen sollte. Wenn ich es ausführe, funktioniert es gut und ich bekomme einen Antwortcode von 200.

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

Update 1

nur als Referenz, die Anfrage von meinem Client-Anwendung wie folgt aussieht:

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 wie folgt 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 

Antwort

1

Es stellt sich heraus, dass es überhaupt kein Problem mit CORS gab. Ich hatte eine Interceptor-Klasse, die die Header falsch änderte. Ich empfehle für zukünftige Referenz, jemand anderes mit diesen Problemen, wenn Sie Ihre CORS entweder in WebConfig.cs oder Ihre Startup-Klasse oder sogar die web.config eingerichtet haben, dann müssen Sie überprüfen, dass nichts Ihre Header ändert. Wenn dies der Fall ist, deaktivieren Sie es und testen Sie es erneut.

+4

Pflege um zu erarbeiten? – user210757

+0

Wie sieht Ihr Interceptor jetzt aus? – RandomUs1r

1

Wir liefen in eine ähnliche Situation und endete mit der Angabe einiger CORS-Daten im Knoten system.webServer der Datei web.config, um die Preflight-Prüfung zu bestehen. Deine Situation ist etwas anders als bei uns, aber vielleicht hilft dir das auch.

Hier ist, was wir haben:

<httpProtocol> 
    <customHeaders> 
    <add name="Access-Control-Allow-Origin" value="*" /> 
    <add name="Access-Control-Allow-Headers" value="Content-Type" /> 
    <add name="Access-Control-Allow-Credentials" value="true" /> 
    <add name="Access-Control-Allow-Methods" value="GET, POST, OPTIONS" /> 
    </customHeaders> 
</httpProtocol> 
+0

ich versuchte, dass, dass wasn‘ t das Problem. Irgendwann habe ich es herausgefunden. Ich werde die Lösung posten. Es hat nichts mit CORs zu tun, wie ich erwartet hatte :( – r3plica

2

Da OAuthAuthorizationServer läuft als Owin Middleware Sie das entsprechende Paket verwenden müssen Microsoft.Owin.Cors CORS zu ermöglichen, die mit jeder Middleware in der Pipeline arbeitet. Denken Sie daran, dass WebApi & Mvc sind nur Middleware selbst in Bezug auf die OWIN-Pipeline.

Entfernen Sie also config.EnableCors(new EnableCorsAttribute("*", "*", "*")); aus Ihrer WebApiConfig und fügen Sie Ihrer Startup-Klasse Folgendes hinzu. Hinweisapp.UseCors muss er die app.UseOAuthAuthorizationServer

app.UseCors(CorsOptions.AllowAll) 
+0

Yep kann es nicht in beiden haben, der richtige Ort ist in startup.cs, meins begann zu arbeiten, sobald ich es aus webapiconfig.cs herausnahm – RandomUs1r

6

Innerhalb von

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) 

Sie sich von diesem befreien vorangehen:

context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" }); 

Derzeit machst du das zweimal CORS Sache. Einmal mit .EnableCors und auch nochmal durch Schreiben des Headers in Ihren Token-Endpunkt.

Für das, was es wert ist, in meiner OWIN Startklasse habe ich dies an der Spitze:

app.UseCors(CorsOptions.AllowAll); 

ich es habe auch nicht in meiner WebAPI Register Methode, wie ich die OWIN Start handelst mich im Stich gelassen es.

+2

Ich lief auf dieses Problem mit aC# web api wird von einer eckigen App aufgerufen. ** App.UseCors (Microsoft.Owin.Cors.CorsOptions.AllowAll) verschieben; ** an den Anfang von ** öffentlichen void-Konfiguration (IAppBuilder-App) {} ** tat Der Trick: Danke Bill. –

3

@ r3plica

hatte ich dieses Problem, und es ist wie so Bill.

die Zeile "app.UseCors" an der Spitze in Konfigurationsmethode()

Beispiel (vor ConfigureOAuth (app) genug ist):

public void Configuration(IAppBuilder app) 
    { 
     app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); 

     HttpConfiguration config = new HttpConfiguration(); 

     ConfigureWebApi(config); 
     ConfigureOAuth(app); 

     app.UseWebApi(config); 
    } 
+2

Willkommen auf der Website! Ich hoffe, Sie kümmern sich nicht um Feedback, um Ihre Antwort zu verbessern. Erstens können Sie nicht '@' jemand in Fragen oder Antworten, nur Kommentare, das ist also nur Rauschen, das Sie löschen können. Das Hinzufügen einer Antwort, die ein Beispiel für eine andere Antwort bietet, ist gut, ich empfehle, in diesem Fall klar mit der Antwort zu verlinken : //stackoverflow.com/a/33265170/1677 912) ... ', aus Gründen der Klarheit. Viel Glück! – Mogsdad

+0

Das war genau das Problem, dem ich begegnete.Ich habe mich gefragt, ob Sie eine Ahnung hatten, warum das passiert? – scapegoat17