CORS with Spring Boot - restrict GET requests to certain domains - spring-boot

I am using Spring Boot and would like to restrict HTTP GET requests only from certain domains. For example, I would like to accept requests only from a list of predefined domains (e.g. https://www.frontend.com, https://www.test-frontend.com). How could I implement such a functionality?
I expected to face CORS issues, but theses do not apply for GET requests. Any ideas?

public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://www.frontend.com", "https://www.test-frontend.com")
.allowedMethods("GET");
}
Reference: https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/cors.html

First .allowedMethods use for allow methods so if you don't want to allow "GET" then put all others methods but do not put GET method, like below:
.allowedMethods("PUT", "DELETE", "PATCH")
and second you can not CORS by non-webapp clients like curl or Postman, any non web app client.

Related

CORS on Webjars in Spring Boot?

I'm using Spring Boot 1.4.0.M2 and Spring 4.3.0.RC1, trying to enable CORS support in a simple app. I have this added:
#Bean
public WebMvcConfigurer webMvcConfigurerAdapter() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
};
}
I also have webjars pulled in through Gradle, such as:
compile('org.webjars:jquery:2.2.3')
The CORS support works fine with my REST API (via #RestController), but somehow it doesn't seem to work when accessing my webjars. For example (UPDATE: less confusing example), requesting:
http://example.org/webjars/bootstrap-datepicker/1.6.0/package.json
gets a response with the correct content but lacking the expected CORS headers, which of course defeats any actual CORS request. I can't figure out why that would be an exception to the /** mapping. How do I fix it?
JavaScript doesn't need cors configuration . In other words, you can access JavaScript resources from a site which doesn't enable cors.
From wiki site, we can obvious know that a website can freely embed images, stylesheets, scripts, iframes, videos and some plugin content (such as Adobe Flash) from any other domain. So even if you are enabling CORS on your website, but your resource is javascript, so the request does not add CORS header.
Your code snippet copied from documentation is just a sample of showing how to enable CROS in a quick way. This code snippet is not complete. Using this will mess up what WebMvcAutoConfigurationAdapter provided for you.

Spring Boot: Secured RESTful API using Spring Social and Spring Security

I am trying to define and secure a RESTful API using Spring Boot. Ideally, I would like to use Spring Social and allow clients (web and mobile) to login via Facebook.
What is working
So far, I managed to have a working API using #RestController and secure it with a basic Spring Security configuration as follows:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/api/**").authenticated()
.antMatchers(HttpMethod.PUT, "/api/**").authenticated()
.antMatchers(HttpMethod.DELETE, "/api/**").authenticated()
.anyRequest().permitAll()
.and().httpBasic()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
The antMatchers could probably be improved, but I made it like this for my own clarity for now and it works fine. Doing GET requests is allowed and all others required to send the standard user:password given by Spring Security at runtime. An example using httpie:
http POST user:a70fd629-1e29-475d-aa47-6861feb6900f#localhost:8080/api/ideas/ title="My first idea"
Which right credentials, it sends a 200 OK back, otherwise a 401 Unauthorized.
Spring Social
Now, I am stuck and can't get my head around using Spring-Social-Facebook to get working with my current setup and keep fully RESTful controllers. Using standard forms and redirects seems trivial, but I couldn't find any solution for a REST-based approach that easily supports web and mobile clients for example.
As I understand, the client will have to handle the flow, since the back-end won't send any redirects to the /connect/facebook URL.
I followed the tutorial Accessing Facebook Data and it works on its own. However, I would like to avoid having to have those facebookConnect.html and facebookConnected.html templates like in the tutorial. So I don't know how to have change that.
Another Spring Boot tutorial for OAuth is also nice and working, but I would like to stick with Spring Social if possible due to the simplicity.
This post, helped for the Method not allowed issue of the /connect/facebook redirect when using those views mentioned above.
Post about Social Config. Probably, I am missing something there.
Any advice, solution or link to a better tutorial would be really helpful.
Thanks!
UPDATE 1
Now, I have a working website with traditional User SignUp and Login over forms. I have a "Login with Facebook" button that sends me over the "OAuth dance". So next issue is that I have to create somehow the User manually after the Facebook login has been successful, because for the moment both "logins" are not related, so even though the user is logged in with Facebook, he doesn't yet have an associated User object with the right authorisations.
SocialAuthenticationFilter by default, redirects to '/signup' in the case you described, user is signed in from a social app, however, no local account exists. You can provide a handler to create a local account. This is also covered in the spring-socal samples.
#RequestMapping(value = { "/signup" }, method = RequestMethod.GET)
public String newRegistrationSocial(WebRequest request, Model model) throws Exception {
String view = "redirect:/home";
try {
Connection<?> connection = providerSignInUtils.getConnectionFromSession(request);
if (connection != null) {
UserProfile up = connection.fetchUserProfile();
registerUser(up.getFirstName(), up.getLastName(), up.getEmail(), "DummyPassword");
providerSignInUtils.doPostSignUp(up.getEmail(), request);
//SignInUtils.signin(up.getEmail());
...
...
}
}
return view;
}

How do I setup login service for Spring-social and spring-security over a REST API?

I want to have a JS application in on client-side (no jsps) that will communicate with back-end only with REST calls. I want also to enable users to be able to login with FB, Twitter accounts. In addition, I also want to enable users to register their own accounts. For this purpose I want to use Spring-security and spring-social on backend and Javascript SDK in front to get access_token from the FB, which will be then passed to backend.
The question is: how do I create a REST controller that would authenticate using spring-social and spring-security facilities?
I read through the examples in:
https://github.com/spring-projects/spring-social-samples
but couldn't really find how I could make use of ProviderSignInController or SpringSocialConfigurer for this purpose. I guess I cannot use the SocialAuthenticationFilter in my case since the "/auth/{providerid}" url is not what I'm looking for. However, I guess the ProviderSingInController seems to be of use here neither. Please correct me if I'm wrong. Ideally I would like to benefit from all capabilities of Spring Security framework.
I will appreciate any suggestions.
Best regards
EDIT
I would like to follow a flow like here: http://porterhead.blogspot.com/2013/01/writing-rest-services-in-java-part-4.html but using the Spring Social and Spring Security combined.
The front-end application is written in AngularJS
2nd EDIT
It turns out that you can simply make use of all the Spring Social modules benefits out of the box. The only thing a client has to do is call a GET on the auth/facebook or whatever link to fire entire 0auth dance which will eventually return the authentication result. Then you can control the flow easily (register account or return some relevant information to the client to let know registration is needed). So the SpringSocialConfigurer works well in this case (apart from the fact that it doesn't support scope setting yet, however, this can be changed manually, check my pull request # github.com/spring-projects/spring-social/pull/141)
3rd EDIT - 14.10.2014
As requested, I will share how I managed to make it work.
Given I have configured my security filter in the following way:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
#Override
public void configure(final HttpSecurity http) throws Exception {
http.formLogin()
...
.and().apply(getSpringSocialConfigurer());
}
private SpringSocialConfigurer getSpringSocialConfigurer() {
final SpringSocialConfigurer config = new SpringSocialConfigurer();
config.alwaysUsePostLoginUrl(true);
config.postLoginUrl("http://somehost.com:1000/myApp");
return config;
}
Once my application is set up, the only thing I need to call is http://somehost.com:1000/myApp/auth/facebook
with GET request.
"In addition, I also want to enable users to register their own
accounts"
If you say that you want to allow users to login with their own credentials (without FB/twiter), you need to let them also to create account, and to support forgot password, etc...
If that is the case, maybe this SO thread might be helpful. The auth-flows package also supports REST API.
Create Account, Forgot Password and Change Password

Spring Data REST CORS - how to handle preflight OPTIONS request?

I'm using Spring Data REST to build a RESTful API. Until now my HTML GUI for this RESTful service was served from the same Tomcat and I had no problems wit Cross Origin requests.
Now I want to serve the static files from a different server. This means the API is on another domain/port. Browsers will send the OPTIONS request to get the Access-Control headers from the server. Unfortunately Spring Data REST does not handle those OPTIONS requests and even returns a HTTP 500.
I tried creating a custom controller that handles all OPTIONS requests
#Controller
#RequestMapping(value = "/**", method = RequestMethod.OPTIONS)
public class OptionsController {
#RequestMapping
public ResponseEntity options() {
return new ResponseEntity<Void>(HttpStatus.OK);
}
}
Which worked for OPTIONS, but then all other requests (like GET) ceased to work.
OPTIONS requests are switched on via the dispatchOptionsRequest dispatcher servlet parameter.
tl;dr: currently Spring Data REST does not answer OPTIONS requests at all.
It might be worth opening a ticket in our JIRA.
Browsers will send the OPTIONS request to get the Access-Control headers from the server.
Is that specified somewhere? If so, it would be cool if the ticket description included a link to that spec.
A few comments regarding your approach for a workaround:
#RequestMapping on the controller method overrides the method attribute and expectedly now matches all HTTP methods, which is why you see all requests intercepted. So you need to define OPTIONS as HTTP method there, too (or maybe instead of in the class mapping).
You're not returning any Allow header which is the whole purpose of OPTIONS in the first place.
I wonder if the approach in general makes sense as it'll be hard to reason about the supported HTTP methods in general.
Just set the parameter dispatchOptionsRequest to true into the dispatcher to process the Options method calls, into the implementation of the WebApplicationInitializer.
ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet(applicationContext));
dispatcher.setInitParameter("dispatchOptionsRequest", "true");
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/*");

DataServiceVersion header missing in the Http response

I am currently developing an OData service using Web Api 2 and EF6 with a Code First Approach. My controllers inherit from the normal ApiController Base.
I have decorated my action methods with the Queryable attribute and have also enabled Query Support in the WebApiConfig file. Through my CORS policy, I have specified the DataServiceVersion and MaxDataServiceVersion as part of my Accept and Exposed Headers.
Strangely, my odata endpoint seems to not return the DataServiceVersion as part of the response header but, if my controllers inherit from the ODataController base I am able to see it in the response.
Is there a way to enable this header while using ApiController as the base.
This header is needed as datajs requires it on the client side.
First to answer your question:
Yes, you can expose the DataServiceVersion http header yourself. It's custom code though, not a setting on an existing component.
Add a "Filter" to your global http configuration. A filter is a class derived from "System.Web.Http.Filters.ActionFilterAttribute".
for example;
internal class DataServiceVersionHeaderFilterWebAPI : System.Web.Http.Filters.ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
actionExecutedContext.Response.Content.Headers.Add("DataServiceVersion", "3.0");
actionExecutedContext.Response.Content.Headers.Add("Access-Control-Expose-Headers", "DataServiceVersion");
}
}
Then configure this filter to be used (in application start of global.asax)
GlobalConfiguration.Configuration.Filters.Add( new DataServiceVersionHeaderFilterWebAPI() );
This will allow your cross domain OData query from a security perspective. There is however another issue with this;
OData is a specification larger than just the request URI's & HTTP headers. It also specifies how to exchange model information and the actual data exchange is a predefined object structure. Simple, but still a predefined structure.
object.d = service returned content
You will have to implement all those pieces of the specification ($filter,$metadata,$top, return formats, etc) yourself.
Some food for thought.

Resources