504 lines
18 KiB
Objective-C
504 lines
18 KiB
Objective-C
#import "RNFirebaseAuth.h"
|
|
#import "RNFirebaseErrors.h"
|
|
#import "RNFirebaseEvents.h"
|
|
|
|
@implementation RNFirebaseAuth
|
|
|
|
typedef void (^UserWithTokenResponse)(NSDictionary *, NSError *);
|
|
|
|
RCT_EXPORT_MODULE(RNFirebaseAuth);
|
|
|
|
RCT_EXPORT_METHOD(signInAnonymously:
|
|
(RCTResponseSenderBlock) callBack)
|
|
{
|
|
@try {
|
|
[[FIRAuth auth] signInAnonymouslyWithCompletion
|
|
:^(FIRUser *user, NSError *error) {
|
|
if (!user) {
|
|
NSDictionary *evt = @{
|
|
@"eventName": AUTH_ANONYMOUS_ERROR_EVENT,
|
|
@"msg": [error localizedDescription]
|
|
};
|
|
|
|
|
|
[self sendJSEvent:AUTH_CHANGED_EVENT
|
|
props: evt];
|
|
|
|
callBack(@[evt]);
|
|
} else {
|
|
[self userCallback:callBack user:user];
|
|
}
|
|
}];
|
|
} @catch(NSException *ex) {
|
|
NSDictionary *eventError = @{
|
|
@"eventName": AUTH_ANONYMOUS_ERROR_EVENT,
|
|
@"msg": ex.reason
|
|
};
|
|
|
|
[self sendJSEvent:AUTH_ERROR_EVENT
|
|
props:eventError];
|
|
NSLog(@"An exception occurred: %@", ex);
|
|
callBack(@[eventError]);
|
|
}
|
|
}
|
|
|
|
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(signOut:(RCTResponseSenderBlock)callback)
|
|
{
|
|
NSError *error;
|
|
[[FIRAuth auth] signOut:&error];
|
|
if (!error) {
|
|
// Sign-out succeeded
|
|
callback(@[[NSNull null], @YES]);
|
|
} else {
|
|
NSDictionary *err = @{
|
|
@"error": @"Signout error",
|
|
@"name": @([error code]),
|
|
@"description": [error description]
|
|
};
|
|
callback(@[err]);
|
|
}
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(listenForAuth)
|
|
{
|
|
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(unlistenForAuth:(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(createUserWithEmail:(NSString *)email
|
|
pass:(NSString *)password
|
|
callback:(RCTResponseSenderBlock) callback)
|
|
{
|
|
[[FIRAuth auth]
|
|
createUserWithEmail:email
|
|
password:password
|
|
completion:^(FIRUser *_Nullable user,
|
|
NSError *_Nullable error) {
|
|
if (user != nil) {
|
|
[self userCallback:callback user:user];
|
|
} else {
|
|
NSDictionary *err = @{
|
|
@"error": @"createUserWithEmailError",
|
|
@"name": @([error code]),
|
|
@"description": [error localizedDescription]
|
|
};
|
|
callback(@[err]);
|
|
}
|
|
}];
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(signInWithEmail:(NSString *)email
|
|
pass:(NSString *)password
|
|
callback:(RCTResponseSenderBlock) callback)
|
|
{
|
|
[[FIRAuth auth] signInWithEmail:email
|
|
password:password
|
|
completion:^(FIRUser *user, NSError *error) {
|
|
if (user != nil) {
|
|
[self userCallback:callback user:user];
|
|
} else {
|
|
[self userErrorCallback:callback error:error user:user msg:@"signinError"];
|
|
}
|
|
}];
|
|
}
|
|
|
|
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(deleteUser:(RCTResponseSenderBlock) callback)
|
|
{
|
|
FIRUser *user = [FIRAuth auth].currentUser;
|
|
|
|
if (user) {
|
|
[user deleteWithCompletion:^(NSError *_Nullable error) {
|
|
if (error) {
|
|
[self userErrorCallback:callback error:error user:user msg:@"deleteUserError"];
|
|
} else {
|
|
callback(@[[NSNull null], @{@"result": @(true)}]);
|
|
}
|
|
}];
|
|
} else {
|
|
[self noUserCallback:callback isError: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];
|
|
}
|
|
}
|
|
|
|
- (NSDictionary *) userPropsFromFIRUser:(FIRUser *) user
|
|
{
|
|
NSMutableDictionary *userProps = [@{
|
|
@"uid": user.uid,
|
|
@"email": user.email ? user.email : @"",
|
|
@"emailVerified": @(user.emailVerified),
|
|
@"anonymous": @(user.anonymous),
|
|
@"displayName": user.displayName ? user.displayName : @"",
|
|
@"refreshToken": user.refreshToken,
|
|
@"providerID": user.providerID
|
|
} mutableCopy];
|
|
|
|
if ([user valueForKey:@"photoURL"] != nil) {
|
|
[userProps setValue: [NSString stringWithFormat:@"%@", user.photoURL]
|
|
forKey:@"photoURL"];
|
|
}
|
|
|
|
return userProps;
|
|
}
|
|
|
|
- (void) userPropsFromFIRUserWithToken:(FIRUser *) user
|
|
andCallback:(UserWithTokenResponse) callback
|
|
{
|
|
NSMutableDictionary *userProps = [[self userPropsFromFIRUser: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) userCallback:(RCTResponseSenderBlock) callback
|
|
user:(FIRUser *) user {
|
|
NSDictionary *userProps = [self userPropsFromFIRUser:user];
|
|
callback(@[[NSNull null], userProps]);
|
|
}
|
|
|
|
- (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]);
|
|
}
|
|
|
|
|
|
@end
|