WebAPI CORS with Windows Authentication - allow Anonymous OPTIONS request - angularjs

I have a WebAPI 2 REST service running with Windows Authentication. It is hosted separately from the website, so I've enabled CORS using the ASP.NET CORS NuGet package. My client site is using AngularJS.
So far, here's what I've been through:
I didn't have withCredentials set, so the CORS requests were returning a 401. Resolved by adding withCredentials to my $httpProvider config.
Next, I had set my EnableCorsAttribute with a wildcard origin, which isn't allowed when using credentials. Resolved by setting the explicit list of origins.
This enabled my GET requests to succeed, but my POST issued a preflight request, and I hadn't created any controller actions to support the OPTIONS verb. To resolve this, I've implemented a MessageHandler as a global OPTIONS handler. It simply returns 200 for any OPTIONS request. I know this isn't perfect, but works for now, in Fiddler.
Where I'm stuck - my Angular preflight calls aren't including the credentials. According to this answer, this is by design, as OPTIONS requests are designed to be anonymous. However, the Windows Authentication is stopping the request with a 401.
I've tried putting the [AllowAnonymous] attribute on my MessageHandler. On my dev computer, it works - OPTIONS verbs do not require authentication, but other verbs do. When I build and deploy to the test server, though, I am continuing to get a 401 on my OPTIONS request.
Is it possible to apply [AllowAnonymous] on my MessageHandler when using Windows Authentication? If so, any guidance on how to do so? Or is this the wrong rabbit hole, and I should be looking at a different approach?
UPDATE:
I was able to get it to work by setting both Windows Authentication and Anonymous Authentication on the site in IIS. This caused everything to allow anonymous, so I've added a global filter of Authorize, while retaining the AllowAnonymous on my MessageHandler.
However, this feels like a hack...I've always understood that only one authentication method should be used (no mixed). If anyone has a better approach, I'd appreciate hearing about it.

I used self-hosting with HttpListener and following solution worked for me:
I allow anonymous OPTIONS requests
Enable CORS with SupportsCredentials set true
var cors = new EnableCorsAttribute("*", "*", "*");
cors.SupportsCredentials = true;
config.EnableCors(cors);
var listener = appBuilder.Properties["System.Net.HttpListener"] as HttpListener;
if (listener != null)
{
listener.AuthenticationSchemeSelectorDelegate = (request) => {
if (String.Compare(request.HttpMethod, "OPTIONS", true) == 0)
{
return AuthenticationSchemes.Anonymous;
}
else
{
return AuthenticationSchemes.IntegratedWindowsAuthentication;
}};
}

I have struggled for a while to make CORS requests work within the following constraints (very similar to those of the OP's):
Windows Authentication for all users
No Anonymous authentication allowed
Works with IE11 which, in some cases, does not send CORS preflight requests (or at least do not reach global.asax BeginRequest as OPTIONS request)
My final configuration is the following:
web.config - allow unauthenticated (anonymous) preflight requests (OPTIONS)
<system.web>
<authentication mode="Windows" />
<authorization>
<allow verbs="OPTIONS" users="*"/>
<deny users="?" />
</authorization>
</system.web>
global.asax.cs - properly reply with headers that allow caller from another domain to receive data
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (Context.Request.HttpMethod == "OPTIONS")
{
if (Context.Request.Headers["Origin"] != null)
Context.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
Context.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, MaxDataServiceVersion");
Context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
Context.Response.AddHeader("Access-Control-Allow-Credentials", "true");
Response.End();
}
}
CORS enabling
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// all requests are enabled in this example. SupportsCredentials must be here to allow authenticated requests
var corsAttr = new EnableCorsAttribute("*", "*", "*") { SupportsCredentials = true };
config.EnableCors(corsAttr);
}
}
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
}

This is a much simpler solution -- a few lines of code to allow all "OPTIONS" requests to effectively impersonate the app pool account. You can keep Anonymous turned Off, and configure CORS policies per normal practices, but then add the following to your global.asax.cs:
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (Context.Request.HttpMethod == "OPTIONS" && Context.User == null)
{
Context.User = System.Security.Principal.WindowsPrincipal.Current;
}
}

