react-native-firebase/ios/RNFirebase/RNFirebaseAuth.m

560 lines
20 KiB
Objective-C

#import "RNFirebaseAuth.h"
#import "RNFirebaseErrors.h"
#import "RNFirebaseEvents.h"
@implementation RNFirebaseAuth
typedef void (^UserWithTokenResponse)(NSDictionary *, NSError *);
RCT_EXPORT_MODULE(RNFirebaseAuth);
/**
signOut
@param RCTPromiseResolveBlock resolve
@param RCTPromiseRejectBlock reject
@return
*/
RCT_EXPORT_METHOD(signOut:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) {
FIRUser *user = [FIRAuth auth].currentUser;
if (user) {
NSError *error;
[[FIRAuth auth] signOut:&error];
if (!error) [self promiseNoUser:resolve rejecter:reject isError:NO];
// TODO authExceptionToDict
else reject(@"auth/unknown", @"An unknown error has occurred.", error);
} else {
[self promiseNoUser:resolve rejecter:reject isError:YES];
}
}
/**
signInAnonymously
@param RCTPromiseResolveBlock resolve
@param RCTPromiseRejectBlock reject
@return
*/
RCT_EXPORT_METHOD(signInAnonymously:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) {
[[FIRAuth auth] signInAnonymouslyWithCompletion:^(FIRUser *user, NSError *error) {
if (error) {
// TODO authExceptionToDict
reject(@"auth/todo", [error localizedDescription], error);
} else {
[self promiseWithUser:resolve rejecter:reject user:user];
}
}];
}
/**
signInWithEmailAndPassword
@param NSString NSString email
@param NSString NSString password
@param RCTPromiseResolveBlock resolve
@param RCTPromiseRejectBlock reject
@return return
*/
RCT_EXPORT_METHOD(signInWithEmailAndPassword:(NSString *)email pass:(NSString *)password resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) {
[[FIRAuth auth] signInWithEmail:email password:password completion:^(FIRUser *user, NSError *error) {
if (error) {
// TODO authExceptionToDict
reject(@"auth/todo", [error localizedDescription], error);
} else {
[self promiseWithUser:resolve rejecter:reject user:user];
}
}];
}
/**
createUserWithEmailAndPassword
@param NSString NSString email
@param NSString NSString password
@param RCTPromiseResolveBlock resolve
@param RCTPromiseRejectBlock reject
@return return
*/
RCT_EXPORT_METHOD(createUserWithEmailAndPassword:(NSString *)email pass:(NSString *)password resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) {
[[FIRAuth auth] createUserWithEmail:email password:password completion:^(FIRUser *user, NSError *error) {
if (error) {
// TODO authExceptionToDict
reject(@"auth/todo", [error localizedDescription], error);
} else {
[self promiseWithUser:resolve rejecter:reject user:user];
}
}];
}
/**
deleteUser
@param RCTPromiseResolveBlock resolve
@param RCTPromiseRejectBlock reject
@return return
*/
RCT_EXPORT_METHOD(delete:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) {
FIRUser *user = [FIRAuth auth].currentUser;
if (user) {
[user deleteWithCompletion:^(NSError *_Nullable error) {
if (error) {
// TODO authExceptionToDict
reject(@"auth/unknown", @"An unknown error has occurred.", error);
} else {
[self promiseNoUser:resolve rejecter:reject isError:NO];
}
}];
} else {
[self promiseNoUser:resolve rejecter:reject isError:YES];
}
}
// TODO ------------------------------------------------------- CLEAN UP --------------------------
// TODO ------------------------------------------------------- CLEAN UP --------------------------
// TODO ------------------------------------------------------- CLEAN UP --------------------------
// TODO ------------------------------------------------------- CLEAN UP --------------------------
RCT_EXPORT_METHOD(signInWithCustomToken:
(NSString *)customToken
callback:(RCTResponseSenderBlock) callback)
{
[[FIRAuth auth]
signInWithCustomToken:customToken
completion:^(FIRUser *user, NSError *error) {
if (user != nil) {
[self userCallback:callback user:user];
} else {
[self userErrorCallback:callback error:error user:user msg:AUTH_ERROR_EVENT];
}
}];
}
RCT_EXPORT_METHOD(signInWithProvider:
(NSString *)provider
token:(NSString *)authToken
secret:(NSString *)authTokenSecret
callback:(RCTResponseSenderBlock)callback)
{
FIRAuthCredential *credential = [self getCredentialForProvider:provider
token:authToken
secret:authTokenSecret];
if (credential == nil) {
NSDictionary *err = @{
@"error": @"Unhandled provider"
};
return callback(@[err]);
}
@try {
[[FIRAuth auth] signInWithCredential:credential
completion:^(FIRUser *user, NSError *error) {
if (user != nil) {
// User is signed in.
[self userCallback:callback user:user];
} else {
NSLog(@"An error occurred: %@", [error localizedDescription]);
// No user is signed in.
NSDictionary *err = @{
@"error": @"No user signed in",
@"description": [error localizedDescription]
};
callback(@[err]);
}
}];
} @catch (NSException *exception) {
[RNFirebaseErrors handleException:exception
withCallback:callback];
}
}
RCT_EXPORT_METHOD(addAuthStateListener)
{
self->listening = true;
self->authListenerHandle =
[[FIRAuth auth] addAuthStateDidChangeListener:^(FIRAuth *_Nonnull auth, FIRUser *_Nullable user) {
if (user != nil) {
// User is signed in.
[self userPropsFromFIRUserWithToken:user
andCallback:^(NSDictionary *userProps, NSError * error) {
if (error != nil) {
[self
sendJSEvent:AUTH_CHANGED_EVENT
props: @{
@"eventName": @"userTokenError",
@"msg": [error localizedDescription]
}];
} else {
[self
sendJSEvent:AUTH_CHANGED_EVENT
props: @{
@"eventName": @"user",
@"authenticated": @(true),
@"user": userProps
}];
}
}];
} else {
// TODO: Update this with different error states
NSDictionary *err = @{
@"error": @"No user logged in"
};
[self sendJSEvent:AUTH_CHANGED_EVENT
props:@{
@"eventName": @"no_user",
@"authenticated": @(false),
@"error": err
}];
}
}];
}
RCT_EXPORT_METHOD(removeAuthStateListener:(RCTResponseSenderBlock)callback)
{
if (self->authListenerHandle != nil) {
[[FIRAuth auth] removeAuthStateDidChangeListener:self->authListenerHandle];
self->listening = false;
callback(@[[NSNull null]]);
}
}
RCT_EXPORT_METHOD(getCurrentUser:(RCTResponseSenderBlock)callback)
{
FIRUser *user = [FIRAuth auth].currentUser;
if (user != nil) {
[self userCallback:callback user:user];
} else {
// No user is signed in.
NSDictionary *err = @{
@"user": @"No user logged in"
};
callback(@[err]);
}
}
RCT_EXPORT_METHOD(updateUserEmail:(NSString *)email
callback:(RCTResponseSenderBlock) callback)
{
FIRUser *user = [FIRAuth auth].currentUser;
if (user) {
[user updateEmail:email completion:^(NSError *_Nullable error) {
if (error) {
// An error happened.
[self userErrorCallback:callback error:error user:user msg:@"updateEmailError"];
} else {
// Email updated.
[self userCallback:callback user:user];
}
}];
} else {
[self noUserCallback:callback isError:true];
}
}
RCT_EXPORT_METHOD(updateUserPassword:(NSString *)newPassword
callback:(RCTResponseSenderBlock) callback)
{
FIRUser *user = [FIRAuth auth].currentUser;
if (user) {
[user updatePassword:newPassword completion:^(NSError *_Nullable error) {
if (error) {
// An error happened.
[self userErrorCallback:callback error:error user:user msg:@"updateUserPasswordError"];
} else {
// Email updated.
[self userCallback:callback user:user];
}
}];
} else {
[self noUserCallback:callback isError:true];
}
}
RCT_EXPORT_METHOD(sendPasswordResetWithEmail:(NSString *)email
callback:(RCTResponseSenderBlock) callback)
{
[[FIRAuth auth] sendPasswordResetWithEmail:email
completion:^(NSError *_Nullable error) {
if (error) {
// An error happened.
NSDictionary *err = @{
@"error": @"sendPasswordResetWithEmailError",
@"description": error.localizedDescription
};
callback(@[err]);
} else {
// Email updated.
callback(@[[NSNull null], @{
@"result": @(true)
}]);
}
}];
}
RCT_EXPORT_METHOD(getToken:(RCTResponseSenderBlock) callback)
{
FIRUser *user = [FIRAuth auth].currentUser;
if (user) {
[user getTokenWithCompletion:^(NSString *token, NSError *_Nullable error) {
if (error) {
[self userErrorCallback:callback error:error user:user msg:@"getTokenError"];
} else {
callback(@[[NSNull null], token]);
}
}];
} else {
[self noUserCallback:callback isError:true];
}
}
RCT_EXPORT_METHOD(getTokenWithCompletion:(RCTResponseSenderBlock) callback)
{
FIRUser *user = [FIRAuth auth].currentUser;
if (user) {
[user getTokenWithCompletion:^(NSString *token , NSError *_Nullable error) {
if (error) {
[self userErrorCallback:callback error:error user:user msg:@"getTokenWithCompletion"];
} else {
callback(@[[NSNull null], token]);
}
}];
} else {
[self noUserCallback:callback isError:true];
}
}
RCT_EXPORT_METHOD(reauthenticateWithCredentialForProvider:
(NSString *)provider
token:(NSString *)authToken
secret:(NSString *)authTokenSecret
callback:(RCTResponseSenderBlock)callback)
{
FIRAuthCredential *credential = [self getCredentialForProvider:provider
token:authToken
secret:authTokenSecret];
if (credential == nil) {
NSDictionary *err = @{
@"error": @"Unhandled provider"
};
return callback(@[err]);
}
FIRUser *user = [FIRAuth auth].currentUser;
[user reauthenticateWithCredential:credential completion:^(NSError *_Nullable error) {
if (error) {
[self userErrorCallback:callback error:error user:user msg:@"reauthenticateWithCredentialForProviderError"];
} else {
callback(@[[NSNull null], @{@"result": @(true)}]);
}
}];
}
RCT_EXPORT_METHOD(updateUserProfile:(NSDictionary *)userProps
callback:(RCTResponseSenderBlock) callback)
{
FIRUser *user = [FIRAuth auth].currentUser;
if (user) {
FIRUserProfileChangeRequest *changeRequest = [user profileChangeRequest];
NSMutableArray *allKeys = [[userProps allKeys] mutableCopy];
for (NSString *key in allKeys) {
// i.e. changeRequest.displayName = userProps[displayName];
@try {
if ([key isEqualToString:@"photoURL"]) {
NSURL *url = [NSURL URLWithString:[userProps valueForKey:key]];
[changeRequest setValue:url forKey:key];
} else {
[changeRequest setValue:[userProps objectForKey:key] forKey:key];
}
}
@catch (NSException *exception) {
NSLog(@"Exception occurred while configuring: %@", exception);
}
@finally {
[changeRequest commitChangesWithCompletion:^(NSError *_Nullable error) {
if (error) {
// An error happened.
[self userErrorCallback:callback error:error user:user msg:@"updateEmailError"];
} else {
// Profile updated.
[self userCallback:callback user:user];
}
}];
}
}
} else {
[self noUserCallback:callback isError:true];
}
}
- (void) userPropsFromFIRUserWithToken:(FIRUser *) user
andCallback:(UserWithTokenResponse) callback
{
NSMutableDictionary *userProps = [[self firebaseUserToDict:user] mutableCopy];
[user getTokenWithCompletion:^(NSString * _Nullable token, NSError * _Nullable error) {
if (error != nil) {
return callback(nil, error);
}
[userProps setValue:token forKey:@"idToken"];
callback(userProps, nil);
}];
}
- (FIRAuthCredential *)getCredentialForProvider:(NSString *)provider token:(NSString *)authToken secret:(NSString *)authTokenSecret {
FIRAuthCredential *credential;
if ([provider compare:@"twitter" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
credential = [FIRTwitterAuthProvider credentialWithToken:authToken
secret:authTokenSecret];
} else if ([provider compare:@"facebook" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
credential = [FIRFacebookAuthProvider credentialWithAccessToken:authToken];
} else if ([provider compare:@"google" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
credential = [FIRGoogleAuthProvider credentialWithIDToken:authToken
accessToken:authTokenSecret];
} else {
NSLog(@"Provider not yet handled: %@", provider);
}
return credential;
}
// Not sure how to get away from this... yet
- (NSArray<NSString *> *)supportedEvents {
return @[AUTH_CHANGED_EVENT, AUTH_ANONYMOUS_ERROR_EVENT, AUTH_ERROR_EVENT];
}
- (void) sendJSEvent:(NSString *)title
props:(NSDictionary *)props
{
@try {
if (self->listening) {
[self sendEventWithName:title
body:props];
}
}
@catch (NSException *err) {
NSLog(@"An error occurred in sendJSEvent: %@", [err debugDescription]);
}
}
- (void) noUserCallback:(RCTResponseSenderBlock) callback
isError:(Boolean) isError {
if (isError) {
NSDictionary *err = @{
@"error": @"Unhandled provider"
};
return callback(@[err]);
}
return callback(@[[NSNull null], [NSNull null]]);
}
- (void) userErrorCallback:(RCTResponseSenderBlock) callback
error:(NSError *)error
user:(FIRUser *) user
msg:(NSString *) msg {
// An error happened.
NSDictionary *err = [RNFirebaseErrors handleFirebaseError:msg
error:error
withUser:user];
callback(@[err]);
}
- (void) userCallback:(RCTResponseSenderBlock) callback user:(FIRUser *) user {
NSDictionary *userProps = [self firebaseUserToDict:user];
callback(@[[NSNull null], userProps]);
}
// END ------------------------------------------------------- CLEAN UP -^ -----------------------
// END ------------------------------------------------------- CLEAN UP --------------------------
// END ------------------------------------------------------- CLEAN UP --------------------------
// END ------------------------------------------------------- CLEAN UP --------------------------
- (NSDictionary *) authExceptionToDict:(NSError *) error {
// TODO
// NSDictionary *evt = @{ @"eventName": AUTH_ANONYMOUS_ERROR_EVENT,
// @"msg": [error localizedDescription] };
}
/**
Resolve or reject a promise based on isError value
@param resolve RCTPromiseResolveBlock
@param reject RCTPromiseRejectBlock
@param isError BOOL
*/
- (void) promiseNoUser:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject isError:(BOOL) isError {
if (isError) {
reject(@"auth/no_current_user", @"No user currently signed in.", nil);
} else {
resolve([NSNull null]);
}
}
/**
Resolve or reject a promise based on FIRUser value existance
@param resolve RCTPromiseResolveBlock
@param reject RCTPromiseRejectBlock
@param user FIRUser
*/
- (void) promiseWithUser:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject user:(FIRUser *) user {
if (user) {
NSDictionary *userDict = [self firebaseUserToDict:user];
resolve(userDict);
} else {
[self promiseNoUser:resolve rejecter:reject isError:YES];
}
}
/**
Converts a FIRUser instance into a dictionary to send via RNBridge
@param user FIRUser
@return NSDictionary
*/
- (NSDictionary *) firebaseUserToDict:(FIRUser *) user {
NSMutableDictionary *userDict = [
@{ @"uid": user.uid,
@"email": user.email ? user.email : [NSNull null],
@"emailVerified": @(user.emailVerified),
@"isAnonymous": @(user.anonymous),
@"displayName": user.displayName ? user.displayName : [NSNull null],
@"refreshToken": user.refreshToken,
@"providerId": [user.providerID lowercaseString]
}
mutableCopy
];
if ([user valueForKey:@"photoURL"] != nil) {
[userDict setValue: [NSString stringWithFormat:@"%@", user.photoURL] forKey:@"photoURL"];
}
return userDict;
}
@end