keppelen / react-facebook-login

A Component React for Facebook Login
1.18k stars 406 forks source link

Unable to get the required redirect_uri #326

Open FSocietyLK opened 3 years ago

FSocietyLK commented 3 years ago

I'm trying to implement the Facebook OAuth in my express/NodeJS app using authorization code flow. I'm using this node module to fetch the authorization code. In my react app, I could get the authorization code successfully. But in server side, I can't request the access token from the Facebook API as I'm getting an error message "redirect_uri is not identical to the one you used in the OAuth dialog request".

Code in my react app,

facebookLogin = async (signedRequest) => {
    return fetch('/api/auth/facebook', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ signedRequest }),
    }).then((res) => {
      if (res.ok) {
        return res.json();
      } else {
        return Promise.reject(res);
      }
    });
  };

  responseFacebook = async (response) => {
    try {
      if (response['signedRequest']) {
        const userProfile = await this.facebookLogin(response['signedRequest']);
        console.log(userProfile);
      } else {
        throw new Error(response.error);
      }
    } catch (err) {
      console.log(err);
    }
  };

render() {
    <FacebookLogin
            appId={process.env.FACEBOOK_CLIENT_ID}
            fields="name,email"
            responseType="code"
            redirectUri="http://localhost:3000/"
            callback={this.responseFacebook}
    />

In my app.js

const facebookOAuth = require('./config/facebookOAuth');

// facebook oauth route
app.post("/api/auth/facebook", async (req, res) => {
  try {
    const signedRequest = req.body.signedRequest;
    const profile = await facebookOAuth.getProfile(signedRequest);
    console.log(profile);

    res.send({ profile });
  } catch (err) {
    console.log(err);
    res.status(401).send();
  }
});

facebookOAuth.js look like this

const fetch = require('node-fetch');

const getData = async (userId, accessToken) => {
    const userData = await fetch(`https://graph.facebook.com/${userId}?fields=name,email&access_token=${accessToken}`, {
        method: 'GET'
    }).then((res) => {
        return res.json();
    }).then((userData) => {
        return userData;
    });

    return userData;
};

exports.getProfile = async (signedRequest) => {
    const decodedSignedRequest = JSON.parse(Buffer.from((signedRequest.split(".")[1]), 'base64').toString());
    const profile = await fetch(`https://graph.facebook.com/oauth/access_token?client_id=${process.env.FACEBOOK_CLIENT_ID}&redirect_uri=${encodeURIComponent('http://localhost:3000/')}&client_secret=${process.env.FACEBOOK_CLIENT_SECRET}&code=${decodedSignedRequest.code}`, {
        method: 'GET'
    }).then((res) => {
        return res.json();
    }).then((token) => {
        console.log(token);
        const userData = getData(decodedSignedRequest.user_id, token.access_token);
        return userData;
    }).catch((err) => {
        console.log(err);
        return err;
    });

    return profile;
}

What I'm getting is this error

"error": {
      "message": "Error validating verification code. Please make sure your redirect_uri is identical to the one you used in the OAuth dialog request",
      "type": "OAuthException",
      "code": 100,
      "error_subcode": 36008,
      "fbtrace_id": "APx0xJoFPNkqgkLpyob-_Vl"
}

I think the problem lies in my redirect_uri. Apparently, the redirect uri I obtained from the Facebook auth dialog is different from the one that I'm passing to the facebook API in my server side and in the facebook app (http://localhost:3000/).

I believe there's something to do with the origin parameter of the redirect_uri. Initial auth dialog request uri indicates that it's origin parameter value is something like "origin=localhost:3000/f370b6cb4b5a9c". Some sort of trailing value is there, which I can't understand why?

https://web.facebook.com/v2.3/dialog/oauth?app_id=249141440286033&auth_type=&cbt=1620173773354&channel_url=https://staticxx.facebook.com/x/connect/xd_arbiter/?version=46#cb=f39300d6265e5c4&domain=localhost&origin=http%3A%2F%2Flocalhost%3A3000%2Ff370b6cb4b5a9c&relation=opener&client_id=249141440286033&display=popup&domain=localhost&e2e={}&fallback_redirect_uri=http://localhost:3000/&locale=en_US&logger_id=f1b3fba38c5e31c&origin=1&redirect_uri=https://staticxx.facebook.com/x/connect/xd_arbiter/?version=46#cb=f17641be4cce4d4&domain=localhost&origin=http%3A%2F%2Flocalhost%3A3000%2Ff370b6cb4b5a9c&relation=opener&frame=f3960892790a6d4&response_type=token,signed_request,graph_domain&return_scopes=false&scope=public_profile,email&sdk=joey&version=v2.3

@keppelen I tried finding everywhere about this but no luck. Anyone has clue about this, much appreciated.

xyanlucasx commented 3 years ago

same problem here. updating the redirectUri tag has no effect. the redirectUri keeps getting window.location.href

madhawa-se commented 3 years ago

same here

FSocietyLK commented 3 years ago

I think the best way is to use short lived access token instead of authorization code to exchange with a long lived access token.

In app.js

// facebook oauth route
router.post("/api/auth/facebook", async (req, res) => {
  try {
    const userId = req.body.userId;
    const accessToken = req.body.accessToken;
    const profile = await facebookOAuth.getProfile(userId, accessToken);
    var user = {
      id: profile.id,
      name: profile.name,
      email: profile.email,
    };
    res.send({ user });
  } catch (err) {
    console.log(err);
    res.status(401).send();
  }
});

In facebookOAuth.js

const getData = async (userId, longLivedTokenAccessToken) => {
    const urlFetchData = new URL(`https://graph.facebook.com/${userId}`);

    const params = {
        fields: "name,email",
        access_token: longLivedTokenAccessToken
    }

    urlFetchData.search = new URLSearchParams(params).toString();

    const userData = await fetch(urlFetchData, {
        method: 'GET'
    }).then((res) => {
        return res.json();
    }).then((userData) => {
        return userData;
    }).catch((err) => {
        console.log(err);
        return err;
    });

    return userData;
};

exports.getProfile = async (userId, shortLivedAccessToken) => {
    const urlFetchToken = new URL('https://graph.facebook.com/oauth/access_token');

    const params = {
        grant_type: "fb_exchange_token",
        client_id: process.env.FACEBOOK_CLIENT_ID,
        client_secret: process.env.FACEBOOK_CLIENT_SECRET,
        fb_exchange_token: shortLivedAccessToken
    };

    urlFetchToken.search = new URLSearchParams(params).toString();

    const profile = await fetch(urlFetchToken, {
        method: 'GET'
    }).then((res) => {
        return res.json();
    }).then((longLivedToken) => {
        const userData = getData(userId, longLivedToken.access_token);
        return userData;
    }).catch((err) => {
        console.log(err);
        return err;
    });

    return profile;
}

This is how I managed to do this. Hope this helps :-)

dantehemerson commented 1 year ago

@FSocietyLK use an empty string as value for redirect_uri.

It worked for me. https://graph.facebook.com/v15.0/oauth/access_token?client_id=1521669324940129&redirect_uri=&client_secret=xxx&code=AQBtA4qgoXKUZqSYEZyHPDhmr9Tofz7Vnzxn0f6hfOADNIx1scQ3hD1AwSZeZQ9oFt9sskkLwZXY5ShIKn8-Aah_FQAcwilWn_zJ4693pl-ANXaWpeNg5DRn0M5K78EvfwulMkvWvdJIybyBm_JnOaFmRvD1ohp_smyMccjnCD2k8L-rynTrZcr94DMSgIFfYuGEzMAAUjY14CWrYbkDAYGrF9tg9ujPUpS0zs645EOT14Ic_O7ID8Kjna3mbh9Rb7hrYZHb5LUYNOe915Ntl8n-UXcMZijoKKIm6YgxtVb2dpxrkXE7mTC88KxiFoczeMOSzMo7GY6CLcfBGBlQPJ2tqpv_Djuq_lomcuxE3Z9eBA

image

Alx90s commented 9 months ago

@FSocietyLK use an empty string as value for redirect_uri.

It worked for me. https://graph.facebook.com/v15.0/oauth/access_token?client_id=1521669324940129&redirect_uri=&client_secret=xxx&code=AQBtA4qgoXKUZqSYEZyHPDhmr9Tofz7Vnzxn0f6hfOADNIx1scQ3hD1AwSZeZQ9oFt9sskkLwZXY5ShIKn8-Aah_FQAcwilWn_zJ4693pl-ANXaWpeNg5DRn0M5K78EvfwulMkvWvdJIybyBm_JnOaFmRvD1ohp_smyMccjnCD2k8L-rynTrZcr94DMSgIFfYuGEzMAAUjY14CWrYbkDAYGrF9tg9ujPUpS0zs645EOT14Ic_O7ID8Kjna3mbh9Rb7hrYZHb5LUYNOe915Ntl8n-UXcMZijoKKIm6YgxtVb2dpxrkXE7mTC88KxiFoczeMOSzMo7GY6CLcfBGBlQPJ2tqpv_Djuq_lomcuxE3Z9eBA

image

For me its working too. I wasted 5 hours... of my life :3