Page 1 of 2 12 LastLast
Results 1 to 10 of 17

Thread: Possible problem with Internet Explorer

  1. #1
    Join Date
    Jan 2013
    Posts
    7

    Default Possible problem with Internet Explorer (continuously bouncing btw two pages)

    I am using spring-social for my kind of social game. I picked up canvas sample and build my app on it. Everything was alright till I tried my app on Internet Explorer (v 9.0.8...).
    When I try to open it up with IE9 it continuously bounces between two pages (looks like an infinite loop);
    1. http://localhost:8080/signin/faceboo...IgLw2 8GV#_=_ (code changes each time)
    2. http://apps.facebook.com/MY_APP_ADDRESS



    Here is a part of my pom to give you an idea

    HTML Code:
        <properties>
            <java-version>1.6</java-version>
            <org.springframework.social-version>1.1.0.BUILD-SNAPSHOT</org.springframework.social-version>
            <org.springframework.social.facebook-version>1.1.0.BUILD-SNAPSHOT</org.springframework.social.facebook-version>
            <org.springframework-version>3.2.1.RELEASE</org.springframework-version>
            <org.springframework.security.crypto-version>3.1.3.RELEASE</org.springframework.security.crypto-version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
    Would really appreciate if someone could help or at least say something if having the same problem
    Last edited by tirbison; Jan 29th, 2013 at 06:16 PM.

  2. #2
    Join Date
    Aug 2004
    Posts
    1,075

    Default

    It looks like you're trying to use the new spring security stuff (or possibly ProviderSignInController) within a canvas app. As I've explained in this forum several times over the past few days, that's not really the best approach.

    When you go to http://apps.facebook.com/YOUR_APP, Facebook will open your app within an iframe by POSTing to your app's canvas URL...which appears to be at http://localhost:8080, based on the URLs you give. When it does, it will include a signed_request parameter that when decoded will give you an access token. From that you can create a connection or do whatever you need.

    (Admittedly, the Spring Social Canvas example doesn't serve as a good example of how to do this. I have plans to fix that sample soon to show the right way of doing it, but it just hasn't bubbled up in priority yet.)

    Anyhow, I suspect that once Facebook does the callback (after authorization) to your app that you've got some Javascript that attempts to reload the app at http://apps.facebook.com. If that's not done right, it triggers a redirect loop. I've seen this happen in my own code, but I can't say that I've seen it happen only on specific browsers.

    If it is happening only on specific browsers and if I am right about there being a JS redirect (e.g., window.location='some url'), then I would call the JS code into question. Break the solution down to its simplest parts and figure out what the JS code is redirecting to for IE9 and then see if it does something different in Firefox or some other browser.
    Craig Walls
    Spring Social Project Lead

  3. #3
    Join Date
    Jan 2013
    Posts
    7

    Default

    Well, I hadn't got any js redirects but to give it a try I removed all the js code from the page (page is completely empty except for head, body etc) but nothing changed.


    btw I tried it on other pcs but it is the same.
    Last edited by tirbison; Jan 30th, 2013 at 03:09 AM.

  4. #4
    Join Date
    Jan 2013
    Posts
    7

    Default

    I traced down UserInterceptor line by line for both IE and Chrome/Firefox (deleted cookies cleaned up everything on browsers and restart them & my server)

    on the fourth preHandle call (after facebook redirects ):
    Code:
    	private void rememberUser(HttpServletRequest request, HttpServletResponse response) {
    		String userId = userCookieGenerator.readCookieValue(request);
    		if (userId == null) {
    			return;
    		}
    		if (!userNotFound(userId)) {
    			userCookieGenerator.removeCookie(response);
    			return;
    		}
    		SecurityContext.setCurrentUser(new User(userId));
    	}
    for chrome:
    Code:
    String userId = userCookieGenerator.readCookieValue(request);
    returns a valid object

    for IE9:
    Code:
    String userId = userCookieGenerator.readCookieValue(request);
    returns null

    so what could the solution be?

  5. #5

    Default

    so what could the solution be?
    Solution is use completely another approach as Graig suggested.

    I have working canvas app using Graig's approach and there is no problem running it in Chrome and IE9 (just tested).

    I did following general steps for create working code.

    1. I took spring-social, social-security, social-facebook+web snapshoots, build them locally and installed into local Maven repo for using by gradle build script.

    The reason for building that stuff is primary necessity to get access to two classes from spring-social-facebook:
    import org.springframework.social.facebook.web.SignedRequ estDecoder;
    import org.springframework.social.facebook.web.SignedRequ estException;

    They have 'package' visibility, so I had to make them 'public' for using in my code.
    Best regards.

  6. #6

    Default

    2.
    Main processing flow goes in controller with some code parts borrowed from showcase example's 'ProviderSignInController':
    Code:
    @Controller
    @RequestMapping("/canvas")
    public class CanvasProviderSignInController {
    
        private SignedRequestDecoder decoder;
        @Autowired
        private final SocialAuthenticationServiceLocator socialAuthenticationServiceLocator;
    
         private final UsersConnectionRepository usersConnectionRepository; // mongodb repository class
         private final ConnectSupport webSupport = new ConnectSupport();
    
        @Autowired
        private FacebookServiceProvider facebookServiceProvider;
    
        private Facebook facebook;
        private AccountService accountService; // custom helper mongodb service class
    
    ........... // other stuff
    
        @Inject
       	public CanvasProviderSignInController(
                SocialAuthenticationServiceLocator connectionFactoryLocator,
                FacebookServiceProvider facebookServiceProvider,
                UsersConnectionRepository usersConnectionRepository,
                SignInAdapter signInAdapter,
                SignedRequestDecoder decoder,
                Facebook facebook,
                AccountService accountService
                ) {
    ........... //constructor called from custom 'SocialAndSecurityConfig' configuration component class
    ......
    
        @RequestMapping(value = "/", method = RequestMethod.GET)
        public String processAccessToCanvas(NativeWebRequest request, Model model) {
            log.error("Error accessing canvas FB app. 'signed_request' can't be found in GET access. Redirecting to " + baseErrorPage);
         ........ // USER CAN'T use app by GET url
       }
    
        @RequestMapping(value="/", method=RequestMethod.GET, params={"install"})
        public String installApplication(
                @RequestParam String install, Model model, NativeWebRequest request) {
          .......... // I have my own 'canvas app install link' and I show it on 'error' page
            OAuth2ConnectionFactory<?> connectionFactory = (OAuth2ConnectionFactory<?>)
                    socialAuthenticationServiceLocator.getConnectionFactory("facebook");
            String authorizationUrl = buildFacebookRequestUrl(request, connectionFactory);
            model.addAttribute("authorizationUrl", authorizationUrl);
            return "/facebook/signin"; // page with inlined JS script to go to 'install FB app' page
    
       }
       	@RequestMapping(value="/", method=RequestMethod.GET, params={"error_reason"})
       	public String canceledAuthorization(
                   @RequestParam String error_reason, Model model, NativeWebRequest request) {
               log.error("Cancelled installing canvas FB app. 'signed_request' value is not found in GET access. Redirecting to " +         baseErrorPage);
           //  user cancelled usign fb app, offer him to install app again by providing link on page
               ..........
                // url for installing FB canvas app by inline JS code on target HTML page
               model.addAttribute("facebookInstallAppUrl", canvasAppInstallUrl);
       		return baseErrorPage;
            }
    
        private String buildFacebookRequestUrl(NativeWebRequest request, OAuth2ConnectionFactory<?> connectionFactory) {
            String authorizationUrl = webSupport.buildOAuthUrl(connectionFactory, request);
            String sessionId = request.getSessionId();
            if (sessionId != null && !sessionId.isEmpty()) {
                authorizationUrl += "&state=" + sessionId;
            }
            authorizationUrl += "&scope=email"; // all necessary FB app permissions go here
            return authorizationUrl;
        }
    
    /// The main working method goes here:
    
    @RequestMapping(value = "/", method = RequestMethod.POST)
        public String processFaceBookCanvasRequest(
                NativeWebRequest request,
                Model model) {
            // get 'signed_request' parameters POST-ed by FB
            String signedRequest = request.getParameter("signed_request");
            // create Spring Social connection factory for later use
            OAuth2ConnectionFactory<?> connectionFactory = (OAuth2ConnectionFactory<?>)
                    socialAuthenticationServiceLocator.getConnectionFactory("facebook");
    
            if (signedRequest != null && !signedRequest.isEmpty()) {
                Map<String, ?> decoded = null;
                try {
                    // try to decode 'signed_request'
                    decoded = decoder.decodeSignedRequest(signedRequest);
                } catch (SignedRequestException e) {
             ..............
    
                try {
                    // facebook returns 'basic' data first access time
                    Map<String, String> userMap = (Map<String, String>)decoded.get("user"); // that data is known every time
    
                    // that fb data exists ONLY when user installed fb canvas app in his FB profile
                    String providerUserId = (String)decoded.get("user_id");
                    String oauthTokenValue = (String)decoded.get("oauth_token");
                    Integer expiresTimeValue = (Integer)decoded.get("expires");
    
                    // If user has canvas app installed in his FB profile ?
                    if (providerUserId == null &&
                            oauthTokenValue == null
                            && expiresTimeValue == null) {
                        // user DON'T HAVE canvas app installed in his FB profile
                        // new user, redirect to 'install FB app' page
                        String authorizationUrl = buildFacebookRequestUrl(request, connectionFactory);
                        model.addAttribute("authorizationUrl", authorizationUrl);
                        return "/facebook/signin"; // page with inlined JS script to go to 'install FB app' page
    
    
                    } else {
    
                        // user DO HAVE canvas app installed in his FB profile
                        // that should be registered user
                        // user can be 'temporary' which is created by Spring Social in UserConnection table (not fully registered)
                        // user can be 'fully registered' by our app and has 'completed User/Profile' stuff with UserConnection record related to him
    
                        ConnectionKey key =  new ConnectionKey("facebook", providerUserId);
                        ConnectionRepository connectionRepository =
                                usersConnectionRepository.createConnectionRepository(providerUserId);
                        Connection<?> connection = null;
                        try {
                            connection = connectionRepository.getConnection(key);
                        } catch (NoSuchConnectionException e) {
                            // something is wrong, userConnection is not found - go to install FB app again
                            String authorizationUrl = buildFacebookRequestUrl(request, connectionFactory);
                            model.addAttribute("authorizationUrl", authorizationUrl);
                            return "/facebook/signin"; // page with inlined JS script to go to 'install FB app' page
                        }
    
                        // find 'temporary' user created by 'Spring Social' code internally (custom code)
                        User notExistedUser = accountService.findOneByProviderUserId(providerUserId, providerUserId);
    
                        if (notExistedUser != null) {
                            // this is really 'new' user, but he should finish registration first
                            facebook = facebookServiceProvider.getApi(oauthTokenValue);
                            FacebookProfile userProfile = facebook.userOperations().getUserProfile();
                            model.addAttribute("profile", userProfile);
    
                            handleSignIn(connection, request, model);
                            // redirect user to page for finishing registration
                            String resultView = "redirect:/facebook/finishRegistration";
                            return  resultView + "?providerUserId=" + providerUserId........ // pass other params from FB profile
    
                        } else {
    
                            // let's find 'internal User' who has finished registration previously  (custom code)
                            notExistedUser = accountService.findOneByUserConnectionValuesInSocialSet(
                                    providerUserId, "facebook", providerUserId);
    
                            if (notExistedUser != null) {
                                // we have completely registred User
                                HttpServletRequest httpRequest =
                                                (HttpServletRequest) request.getNativeRequest();
                                // make automatic login into system via Spring Security
                                AccountUtils.setLoginUserAccount(notExistedUser, httpRequest);
                                return "profile/profile"; // show his profile
    
                            } else {
    
                                // it's should not happen, but let's have error processing
                                model.addAttribute("error", "Canvas Facebook App user not found error");
                                return baseErrorPage;
    
                            }
    
                } catch (Exception e) {
                    log.error("Exception while handling 'signed_request'/'authToken' value (" + e + "). Redirecting to " + baseErrorPage);
                ..................
            }
            log.error("'signed_request' value is not found (" + signedRequest + "). Redirecting to " + baseErrorPage);
            model.addAttribute("error", "'signed_request' value POST-ed by Facebook is not found");
            model.addAttribute("error_description", "Looks like you are trying to install Facebook App outside of Facebook site ?");
            return baseErrorPage;
        }
    Best regards.

  7. #7

    Default

    Other borrowed code from 'ProviderSignInController'
    Code:
        private String handleSignIn(Connection<?> connection, NativeWebRequest request, Model model) {
       		List<String> userIds = usersConnectionRepository.findUserIdsWithConnection(connection);
         ......... // a bit modified code from 'ProviderSignInController'
          }
    
        @RequestMapping(value="/", method=RequestMethod.GET, params="code")
       	public String oauth2Callback(
                @RequestParam("code") String code,
                NativeWebRequest request,
                Model model) {
              ..............
           }
    	private String redirect(String url) {
    		return "redirect:" + url;
    	}
    }
    That big code snipped is quite far from all stuff I've used for canvas app, but it gives you main idea for processing loop.
    The most logic is handled by 'POST' method and oauth2Callback(), other methods are additional.
    oauth2Callback() creates Connection<?> connection internally, it's the record in 'UserConnection' table about FB user with oauth_token which can be used for getting access to FB.

    My test canvasApp is configured in FB as :
    Canvas URL = http://1270.0.0.1:8080/canvas/ (the main url for test app)
    Web Site URL = http://1270.0.0.1:8080/ (additonal and it's not used by canvas app)
    Domain app URL like: http://apps.facebook.com/my-test-super-canvas-app/

    'signin' page has JS code to trigger redirecting browser's top window to prepared FB Auth URL:
    Code:
    signin.jsp
    
    <script>
    top.location.href="${authorizationUrl}";
    </script>
    
    URL like:
    https://www.facebook.com/dialog/oauth?client_id=465XXXXXXX87332&response_type=code&redirect_uri=http%3A%2F%2F127.0.0.1%3A8080%2Fsignin%2Ffacebook&scope=email
    That's the main idea behind processing FB canvas app.
    Every time User hit in browser app url - http://apps.facebook.com/my-test-super-canvas-app/
    we should check in 'POST' processing if he meet conditions like:
    - if he has installed our canvas app (if not - send him to install url)
    - if he is found in UserConnection table (and probably in our 'User/Account' entity as temporary user with incomplete registration)
    - if he completely registered inside our app (if not completely - finish registration explicitly)
    - pass him into app and authenticate by our security code

    Then he uses internal app urls/pages as authenticated user till again refreshes url in browser : http://apps.facebook.com/my-test-super-canvas-app/ the all POST logic is executed every time.

    Use - https://developers.facebook.com/docs...book/tutorial/
    for internal details of canvas app flow.
    Best regards.

  8. #8
    Join Date
    Jan 2013
    Posts
    7

    Default

    Well
    the problem is obviously about cookies, why would this approach sort the problem out? I guess I couldn't get it.

  9. #9
    Join Date
    Aug 2004
    Posts
    1,075

    Default

    The reason that blanger's code works is because it's a *COMPLETELY* different approach to obtaining an access token. Rather than go through the typical OAuth2 flow which involves redirects, he simply accepts the access token via the signed_request that is given to his controller when Facebook POSTs to the canvas app.

    I can't say why the cookie stuff isn't working. That's not really a Spring Social issue as it'd be a general question of why IE9 doesn't like the cookies created in that code. Which, BTW, is code that's in a sample application that is not really handling FB canvas apps correctly.

    (I really need to fix that sample or remove it entirely, as it is creating too much confusion over the past several days.)
    Craig Walls
    Spring Social Project Lead

  10. #10
    Join Date
    Jan 2013
    Posts
    7

    Default

    Ok
    I got it now,
    As a dummy spring-social user for me, it would be wonderful to have a simple canvas sample/tutorial (for instance blandger's example).

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •