Skip to content

Instantly share code, notes, and snippets.

@jforaker
Last active December 9, 2018 12:45
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save jforaker/bbd1b27138f259f2d7fa to your computer and use it in GitHub Desktop.
Save jforaker/bbd1b27138f259f2d7fa to your computer and use it in GitHub Desktop.
react-native android Facebook login

React-native Android Facebook login implementation

gif

Many thanks to @satya164 and this thread for getting things moving.

##Setup your app on Facebook

FB quickstart

#####Make sure to Set your app to public or else you will have to assing test users (cannot be yourself)

#####Add the Facebook SDK to your android/app/build.gradle under dependencies:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:23.0.1'
    compile 'com.facebook.react:react-native:0.12.+'
    compile 'com.facebook.android:facebook-android-sdk:4.7.0'   // <---- add this
}

#####Make sure mavenLocal() is under the main android/build.gradle

allprojects {
    repositories {
        mavenLocal()    <---  add this if its not there already  *****
        jcenter()
    }
}

#####Add / modify all the files in this gist as indicated

files

#####Add a NativeModules js module to your project (FacebookLoginModule.android.js below)

#####Create a react-native view that exposes the .pickAccount() method of FacebookLoginModule.android.js, for es:

var FacebookLogin = require('./FacebookLoginModule.android');

    ...
    
    _onFacebookPress() {
        FacebookLogin.pickAccount(e => this._onSignIn(e))
    }

    ...

    <TouchableHighlight onPress={this._onFacebookPress}>  ... </TouchableHighlight>

#####Build and run your project

On login, you will recieve the following response: response

Now you are ready to build, run, repeat, and enjoy!