In our situation:
Windows Authentication
Multiple CORS origins
SupportCredentials set to true
IIS Hosting
we found that the solution was elsewhere:
In Web.Config all we had to do was to add runAllManagedModulesForAllRequests=true
<modules runAllManagedModulesForAllRequests="true">
We ended up to this solution by looking into a solution on why the Application_BeginRequest was not being triggered.
The other configurations that we had:
in Web.Config
<authentication mode="Windows" />
<authorization>
<allow verbs="OPTIONS" users="*" />
<deny users="?"/>
</authorization>
in WebApiConfig
private static string GetAllowedOrigins()
{
return ConfigurationManager.AppSettings["CorsOriginsKey"];
}
public static void Register(HttpConfiguration config)
{
//set cors origins
string origins = GetAllowedOrigins();
var cors = new EnableCorsAttribute(origins, "*", "*");
config.EnableCors(cors);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
BTW "*" cors origin is not compatible with Windows Authentication / SupportCredentials = true
https://learn.microsoft.com/en-us/aspnet/web-api/overview/security/enabling-cross-origin-requests-in-web-api#pass-credentials-in-cross-origin-requests

I solved it in a very similar way but with some details and focused on oData service
I didn't disable anonymous authentication in IIS since i needed it to POST request
And I've added in Global.aspx (Adding MaxDataServiceVersion in Access-Control-Allow-Headers) the same code than above
protected void Application_BeginRequest(object sender, EventArgs e)
{
if ((Context.Request.Path.Contains("api/") || Context.Request.Path.Contains("odata/")) && Context.Request.HttpMethod == "OPTIONS")
{
Context.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
Context.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept,MaxDataServiceVersion");
Context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
Context.Response.AddHeader("Access-Control-Allow-Credentials", "true");
Context.Response.End();
}
}
and WebAPIConfig.cs
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
var cors = new EnableCorsAttribute("*", "*", "*");
cors.SupportsCredentials = true;
config.EnableCors(cors);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
and AngularJS call
$http({
method: 'POST',
url: 'http://XX.XXX.XXX.XX/oData/myoDataWS.svc/entityName',
withCredentials: true,
headers: {
'Content-Type': 'application/json;odata=verbose',
'Accept': 'application/json;odata=light;q=1,application/json;odata=verbose;q=0.5',
'MaxDataServiceVersion': '3.0'
},
data: {
'#odata.type':'entityName',
'field1': 1560,
'field2': 24,
'field3': 'sjhdjshdjsd',
'field4':'wewewew',
'field5':'ewewewe',
'lastModifiedDate':'2015-10-26T11:45:00',
'field6':'1359',
'field7':'5'
}
});

Dave,
After playing around with the CORS package, this is what caused it to work for me: [EnableCors(origins: "", headers: "", methods: "*", SupportsCredentials=true)]
I had to enable SupportsCredentials=true. Origins,Headers, and Methods are all set to "*"

disable anonymous authentication in IIS if you don't need it.
Than add this in your global asax:
protected void Application_BeginRequest(object sender, EventArgs e)
{
if ((Context.Request.Path.Contains("api/") || Context.Request.Path.Contains("odata/")) && Context.Request.HttpMethod == "OPTIONS")
{
Context.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
Context.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
Context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
Context.Response.AddHeader("Access-Control-Allow-Credentials", "true");
Context.Response.End();
}
}
Make sure that where you enable cors you also enable the credential usage, like:
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
var cors = new EnableCorsAttribute("*", "*", "*");
cors.SupportsCredentials = true;
config.EnableCors(cors);
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
As you can see I enable CORS globally and using the application BeginRequest hook I authenticate all the OPTIONS requests for the api (Web Api) and the odata requests (if you use it).
This works fine with all browsers, in the client side remember to add the xhrFiled withCredentials like shown below.
$.ajax({
type : method,
url : apiUrl,
dataType : "json",
xhrFields: {
withCredentials: true
},
async : true,
crossDomain : true,
contentType : "application/json",
data: data ? JSON.stringify(data) : ''
}).....
I'm trying to find another solution avoiding to use the hook but without success until now,
I would use the web.config configuration to do something like the following:
WARNING THE CONFIGURATION BELOW DOESN'T WORK!
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
<authentication mode="Windows" />
<authorization>
<deny verbs="GET,PUT,POST" users="?" />
<allow verbs="OPTIONS" users="?"/>
</authorization>
</system.web>
<location path="api">
<system.web>
<authorization>
<allow users="?"/>
</authorization>
</system.web>
</location>

Other solutions I found on the web didn't work for me or seemed too hacky; in the end I came up with a simpler and working solution:
web.config:
<system.web>
...
<authentication mode="Windows" />
<authorization>
<deny users="?" />
</authorization>
</system.web>
Project properties:
Turn on Windows Authentication
Turn off Anonymous Authentication
Setup CORS:
[assembly: OwinStartup(typeof(Startup))]
namespace MyWebsite
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
This requires Microsoft.Owin.Cors assembly that is avaliable on NUget.
Angular initialization:
$httpProvider.defaults.withCredentials = true;

This is my solution.
Global.asax*
protected void Application_BeginRequest(object sender, EventArgs e)
{
if(!ListOfAuthorizedOrigins.Contains(Context.Request.Headers["Origin"])) return;
if (Request.HttpMethod == "OPTIONS")
{
HttpContext.Current.Response.Headers.Remove("Access-Control-Allow-Origin");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
HttpContext.Current.Response.StatusCode = 200;
HttpContext.Current.Response.End();
}
if (Request.Headers.AllKeys.Contains("Origin"))
{
HttpContext.Current.Response.Headers.Remove("Access-Control-Allow-Origin");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
}
}

Related

Access to fetch from origin has been blocked by CORS policy, server api already supports middleware

I'm trying to get my ReactJS app (on an AWS S3 machine) PUT request working with my Server API (on an AWS Windows EC2 machine). Seems I am being tripped up by the preflight message that is being sent out. I've been searching on how to handle this and came across these two stackoverflow posts:
Enable OPTIONS header for CORS on .NET Core Web API
How to handle OPTION header in dot net core web api
I've ensured IIS accepts the OPTIONS verb and have added the middleware described. I can see the OPTIONS preflight handling being called through the logging but for some reason I am still getting the CORS error. Listed the main sections of the code below, any help would be really appreciated.
ReactJS PUT request
var myHeaders = new Headers();
myHeaders.append('Accept', 'application/json');
myHeaders.append('Content-Type', 'application/json-patch+json');
var rawObject = {
Name: this.state.recipeEdit.name,
Type: this.state.recipeTypeEdit,
Description: this.state.recipeEdit.description,
Ingredients: this.state.recipeIngredients,
Steps: this.state.recipeSteps,
};
var requestOptions = {
method: 'PUT',
headers: myHeaders,
body: JSON.stringify(rawObject),
redirect: 'follow',
};
fetch(this.state.url, requestOptions)
.then((response) => response.json())
.then((data) => {
this.setState({ recipeDetail: data });
});
Middleware Class
public class OptionsMiddleware
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private readonly RequestDelegate _next;
public OptionsMiddleware(RequestDelegate next)
{
_next = next;
}
public Task Invoke(HttpContext context)
{
return BeginInvoke(context);
}
private Task BeginInvoke(HttpContext context)
{
if (context.Request.Method == "OPTIONS")
{
log.Error("Handling the OPTIONS preflight message");
context.Response.Headers.Add("Access-Control-Allow-Origin", new[] { (string)context.Request.Headers["Origin"] });
context.Response.Headers.Add("Access-Control-Allow-Headers", new[] { "Origin, X-Requested-With, Content-Type, Accept" });
context.Response.Headers.Add("Access-Control-Allow-Methods", new[] { "GET, POST, PUT, DELETE, OPTIONS" });
context.Response.Headers.Add("Access-Control-Allow-Credentials", new[] { "true" });
context.Response.StatusCode = 200;
return context.Response.WriteAsync("OK");
}
log.Error("Invoking message");
return _next.Invoke(context);
}
}
public static class OptionsMiddlewareExtentions
{
public static IApplicationBuilder UseOptions(this IApplicationBuilder builder)
{
return builder.UseMiddleware<OptionsMiddleware>();
}
}
CORS Configuration in Startup.cs
public void ConfigureServices(IServiceCollection services)
{
log.Error("Entered ConfigureServices");
try
{
#if DEBUG
services.AddCors();
#else
services.AddCors(o => o.AddPolicy("MyCorsPolicy", builder =>
{
builder.SetIsOriginAllowed((host) => true)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
}));
#endif
services.AddControllersWithViews().AddNewtonsoftJson();
services.AddControllersWithViews(options =>
{
options.InputFormatters.Insert(0, GetJsonPatchInputFormatter());
});
services.AddMvc(options => options.EnableEndpointRouting = false).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddMvc(options => options.Filters.Add(typeof(homebakeExceptionFilter)));
#if USE_SQLITE
log.Error("Using SQLITE");
services.AddDbContext<SqliteDbContext>(options =>
{
options.UseSqlite("Data Source=./homebake.db");
});
#else
services.AddDbContext<AppDbContext>(options =>
{
options.UseInMemoryDatabase("homebakeapp-api-in-memory");
});
#endif
log.Error("Adding services");
services.AddScoped<IIngredientRepository, IngredientRepository>();
services.AddScoped<IRecipeStepRepository, RecipeStepRepository>();
services.AddScoped<IRecipeRepository, RecipeRepository>();
services.AddScoped<IIngredientService, IngredientService>();
services.AddScoped<IRecipeStepService, RecipeStepService>();
services.AddScoped<IRecipeService, RecipeService>();
services.AddScoped<IUnitOfWork, UnitOfWork>();
log.Error("Adding auto mapper");
services.AddAutoMapper(typeof(Startup));
}
catch (System.Exception ex)
{
log.Error(ex.Message);
if (ex.InnerException != null )
log.Error(ex.InnerException);
}
}
private static NewtonsoftJsonPatchInputFormatter GetJsonPatchInputFormatter()
{
var builder = new ServiceCollection()
.AddLogging()
.AddMvc()
.AddNewtonsoftJson()
.Services.BuildServiceProvider();
return builder
.GetRequiredService<IOptions<MvcOptions>>()
.Value
.InputFormatters
.OfType<NewtonsoftJsonPatchInputFormatter>()
.First();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddLog4Net();
log.Error("Entered Configure");
app.UseOptions();
#if DEBUG
app.UseCors(options => options.WithOrigins("http://localhost:3000").AllowAnyMethod().AllowAnyHeader());
#else
log.Error("Using cors policy");
app.UseCors("MyCorsPolicy");
#endif
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
//app.use
app.UseHttpsRedirection();
log.Error("Using MVC");
app.UseMvc();
}
I have seen this error when the server-side CORS settings are set using both the web.config and in the code, like in your middleware, which at runtime results in duplicates and cause this type of behavior. Also, you may want to add the following to your web.config and see if it helps. This will ensure your CORS settings are only set by the code.
<httpProtocol>
<customHeaders>
<remove name="Access-Control-Allow-Headers" />
<remove name="Access-Control-Allow-Methods" />
<remove name="Access-Control-Allow-Origin" />
</customHeaders>
</httpProtocol>
In the end the issue was to do with IIS configuration. After more searching I found the solution here:
How do I enable HTTP PUT and DELETE for ASP.NET MVC in IIS?
Essentially I had to update the ExtensionlessUrlHandler-Integrated-4.0 setting to accept the PUT and DELETE verbs (access it from the Handler Mappings option in IIS) and also disable both the WebDav module and handler. After that the requests went through and were processed correctly. I'm also still running the Middleware code as detailed above in case anyone else ever comes across this issue.
What made me look at the IIS configuration was that I got the multiple entries for Access-Control-Allow-Origin if I added it to my web.config file, if so, how could it be missing when not included there. Big thanks to #Masoud Safi for al the help he gave on this too.

Enable CORS in springboot mail API

I have easy mail sending springboot API and reactjs app for fetch.
Hosted in nginx server.
React app running well localhost, email working everything okei but if I run on a server it gives an error.
I have tried to add different Nginx server settings and add Springboot
#CrossOrigin(origins = "http://xx.xxx.xxx.xxx/") but have not found any help so far.
Where do I have to allow CORS if I deploy to server?
Nginx server default:
server {
listen 80 default_server;
root /var/www/name/build;
server_name xx.xxx.xxx.xxx;
index index.html index.htm;
location / {
}
}
Springboot:
#PostMapping
public void sendFeedback(#RequestBody Feedback feedback,
BindingResult bindingResult){
if(bindingResult.hasErrors()){
throw new ValidationException("Feedback is not valid");
}
// Create a mail sender
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
Properties props = mailSender.getJavaMailProperties();
mailSender.setHost(this.emailCfg.getHost());
mailSender.setPort(this.emailCfg.getPort());
mailSender.setUsername(this.emailCfg.getUsername());
mailSender.setPassword(this.emailCfg.getPassword());
// Create an email instance
SimpleMailMessage mailMessage = new SimpleMailMessage();
mailMessage.setFrom(feedback.getEmail());
mailMessage.setTo("test#gmail.com");
mailMessage.setSubject("Testing mail");
mailMessage.setText(feedback.getFeedback());
mailSender.send(mailMessage);
}
Reactjs code I have:
fetch(url,{
method: 'POST',
headers:{
'Content-Type': 'application/json'
},
body: JSON.stringify(state)
}).then(response =>{
console.log(data)
}).catch(error =>{
console.log(error)
})
Output:
Access to fetch at 'http://xx.xxx.xxx.xxx:8080/feedback-0.0.1-SNAPSHOT' from
origin 'http://xx.xxx.xxx.xxx' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check:
Redirect is not allowed for a preflight request.
Add a CORS config as below :
CORSConfig.java
#Configuration
public class CORSConfig implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD");
}
}
OR
Annonate your controller
#CrossOrigin
#PostMapping
public void sendFeedback(#RequestBody Feedback feedback,
BindingResult bindingResult){
if(bindingResult.hasErrors()){
throw new ValidationException("Feedback is not valid");
}
// Create a mail sender
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
Properties props = mailSender.getJavaMailProperties();
mailSender.setHost(this.emailCfg.getHost());
mailSender.setPort(this.emailCfg.getPort());
mailSender.setUsername(this.emailCfg.getUsername());
mailSender.setPassword(this.emailCfg.getPassword());
// Create an email instance
SimpleMailMessage mailMessage = new SimpleMailMessage();
mailMessage.setFrom(feedback.getEmail());
mailMessage.setTo("test#gmail.com");
mailMessage.setSubject("Testing mail");
mailMessage.setText(feedback.getFeedback());
mailSender.send(mailMessage);
}
This is caused by your server not responding with the proper headers. If you look at your network tab you will see and OPTIONS request that fires before your POST request. Your server needs to respond to that OPTIONS request to let the browser know its ok for the requesting client to access the resource. You can read more about it here at MDN but at the very least your server should respond to the preflight with headers like:
Access-Control-Allow-Origin: http://foo.example <== your domain here
Access-Control-Allow-Methods: POST, OPTIONS
Access-Control-Allow-Headers: Content-Type
#CrossOrigin(origins="http://localhost:9000")
#GetMapping("/hello")
public Greeting greeting() {
return "world";
}
or
#CrossOrigin(origins="http://localhost:9000", maxAge=3600)
#RestController
public class RestController {}

Spring Security and Angular token based login success but subsequent requests fails with 401

I'm developing an Angular application with a Spring based REST backend. Now I have the following issue.
I can login and get the Token from my angular application without an issue. But subsequent requests fail with 401 error. When I try the same rest endpoint in Postman with the token obtained from angular login component, it works fine.
I'm using HttpSessionStrategy, not OAuth, JWT.
I suspect some issue in my spring security config though I cannot figure it out. Could you please help me resolve this issue.
Following is the relevant part of my Spring security config.
#Bean
public SessionRepository<ExpiringSession> sessionRepository() {
return new MapSessionRepository();
}
#Bean
public HttpSessionStrategy httpSessionStrategy() {
HeaderHttpSessionStrategy strategy = new HeaderHttpSessionStrategy();
strategy.setHeaderName(headerName);
return strategy;
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD"));
configuration.setExposedHeaders(Arrays.asList("x-auth-token"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.headers().frameOptions().disable();
http.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.antMatchers("/**").hasAnyAuthority("ADMIN", "OPERATOR")
.anyRequest().authenticated()
.and().exceptionHandling().authenticationEntryPoint(new RESTAuthenticationEntryPoint())
.and().cors().configurationSource(corsConfigurationSource())
.and().csrf().disable()
.formLogin()
.successHandler(new RESTAuthenticationSuccessHandler(objectMapper, userDetailService))
.failureHandler(new RESTAuthenticationFailureHandler())
.and()
.logout().logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler());
}
And, following is the failed request and response headers of my Angular application.
Instead of using the wildcard value (*) for Access-Control-Allow-Headers, enumerate the allowed headers:
configuration.setAllowedHeaders(Arrays.asList("x-auth-token"));
From the Docs:
Access-Control-Allow-Headers
Compatibility notes
The wildcard value (*) that is mentioned in the latest specification, is not yet implemented in browsers:
Chromium: Issue 615313
Firefox: bug 1309358
Servo: Issue 13283
— MDN HTTP Reference (Access-Control-Allow-Headers)

Cross-Origin Request Blocked, angularjs rest call to jersey api

I'm completely stumped. I am very new to AngularJS and I am trying to make a rest call to my jersey server api but I am having no luck. It works using curl or Advanced Rest client (Chrome browser add-on). However I recevied the following when attempting to hit my rest using my angularjs app..
"Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8080/JerseyDemos/rest/employees. (Reason: CORS header 'Access-Control-Allow-Origin' missing)."
CLIENT: snippet of my angularjs code
$scope.login = function() {
This lets me connect to my server on a different domain
$http.defaults.headers.common['Authorization'] = 'Basic ' + Base64.encode('username' + ':' + 'password');
$http({method: 'GET', url: 'http://localhost:8080/JerseyDemos/rest/employees'}).
success(function(data) {
console.log(data)
}).
SERVER: I am using jersey framework
Heres my CORS Filter...
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
public class CorsResponseFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext request,
ContainerResponseContext response) throws IOException {
response.getHeaders().add("Access-Control-Allow-Origin", "*");
response.getHeaders().add("Access-Control-Allow-Headers",
"origin, content-type, accept, authorization");
response.getHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHeaders().add("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS, HEAD");
}
}
Application class to register my CORS Filter
import com.howtodoinjava.jersey.provider.CorsResponseFilter;
import org.glassfish.jersey.filter.LoggingFilter;
import org.glassfish.jersey.server.ResourceConfig;
import com.howtodoinjava.jersey.provider.AuthenticationFilter;
import com.howtodoinjava.jersey.provider.GsonMessageBodyHandler;
public class CustomApplication extends ResourceConfig {
public CustomApplication()
{
packages("com.howtodoinjava.jersey");
register(CorsResponseFilter.class);
register(LoggingFilter.class);
register(GsonMessageBodyHandler.class);
register(AuthenticationFilter.class);
}
}
Web.xml
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>jersey-serlvet</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.howtodoinjava.jersey.CustomApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jersey-serlvet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
Employee rest snippet
#Provider
#Path("/employees")
public class JerseyService {
#Path("/all")
#RolesAllowed("ADMIN")
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getAllEmployees()
{
Employees list = new Employees();
list.setEmployeeList(new ArrayList<Employee>());
list.getEmployeeList().add(new Employee(1, "Lokesh Gupta"));
list.getEmployeeList().add(new Employee(2, "Alex Kolenchiskey"));
list.getEmployeeList().add(new Employee(3, "David Kameron"));
return Response.status(200).entity(list).header("Access-Control-Allow-Origin", "GET, POST, PUT, DELETE, OPTIONS, HEAD").build();
}
This is a very common error for people who are just getting started with Web Services, it’s really simple to solve but sometimes developers spend hours struggling to find a solution. It happens when you create a web service and tries to access it from a different application, it won’t work because you don’t have Cross-Origin Resource Sharing (CORS) enabled, which means an application loaded in one domain cannot interact with resources from a different domain. All you have to do is to enable CORS.
How you can active it will depending on your scenario, in this tutorial I’m going to show how to enable CORS for a Java EE application running on Glassfish, I’m assuming you have an EJB RESTful web service similar to this one, and when other applications tries to consume it you see the Cross-Origin Request Blocked error on your firebug console, in this case all you have to do is to create a filter in your application, just create a class exactly like this one on your project:
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
public class CORSFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
final HttpServletResponse response = (HttpServletResponse) servletResponse;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, HEAD, OPTIONS");
response.setHeader("Access-Control-Allow-Headers", "Origin, Accept, x-auth-token, "
+ "Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers");
filterChain.doFilter(servletRequest, servletResponse);
}
#Override
public void destroy() {
}
}
Now you have to register the filter in your web.xml, copy the following code and replace “yourPackage” with your actual package name:
<filter>
<filter-name>cors</filter-name>
<filter-class>yourPackage.CORSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>cors</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
That’s it! Now your application will allow its resources to be shared with other domains.
Other Cross-Origin Request fix..

