Love Fuel?    Donate

FuelPHP Forums

Ask your question about FuelPHP in the appropriate forum, or help others by answering their questions.
Getting Facebook and Opauth working correctly.
  • Hello,

    I am trying to get Facebook login working using Opauth library and it is not working correctly.  After finally getting everything set up and the config in the right place I am receiving this error:

    Auth\OpauthException [ Error ]:
    no valid response received in the callback

    PKGPATH/auth/classes/auth/opauth.php @ line 367


    And I'm not really sure what the issue is, if there is anyone that can help I would greatly appreciate it.
  • That is usually caused by incorrect configuration at the facebook end.

    We have a login controller that contains the callback method (the example in the docs is a virtual copy of our login controller), and on de facebook developer page, under "My apps", "Settings", I have this as website URL: "http://sitename/login/callback/facebook".

    This has to be correct, otherwise it doesn't work.
  • Ok, so I'm assuming this has to be the website for the server, not for our front end.  We have our "auth" controller which handles our login/registration at /api/auth so would it be http://sitename/api/auth/login/callback/facebook or how is this determined?

    Edit:

    I call the 

            $opauth = \Auth_Opauth::forge(false);
            $status = $opauth->login_or_register();

    from action_facebook_login() in our auth controller.

    So am I safe to assume the url should be http://sitename/api/auth/facebook_login/callback/facebook?

    When I enter this it still doesn't work though :(

    Thanks!
  • Looks ok.

    It is this line:

    $opauth = \Auth_Opauth::forge(false);

    that will create an Opauth object, and the next line:

    $status = $opauth->login_or_register();

    will call the callback processor in the Opauth class. Both are outside the realm of Fuel.

    So I guess it is time for some debugging, what is in your callback() method, and what info does it get returned from facebook? Check from within the Auth_Opauth class, in the callback method.
  • I think this is what you're looking for:

    protected function callback()
    {
    // fetch the response and decode it
    $this->response = \Input::get('opauth', false) and $this->response = unserialize(base64_decode($this->response));

    // did we receive a response at all?
    if ( ! $this->response)
    {
    throw new \OpauthException('no valid response received in the callback');
    }

    // did we receive one, but was it an error
    if (array_key_exists('error', $this->response))
    {
    throw new \OpauthException('Authentication error: the callback returned an error auth response');
    }

    // validate the response
    if ($this->get('auth') === null or $this->get('timestamp') === null or
    $this->get('signature') === null or $this->get('auth.provider') === null or $this->get('auth.uid') === null)
    {
    throw new \OpauthException('Invalid auth response: Missing key auth response components');
    }
    elseif ( ! $this->opauth->validate(sha1(print_r($this->get('auth'), true)), $this->get('timestamp'), $this->get('signature'), $reason))
    {
    throw new \OpauthException('Invalid auth response: '.$reason);
    }
    }

    I haven't set any of this up, everything that I have is default except for that method that I wrote in our /api/auth controller so maybe I'm just missing something.

    I thought this whole time it was because I had http://www.sitename.local instead of just http://sitename.local but no difference :(

    If it helps at all when I dump $opauth I get

    'callback_url' <font color='#888a85'>=></font> <small>string</small> <font color='#cc0000'>'/api/auth/callback/'</font> <i>(length=19)</i>
  • Okay, wasn't 100% sure if I should do this but I added a new method to our auth controller:

    public function action_callback() that is basically like in the Opauth example code and I think I'm getting closer but I'm still getting the same response.

    Also I would think that calling

    \Auth_Opauth::forge(false); would actually bring up the Facebook dialog box and that is not happening.

    I should probably also mention that our front end is completely separate from the back end and we just treat it as a REST service, not sure if this changes anything.
  • Yes, that doesn't work at all with OAuth and similar HTTP based authentication services. They all need to redirect you to either prompt you for a login or return a token if you are already logged in.

    That will never work if your frontend does a REST call to a backend that needs to do the authentication.

    Wrong application design for this type of authentication.
  • So do you know if there is anyway to get this to work then?  I was using the JS Facebook library but then just trying to do a force login and you said this wasn't recommended.
  • HarroHarro
    Accepted Answer
    You have to handle the authentication in the frontend.

    If your backend is separated from your frontend you can not use the Auth_Opauth class as that requires all Auth to be local. So you have to write your own version of that class, that does not use the local Auth functions and user table, but uses REST calls to process that part in the backend.

    Going through the methods:
    _init(): replace the references to the "auth" config by hardcoded use of Ormauth or Simpleauth (depending on your backend)
    forge(): can stay as-is
    __construct(): can stay as-is
    login_or_register(): needs to be rewritten, DB calls must become REST calls
    link_provider(): needs to be rewritten, DB calls must become REST calls
    get(): can stay as-is
    callback(): can stay as-is
    create_user(): needs to be rewritten to do a REST call to create the user
    get_instance(): can stay as-is

  • To be complete, it is the frontend that has to maintain the session with the user (the users browser), so the login needs to happen there.

    A login in the backend isn't very useful, there is no cookie mechanism to maintain state, so you need to design an authentication mechanism for your API, which can be either stateless (send the users credentials to the backend on every REST request) or stateful (have an authentication service where the frontend logs into the backend, and returns a token. This token is then passed on each REST request to authenticate the request).
  • Ugh, I have a feeling we are doing this all wrong then -_-.  We wrote the whole site in fuel but then had our iOS app so we thought to just have everything use the same API calls.  So we made a separate front end that hits the same endpoints but to take login as an example we just send the username/password to the server and call Auth::login(); and we were just assuming that cookies maintain the session, it seems to be working so far except for in this case of Facebook.
  • Problem is that at some point in your app, you need to convert the Facebook login to a Fuel Auth login, which means you need a mapping between Facebook user "A" and Fuel user "1".

    This is essentially what the Auth_Opauth class does, it links the two, and once linked, it uses force_login() to login the user locally.

    This al works fine if you have a standard app. This fails if you have a frontend that has the facebook info, and a backend that has the Fuel info, so you need to interface the two, which is what I attempted to describe in my previous post.

    I don't think it's that complex. You have to have a close look at the login_or_register() method and the link_provider() method, and determine which part of the logic is frontend, which part is backend, and find a way to split the two and have REST communication in between.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

In this Discussion