agorapulse / grails-facebook-sdk

Facebook SDK Grails Plugin
http://agorapulse.github.com/grails-facebook-sdk/guide
30 stars 13 forks source link

Unexpected signature when running from Facebook Page Tab #63

Closed crudolf closed 10 years ago

crudolf commented 10 years ago

If I run my page from the usual website everything works fine. If I run the same page from a facebook page tab, then I get an exception from this plugin:

Invalid signed request. Expression: (expectedSignature == encodedSignature.decodeBase64()). Values: expectedSignature 
    at grails.plugin.facebooksdk.FacebookSignedRequest.parseSignedRequest(FacebookSignedRequest.groovy:88)
    at grails.plugin.facebooksdk.FacebookSignedRequest.<init>(FacebookSignedRequest.groovy:35)
    at grails.plugin.facebooksdk.FacebookContext.afterPropertiesSet(FacebookContext.groovy:70)
    ... 3 more

Is there some workaround to make the page work, when run from page tab?

benorama commented 10 years ago

That's strange, we are using the app in Facebook Page Tab every day without issue. Could you give us more detail on how to reproduce this issue? Could you double check that app id / secret key are well configured in your app? Thanks.

crudolf commented 10 years ago

The app itself is working fine using the direct URL, check out: https://support.zeitfest.de If it's used in facebook page tab: https://www.facebook.com/zeitfest (click the green button) you receive an error message. However, this error appears only once. If you reload the iFrame the page works :(

crudolf commented 10 years ago

I deactivated the plugin temporarily, so you cannot check it online. However the issue is:

Possibly this is because of a subdomain configuration? The app is registered to "domain.xy" and is then called from "abc.domain.xy", "def.domain.xy". Are there any restrictions for the page tab? Does it have to be the same application, which it runs within? E.g. the facebook page tab is ANOTHER application, then the connect is going to. Could this be the issue? However it should be possible I guess.

So there is one facebook app having for the login, and there are plenty of facebook apps, which give a page tab.

benorama commented 10 years ago

Indeed, you might have some issue with subdomains. Have you tried to enter all the subdomains in your FB app settings? You might also try *.domain.com ?

crudolf commented 10 years ago

It's a feature from facebook: If you enter a domain, facebook will automatically accept all subdomains. It is also working on XY.domain.com and AB.domain.com, if I use the app directly.

Only if the page is embedded in a page tab, I receive this exception.

crudolf commented 10 years ago

I am still receiving this exception:

Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [de.zeitfest.client.LoginController]: Constructor threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'facebookContext': Invocation of init method failed; nested exception is java.lang.AssertionError: Invalid signed request. Expression: (expectedSignature == encodedSignature.decodeBase64()). Values: expectedSignature = [-20, 52, -51, -124, 55, 50, 79, 83, 42, 7, -57, -102, -106, -81, -100, -78, 34, 93, 28, -1, -61, 84, -35, 121, 32, -97, 14, -41, 124, -106, 28, -15]
        ... 3 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'facebookContext': Invocation of init method failed; nested exception is java.lang.AssertionError: Invalid signed request. Expression: (expectedSignature == encodedSignature.decodeBase64()). Values: expectedSignature = [-20, 52, -51, -124, 55, 50, 79, 83, 42, 7, -57, -102, -106, -81, -100, -78, 34, 93, 28, -1, -61, 84, -35, 121, 32, -97, 14, -41, 124, -106, 28, -15]
        ... 3 more
Caused by: java.lang.AssertionError: Invalid signed request. Expression: (expectedSignature == encodedSignature.decodeBase64()). Values: expectedSignature = [-20, 52, -51, -124, 55, 50, 79, 83, 42, 7, -57, -102, -106, -81, -100, -78, 34, 93, 28, -1, -61, 84, -35, 121, 32, -97, 14, -41, 124, -106, 28, -15]
        at grails.plugin.facebooksdk.FacebookSignedRequest.parseSignedRequest(FacebookSignedRequest.groovy:88)
        at grails.plugin.facebooksdk.FacebookSignedRequest.<init>(FacebookSignedRequest.groovy:35)
        at grails.plugin.facebooksdk.FacebookContext.afterPropertiesSet(FacebookContext.groovy:70)
        ... 3 more

I would love to use this plugin. Do you have any hints? I tried the latest version 0.5 today.

benorama commented 10 years ago

Sorry to hear that you still have this issue. Indeed 0.5 version is just an upgrade of the underlying RestFB java lib, so nothing has changed on the signed request handling.

From what I see in the stacktrace, it looks like the app secret key is incorrect: are you sure that you are using the app secret key of the tab app? You said that you are using another App for the connect? This might be the source of the problem.

Outside Facebook, FB Connect is based on a encoded signed request in a cookie set by the FB JS SDK (or a redirect to FB Login page). Grails Facebook SDK automatically decodes this cookie when detected.

When FB load you app in a tab, it's different. During the initial page loading, FB sends by POST an encoded signed_request parameter to the iframe so that if the user has already authorized your app, he will be automatically authenticated. The signed_request params is detected and decoded by the Grails Facebook SDK to get all the user info (authenticated, user id...) which are then put in session scope. After that, when the user navigate in your iframe, info from session scope are used.

To decode this signed_request parameter, you must configure the app secret corresponding to tab app. So double check that the app secret used during the initial page load is the one from the FB app used to installed the tab on your FB page. Let me know how it goes.

crudolf commented 10 years ago

Oh thank you for this response.

Indeed we have multiple facebook apps having just a facebook page tab, and this is obviously the problem. Thanks for explaining.

However we permanently create new facebookapps on runtime (since we get more customers). It would be fantastic, if there would be the possibility to define facebook id, secret and permissions on runtime. Do you think that this might a useful feature request for you?

crudolf commented 10 years ago

If you agree, I would create a new ticket, since this is solved now! Thanks

benorama commented 10 years ago

Instead of using the facebookContext bean which is automatically created for you, you should be able dynamically create the context (in a Filter or beforeInterceptors) for a given app.

So for example, let's say that you have a FacebookApp domain (that you would use to store each new FB app), if you pass a appFacebookId parameter, you could have something like this:

app = FacebookApp.findByFacebookId(params.long('appFacebookId'))

facebookContext = new FacebookContext()
facebookContext.grailsApplication = grailsApplication
facebookContext.app = new FacebookContextApp(
                id: app.facebookId,
                context: facebookContext,
                permissions: PERMISSIONS,
                secret: app.facebookSecretKey
)
facebookContext.user = new FacebookContextUser(context: facebookContext)
if (facebookContext.cookie.value && !params?.state && !params?.code) { // Ignore cookie when coming from a server side OAuth redirect
    facebookContext.signedRequest = new FacebookSignedRequest(app.facebookSecretKey, facebookContext.cookie.value, FacebookSignedRequest.TYPE_COOKIE)
}

if (facebookContext.authenticated && facebookContext.user.getToken()) {
    // User is authenticated
}