Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement CORS Support #86

Closed
scottoffen opened this issue Jul 27, 2016 · 9 comments
Closed

Implement CORS Support #86

scottoffen opened this issue Jul 27, 2016 · 9 comments
Assignees
Milestone

Comments

@scottoffen
Copy link
Owner

scottoffen commented Jul 27, 2016

Given that CORS is disabled by default when using HttpListener, here is a work around until I can provide a more elegant solution.

Implementing CORS

You can implement access control via CORS for all requests using a BeforeRouting delegate. Add additional filtering logic if you only want to the policy to vary based on the request. This avoids the need to add these lines to individual routes, and allows you to manage the policy for the entire server in a single location.

server.Router.BeforeRouting += MyCorsPolicy;

Following are several ways you can define your policy.

Using the Wildcard

When using the wildcard value, it is important to remember that:

For requests without credentials, the literal value "*" can be specified, as a wildcard; the value tells browsers to allow requesting code from any origin to access the resource. Attempting to use the wildcard with credentials will result in an error. source

void MyCorsPolicy(IHttpContext context)
{
    context.Response.AddHeader("Access-Control-Allow-Origin", "*");
    context.Response.AddHeader("Access-Control-Allow-Headers", "X-Requested-With");
}

Additionally, when specifying Access-Control-Allow-Headers header:

The simple headers, Accept, Accept-Language, Content-Language, Content-Type (but only with a MIME type of its parsed value (ignoring parameters) of either application/x-www-form-urlencoded, multipart/form-data, or text/plain), are always available and don't need to be listed by this header. source

Single Origin

When specifying a single origin, it is important to remeber that:

Two URLs have the same origin if the protocol, port (if specified), and host are the same for both. source

void MyCorsPolicy(IHttpContext context)
{
    context.Response.AddHeader("Access-Control-Allow-Origin", "http://localhost:1234/");
    context.Response.AddHeader("Vary", "Origin");
}

Additionally, the Vary header should be provided:

If the server sends a response with an Access-Control-Allow-Origin value that is an explicit origin (rather than the "*" wildcard), then the response should also include a Vary response header with the value Origin — to indicate to browsers that server responses can differ based on the value of the Origin request header. source

Dynamic Origin

Limiting the possible Access-Control-Allow-Origin values to a set of allowed origins requires code on the server side to check the value of the Origin request header, compare that to a list of allowed origins, and then if the Origin value is in the list, to set the Access-Control-Allow-Origin value to the same value as the Origin value. source

void MyCorsPolicy(IHttpContext context)
{
    var domain = context.Request.UrlReferrer?.ToString();

    if (!string.IsNullOrWhiteSpace(domain) && ValidOrigins.Contains(domain))
    {
        context.Response.AddHeader("Access-Control-Allow-Origin", domain);
        context.Response.AddHeader("Vary", "Origin");
    }
}

IEnumerable<string> ValidOrigins
{
    get
    {
        yield return "http://mydomain.org/";
        yield return "http://localhost:1234";
    }
}

Dynamic Routes

If only some routes should allow CORS, you can put whatever logic you want to see in the delegate based on the incoming request. Just remember to follow the rules outlined above!

@scottoffen scottoffen added this to the Backlog milestone Jul 27, 2016
@scottoffen scottoffen self-assigned this Jul 27, 2016
@scottoffen scottoffen modified the milestones: 4.0.1, Backlog Aug 5, 2016
@scottoffen scottoffen changed the title Add flag to enable CORS Implement CORS Support Aug 16, 2016
@scottoffen scottoffen modified the milestones: Backlog, 4.0.0.next, 4.1.0 Aug 18, 2016
@scottoffen scottoffen modified the milestone: 4.1.0.x Mar 10, 2017
@wizicer
Copy link
Contributor

wizicer commented Apr 1, 2017

Before this feature be implemented, in case someone need CORS support. Could add following before sending response.

context.Response.Headers["Access-Control-Allow-Origin"] = "*";

@DavidFlamini
Copy link

I couldn't get around enabling CORS together with authentication, any ideas?

@lacu
Copy link

lacu commented Nov 3, 2017

I have not been able to solve the CORS problem, I have added the header to the response but it does not work
this is my code in the app

context.Response.Headers["Access-Control-Allow-Origin"] = "*";
context.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
context.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With");
context.Response.AddHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS,PATCH");
context.Response.AddHeader("Access-Control-Allow-Origin", "*");

and this is the result in browser
image

Someone find a solution???
Thanks!!!

@DavidFlamini
Copy link

DavidFlamini commented Nov 3, 2017

@lacu I just checked my code from a couple of months ago, this is all that I had to add for CORS to work in my setup:

context.Response.AddHeader("Access-Control-Allow-Origin", "*");
context.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With");

If you are doing auth then it doesn't work, I had to remove auth for CORS to work:

This doesn't work with CORS:

server.Advanced.AuthenticationSchemes = System.Net.AuthenticationSchemes.Basic;

If you find some way to get both working together please share, I'm interested :)

@lacu
Copy link

lacu commented Nov 3, 2017

@DavidFlamini ,
I will have to put this line in each route like this?

[RestRoute(HttpMethod = HttpMethod.PUT, PathInfo = @"^/ticket(\?[^/]*)?$")]
[RestRoute(HttpMethod = HttpMethod.PUT, PathInfo = @"^/ticket:(\?[^/]*)?$")]
[RestRoute(HttpMethod = HttpMethod.PUT, PathInfo ="/ticket")]
[RestRoute(HttpMethod = HttpMethod.OPTIONS, PathInfo = "/ticket")]
public IHttpContext Ticket(IHttpContext context)
{
    context.Response.AddHeader("Access-Control-Allow-Origin", "*");
    context.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With");

@DavidFlamini
Copy link

@lacu If I remember correctly, unless you are setting it upstream, then yes. You could have an upstream route set those for you, too. Please check here: https://sukona.github.io/Grapevine/en/getting-started.html "Responding To Requests"

@DavidFlamini
Copy link

Hi, today I wanted to test locally with both CORS + Basic Auth without configuring a proxy and run into my own post, I ended up parsing the auth header myself like so:

static private bool IsAuthorized(string AuthorizationHeader)
{
    try
    {
        var identity = Encoding.UTF8.GetString(Convert.FromBase64String(AuthorizationHeader.Split(" ")[1])).Split(":");
        var name = identity[0];
        var password = identity[1];


        if (name == "user" && password == "password")
        {
            return true;
        }

        return false;
    }
    catch (Exception ex )
    {

        return false;
    }
}
[RestRoute(PathInfo = "/api/v1/services")]
public IHttpContext Services(IHttpContext context)
{
    context.Response.ContentType = ContentType.JSON;

    if (IsAuthorized(context.Request.Headers["Authorization"]))
    {
        // Do stuff and send some response

    }
    else
    {
        context.Response.StatusCode = Grapevine.Shared.HttpStatusCode.Unauthorized;
        context.Response.SendResponse("{\"Error\": \"Unauthorized\"}");
    }
    return context;
}

@scottoffen
Copy link
Owner Author

I just finished updating the text of this issue to reflect the suggestions here for implementing CORS. Thanks for all of your input, guys!

@scottoffen scottoffen pinned this issue Dec 30, 2020
@virtruvio
Copy link

Hi Scott -- read your notes for 5.0 beta 1 respecting the UseCorsPolicy method. I'm finding that the several overloads that accept an Allow-Origin add a trailing slash to the transmitted header value. (e.g. "https://my.domain.com/" rather than "https://my.domain.com" . Current chrome transmits a non-slashed origin (e.g. "https://my.domain.com" which causes the CORS preflight to fail as a mismatched origin. I've worked around this by adding an async Task method that can be called to add Response headers during OnRequestAsync event, and adding my own headers via the .AddHeader Response method. This works to match the origin & supplied Allow-Origin header. But this isn't likely to be the best practice for overcoming. Am I doing something wrong with UseCorsPolicy? Also, does the UseCorsPolicy respond to OPTIONS requests during preflight with a HTTP_OK? It doesn't appear to do so. I assume it should be handled with an OPTIONS route?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants