405 Method Not Allowed on /api/login OPTIONS request with grails-spring-security-rest plugin (and the fight continues...) - spring

In my app, I am using grails-spring-security-rest plugin and I am currently at the stage of building authentication flow.
If I use a rest client everything works as expected: I am able to login by posting username & password in json and get tokens back. Perfect!
Now, I am trying to integrate this whole thing with the web form and, of course, the browser sends preflight OPTIONS request.
I have a simple interceptor setup:
#GrailsCompileStatic
class CorsInterceptor {
int order = HIGHEST_PRECEDENCE
CorsInterceptor() {
matchAll() // match all controllers
//.excludes(controller:"login") // uncomment to add exclusion
}
boolean before() {
String origin = request.getHeader("Origin");
boolean options = "OPTIONS".equals(request.getMethod());
if (options) {
if (origin == null) return;
response.addHeader("Access-Control-Allow-Headers", "origin, authorization, accept, content-type, x-requested-with");
response.addHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS");
response.addHeader("Access-Control-Max-Age", "3600");
}
response.addHeader("Access-Control-Allow-Origin", origin == null ? "*" : origin);
response.addHeader("Access-Control-Allow-Credentials", "true");
true // proceed to controller
}
boolean after() { true }
void afterView() {
// no-op
}
}
The interceptor works perfectly got valid get requests and adds the headers into the response. However, when I am trying to senf this:
curl -X "OPTIONS" "http://localhost:8080/api/login" \
-H "Origin: http://localhost:3000" \
-H "Content-Type: application/json" \
-d "{\"username\":\"customer\",\"password\":\"password\"}"
I am always getting 405 Method Not Allowed back and the execution is not even getting to interceptor at all.
My assumption is that the login controller provided by the plugin is not allowing that, and I need to put an additional URL mapping to overcome this problem. My problem is, what this mapping support to look like?
Also, it is possible to setup mapping that will work for all OPTIONS requests, so I don' need to specify them one by one?
Given all that, it is only my assumption... Am I even in the right direction with it?
Thanks,

this problem was faced by many other users as well and was repeatedly asked on github and slack channels. I've created a sample example which has CORS filter in under src/ directory and I've registered it as spring bean. Here is github repo with example app. The Cors filter code is below
#Priority(Integer.MIN_VALUE)
class CorsFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse resp, FilterChain chain)
throws ServletException, IOException {
String origin = req.getHeader("Origin");
boolean options = "OPTIONS".equals(req.getMethod());
if (options) {
if (origin == null) return;
resp.addHeader("Access-Control-Allow-Headers", "origin, authorization, accept, content-type, x-requested-with");
resp.addHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS");
resp.addHeader("Access-Control-Max-Age", "3600");
}
resp.addHeader("Access-Control-Allow-Origin", origin == null ? "*" : origin);
resp.addHeader("Access-Control-Allow-Credentials", "true");
if (!options) chain.doFilter(req, resp);
}
}
Register this as spring bean in resources.groovy file as below:
beans = {
corsFilter(CorsFilter)
}
Here is the question asked on github repo of this plugin.
Update
Grails3 CORS interceptor plugin has been update to include SpringSecurityCorsFilter. For details of how to use, refer to this sample
This plugin is much better than the servlet filter I've written above.

Shurik, the 'filters' you speak of are now called 'interceptors' to match more common nomenclature. This 'filter' is NOT deprecated as this is an 'ACTUAL' filter in Grails/SpringBoot. This is why they deprecated the Interceptors from being called this because of all the naming confusion.

Related

springboot rest api how to answer to a preflight request sent by the CORS

I'm developing a webapp angular-springboot with some other people, and to a few of those certain requests of the app are blocked by the cors with this error:
Access to XMLHttpRequest at 'https://localhost:8443/api/contratto/update' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource
so I have researched what a preflight request is and I've added this method to the controller:
#RequestMapping(value = "/update",method = RequestMethod.OPTIONS)
public ResponseEntity<String> preFlightHandler(){
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("Access-Control-Allow-Origin",
"https://localhost:8443");
return ResponseEntity.ok()
.headers(responseHeaders)
.body("gggg");
}
but it never even gets executed, how do I create a method mapped specifically for preflights?
didn't make a method mapped for that but I solved the error, Im' using the WebSecurityConfigurerAdapter and in the method configure(HttpSecurity http) I added the line
http.cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues());
I have backend API which was accessible with GET, but couldn't be successful with POST, due to PREFLIGHT issue, which incurred CORS blockage.
Thus, in this site,
https://newbedev.com/http-request-from-angular-sent-as-options-instead-of-post#:~:text=HTTP%20request%20from%20Angular%20sent%20as%20OPTIONS%20instead,is%20allowed%20from%20a%20particular%20domain%20as%20follows%3A
I have found that, you just simply play with OPTIONS method, which your browser calls to backend for before "ACTUAL" call. this is called Preflight request.
It uses OPTIONS method instead of get/post/put. Thus, this might be of help.
If you use Node Js Server:
if (req.method == "OPTIONS")
{
res.writeHead(200, {"Content-Type": "application/json"});
res.end();
}
With PHP, I use this code:
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
header("HTTP/1.1 200 ");
exit;
}
These are my headers in PHP:
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
header("Access-Control-Max-Age: 3600");
header("HTTP/1.1 200");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Methods, Access-Control-Allow-Headers, Authorization, X-Requested-With, Origin");
Note the OPTIONS method in the headers.
If you use other language, that might be easy for you using this concept.
That's it.

Handle preflight request with Golang, Revel

I made API application with Golang + Revel framework
Now I tried to send http request from front end application, made by vue.js.
But because of cors, PUT method cannot be handled.(POST method worked fine now)
In revel, I thought we can set header in app/init.go file, like this
var HeaderFilter = func(c *revel.Controller, fc []revel.Filter) {
c.Response.Out.Header().Add("X-Frame-Options", "SAMEORIGIN")
c.Response.Out.Header().Add("X-XSS-Protection", "1; mode=block")
c.Response.Out.Header().Add("X-Content-Type-Options", "nosniff")
c.Response.Out.Header().Add("Referrer-Policy", "strict-origin-when-cross-origin")
// Add them by myself
c.Response.Out.Header().Add("Access-Control-Allow-Headers", "Origin, Content-Type, Accept")
c.Response.Out.Header().Add("Access-Control-Allow-Origin", "*")
c.Response.Out.Header().Add("Access-Control-Allow-Method", "POST, GET, OPTIONS, PUT, DELETE")
c.Response.Out.Header().Add("Content-Type", "application/json; charset=UTF-8")
fc[0](c, fc[1:]) // Execute the next filter stage.
But still I got 404 error from API and request method is shown as OPTIONS.
How can I set request header to enable to handle every requests ?
Add a filters before revel.PanicFilter
revel.Filters = []revel.Filter{
ValidateOrigin,
revel.PanicFilter, // Recover from panics and display an error page instead.
revel.RouterFilter, // Use the routing table to select the right Action
revel.FilterConfiguringFilter, // A hook for adding or removing per-Action filters.
revel.ParamsFilter, // Parse parameters into Controller.Params.
IpLimitFilter,
revel.SessionFilter, // Restore and write the session cookie.
revel.FlashFilter, // Restore and write the flash cookie.
revel.ValidationFilter, // Restore kept validation errors and save new ones from cookie.
revel.I18nFilter, // Resolve the requested language
HeaderFilter,
revel.InterceptorFilter, // Run interceptors around the action.
revel.CompressFilter, // Compress the result.
revel.BeforeAfterFilter, // Call the before and after filter functions
revel.ActionInvoker, // Invoke the action.
}
var ValidateOrigin = func(c *revel.Controller, fc []revel.Filter) {
if c.Request.Method == "OPTIONS" {
c.Response.Out.Header().Add("Access-Control-Allow-Origin", "*")
c.Response.Out.Header().Add("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization") //自定义 Header
c.Response.Out.Header().Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
c.Response.Out.Header().Add("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
c.Response.Out.Header().Add("Access-Control-Allow-Credentials", "true")
c.Response.SetStatus(http.StatusNoContent)
// 截取复杂请求下post变成options请求后台处理方法(针对跨域请求检测)
} else {
c.Response.Out.Header().Add("Access-Control-Allow-Headers", "Origin, Content-Type, Accept")
c.Response.Out.Header().Add("Access-Control-Allow-Origin", "*")
c.Response.Out.Header().Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
c.Response.Out.Header().Add("Content-Type", "application/json; charset=UTF-8")
c.Response.Out.Header().Add("X-Frame-Options", "SAMORIGIN")
c.Response.Out.Header().Add("Vary", "Origin, Access-Control-Request-Method, Access-Control-Request-Headers")
fc[0](c, fc[1:]) // Execute the next filter stage.
}
}
...
Because ajax turns a simple request (single post) request into a secondary request, that is, an options request is first sent to determine whether the domain is allowed, and then the real request post is sent to obtain the result.

Http OPTION REQUEST with cors in web api with authentication

I am using Microsoft.AspNet.WebApi.Cors to support cross origin request as per this
https://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api
My web apis are configured to use windows authentication and every request coming from angular has withCredentials set to true. Everything is working with HTTP GET but with PUT request sends preflight request which in getting unauthorised. My question is does Microsoft.AspNet.WebApi.Cors support configuration of OPTION request.
Put this in your Global.asax.cs
(I'm sure you either found a solution or gave up, but this is the link that I found on Google while looking for a solution to this.)
protected void Application_BeginRequest()
{
if (Request.HttpMethod == "OPTIONS")
{
Response.StatusCode = (int)HttpStatusCode.OK;
Response.AppendHeader("Access-Control-Allow-Origin", Request.Headers.GetValues("Origin")[0]);
Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
Response.AppendHeader("Access-Control-Allow-Credentials", "true");
Response.End();
}
}

Enabling WebAPI CORS for Angular 2 authentification

I've seen a few answers on stackoverflow and I'm lost.
I have webapi 2 + standalone angular 2
webapi project is from template. the only thing i've changed is that i added CORS
and following line to IdentityConfig.cs > ApplicationUserManager Create()
context.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "http://localhost:3000" });
here I've all standard from template:
[Authorize]
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
On the client side I have function to get access token, that works properly:
authenticate(loginInfo: Login): boolean {
let headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded');
this.http.post(this.baseUrl + 'Token', 'grant_type=password&username=alice2#example.com&password=Password2!',
{
headers: headers
})
.subscribe(
data => this.saveAuthToken(<AccessToken>(data.json())),
err => this.handleError(err),
() => console.log('authentication Complete')
);
return true;
}
And get function, that works ok without authentication (commented code) :
get(url: string) {
var jwt = sessionStorage.getItem(this.idTokenName);
var authHeader = new Headers();
if (jwt) {
authHeader.append('Authorization', 'Bearer ' + jwt);
}
return this.http.get(this.apiUrl + url, {
headers: authHeader
})
.map(res => res.json())
.catch(this.handleError);
//return this.http.get(this.apiUrl + url)
// .map(res => res.json())
// .catch(this.handleError);
}
But when i try to add Authorization header server returns:
XMLHttpRequest cannot load http://localhost:3868/api/values. Response for preflight has invalid HTTP status code 405
How to allow user to authenticate through Angular properly?
Install-Package Microsoft.Owin.Cors
Add to App_Start > Startup.Auth.cs > ConfigureAuth(IAppBuilder app)
app.UseCors(CorsOptions.AllowAll);
Only one line. That's all.
You could explicitly add the needed headers and methods:
context.Response.Headers.Add(
"Access-Control-Allow-Headers",
new[] { "Content-Type, Authorization" }
);
context.Response.Headers.Add(
"Access-Control-Allow-Methods",
new[] { "GET, POST, OPTIONS" }
);
I had to add the following to the globalasax.cs:
protected void Application_BeginRequest()
{
var req = HttpContext.Current.Request;
var res = HttpContext.Current.Response;
var val = res.Headers.GetValues("Access-Control-Allow-Origin");
if (val == null)
{
if (!req.Url.ToString().ToLower().Contains("token") || (req.Url.ToString().ToLower().Contains("token") && req.HttpMethod == "OPTIONS"))
{
res.AppendHeader("Access-Control-Allow-Origin", "http://localhost:4200");
}
}
if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS")
{
res.AppendHeader("Access-Control-Allow-Credentials", "true");
res.AppendHeader("Access-Control-Allow-Headers", "Content-Type, X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name");
res.AppendHeader("Access-Control-Allow-Methods", "POST,GET,PUT,PATCH,DELETE,OPTIONS");
res.StatusCode = 200;
res.End();
}
}
When talking to webapi angular and using a http post that either contains non-standard body contents (i.e json) or authentication then a pre-flight request is set that basically says 'am i okay to send the actual request'. Now there are several ways around this that essentially involve short cuts - use IE (if the server is on the same machine as IE ignores the port when deciding what the same machine is) or open CORS up to permit all (which is dangerous as the granting permission to an authenticated user opens your system up to all manner of hacks).
Anyway the solution we used was to add a method to the Globals.asax.cs on the server
protected void Application_BeginRequest()
{
if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS")
{
var origin = HttpContext.Current.Request.Headers["Origin"];
Response.Headers.Add("Access-Control-Allow-Origin", origin);
Response.Headers.Add("Access-Control-Allow-Headers", "content-type, withcredentials, Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers");
Response.Headers.Add("Access-Control-Allow-Credentials", "true");
Response.Headers.Add("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS, POST, PUT, DELETE");
Response.Flush();
}
}
Now the above is checking for the pre-flight very specifically and if it finds it it adds permissions to send the next request. On your system you may need to tweek the Allow_Headers request (easiest way is to use your browser f12 to look at what headers your pre-flight request is actually sending out.
Note that the above just deals with the pre-flight CORS will still apply for the actual http POST which will need correctly handling. For this we added the server we wanted to allow in to settings and then added the System.Web.Http.Cors to the WebApiConfig Register method as follows
var cors = new EnableCorsAttribute(Properties.Settings.Default.CORSOriginPermittedSite, "*", "GET, HEAD, OPTIONS, POST, PUT, DELETE");
cors.SupportsCredentials = true;
config.EnableCors(cors);
This avoids hard coding the site which a production system really wants to avoid.
Anyway hopefully that will help.

HTTP OPTIONS error in Phil Sturgeon's Codeigniter Restserver and Backbone.js

My backbone.js application throwing an HTTP OPTIONS not found error when I try to save a model to my restful web service that's located on another host/URL.
Based on my research, I gathered from this post that :
a request would constantly send an OPTIONS http request header, and not trigger the POST request at all.
Apparently CORS with requests that will "cause side-effects on user data" will make your browser "preflight" the request with the OPTIONS request header to check for approval, before actually sending your intended HTTP request method.
I tried to get around this by:
Settting emulateHTTP in Backbone to true.
Backbone.emulateHTTP = true;
I also allowed allowed all CORS and CSRF options in the header.
header('Access-Control-Allow-Origin: *');
header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept");
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
The application crashed when the Backbone.emulateHTTP line of code was introduced.
Is there a way to respond to OPTIONS request in CodeIgniter RESTServer and are there any other alternatives to allow either disable this request from talking place?
I found this on Github as one solution. I am not sure if I should use it as it seems a bit outdated.
I encountered exactly the same problem. To solve it I have a MY_REST_Controller.php in core and all my REST API controllers use it as a base class. I simply added a constructor like this to handle OPTIONS requests.
function __construct() {
header('Access-Control-Allow-Origin: *');
header("Access-Control-Allow-Headers: X-API-KEY, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method");
header("Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE");
$method = $_SERVER['REQUEST_METHOD'];
if($method == "OPTIONS") {
die();
}
parent::__construct();
}
This just checks if the request type is OPTIONS and if so just dies out which return a code 200 for the request.
You can also modify the $allowed_http_methods property in your subclass to exclude the options method. Previous versions of REST_controller did nothing with OPTIONS and adding this line seems to mimic that behavior:
protected $allowed_http_methods = array('get', 'delete', 'post', 'put');
I solved in this way:
header('Access-Control-Allow-Origin: *');
header("Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE");
header("Access-Control-Allow-Headers: X-API-KEY, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method, x_requested_with");
if ( "OPTIONS" === $_SERVER['REQUEST_METHOD'] ) {
die();
}
Pay attention to add x_requested_with in Access-Control-Allow-Headers.

Resources