how to enable CORS with Vert.x 2.x

I am trying to make cross-domain requests with Angularjs 1.4.5.
But can't get success. I have configured $httpprovider
.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
$httpProvider.defaults.headers.common['Accept']= "application/json, text/plain, */*";
$httpProvider.defaults.headers.put["Content-Type"] = "application/x-www-form-urlencoded;charset=utf-8";
$httpProvider.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded;charset=utf-8";
$httpProvider.interceptors.push('authenticationFailedInterceptor');
}])
But still con't get success. How to enable CORS support with Vert.x 2.x http server.
CORS is supported in Vert.x 3.x but Right now I can't upgrade Vert.x.
have you tried with something like this on your responses?
vertx.createHttpServer()
.requestHandler(function (req) {
req.response()
.putHeader("content-type", "text/plain")
.putHeader("Access-Control-Allow-Origin", "*")
.putHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
.putHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
.end("Hello from Vert.x!");
}).listen(8080);
for sure, you have to modify this to your needs... but the changes has to be done in the server and at least, you need these three headers.
Complete example to enable cors:
We need to create two route matcher.
One helps to enable cors and other handle the requests.
Below is to enable cors. It Accept all request and add all the required headers needs to enable cors. After that we need to hand over request to the actual route matcher to handle request. We have that by the name secureRoutes.
RouteMatcher routeMatcher = new RouteMatcher();
routeMatcher.options(".*",new Handler<HttpServerRequest>() {
#Override
public void handle(final HttpServerRequest request) {
request.response().putHeader("Access-Control-Allow-Origin", "*");
request.response().putHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
request.response().putHeader("Access-Control-Allow-Headers", "accept, authorization, content-type, email");
request.response().end();
}
})
.all(".*",new Handler<HttpServerRequest>() {
#Override
public void handle(final HttpServerRequest request) {
request.response().putHeader("Access-Control-Allow-Origin", "*");
secureRoutes.getRouteMatcher().handle(request);
}
});
Another route matcher:
public class SecureRoutes {
private static final RouteMatcher routeMatcher = new RouteMatcher();
#Inject
protected Container container;
#Inject
private SigninController signinController;
#Inject
private SignupController signupController;
#Inject
private OauthController oauthController;
#Inject
ClientNetworkSignalController clientNetworkSignalController;
public void initRoutes() {
// APP routes. they use User token for authentication
routeMatcher.get("/", new Handler<HttpServerRequest>() {
#Override
public void handle(final HttpServerRequest request) {
request.response().putHeader("Cache-Control",
"public, max-age=86400");
request.response().sendFile("web/public/index.html");
}
});
routeMatcher.post("/signin", signinController.signin());
routeMatcher.post("/signup", signupController.signup());
routeMatcher.post("/oauth2/token", oauthController.token());
routeMatcher.post("/oauth2/invalidate_token", oauthController.invalidateToken());
}
public RouteMatcher getRouteMatcher() {
return routeMatcher;
}
}
Now finally add requestHandler to server:
server.requestHandler(routeMatcher).listen(port,
host, new Handler<AsyncResult<HttpServer>>() {
public void handle(AsyncResult<HttpServer> asyncResult) {
if (asyncResult.succeeded()) {
logger.info(s + ": Started on " + host + ":"
+ port);
} else {
logger.info(s + ": Unable to start server.\n "
+ asyncResult.cause());
}
}
});
You may have a question What is the use of http options type request handler. The answer is for that is very interesting. Javascript is a secured language that do not allow Cross origin Http request. So, to allow cross origin request javascript send a options type request for each http request request and check weather CORS is supported or not. In such Javascript hits server two times one to check cors is supported or not and one to fatch data.

Resources