Announcement Announcement Module
Collapse
No announcement yet.
400 Bad Request Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • 400 Bad Request

    Hi,

    I have tried to connect to Facebook with Spring Social but it didn't work - I always get this exception trace above.
    Does anybody know what I' am doing wrong? The accessToken is the right one, also appl ID. Secret Key is faced.
    Thanks a lot for help!
    Generic1

    org.springframework.web.util.NestedServletExceptio n: Request processing failed; nested exception is org.springframework.web.client.HttpClientErrorExce ption: 400 Bad Request
    org.springframework.web.servlet.FrameworkServlet.p rocessRequest(FrameworkServlet.java:681)
    org.springframework.web.servlet.FrameworkServlet.d oGet(FrameworkServlet.java:574)
    javax.servlet.http.HttpServlet.service(HttpServlet .java:617)
    javax.servlet.http.HttpServlet.service(HttpServlet .java:717)

    root cause

    org.springframework.web.client.HttpClientErrorExce ption: 400 Bad Request
    org.springframework.web.client.DefaultResponseErro rHandler.handleError(DefaultResponseErrorHandler.j ava:75)
    org.springframework.web.client.RestTemplate.handle ResponseError(RestTemplate.java:486)
    org.springframework.web.client.RestTemplate.doExec ute(RestTemplate.java:443)
    org.springframework.web.client.RestTemplate.execut e(RestTemplate.java:401)
    org.springframework.web.client.RestTemplate.postFo rObject(RestTemplate.java:279)
    org.springframework.social.facebook.connect.Facebo okOAuth2Template.postForAccessGrant(FacebookOAuth2 Template.java:57)
    org.springframework.social.oauth2.OAuth2Template.e xchangeForAccess(OAuth2Template.java:104)
    at.eventtiming.frontend.controller.HomeController. handle(HomeController.java:40)
    Code:
    public final class HomeController {
    
        public HomeController() {}
    
        @RequestMapping("/index")
        public String handle(final HttpServletRequest request, final HttpServletResponse response) throws Exception {
            FacebookConnectionFactory connectionFactory = new FacebookConnectionFactory("338138429607553", "acafxxxxxxxxxb713200ffa81081");
            OAuth2Operations oauthOperations = connectionFactory.getOAuthOperations();
            OAuth2Parameters params = new OAuth2Parameters();
            params.setRedirectUri("http://localhost:8080/JSPTest/index.htm");
            String authorizeUrl = oauthOperations.buildAuthorizeUrl(GrantType.AUTHORIZATION_CODE, params);
            System.out.println("authorizeUrl: " + authorizeUrl);
            response.sendRedirect(authorizeUrl);
            String accessToken = "AAAEziPz7ToEBAHsiLzF4GAte8oGa7lFTbzuRoZBzMubg2gfNSt0K2DS72sQSRmyRCVhRDjmmV4QWcFZCjTn6XPqsVIHltc1LpZA0kmJ2VxSYVw1TuwN";
    
            // upon receiving the callback from the provider:
            AccessGrant accessGrant = oauthOperations.exchangeForAccess(accessToken, "http://localhost:8080/JSPTest/index.htm", null);
            Connection<Facebook> connection = connectionFactory.createConnection(accessGrant);
            return "home";
            }
    }

  • #2
    It's clear from looking at this that you're misusing exchangeForAccess(). The proper way to use that method is to exchange an authorization code for an access grant (which contains the access token value). But it looks like you're trying to exchange an access token for an access token, which isn't correct.

    What is supposed to happen is that you redirect your user's browser to the authorization URL where they will accept or deny authorization. Assuming that they accept, then Facebook will redirect them back to your application with an authorization code as a query parameter. It's this authorization code that you pass to exchangeForAccess() to get an access token (and then you can create a connection, use the token to access the API, or whatever it is you'd like to do.

    What puzzles me more, however, is why you're trying to implement the OAuth2 authorization flow with Facebook when Spring Social already implements that with ConnectController? Everything that it appears that you're trying to do in HomeController is already handled by ConnectController for you, so why not use that?

    Comment


    • #3
      Hi,
      Thanks a lot for your help - I was not sure about ConnectController does all the things. I read a tutorial where it was explained like I did above.
      Now I have tried to create my example like in this tutorialhttp://static.springsource.org/sprin...onnecting.html
      but I have still some problems.
      The ControllerConnection- Controller isn't invoked after clicking the login- button - I get a HTTP 404 code - the ConnectionController for /connect/twitter can't be found.
      For me it is not clear where I have made the fault - at the ViewResolver or the Spring Social config.
      Here my ViewResolver:
      HTML Code:
      <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
              <property name="prefix" value="/WEB-INF/jsp/" />
              <property name="suffix" value=".jsp" />
           </bean>
      and here my Spring Social config:

      HTML Code:
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:jdbc="http://www.springframework.org/schema/jdbc"
             xmlns:p="http://www.springframework.org/schema/p"
             xmlns:aop="http://www.springframework.org/schema/aop"
             xmlns:tx="http://www.springframework.org/schema/tx"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
             http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
             http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
             http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd">
      
            <jdbc:embedded-database id="dataSource" type="H2">
      		<jdbc:script location="classpath:/org/springframework/social/connect/jdbc/JdbcUsersConnectionRepository.sql" />
            </jdbc:embedded-database>
      
            <bean id="connectionFactoryLocator" class="org.springframework.social.connect.support.ConnectionFactoryRegistry">
                  <property name="connectionFactories">
                      <list>
                          <bean class="org.springframework.social.twitter.connect.TwitterConnectionFactory">
                              <constructor-arg value="JB7veSg6Bxxxxxxxxxng" />
                              <constructor-arg value="D9fKgpHjUq6yfffffffffffffffD0fjaBu1HcvKsesk" />
                          </bean>
                      </list>
                  </property>
              </bean>
      
              <bean id="usersConnectionRepository" class="org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository">
                  <constructor-arg ref="dataSource" />
                  <constructor-arg ref="connectionFactoryLocator" />
                  <constructor-arg ref="textEncryptor" />
              </bean>
      
              <bean id="connectionRepository" factory-method="createConnectionRepository"
                    factory-bean="usersConnectionRepository" scope="request">
                  <constructor-arg value="#{request.userPrincipal.name}" />
                  <aop:scoped-proxy proxy-target-class="false" />
              </bean>
      
            <bean id="textEncryptor" class="org.springframework.security.crypto.encrypt.Encryptors" factory-method="noOpText" />
      
              <bean class="org.springframework.social.connect.web.ConnectController">
                  <constructor-arg ref="connectionFactoryLocator" />
                  <constructor-arg ref="connectionRepository" />
                  <!--<property name="applicationUrl" value="http://localhost:8080/JSPTest/connect/twitter" />-->
              </bean>
      
      </beans>
      I have read your posts from http://forum.springsource.org/archiv.../t-105079.html but I couldn't solve it.

      Maybe you have a hint what I can do in order to get it work.
      Thanks a lot for your help!
      Generic





      I have also uploaded my simple small poject at http://www.file-upload.net/download-...PTest.zip.html.

      Comment


      • #4
        I can't belief it but it works :-) I had to add

        <context:component-scan base-package="org.springframework.social.connect.web">
        <context:exclude-filter type="regex" expression="org.springframework.social.connect.web .ProviderSignInController"/>
        </context:component-scan>

        ,then it worked.

        Whats still not clear for me is, where I can change the redirect- URL for Twitter. Now I had to make the structure WEB-INF/jsp/connect/twitterConnected.jsp and there I am redirected after login at twitter. But the redirection for my application should be WEB-INF/jsp/home.jsp.
        Is there a possibility to redirect to my home.jsp?

        And I would have a further question: before the redirection, where can I get the Twitter- API to set the Name and Profile- image into the session of my web application in order to show the name und profile image after login?

        Thanks a lot for your help!!
        Generic

        Comment


        • #5
          I'm glad you got it to work, but...you could've accomplished the same thing without component-scanning and simply explicitly configuring ConnectController. That's how all of the samples at https://github.com/SpringSource/spring-social-samples do it. And if you explicitly configure it, then you don't have to exclude ProviderSignInController.

          As for the redirect: It *must* come back to ConnectController so that ConnectController can exchange the authorization code/verifier for an access token and complete the connection. And, ConnectController then sends the request to the view whose logical view name is "connect/{provider name}Connected"...in your case that is WEB-INF/jsp/connect/twitterConnected.jsp. You can make it go to any view you want, however, by subclassing ConnectController and overriding the connectView() method to return whatever view name you want (you can even use a "redirect:" prefix to do a redirect if you'd like...for example, it could return "redirect:/home").

          Comment


          • #6
            Thanks once more. It works fine. My overridden ConnectController now look like this:

            Code:
            package at.company.frontend.controller;
            
            import javax.inject.Inject;
            import org.springframework.social.connect.ConnectionFactoryLocator;
            import org.springframework.social.connect.ConnectionRepository;
            import org.springframework.social.connect.web.ConnectController;
            import org.springframework.social.connect.web.ConnectInterceptor;
            import org.springframework.stereotype.Controller;
            import org.springframework.web.context.request.NativeWebRequest;
            import org.springframework.web.servlet.view.RedirectView;
            
            @Controller
            public class MyConnectController extends ConnectController {
            
                @Inject
                public AtleticusConnectController(ConnectionFactoryLocator connectionFactoryLocator, ConnectionRepository connectionRepository) {
                    super(connectionFactoryLocator, connectionRepository);
                    ConnectInterceptor ci = new MyTwitterConnectInterceptor();
                    super.addInterceptor(ci);
                    }
                
                @Override
                public RedirectView oauth1Callback(String providerId, NativeWebRequest nwr) {
                    return super.oauth1Callback(providerId, nwr);
            	}
            
                @Override
                public String connectView(String id) {
                    super.connectView(id);
                    return "redirect:/home.htm";
                    }
            }
            The only thing what isn't working is, the MyTwitterConnectInterceptor#postConnect method isn't invoked (preConnect is invoked as expected) and I get a Warning:

            WARNUNG: Exception while handling OAuth1 callback (No connection factory for service provider 'null' is registered). Redirecting to null connection status page.

            If I don't override oauth1Callback, postConnect is invoked but I will be redirected to the standard redirection- page /jsp/connect/twitterConnected.jsp.

            Maybe you can help me once again.
            Thanks a lot.
            Generic

            Comment


            • #7
              What's the URL when handling the redirect? Its path should be /connect/twitter. If it's not, then what is it? Is it /connect/null? If so, I feel like I'm missing something from your config that would explain this.

              Comment


              • #8
                I have made a workaround that works for me:

                @Override
                public RedirectView oauth1Callback(String providerId, NativeWebRequest nwr) {
                super.oauth1Callback("twitter", nwr);
                return new RedirectView("/twitter.hml", true);
                }

                Actually I don't know why providerId is null. I havn't intentionally configured something in order that providerId is null.

                I have uploaded once again my current Version (I use Netbeans as ID). Maybe someone has the delight to throw a glance over it.
                Thanks!!
                Generic

                http://www.file-upload.net/download-...PTest.zip.html

                Comment


                • #9
                  I had a look at your code earlier and couldn't spot what was wrong, but I think I've just realised what's missing - it's the @PathVariable annotation before the providerId parameter in the method signature.

                  Your overridden method should be:

                  @Override
                  public RedirectView oauth1Callback(@PathVariable String providerId, NativeWebRequest nwr) {
                  return super.oauth1Callback(providerId, nwr);
                  }

                  This will allow spring mvc to set the providerId based on the callback path.

                  Comment


                  • #10
                    Just to add to my previous comment.. while I believe adding the @PathVariable to the overridden callback method will resolve this issue, I'm not sure you need to be overriding this method at all.

                    To override the default status views you should only need override the connectView methods. I've just tried a simple local example - subclassing ConnectController with these methods overridden and the app respected these overrides and redirected to my custom views. I also had a ConnectInterceptor wired in whose postConnect method was called, so you shouldn't need to override the callback for this reason.

                    I may have missed something here, there may be something else in your setup which means you'd like to override the callback method, but in theory you shouldn't need to.

                    Hope this helps,

                    Michael

                    Comment


                    • #11
                      Michael's right. If all you're doing is calling super.oauth1Callback() in the overriding method, then there's really no reason to override that method at all. (But if you do, be sure to annotate the providerId parameter with @PathVariable).

                      While we're on the topic, I've been wondering for a *long* time about overriding the connectView() method versus having a connectView property that can be configured in Spring. Certainly, overriding connectView() gives more flexibility in the sense that you could construct the returned value intelligently using whatever facilities Java offers...but I also think that the most common case would be a view that is expressed as a simple String (and thus, can be easily configured via Spring injection without creating a subclass of ConnectController). Curious what your thoughts are?

                      Comment


                      • #12
                        Thanks a lot for guys for the help. I have added @PathVariable and overwritten connectedView:

                        @Override
                        protected String connectedView(String providerId) {
                        return "redirect:/home.htm";
                        }

                        Now it works great.

                        >> Curious what your thoughts are?
                        Actually my Spring experiences are a bit outdated. The last 6 years I have used OSGi and there override Methodes is common.
                        But thanks for the hint. I think I will configure it via Spring injection.

                        Thanks once again and all the best.
                        Generic
                        Last edited by Generic1; Aug 14th, 2012, 01:16 AM.

                        Comment


                        • #13
                          I would have one last question.
                          For JdbcUsersConnectionRepository I need a dataSource (in my real web application it is a mysql connection) and in my MySQL Database I need a table like the SQL at the bottom. And in this table the user logged in over e.g. twitter where stored. Am I right or did I have something misunderstood?
                          Thanks once again.

                          By the way, if you are interested why I need all this things. We will implement Spring Social at this web application: http://www.atleticus.net

                          Code:
                          create table UserConnection (userId varchar(255) not null,
                          	providerId varchar(255) not null,
                          	providerUserId varchar(255),
                          	rank int not null,
                          	displayName varchar(255),
                          	profileUrl varchar(512),
                          	imageUrl varchar(512),
                          	accessToken varchar(255) not null,					
                          	secret varchar(255),
                          	refreshToken varchar(255),
                          	expireTime bigint,
                          	primary key (userId, providerId, providerUserId));
                          create unique index UserConnectionRank on UserConnection(userId, providerId, rank);

                          Comment


                          • #14
                            With regards to Craig's question about overriding connectView() vs. having an injectable property, my personal opinion is that I like to keep the consistency and default behaviour of the current implementation, but I'd like to be able to customise this behaviour without subclassing. Ideally I'd like to be able to augment behaviour of an existing Spring Social webapp, allowing configurations to be changed through properties/composition rather than inheritance.

                            Considering my own use-case using spring-social-security :

                            I'd like to have a shared view which has connection options to available providers dynamically generated - a single jsp for example which accepts a "providerId" attribute and displays the relevant connect with provider buttons, or displays all provider connect options if this parameter is not present. Using the existing implementation of ConnectController, I've needed to create logical views for each provider (tiles definitions) - each referencing the same underlying jsp and passing in a "providerId" attribute to the jsp.

                            In this use-case, I'm registering new providers using component-scanning - having to create provider-specific views for essentially shared behaviour isn't as clean a solution as I'd like, and limits an application's ability to add new providers dynamically.

                            If ConnectController were able to be configured with its connectView through a property, this would remove the need to create these provider-specific views, *if* the relevant providerId were passed into the view as a model attribute for example.

                            I was wondering whether a hybrid approach may be possible? - to encapsulate the existing view name creation logic into a strategy class which can be injected into the ConnectController. This strategy could be set to the current strategy by default, but applications could inject their own alternatives - in my case a strategy to building up a single view with a model attribute set for the providerId.

                            This would allow new providers to be added (in my case through component-scanning) without the application needing to make provider-specific amendments.

                            (As an aside, if ConnectController were to go in this direction, I feel that the connectionStatusRedirect method would also need to be customisable in the same was as connectView).

                            Just my 2 cents worth - I think there are pros and cons to each approach and can see why it's a tricky issue to determine to the best approach.

                            Comment


                            • #15
                              Generic1: Yes, you'll need to create a table somewhat like what you described or as is described in JdbcUsersConnectionRepository.sql. Note, however, that JdbcUsersConnectionRepository.sql serves only as a guideline for how such a table should be created and may not work with all databases--you may need to tweak the schema to accomodate the quirks of whatever DB you're using.

                              I think you have a good understanding of what that table is used for, but to clarify the terminology: When you say "user logged in over e.g. twitter", it's actually an authorization process, not an authentication process--that is, the user is authorizing your application to access their data and post updates on Twitter. At the end of that authorization process (OAuth 1.0a in the case of Twitter), your application receives an access token and secret. On Twitter's end, they're already associating your application, the user, and the token/secret in whatever DB they use. Within a Spring Social-enabled application, the user connection repository (and the underlying table) is what keeps track of that relationship between your user, your application, and Twitter. We call that relationship a "connection".

                              To be clear again, ConnectController's job is to orchestrate the connection process, which is an authorization process. That said, the authorization process can be used as a form of authentication as well. ProviderSignInController is a Spring Social controller that can be used to orchestrate the connection process in such a way that the user ends up authenticated into your application.

                              Comment

                              Working...
                              X