package io.fullstack.firestack; import android.content.Context; import android.util.Log; import java.util.Map; import android.net.Uri; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.Callback; import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableNativeMap; import com.facebook.react.modules.core.DeviceEventManagerModule; import com.facebook.react.bridge.ReactContext; import com.google.android.gms.tasks.OnCompleteListener; import com.google.android.gms.tasks.OnFailureListener; import com.google.android.gms.tasks.Task; import com.google.firebase.FirebaseApp; import com.google.firebase.FirebaseOptions; import com.google.firebase.auth.AuthCredential; import com.google.firebase.auth.AuthResult; import com.google.firebase.auth.UserProfileChangeRequest; import com.google.firebase.auth.FacebookAuthProvider; import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.auth.FirebaseUser; import com.google.firebase.auth.GetTokenResult; import com.google.firebase.auth.GoogleAuthProvider; import com.google.firebase.auth.FirebaseAuthException; import com.google.firebase.auth.TwitterAuthProvider; class FirestackAuthModule extends ReactContextBaseJavaModule { private final int NO_CURRENT_USER = 100; private final int ERROR_FETCHING_TOKEN = 101; private static final String TAG = "FirestackAuth"; private Context context; private ReactContext mReactContext; private FirebaseAuth mAuth; private FirebaseApp app; private FirebaseUser user; private FirebaseAuth.AuthStateListener mAuthListener; public FirestackAuthModule(ReactApplicationContext reactContext) { super(reactContext); this.context = reactContext; mReactContext = reactContext; Log.d(TAG, "New FirestackAuth instance"); } @Override public String getName() { return TAG; } @ReactMethod public void listenForAuth() { mAuthListener = new FirebaseAuth.AuthStateListener() { @Override public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) { WritableMap msgMap = Arguments.createMap(); msgMap.putString("eventName", "listenForAuth"); if (firebaseAuth.getCurrentUser() != null) { WritableMap userMap = getUserMap(); msgMap.putBoolean("authenticated", true); msgMap.putMap("user", userMap); FirestackUtils.sendEvent(mReactContext, "listenForAuth", msgMap); } else { msgMap.putBoolean("authenticated", false); FirestackUtils.sendEvent(mReactContext, "listenForAuth", msgMap); } } }; mAuth = FirebaseAuth.getInstance(); mAuth.addAuthStateListener(mAuthListener); } @ReactMethod public void unlistenForAuth(final Callback callback) { if (mAuthListener != null) { mAuth.removeAuthStateListener(mAuthListener); WritableMap resp = Arguments.createMap(); resp.putString("status", "complete"); callback.invoke(null, resp); } } @ReactMethod public void createUserWithEmail(final String email, final String password, final Callback callback) { mAuth = FirebaseAuth.getInstance(); mAuth.createUserWithEmailAndPassword(email, password) .addOnCompleteListener(new OnCompleteListener<AuthResult>() { @Override public void onComplete(@NonNull Task<AuthResult> task) { if (task.isSuccessful()) { FirestackAuthModule.this.user = task.getResult().getUser(); userCallback(FirestackAuthModule.this.user, callback); } else { // userErrorCallback(task, callback); } } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception ex) { userExceptionCallback(ex, callback); } }); } @ReactMethod public void signInWithEmail(final String email, final String password, final Callback callback) { mAuth = FirebaseAuth.getInstance(); mAuth.signInWithEmailAndPassword(email, password) .addOnCompleteListener(new OnCompleteListener<AuthResult>() { @Override public void onComplete(@NonNull Task<AuthResult> task) { if (task.isSuccessful()) { FirestackAuthModule.this.user = task.getResult().getUser(); userCallback(FirestackAuthModule.this.user, callback); } else { // userErrorCallback(task, callback); } } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception ex) { Log.e(TAG, "An exception occurred: " + ex.getMessage()); userExceptionCallback(ex, callback); } }); } @ReactMethod public void signInWithProvider(final String provider, final String authToken, final String authSecret, final Callback callback) { if (provider.equals("facebook")) { this.facebookLogin(authToken,callback); } else if (provider.equals("google")) { this.googleLogin(authToken,callback); } else if (provider.equals("twitter")) { this.twitterLogin(authToken,authSecret,callback); } else // TODO FirestackUtils.todoNote(TAG, "signInWithProvider", callback); } @ReactMethod public void signInAnonymously(final Callback callback) { mAuth = FirebaseAuth.getInstance(); mAuth.signInAnonymously() .addOnCompleteListener(new OnCompleteListener<AuthResult>() { @Override public void onComplete(@NonNull Task<AuthResult> task) { Log.d(TAG, "signInAnonymously:onComplete:" + task.isSuccessful()); if (task.isSuccessful()) { FirestackAuthModule.this.user = task.getResult().getUser(); userCallback(FirestackAuthModule.this.user, callback); } else { // userErrorCallback(task, callback); } } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception ex) { userExceptionCallback(ex, callback); } }); } @ReactMethod public void signInWithCustomToken(final String customToken, final Callback callback) { mAuth = FirebaseAuth.getInstance(); mAuth.signInWithCustomToken(customToken) .addOnCompleteListener(new OnCompleteListener<AuthResult>() { @Override public void onComplete(@NonNull Task<AuthResult> task) { Log.d(TAG, "signInWithCustomToken:onComplete:" + task.isSuccessful()); if (task.isSuccessful()) { FirestackAuthModule.this.user = task.getResult().getUser(); userCallback(FirestackAuthModule.this.user, callback); } else { // userErrorCallback(task, callback); } } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception ex) { userExceptionCallback(ex, callback); } }); } @ReactMethod public void reauthenticateWithCredentialForProvider(final String provider, final String authToken, final String authSecret, final Callback callback) { AuthCredential credential; if (provider.equals("facebook")) { credential = FacebookAuthProvider.getCredential(authToken); } else if (provider.equals("google")) { credential = GoogleAuthProvider.getCredential(authToken, null); } else if (provider.equals("twitter")) { credential = TwitterAuthProvider.getCredential(authToken, authSecret); } else { // TODO: FirestackUtils.todoNote(TAG, "reauthenticateWithCredentialForProvider", callback); // AuthCredential credential; // Log.d(TAG, "reauthenticateWithCredentialForProvider called with: " + provider); return; } FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser(); if (user != null) { user.reauthenticate(credential) .addOnCompleteListener(new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { if (task.isSuccessful()) { Log.d(TAG, "User re-authenticated with " + provider); FirebaseUser u = FirebaseAuth.getInstance().getCurrentUser(); userCallback(u, callback); } else { // userErrorCallback(task, callback); } } }); } else { WritableMap err = Arguments.createMap(); err.putInt("errorCode", NO_CURRENT_USER); err.putString("errorMessage", "No current user"); callback.invoke(err); } } @ReactMethod public void updateUserEmail(final String email, final Callback callback) { FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser(); if (user != null) { user.updateEmail(email) .addOnCompleteListener(new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { if (task.isSuccessful()) { Log.d(TAG, "User email address updated"); FirebaseUser u = FirebaseAuth.getInstance().getCurrentUser(); userCallback(u, callback); } else { // userErrorCallback(task, callback); } } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception ex) { userExceptionCallback(ex, callback); } }); } else { WritableMap err = Arguments.createMap(); err.putInt("errorCode", NO_CURRENT_USER); err.putString("errorMessage", "No current user"); callback.invoke(err); } } @ReactMethod public void updateUserPassword(final String newPassword, final Callback callback) { FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser(); if (user != null) { user.updatePassword(newPassword) .addOnCompleteListener(new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { if (task.isSuccessful()) { Log.d(TAG, "User password updated"); FirebaseUser u = FirebaseAuth.getInstance().getCurrentUser(); userCallback(u, callback); } else { // userErrorCallback(task, callback); } } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception ex) { userExceptionCallback(ex, callback); } }); } else { WritableMap err = Arguments.createMap(); err.putInt("errorCode", NO_CURRENT_USER); err.putString("errorMessage", "No current user"); callback.invoke(err); } } @ReactMethod public void sendPasswordResetWithEmail(final String email, final Callback callback) { mAuth = FirebaseAuth.getInstance(); mAuth.sendPasswordResetEmail(email) .addOnCompleteListener(new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { if(task.isSuccessful()){ WritableMap resp = Arguments.createMap(); resp.putString("status", "complete"); callback.invoke(null, resp); } else { callback.invoke(task.getException().toString()); } } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception ex) { userExceptionCallback(ex, callback); } }); } @ReactMethod public void deleteUser(final Callback callback) { FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser(); if (user != null) { user.delete() .addOnCompleteListener(new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { if (task.isSuccessful()) { Log.d(TAG, "User account deleted"); WritableMap resp = Arguments.createMap(); resp.putString("status", "complete"); resp.putString("msg", "User account deleted"); callback.invoke(null, resp); } else { // userErrorCallback(task, callback); } } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception ex) { userExceptionCallback(ex, callback); } }); } else { WritableMap err = Arguments.createMap(); err.putInt("errorCode", NO_CURRENT_USER); err.putString("errorMessage", "No current user"); callback.invoke(err); } } @ReactMethod public void getToken(final Callback callback) { FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser(); user.getToken(true) .addOnCompleteListener(new OnCompleteListener<GetTokenResult>() { @Override public void onComplete(@NonNull Task<GetTokenResult> task) { if (task.isSuccessful()) { String token = task.getResult().getToken(); WritableMap resp = Arguments.createMap(); resp.putString("status", "complete"); resp.putString("token", token); callback.invoke(null, resp); } else { WritableMap err = Arguments.createMap(); err.putInt("errorCode", ERROR_FETCHING_TOKEN); err.putString("errorMessage", task.getException().getMessage()); callback.invoke(err); } } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception ex) { userExceptionCallback(ex, callback); } }); } @ReactMethod public void updateUserProfile(ReadableMap props, final Callback callback) { FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser(); UserProfileChangeRequest.Builder profileBuilder = new UserProfileChangeRequest.Builder(); Map<String, Object> m = FirestackUtils.recursivelyDeconstructReadableMap(props); if (m.containsKey("displayName")) { String displayName = (String) m.get("displayName"); profileBuilder.setDisplayName(displayName); } if (m.containsKey("photoUri")) { String photoUriStr = (String) m.get("photoUri"); Uri uri = Uri.parse(photoUriStr); profileBuilder.setPhotoUri(uri); } UserProfileChangeRequest profileUpdates = profileBuilder.build(); user.updateProfile(profileUpdates) .addOnCompleteListener(new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { if (task.isSuccessful()) { Log.d(TAG, "User profile updated"); FirebaseUser u = FirebaseAuth.getInstance().getCurrentUser(); userCallback(u, callback); } else { // userErrorCallback(task, callback); } } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception ex) { userExceptionCallback(ex, callback); } }); } @ReactMethod public void signOut(final Callback callback) { FirebaseAuth.getInstance().signOut(); this.user = null; WritableMap resp = Arguments.createMap(); resp.putString("status", "complete"); resp.putString("msg", "User signed out"); callback.invoke(null, resp); } @ReactMethod public void getCurrentUser(final Callback callback) { mAuth = FirebaseAuth.getInstance(); this.user = mAuth.getCurrentUser(); if(this.user == null){ noUserCallback(callback); }else{ userCallback(this.user, callback); } } // TODO: Check these things @ReactMethod public void googleLogin(String IdToken, final Callback callback) { mAuth = FirebaseAuth.getInstance(); AuthCredential credential = GoogleAuthProvider.getCredential(IdToken, null); mAuth.signInWithCredential(credential) .addOnCompleteListener(new OnCompleteListener<AuthResult>() { @Override public void onComplete(@NonNull Task<AuthResult> task) { if (task.isSuccessful()) { FirestackAuthModule.this.user = task.getResult().getUser(); userCallback(FirestackAuthModule.this.user, callback); }else{ // userErrorCallback(task, callback); } } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception ex) { userExceptionCallback(ex, callback); } }); } @ReactMethod public void facebookLogin(String Token, final Callback callback) { mAuth = FirebaseAuth.getInstance(); AuthCredential credential = FacebookAuthProvider.getCredential(Token); mAuth.signInWithCredential(credential) .addOnCompleteListener(new OnCompleteListener<AuthResult>() { @Override public void onComplete(@NonNull Task<AuthResult> task) { if (task.isSuccessful()) { FirestackAuthModule.this.user = task.getResult().getUser(); userCallback(FirestackAuthModule.this.user, callback); }else{ // userErrorCallback(task, callback); } } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception ex) { userExceptionCallback(ex, callback); } }); } @ReactMethod public void twitterLogin(String Token, String Secret, final Callback callback) { mAuth = FirebaseAuth.getInstance(); AuthCredential credential = TwitterAuthProvider.getCredential(Token, Secret); mAuth.signInWithCredential(credential) .addOnCompleteListener(new OnCompleteListener<AuthResult>() { @Override public void onComplete(@NonNull Task<AuthResult> task) { if (task.isSuccessful()) { FirestackAuthModule.this.user = task.getResult().getUser(); userCallback(FirestackAuthModule.this.user, callback); }else{ // userErrorCallback(task, callback); } } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception ex) { userExceptionCallback(ex, callback); } }); } // Internal helpers public void userCallback(FirebaseUser passedUser, final Callback callback) { if (passedUser == null) { mAuth = FirebaseAuth.getInstance(); this.user = mAuth.getCurrentUser(); } else { this.user = passedUser; } this.user.getToken(false).addOnCompleteListener(new OnCompleteListener<GetTokenResult>() { @Override public void onComplete(@NonNull Task<GetTokenResult> task) { WritableMap msgMap = Arguments.createMap(); WritableMap userMap = getUserMap(); boolean authenticated = false; if (FirestackAuthModule.this.user != null) { final String token = task.getResult().getToken(); userMap.putString("token", token); authenticated = true; } msgMap.putMap("user", userMap); msgMap.putBoolean("authenticated", authenticated); callback.invoke(null, msgMap); } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception ex) { userExceptionCallback(ex, callback); } }); } public void noUserCallback(final Callback callback) { WritableMap message = Arguments.createMap(); message.putString("errorMessage", "no_user"); message.putString("eventName", "no_user"); message.putBoolean("authenticated", false); callback.invoke(null, message); } public void userErrorCallback(Task task, final Callback onFail) { userExceptionCallback(task.getException(), onFail); } public void userExceptionCallback(Exception exp, final Callback onFail) { WritableMap error = Arguments.createMap(); error.putString("errorMessage", exp.getMessage()); error.putString("allErrorMessage", exp.toString()); try { throw exp; } catch (FirebaseAuthException ex) { error.putString("errorCode", ex.getErrorCode()); } catch (Exception ex) { Log.e(TAG, ex.getMessage()); } onFail.invoke(error); } private WritableMap getUserMap() { WritableMap userMap = Arguments.createMap(); FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser(); if (user != null) { final String email = user.getEmail(); final String uid = user.getUid(); final String provider = user.getProviderId(); final String name = user.getDisplayName(); final Uri photoUrl = user.getPhotoUrl(); userMap.putString("email", email); userMap.putString("uid", uid); userMap.putString("providerId", provider); userMap.putBoolean("emailVerified", user.isEmailVerified()); userMap.putBoolean("anonymous", user.isAnonymous()); if (name != null) { userMap.putString("displayName", name); } if (photoUrl != null) { userMap.putString("photoUrl", photoUrl.toString()); } } else { userMap.putString("msg", "no user"); } return userMap; } }