I was experiencing a strange error about my Key Hashes not matching what I had generated for Facebook, but I simply copied the hash from the error message and added to FB, and the error was resolved.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yourApp">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!--add com.facebook.FacebookActivity-->
<activity
android:name="com.facebook.FacebookActivity"
android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
android:label="@string/app_name"
android:theme="@android:style/Theme.Translucent.NoTitleBar"/>
<!--reference your fb_app_id-->
<meta-data
android:name="com.facebook.sdk.ApplicationId"
android:value="@string/fb_app_id"/>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity"/>
</application>
</manifest>
const React = require('react-native');
const { NativeModules } = React;
const { FacebookLoginModule } = NativeModules;
module.exports = FacebookLoginModule;
//exposes @ReactMethod methods .pickAccount() and .getCurrentToken() via FacebookLoginModule.java
//add to the same directory as MainActivity.java
package com.yourApp;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import com.facebook.AccessToken;
import com.facebook.CallbackManager;
import com.facebook.FacebookCallback;
import com.facebook.FacebookException;
import com.facebook.FacebookRequestError;
import com.facebook.FacebookSdk;
import com.facebook.GraphRequest;
import com.facebook.GraphResponse;
import com.facebook.login.LoginManager;
import com.facebook.login.LoginResult;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableMap;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Arrays;
import java.util.Date;
public class FacebookLoginModule extends ReactContextBaseJavaModule {
private final String CALLBACK_TYPE_SUCCESS = "success";
private final String CALLBACK_TYPE_ERROR = "error";
private final String CALLBACK_TYPE_CANCEL = "cancel";
private Context mActivityContext;
private CallbackManager mCallbackManager;
private Callback mTokenCallback;
public FacebookLoginModule(ReactApplicationContext reactContext, Context activityContext) {
super(reactContext);
mActivityContext = activityContext;
FacebookSdk.sdkInitialize(activityContext.getApplicationContext());
mCallbackManager = CallbackManager.Factory.create();
LoginManager.getInstance().registerCallback(mCallbackManager,
new FacebookCallback<LoginResult>() {
@Override
public void onSuccess(final LoginResult loginResult) {
GraphRequest.newMeRequest(loginResult.getAccessToken(), new GraphRequest.GraphJSONObjectCallback() {
@Override
public void onCompleted(JSONObject me, GraphResponse response) {
if (mTokenCallback != null) {
FacebookRequestError error = response.getError();
if (error != null) {
WritableMap map = Arguments.createMap();
map.putString("errorType", error.getErrorType());
map.putString("message", error.getErrorMessage());
map.putString("recoveryMessage", error.getErrorRecoveryMessage());
map.putString("userMessage", error.getErrorUserMessage());
map.putString("userTitle", error.getErrorUserTitle());
map.putInt("code", error.getErrorCode());
consumeCallback(CALLBACK_TYPE_ERROR, map);
} else {
WritableMap map = Arguments.createMap();
System.out.println("Success");
String jsonresult = String.valueOf(me);
System.out.println("JSON Result" + jsonresult);
map.putString("email", me.optString("email"));
map.putString("token", loginResult.getAccessToken().getToken());
map.putString("expiration", String.valueOf(loginResult.getAccessToken().getExpires()));
consumeCallback(CALLBACK_TYPE_SUCCESS, map);
}
} else {
WritableMap map = Arguments.createMap();
map.putString("message", "Insufficient permissions");
consumeCallback(CALLBACK_TYPE_ERROR, map);
}
}
}).executeAsync();
}
@Override
public void onCancel() {
if (mTokenCallback != null) {
consumeCallback(CALLBACK_TYPE_CANCEL, Arguments.createMap());
}
}
@Override
public void onError(FacebookException exception) {
if (mTokenCallback != null) {
WritableMap map = Arguments.createMap();
map.putString("message", exception.getMessage());
consumeCallback(CALLBACK_TYPE_ERROR, map);
}
if (exception != null) {
if (AccessToken.getCurrentAccessToken() != null) {
LoginManager.getInstance().logOut();
}
}
}
});
}
private void consumeCallback(String type, WritableMap map) {
if (mTokenCallback != null) {
map.putString("type", type);
map.putString("provider", "facebook");
mTokenCallback.invoke(map);
mTokenCallback = null;
}
}
@Override
public String getName() {
return "FacebookLoginModule";
}
@ReactMethod
public void pickAccount(final Callback callback) {
if (mTokenCallback != null) {
AccessToken accessToken = AccessToken.getCurrentAccessToken();
WritableMap map = Arguments.createMap();
if (accessToken != null) {
map.putString("token", AccessToken.getCurrentAccessToken().getToken());
map.putString("expiration", String.valueOf(AccessToken.getCurrentAccessToken()));
map.putBoolean("cache", true);
consumeCallback(CALLBACK_TYPE_SUCCESS, map);
} else {
map.putString("message", "Cannot register multiple callbacks");
consumeCallback(CALLBACK_TYPE_CANCEL, map);
}
}
mTokenCallback = callback;
//set read permissions https://developers.facebook.com/docs/facebook-login/permissions/v2.5
LoginManager.getInstance().logInWithReadPermissions(
(Activity) mActivityContext,
Arrays.asList("public_profile", "email"));
}
@ReactMethod
public void getCurrentToken(final Callback callback) {
callback.invoke(AccessToken.getCurrentAccessToken().getToken());
}
public boolean handleActivityResult(final int requestCode, final int resultCode, final Intent data) {
return mCallbackManager.onActivityResult(requestCode, resultCode, data);
}
}
//add to the same directory as MainActivity.java
package com.yourApp;
import android.content.Context;
import android.content.Intent;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class FacebookLoginPackage implements ReactPackage {
private Context mContext;
private FacebookLoginModule mModuleInstance;
FacebookLoginPackage(Context activityContext) {
mContext = activityContext;
}
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
mModuleInstance = new FacebookLoginModule(reactContext, mContext);
return Arrays.<NativeModule>asList(mModuleInstance);
}
@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Arrays.asList();
}
public boolean handleActivityResult(final int requestCode, final int resultCode, final Intent data) {
if (mModuleInstance == null) {
return false;
}
return mModuleInstance.handleActivityResult(requestCode, resultCode, data);
}
}
var React = require('react-native');
var Dimensions = require('Dimensions');
var windowSize = Dimensions.get('window');
var FacebookLogin = require('./FacebookLoginModule.android');
var {
StyleSheet,
View,
TouchableHighlight,
Text
} = React;
class Login extends React.Component {
constructor(props) {
super(props);
this.redirectAfterSuccess = this.redirectAfterSuccess.bind(this);
this._onFacebookPress = this._onFacebookPress.bind(this);
this._onSignIn = this._onSignIn.bind(this);
}
redirectAfterSuccess(route) {
this.props.navigator.push({
component: route
});
}
_onSignIn(info) {
console.log('data from facebook login', info);
this.redirectAfterSuccess('someRoute')
}
_onFacebookPress() {
//call the magic and handle the response
FacebookLogin.pickAccount(e => this._onSignIn(e))
}
render() {
return (
<View style={styles.container}>
<Text>Your awesome app</Text>
<TouchableHighlight onPress={this._onFacebookPress}>
<View style={styles.login}>
<Text style={styles.whiteFont}>Login with Facebook</Text>
</View>
</TouchableHighlight>
</View>
)
}
}
var styles = StyleSheet.create({
container: {
flexDirection: 'column',
flex: 1,
backgroundColor: 'white'
},
login: {
flex: 1,
backgroundColor: '#3B5998',
padding: 20,
alignItems: 'center'
},
whiteFont: {
color: 'white'
}
});
module.exports = Login;
//Add the package and call the method
package com.yourApp;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.KeyEvent;
import com.facebook.react.LifecycleState;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactRootView;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
public class MainActivity extends Activity implements DefaultHardwareBackBtnHandler {
private ReactInstanceManager mReactInstanceManager;
private ReactRootView mReactRootView;
private FacebookLoginPackage mFacebookLoginPackage; // <--------Add the package
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mFacebookLoginPackage = new FacebookLoginPackage(this); // <--------instantiate the package
mReactRootView = new ReactRootView(this);
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setBundleAssetName("index.android.bundle")
.setJSMainModuleName("index.android")
.addPackage(new MainReactPackage())
.addPackage(mFacebookLoginPackage) // <--------Add the package
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
mReactRootView.startReactApplication(mReactInstanceManager, "yourApp", null);
setContentView(mReactRootView);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) {
mReactInstanceManager.showDevOptionsDialog();
return true;
}
return super.onKeyUp(keyCode, event);
}
@Override
public void onBackPressed() {
if (mReactInstanceManager != null) {
mReactInstanceManager.onBackPressed();
} else {
super.onBackPressed();
}
}
@Override
public void invokeDefaultOnBackPressed() {
super.onBackPressed();
}
@Override
protected void onPause() {
super.onPause();
if (mReactInstanceManager != null) {
mReactInstanceManager.onPause();
}
}
@Override
protected void onResume() {
super.onResume();
if (mReactInstanceManager != null) {
mReactInstanceManager.onResume(this);
}
}
//Call the method in MainActivity
@Override
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
mFacebookLoginPackage.handleActivityResult(requestCode, resultCode, data);
}
}
<resources>
<string name="app_name">yourApp</string>
<string name="fb_app_id">yourFacebookAppID</string>
</resources>
<!--add your Facebook App Id-->
@lwhiteley
Copy link

pretty much what i did, will see how similar i can get it to the IOS version

@cosmith
Copy link

cosmith commented Oct 30, 2015

Thanks! 👍

@jforaker
Copy link
Author

jforaker commented Dec 5, 2015

UPDATE: This was written before any modules were available. Please see this repo https://github.com/magus/react-native-facebook-login for a fully installable version

@sunnynegi
Copy link

I am facing issue with debug apk, it throw error that invalid key hash, The key hash asdgksagdjkgaskjdhkaj/asdas does not match any stored key value

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment