aboutsummaryrefslogtreecommitdiffhomepage
path: root/Firebase/Auth/Source
diff options
context:
space:
mode:
authorGravatar Paul Beusterien <paulbeusterien@google.com>2018-03-07 07:47:09 -0800
committerGravatar GitHub <noreply@github.com>2018-03-07 07:47:09 -0800
commit8ef0f1490a72fd700f609dc9971ec16868d6747b (patch)
tree61cdcbb7099535abd791dc523d8e2d19268faf4f /Firebase/Auth/Source
parent0b8f216e4ce85b968ac0803d729cd24eb110d0b6 (diff)
Adds Email link sign-in (#882)
Diffstat (limited to 'Firebase/Auth/Source')
-rw-r--r--Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailAuthProvider.m4
-rw-r--r--Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.h13
-rw-r--r--Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.m9
-rw-r--r--Firebase/Auth/Source/FIRAuth.m176
-rw-r--r--Firebase/Auth/Source/Public/FIRAuth.h81
-rw-r--r--Firebase/Auth/Source/Public/FIREmailAuthProvider.h9
-rw-r--r--Firebase/Auth/Source/RPCs/FIRAuthBackend.h32
-rw-r--r--Firebase/Auth/Source/RPCs/FIRAuthBackend.m19
-rw-r--r--Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.h5
-rw-r--r--Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.m1
-rw-r--r--Firebase/Auth/Source/RPCs/FIREmailLinkSignInRequest.h66
-rw-r--r--Firebase/Auth/Source/RPCs/FIREmailLinkSignInRequest.m70
-rw-r--r--Firebase/Auth/Source/RPCs/FIREmailLinkSignInResponse.h54
-rw-r--r--Firebase/Auth/Source/RPCs/FIREmailLinkSignInResponse.m32
-rw-r--r--Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.h22
-rw-r--r--Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.m24
16 files changed, 604 insertions, 13 deletions
diff --git a/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailAuthProvider.m b/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailAuthProvider.m
index d27611e..7a871e2 100644
--- a/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailAuthProvider.m
+++ b/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailAuthProvider.m
@@ -32,4 +32,8 @@
return [[FIREmailPasswordAuthCredential alloc] initWithEmail:email password:password];
}
++ (FIRAuthCredential *)credentialWithEmail:(NSString *)email link:(NSString *)link {
+ return [[FIREmailPasswordAuthCredential alloc] initWithEmail:email link:link];
+}
+
@end
diff --git a/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.h b/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.h
index d50bf17..7625685 100644
--- a/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.h
+++ b/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.h
@@ -35,6 +35,11 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property(nonatomic, readonly) NSString *password;
+/** @property link
+ @brief The email sign-in link.
+ */
+@property(nonatomic, readonly) NSString *link;
+
/** @fn initWithEmail:password:
@brief Designated initializer.
@param email The user's email address.
@@ -43,6 +48,14 @@ NS_ASSUME_NONNULL_BEGIN
- (nullable instancetype)initWithEmail:(NSString *)email password:(NSString *)password
NS_DESIGNATED_INITIALIZER;
+/** @fn initWithEmail:link:
+ @brief Designated initializer.
+ @param email The user's email address.
+ @param link The email sign-in link.
+ */
+- (nullable instancetype)initWithEmail:(NSString *)email link:(NSString *)link
+ NS_DESIGNATED_INITIALIZER;
+
@end
NS_ASSUME_NONNULL_END
diff --git a/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.m b/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.m
index 4361366..71cc330 100644
--- a/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.m
+++ b/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.m
@@ -43,6 +43,15 @@
return self;
}
+- (nullable instancetype)initWithEmail:(NSString *)email link:(NSString *)link {
+ self = [super initWithProvider:FIREmailAuthProviderID];
+ if (self) {
+ _email = [email copy];
+ _link = [link copy];
+ }
+ return self;
+}
+
- (void)prepareVerifyAssertionRequest:(FIRVerifyAssertionRequest *)request {
[FIRAuthExceptionUtils raiseMethodNotImplementedExceptionWithReason:
@"Attempt to call prepareVerifyAssertionRequest: on a FIREmailPasswordAuthCredential."];
diff --git a/Firebase/Auth/Source/FIRAuth.m b/Firebase/Auth/Source/FIRAuth.m
index 0f3705f..387fab7 100644
--- a/Firebase/Auth/Source/FIRAuth.m
+++ b/Firebase/Auth/Source/FIRAuth.m
@@ -39,6 +39,8 @@
#import "FIRAuthRequestConfiguration.h"
#import "FIRCreateAuthURIRequest.h"
#import "FIRCreateAuthURIResponse.h"
+#import "FIREmailLinkSignInRequest.h"
+#import "FIREmailLinkSignInResponse.h"
#import "FIRGetOOBConfirmationCodeRequest.h"
#import "FIRGetOOBConfirmationCodeResponse.h"
#import "FIRResetPasswordRequest.h"
@@ -117,6 +119,11 @@ static NSString *const kVerifyEmailRequestType = @"VERIFY_EMAIL";
*/
static NSString *const kRecoverEmailRequestType = @"RECOVER_EMAIL";
+/** @var kEmailLinkSignInRequestType
+ @brief The action code type value for an email sign-in link in the check action code response.
+*/
+static NSString *const kEmailLinkSignInRequestType = @"EMAIL_SIGNIN";
+
/** @var kMissingPasswordReason
@brief The reason why the @c FIRAuthErrorCodeWeakPassword error is thrown.
@remarks This error message will be localized in the future.
@@ -186,6 +193,9 @@ static NSMutableDictionary *gKeychainServiceNameForAppName;
if ([requestType isEqualToString:kRecoverEmailRequestType]) {
return FIRActionCodeOperationRecoverEmail;
}
+ if ([requestType isEqualToString:kEmailLinkSignInRequestType]) {
+ return FIRActionCodeOperationEmailLink;
+ }
return FIRActionCodeOperationUnknown;
}
@@ -509,6 +519,24 @@ static NSMutableDictionary *gKeychainServiceNameForAppName;
});
}
+- (void)fetchSignInMethodsForEmail:(nonnull NSString *)email
+ completion:(nullable FIRSignInMethodQueryCallback)completion {
+ dispatch_async(FIRAuthGlobalWorkQueue(), ^{
+ FIRCreateAuthURIRequest *request =
+ [[FIRCreateAuthURIRequest alloc] initWithIdentifier:email
+ continueURI:@"http://www.google.com/"
+ requestConfiguration:_requestConfiguration];
+ [FIRAuthBackend createAuthURI:request callback:^(FIRCreateAuthURIResponse *_Nullable response,
+ NSError *_Nullable error) {
+ if (completion) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ completion(response.signinMethods, error);
+ });
+ }
+ }];
+ });
+}
+
- (void)signInWithEmail:(NSString *)email
password:(NSString *)password
completion:(FIRAuthResultCallback)completion {
@@ -524,6 +552,23 @@ static NSMutableDictionary *gKeychainServiceNameForAppName;
});
}
+- (void)signInWithEmail:(NSString *)email
+ link:(NSString *)link
+ completion:(FIRAuthDataResultCallback)completion {
+ dispatch_async(FIRAuthGlobalWorkQueue(), ^{
+ FIRAuthDataResultCallback decoratedCallback =
+ [self signInFlowAuthDataResultCallbackByDecoratingCallback:completion];
+ FIREmailPasswordAuthCredential *credential =
+ [[FIREmailPasswordAuthCredential alloc] initWithEmail:email link:link];
+ [self internalSignInAndRetrieveDataWithCredential:credential
+ isReauthentication:NO
+ callback:^(FIRAuthDataResult *_Nullable authResult,
+ NSError *_Nullable error) {
+ decoratedCallback(authResult, error);
+ }];
+ });
+}
+
/** @fn signInWithEmail:password:callback:
@brief Signs in using an email address and password.
@param email The user's email address.
@@ -536,6 +581,7 @@ static NSMutableDictionary *gKeychainServiceNameForAppName;
- (void)signInWithEmail:(NSString *)email
password:(NSString *)password
callback:(FIRAuthResultCallback)callback {
+
FIRVerifyPasswordRequest *request =
[[FIRVerifyPasswordRequest alloc] initWithEmail:email
password:password
@@ -591,6 +637,40 @@ static NSMutableDictionary *gKeychainServiceNameForAppName;
callback:completion];
}
+/** @fn internalSignInWithEmail:link:completion:
+ @brief Signs in using an email and email sign-in link.
+ @param email The user's email address.
+ @param link The email sign-in link.
+ @param callback A block which is invoked when the sign in finishes (or is cancelled.) Invoked
+ asynchronously on the global auth work queue in the future.
+ */
+- (void)internalSignInWithEmail:(nonnull NSString *)email
+ link:(nonnull NSString *)link
+ callback:(nullable FIRAuthResultCallback)callback {
+ NSURLComponents *urlComponents = [NSURLComponents componentsWithString:link];
+ NSDictionary<NSString *, NSString *> *queryItems = FIRAuthParseURL(urlComponents.query);
+ NSString *actionCode = queryItems[@"oobCode"];
+
+ FIREmailLinkSignInRequest *request =
+ [[FIREmailLinkSignInRequest alloc] initWithEmail:email
+ oobCode:actionCode
+ requestConfiguration:_requestConfiguration];
+
+ [FIRAuthBackend emailLinkSignin:request
+ callback:^(FIREmailLinkSignInResponse *_Nullable response,
+ NSError *_Nullable error) {
+ if (error) {
+ callback(nil, error);
+ return;
+ }
+ [self completeSignInWithAccessToken:response.IDToken
+ accessTokenExpirationDate:response.approximateExpirationDate
+ refreshToken:response.refreshToken
+ anonymous:NO
+ callback:callback];
+ }];
+}
+
- (void)signInWithCredential:(FIRAuthCredential *)credential
completion:(FIRAuthResultCallback)completion {
dispatch_async(FIRAuthGlobalWorkQueue(), ^{
@@ -628,24 +708,31 @@ static NSMutableDictionary *gKeychainServiceNameForAppName;
// Special case for email/password credentials
FIREmailPasswordAuthCredential *emailPasswordCredential =
(FIREmailPasswordAuthCredential *)credential;
- [self signInWithEmail:emailPasswordCredential.email
- password:emailPasswordCredential.password
- callback:^(FIRUser *_Nullable user, NSError *_Nullable error) {
+ FIRAuthResultCallback completeEmailSignIn = ^(FIRUser *user, NSError *error) {
if (callback) {
if (error) {
callback(nil, error);
return;
}
- FIRAdditionalUserInfo *additionalUserInfo =
- [[FIRAdditionalUserInfo alloc] initWithProviderID:FIREmailAuthProviderID
- profile:nil
- username:nil
- isNewUser:NO];
+ FIRAdditionalUserInfo *additionalUserInfo =
+ [[FIRAdditionalUserInfo alloc] initWithProviderID:FIREmailAuthProviderID
+ profile:nil
+ username:nil
+ isNewUser:NO];
FIRAuthDataResult *result = [[FIRAuthDataResult alloc] initWithUser:user
additionalUserInfo:additionalUserInfo];
- callback(result, nil);
+ callback(result, error);
}
- }];
+ };
+ if (emailPasswordCredential.link) {
+ [self internalSignInWithEmail:emailPasswordCredential.email
+ link:emailPasswordCredential.link
+ callback:completeEmailSignIn];
+ } else {
+ [self signInWithEmail:emailPasswordCredential.email
+ password:emailPasswordCredential.password
+ callback:completeEmailSignIn];
+ }
return;
}
@@ -1013,6 +1100,31 @@ static NSMutableDictionary *gKeychainServiceNameForAppName;
});
}
+- (void)sendSignInLinkToEmail:(nonnull NSString *)email
+ actionCodeSettings:(nonnull FIRActionCodeSettings *)actionCodeSettings
+ completion:(nullable FIRSendSignInLinkToEmailCallback)completion {
+ dispatch_async(FIRAuthGlobalWorkQueue(), ^{
+ if (!email) {
+ [FIRAuthExceptionUtils raiseInvalidParameterExceptionWithReason:
+ kMissingEmailInvalidParameterExceptionReason];
+ }
+ FIRGetOOBConfirmationCodeRequest *request =
+ [FIRGetOOBConfirmationCodeRequest signInWithEmailLinkRequest:email
+ actionCodeSettings:actionCodeSettings
+ requestConfiguration:_requestConfiguration];
+ [FIRAuthBackend getOOBConfirmationCode:request
+ callback:^(FIRGetOOBConfirmationCodeResponse *_Nullable response,
+ NSError *_Nullable error) {
+ if (completion) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ completion(error);
+ });
+ }
+ }];
+ });
+}
+
+
- (BOOL)signOut:(NSError *_Nullable __autoreleasing *_Nullable)error {
__block BOOL result = YES;
dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
@@ -1031,6 +1143,50 @@ static NSMutableDictionary *gKeychainServiceNameForAppName;
return [self updateCurrentUser:nil byForce:YES savingToDisk:YES error:error];
}
+- (BOOL)isSignInWithEmailLink:(NSString *)link {
+ if (link.length == 0) {
+ return NO;
+ }
+ NSURLComponents *urlComponents = [NSURLComponents componentsWithString:link];
+ if (!urlComponents.query) {
+ return NO;
+ }
+ NSDictionary<NSString *, NSString *> *queryItems = FIRAuthParseURL(urlComponents.query);
+
+ NSString *actionCode = queryItems[@"oobCode"];
+ NSString *mode = queryItems[@"mode"];
+
+ if (actionCode && [mode isEqualToString:@"signIn"]) {
+ return YES;
+ }
+ return NO;
+}
+
+/** @fn FIRAuthParseURL:NSString
+ @brief Parses an incoming URL into all available query items.
+ @param urlString The url to be parsed.
+ @return A dictionary of available query items in the target URL.
+ */
+static NSDictionary<NSString *, NSString *> *FIRAuthParseURL(NSString *urlString) {
+ NSString *linkURL = [NSURLComponents componentsWithString:urlString].query;
+ NSArray<NSString *> *URLComponents = [linkURL componentsSeparatedByString:@"&"];
+ NSMutableDictionary<NSString *, NSString *> *queryItems =
+ [[NSMutableDictionary alloc] initWithCapacity:URLComponents.count];
+ for (NSString *component in URLComponents) {
+ NSRange equalRange = [component rangeOfString:@"="];
+ if (equalRange.location != NSNotFound) {
+ NSString *queryItemKey =
+ [[component substringToIndex:equalRange.location] stringByRemovingPercentEncoding];
+ NSString *queryItemValue =
+ [[component substringFromIndex:equalRange.location + 1] stringByRemovingPercentEncoding];
+ if (queryItemKey && queryItemValue) {
+ queryItems[queryItemKey] = queryItemValue;
+ }
+ }
+ }
+ return queryItems;
+}
+
- (FIRAuthStateDidChangeListenerHandle)addAuthStateDidChangeListener:
(FIRAuthStateDidChangeListenerBlock)listener {
__block BOOL firstInvocation = YES;
diff --git a/Firebase/Auth/Source/Public/FIRAuth.h b/Firebase/Auth/Source/Public/FIRAuth.h
index f18a3d0..c985658 100644
--- a/Firebase/Auth/Source/Public/FIRAuth.h
+++ b/Firebase/Auth/Source/Public/FIRAuth.h
@@ -114,6 +114,14 @@ typedef void (^FIRProviderQueryCallback)(NSArray<NSString *> *_Nullable provider
NSError *_Nullable error)
NS_SWIFT_NAME(ProviderQueryCallback);
+/** @typedef FIRSignInMethodQueryCallback
+ @brief The type of block invoked when a list of sign-in methods for a given email address is
+ requested.
+ */
+typedef void (^FIRSignInMethodQueryCallback)(NSArray<NSString *> *_Nullable,
+ NSError *_Nullable)
+ NS_SWIFT_NAME(SignInMethodQueryCallback);
+
/** @typedef FIRSendPasswordResetCallback
@brief The type of block invoked when sending a password reset email.
@@ -123,6 +131,12 @@ typedef void (^FIRProviderQueryCallback)(NSArray<NSString *> *_Nullable provider
typedef void (^FIRSendPasswordResetCallback)(NSError *_Nullable error)
NS_SWIFT_NAME(SendPasswordResetCallback);
+/** @typedef FIRSendSignInLinkToEmailCallback
+ @brief The type of block invoked when sending an email sign-in link email.
+ */
+typedef void (^FIRSendSignInLinkToEmailCallback)(NSError *_Nullable error)
+ NS_SWIFT_NAME(SendSignInLinkToEmailCallback);
+
/** @typedef FIRConfirmPasswordResetCallback
@brief The type of block invoked when performing a password reset.
@@ -190,6 +204,10 @@ typedef NS_ENUM(NSInteger, FIRActionCodeOperation) {
/** Action code for recover email operation. */
FIRActionCodeOperationRecoverEmail = 3,
+ /** Action code for email link operation. */
+ FIRActionCodeOperationEmailLink = 4,
+
+
} NS_SWIFT_NAME(ActionCodeOperation);
/**
@@ -297,6 +315,24 @@ NS_SWIFT_NAME(Auth)
- (void)fetchProvidersForEmail:(NSString *)email
completion:(nullable FIRProviderQueryCallback)completion;
+/** @fn fetchSignInMethodsForEmail:completion:
+ @brief Fetches the list of all sign-in methods previously used for the provided email address.
+
+ @param email The email address for which to obtain a list of sign-in methods.
+ @param completion Optionally; a block which is invoked when the list of sign in methods for the
+ specified email address is ready or an error was encountered. Invoked asynchronously on the
+ main thread in the future.
+
+ @remarks Possible error codes:
+
+ + `FIRAuthErrorCodeInvalidEmail` - Indicates the email address is malformed.
+
+ @remarks See @c FIRAuthErrors for a list of error codes that are common to all API methods.
+ */
+
+- (void)fetchSignInMethodsForEmail:(NSString *)email
+ completion:(nullable FIRSignInMethodQueryCallback)completion;
+
/** @fn signInWithEmail:password:completion:
@brief Signs in using an email address and password.
@@ -322,6 +358,30 @@ NS_SWIFT_NAME(Auth)
password:(NSString *)password
completion:(nullable FIRAuthResultCallback)completion;
+/** @fn signInWithEmail:link:completion:
+ @brief Signs in using an email address and email sign-in link.
+
+ @param email The user's email address.
+ @param link The email sign-in link.
+ @param completion Optionally; a block which is invoked when the sign in flow finishes, or is
+ canceled. Invoked asynchronously on the main thread in the future.
+
+ @remarks Possible error codes:
+
+ + `FIRAuthErrorCodeOperationNotAllowed` - Indicates that email and email sign-in link
+ accounts are not enabled. Enable them in the Auth section of the
+ Firebase console.
+ + `FIRAuthErrorCodeUserDisabled` - Indicates the user's account is disabled.
+ + `FIRAuthErrorCodeInvalidEmail` - Indicates the email address is invalid.
+
+
+ @remarks See `FIRAuthErrors` for a list of error codes that are common to all API methods.
+ */
+
+- (void)signInWithEmail:(NSString *)email
+ link:(NSString *)link
+ completion:(nullable FIRAuthDataResultCallback)completion;
+
/** @fn signInAndRetrieveDataWithEmail:password:completion:
@brief Signs in using an email address and password.
@@ -654,6 +714,19 @@ NS_SWIFT_NAME(Auth)
actionCodeSettings:(FIRActionCodeSettings *)actionCodeSettings
completion:(nullable FIRSendPasswordResetCallback)completion;
+/** @fn sendSignInLinkToEmail:actionCodeSettings:completion:
+ @brief Sends a sign in with email link to provided email address.
+
+ @param email The email address of the user.
+ @param actionCodeSettings An @c FIRActionCodeSettings object containing settings related to
+ handling action codes.
+ @param completion Optionally; a block which is invoked when the request finishes. Invoked
+ asynchronously on the main thread in the future.
+ */
+- (void)sendSignInLinkToEmail:(NSString *)email
+ actionCodeSettings:(FIRActionCodeSettings *)actionCodeSettings
+ completion:(nullable FIRSendSignInLinkToEmailCallback)completion;
+
/** @fn signOut:
@brief Signs out the current user.
@@ -672,6 +745,14 @@ NS_SWIFT_NAME(Auth)
*/
- (BOOL)signOut:(NSError *_Nullable *_Nullable)error;
+/** @fn isSignInWithEmailLink
+ @brief Checks if link is an email sign-in link.
+
+ @param link The email sign-in link.
+ @return @YES when the link passed matches the expected format of an email sign-in link.
+ */
+- (BOOL)isSignInWithEmailLink:(NSString *)link;
+
/** @fn addAuthStateDidChangeListener:
@brief Registers a block as an "auth state did change" listener. To be invoked when:
diff --git a/Firebase/Auth/Source/Public/FIREmailAuthProvider.h b/Firebase/Auth/Source/Public/FIREmailAuthProvider.h
index 99cd018..5823911 100644
--- a/Firebase/Auth/Source/Public/FIREmailAuthProvider.h
+++ b/Firebase/Auth/Source/Public/FIREmailAuthProvider.h
@@ -51,6 +51,15 @@ typedef FIREmailAuthProvider FIREmailPasswordAuthProvider __attribute__((depreca
*/
+ (FIRAuthCredential *)credentialWithEmail:(NSString *)email password:(NSString *)password;
+/** @fn credentialWithEmail:Link:
+ @brief Creates an `FIRAuthCredential` for an email & link sign in.
+
+ @param email The user's email address.
+ @param link The email sign-in link.
+ @return A FIRAuthCredential containing the email & link credential.
+ */
++ (FIRAuthCredential *)credentialWithEmail:(NSString *)email link:(NSString *)link;
+
/** @fn init
@brief This class is not meant to be initialized.
*/
diff --git a/Firebase/Auth/Source/RPCs/FIRAuthBackend.h b/Firebase/Auth/Source/RPCs/FIRAuthBackend.h
index a82c3a7..5928e71 100644
--- a/Firebase/Auth/Source/RPCs/FIRAuthBackend.h
+++ b/Firebase/Auth/Source/RPCs/FIRAuthBackend.h
@@ -19,6 +19,8 @@
@class FIRAuthRequestConfiguration;
@class FIRCreateAuthURIRequest;
@class FIRCreateAuthURIResponse;
+@class FIREmailLinkSignInRequest;
+@class FIREmailLinkSignInResponse;
@class FIRGetAccountInfoRequest;
@class FIRGetAccountInfoResponse;
@class FIRGetProjectConfigRequest;
@@ -130,6 +132,16 @@ typedef void (^FIRVerifyAssertionResponseCallback)
typedef void (^FIRVerifyPasswordResponseCallback)
(FIRVerifyPasswordResponse *_Nullable response, NSError *_Nullable error);
+/** @typedef FIREmailLinkSigninResponseCallback
+ @brief The type of block used to return the result of a call to the emailLinkSignin
+ endpoint.
+ @param response The received response, if any.
+ @param error The error which occurred, if any.
+ @remarks One of response or error will be non-nil.
+ */
+typedef void (^FIREmailLinkSigninResponseCallback)
+ (FIREmailLinkSignInResponse *_Nullable response, NSError *_Nullable error);
+
/** @typedef FIRVerifyCustomTokenResponseCallback
@brief The type of block used to return the result of a call to the verifyCustomToken
endpoint.
@@ -296,6 +308,15 @@ typedef void (^FIRVerifyClientResponseCallback)
+ (void)verifyPassword:(FIRVerifyPasswordRequest *)request
callback:(FIRVerifyPasswordResponseCallback)callback;
+/** @fn emailLinkSignin:callback:
+ @brief Calls the emailLinkSignin endpoint, which is responsible for authenticating a
+ user through passwordless sign-in.
+ @param request The request parameters.
+ @param callback The callback.
+ */
++ (void)emailLinkSignin:(FIREmailLinkSignInRequest *)request
+ callback:(FIREmailLinkSigninResponseCallback)callback;
+
/** @fn secureToken:callback:
@brief Calls the token endpoint, which is responsible for performing STS token exchanges and
token refreshes.
@@ -461,6 +482,15 @@ typedef void (^FIRVerifyClientResponseCallback)
- (void)verifyPassword:(FIRVerifyPasswordRequest *)request
callback:(FIRVerifyPasswordResponseCallback)callback;
+/** @fn emailLinkSignin:callback:
+ @brief Calls the emailLinkSignin endpoint, which is responsible for authenticating a
+ user through passwordless sign-in.
+ @param request The request parameters.
+ @param callback The callback.
+ */
+- (void)emailLinkSignin:(FIREmailLinkSignInRequest *)request
+ callback:(FIREmailLinkSigninResponseCallback)callback;
+
/** @fn secureToken:callback:
@brief Calls the token endpoint, which is responsible for performing STS token exchanges and
token refreshes.
@@ -472,7 +502,7 @@ typedef void (^FIRVerifyClientResponseCallback)
/** @fn getOOBConfirmationCode:callback:
@brief Calls the getOOBConfirmationCode endpoint, which is responsible for sending email change
- request emails, and password reset emails.
+ request emails, email sign-in link emails, and password reset emails.
@param request The request parameters.
@param callback The callback.
*/
diff --git a/Firebase/Auth/Source/RPCs/FIRAuthBackend.m b/Firebase/Auth/Source/RPCs/FIRAuthBackend.m
index 6b5232b..e380e34 100644
--- a/Firebase/Auth/Source/RPCs/FIRAuthBackend.m
+++ b/Firebase/Auth/Source/RPCs/FIRAuthBackend.m
@@ -51,6 +51,8 @@
#import "FIRVerifyCustomTokenResponse.h"
#import "FIRVerifyPasswordRequest.h"
#import "FIRVerifyPasswordResponse.h"
+#import "FIREmailLinkSignInRequest.h"
+#import "FIREmailLinkSignInResponse.h"
#import "FIRVerifyPhoneNumberRequest.h"
#import "FIRVerifyPhoneNumberResponse.h"
#import <GTMSessionFetcher/GTMSessionFetcher.h>
@@ -430,6 +432,11 @@ static id<FIRAuthBackendImplementation> gBackendImplementation;
[[self implementation] verifyPassword:request callback:callback];
}
++ (void)emailLinkSignin:(FIREmailLinkSignInRequest *)request
+ callback:(FIREmailLinkSigninResponseCallback)callback {
+ [[self implementation] emailLinkSignin:request callback:callback];
+}
+
+ (void)secureToken:(FIRSecureTokenRequest *)request
callback:(FIRSecureTokenResponseCallback)callback {
[[self implementation] secureToken:request callback:callback];
@@ -623,6 +630,18 @@ static id<FIRAuthBackendImplementation> gBackendImplementation;
}];
}
+- (void)emailLinkSignin:(FIREmailLinkSignInRequest *)request
+ callback:(FIREmailLinkSigninResponseCallback)callback {
+ FIREmailLinkSignInResponse *response = [[FIREmailLinkSignInResponse alloc] init];
+ [self postWithRequest:request response:response callback:^(NSError *error) {
+ if (error) {
+ callback(nil, error);
+ } else {
+ callback(response, nil);
+ }
+ }];
+}
+
- (void)secureToken:(FIRSecureTokenRequest *)request
callback:(FIRSecureTokenResponseCallback)callback {
FIRSecureTokenResponse *response = [[FIRSecureTokenResponse alloc] init];
diff --git a/Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.h b/Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.h
index 9f6cbae..8e8f7b0 100644
--- a/Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.h
+++ b/Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.h
@@ -51,6 +51,11 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property(nonatomic, copy, readonly, nullable) NSArray<NSString *> *allProviders;
+/** @property signinMethods
+ @brief A list of sign-in methods available for the passed @c identifier.
+ */
+@property(nonatomic, copy, readonly, nullable) NSArray<NSString *> *signinMethods;
+
@end
NS_ASSUME_NONNULL_END
diff --git a/Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.m b/Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.m
index 12ef97c..6f2937f 100644
--- a/Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.m
+++ b/Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.m
@@ -25,6 +25,7 @@
_registered = [dictionary[@"registered"] boolValue];
_forExistingProvider = [dictionary[@"forExistingProvider"] boolValue];
_allProviders = [dictionary[@"allProviders"] copy];
+ _signinMethods = [dictionary[@"signinMethods"] copy];
return YES;
}
diff --git a/Firebase/Auth/Source/RPCs/FIREmailLinkSignInRequest.h b/Firebase/Auth/Source/RPCs/FIREmailLinkSignInRequest.h
new file mode 100644
index 0000000..e1b10d8
--- /dev/null
+++ b/Firebase/Auth/Source/RPCs/FIREmailLinkSignInRequest.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import <Foundation/Foundation.h>
+
+#import "FIRAuthRPCRequest.h"
+#import "FIRIdentityToolkitRequest.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/** @class FIREmailLinkSignInRequest
+ @brief Represents the parameters for the emailLinkSignin endpoint.
+ */
+@interface FIREmailLinkSignInRequest : FIRIdentityToolkitRequest <FIRAuthRPCRequest>
+
+#pragma mark - Components of "postBody"
+
+/** @property email
+ @brief The email identifier used to complete the email link sign-in.
+ */
+@property(nonatomic, copy, readonly) NSString *email;
+
+/** @property oobCode
+ @brief The OOB code used to complete the email link sign-in flow.
+ */
+@property(nonatomic, copy, readonly) NSString *oobCode;
+
+/** @property idToken
+ @brief The ID Token code potentially used to complete the email link sign-in flow.
+ */
+@property(nonatomic, copy) NSString *IDToken;
+
+/** @fn initWithEndpoint:requestConfiguration:
+ @brief Please use initWithProviderID:requestConfifuration instead.
+ */
+- (instancetype)initWithEndpoint:(NSString *)endpoint
+ requestConfiguration:(FIRAuthRequestConfiguration *)requestConfiguration NS_UNAVAILABLE;
+
+/** @fn initWithProviderID:requestConfifuration
+ @brief Designated initializer.
+ @param email The email identifier used to complete hte email link sign-in flow.
+ @param oobCode The OOB code used to complete the email link sign-in flow.
+ @param requestConfiguration An object containing configurations to be added to the request.
+
+ */
+- (instancetype)initWithEmail:(NSString *)email
+ oobCode:(NSString *)oobCode
+ requestConfiguration:(FIRAuthRequestConfiguration *)requestConfiguration
+ NS_DESIGNATED_INITIALIZER;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Firebase/Auth/Source/RPCs/FIREmailLinkSignInRequest.m b/Firebase/Auth/Source/RPCs/FIREmailLinkSignInRequest.m
new file mode 100644
index 0000000..9787e8e
--- /dev/null
+++ b/Firebase/Auth/Source/RPCs/FIREmailLinkSignInRequest.m
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import "FIREmailLinkSignInRequest.h"
+
+/** @var kEmailLinkSigninEndpoint
+ @brief The "EmailLinkSignin" endpoint.
+ */
+static NSString *const kEmailLinkSigninEndpoint = @"emailLinkSignin";
+
+/** @var kEmailKey
+ @brief The key for the "identifier" value in the request.
+ */
+static NSString *const kEmailKey = @"email";
+
+/** @var kEmailLinkKey
+ @brief The key for the "emailLink" value in the request.
+ */
+static NSString *const kOOBCodeKey = @"oobCode";
+
+/** @var kIDTokenKey
+ @brief The key for the "IDToken" value in the request.
+ */
+static NSString *const kIDTokenKey = @"idToken";
+
+/** @var kPostBodyKey
+ @brief The key for the "postBody" value in the request.
+ */
+static NSString *const kPostBodyKey = @"postBody";
+
+@implementation FIREmailLinkSignInRequest
+
+- (instancetype)initWithEmail:(NSString *)email
+ oobCode:(NSString *)oobCode
+ requestConfiguration:(FIRAuthRequestConfiguration *)requestConfiguration {
+ self = [super initWithEndpoint:kEmailLinkSigninEndpoint
+ requestConfiguration:requestConfiguration];
+ if (self) {
+ _email = email;
+ _oobCode = oobCode;
+ }
+ return self;
+}
+
+- (nullable id)unencodedHTTPRequestBodyWithError:(NSError *_Nullable *_Nullable)error {
+ NSMutableDictionary *postBody = [@{
+ kEmailKey : _email,
+ kOOBCodeKey : _oobCode,
+ } mutableCopy];
+
+ if (_IDToken) {
+ postBody[kIDTokenKey] = _IDToken;
+ }
+ return postBody;
+}
+
+@end
diff --git a/Firebase/Auth/Source/RPCs/FIREmailLinkSignInResponse.h b/Firebase/Auth/Source/RPCs/FIREmailLinkSignInResponse.h
new file mode 100644
index 0000000..df0a127
--- /dev/null
+++ b/Firebase/Auth/Source/RPCs/FIREmailLinkSignInResponse.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#import <Foundation/Foundation.h>
+
+#import "FIRAuthRPCResponse.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/** @class FIRVerifyAssertionResponse
+ @brief Represents the response from the emailLinkSignin endpoint.
+ */
+@interface FIREmailLinkSignInResponse : NSObject<FIRAuthRPCResponse>
+
+/** @property IDToken
+ @brief The ID token in the email link sign-in response.
+ */
+@property(nonatomic, copy, readonly) NSString *IDToken;
+
+/** @property email
+ @brief The email returned by the IdP.
+ */
+@property(nonatomic, strong, readonly, nullable) NSString *email;
+
+/** @property refreshToken
+ @brief The refreshToken returned by the server.
+ */
+@property(nonatomic, strong, readonly, nullable) NSString *refreshToken;
+
+/** @property approximateExpirationDate
+ @brief The approximate expiration date of the access token.
+ */
+@property(nonatomic, copy, readonly, nullable) NSDate *approximateExpirationDate;
+
+/** @property isNewUser
+ @brief Flag indicating that the user signing in is a new user and not a returning user.
+ */
+@property(nonatomic, assign) BOOL isNewUser;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Firebase/Auth/Source/RPCs/FIREmailLinkSignInResponse.m b/Firebase/Auth/Source/RPCs/FIREmailLinkSignInResponse.m
new file mode 100644
index 0000000..cd36d41
--- /dev/null
+++ b/Firebase/Auth/Source/RPCs/FIREmailLinkSignInResponse.m
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import "FIREmailLinkSignInResponse.h"
+
+@implementation FIREmailLinkSignInResponse
+
+- (BOOL)setWithDictionary:(NSDictionary *)dictionary
+ error:(NSError *_Nullable *_Nullable)error {
+ _email = [dictionary[@"email"] copy];
+ _IDToken = [dictionary[@"idToken"] copy];
+ _isNewUser = [dictionary[@"isNewUser"] boolValue];
+ _refreshToken = [dictionary[@"refreshToken"] copy];
+ _approximateExpirationDate = [dictionary[@"expiresIn"] isKindOfClass:[NSString class]] ?
+ [NSDate dateWithTimeIntervalSinceNow:[dictionary[@"expiresIn"] doubleValue]] : nil;
+ return YES;
+}
+
+@end
diff --git a/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.h b/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.h
index abd59b4..751cfe7 100644
--- a/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.h
+++ b/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.h
@@ -36,6 +36,11 @@ typedef NS_ENUM(NSInteger, FIRGetOOBConfirmationCodeRequestType) {
@brief Requests an email verification code.
*/
FIRGetOOBConfirmationCodeRequestTypeVerifyEmail,
+
+ /** @var FIRGetOOBConfirmationCodeRequestTypeEmailLink
+ @brief Requests an email sign-in link.
+ */
+ FIRGetOOBConfirmationCodeRequestTypeEmailLink,
};
/** @enum FIRGetOOBConfirmationCodeRequest
@@ -91,7 +96,7 @@ typedef NS_ENUM(NSInteger, FIRGetOOBConfirmationCodeRequestType) {
*/
@property(assign, nonatomic) BOOL handleCodeInApp;
-/** @fn passwordResetRequestWithEmail:APIKey:
+/** @fn passwordResetRequestWithEmail:actionCodeSettings:requestConfiguration:
@brief Creates a password reset request.
@param email The user's email address.
@param actionCodeSettings An object of FIRActionCodeSettings which specifies action code
@@ -104,7 +109,7 @@ typedef NS_ENUM(NSInteger, FIRGetOOBConfirmationCodeRequestType) {
actionCodeSettings:(nullable FIRActionCodeSettings *)actionCodeSettings
requestConfiguration:(FIRAuthRequestConfiguration *)requestConfiguration;
-/** @fn verifyEmailRequestWithAccessToken:APIKey:
+/** @fn verifyEmailRequestWithAccessToken:actionCodeSettings:requestConfiguration:
@brief Creates a password reset request.
@param accessToken The user's STS Access Token.
@param actionCodeSettings An object of FIRActionCodeSettings which specifies action code
@@ -117,6 +122,19 @@ typedef NS_ENUM(NSInteger, FIRGetOOBConfirmationCodeRequestType) {
actionCodeSettings:(nullable FIRActionCodeSettings *)actionCodeSettings
requestConfiguration:(FIRAuthRequestConfiguration *)requestConfiguration;
+/** @fn signInWithEmailLinkRequest:actionCodeSettings:requestConfiguration:
+ @brief Creates a sign-in with email link.
+ @param email The user's email address.
+ @param actionCodeSettings An object of FIRActionCodeSettings which specifies action code
+ settings to be applied to the email sign-in link.
+ @param requestConfiguration An object containing configurations to be added to the request.
+ @return An email sign-in link request.
+ */
++ (nullable FIRGetOOBConfirmationCodeRequest *)
+ signInWithEmailLinkRequest:(NSString *)email
+ actionCodeSettings:(nullable FIRActionCodeSettings *)actionCodeSettings
+ requestConfiguration:(FIRAuthRequestConfiguration *)requestConfiguration;
+
/** @fn init
@brief Please use a factory method.
*/
diff --git a/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.m b/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.m
index 653eddd..438f24b 100644
--- a/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.m
+++ b/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.m
@@ -79,6 +79,11 @@ static NSString *const kCanHandleCodeInAppKey = @"canHandleCodeInApp";
*/
static NSString *const kPasswordResetRequestTypeValue = @"PASSWORD_RESET";
+/** @var kEmailLinkSignInTypeValue
+ @brief The value for the "EMAIL_SIGNIN" request type.
+ */
+static NSString *const kEmailLinkSignInTypeValue= @"EMAIL_SIGNIN";
+
/** @var kVerifyEmailRequestTypeValue
@brief The value for the "VERIFY_EMAIL" request type.
*/
@@ -116,6 +121,8 @@ static NSString *const kVerifyEmailRequestTypeValue = @"VERIFY_EMAIL";
return kPasswordResetRequestTypeValue;
case FIRGetOOBConfirmationCodeRequestTypeVerifyEmail:
return kVerifyEmailRequestTypeValue;
+ case FIRGetOOBConfirmationCodeRequestTypeEmailLink:
+ return kEmailLinkSignInTypeValue;
// No default case so that we get a compiler warning if a new value was added to the enum.
}
}
@@ -142,6 +149,17 @@ static NSString *const kVerifyEmailRequestTypeValue = @"VERIFY_EMAIL";
requestConfiguration:requestConfiguration];
}
++ (FIRGetOOBConfirmationCodeRequest *)
+ signInWithEmailLinkRequest:(NSString *)email
+ actionCodeSettings:(nullable FIRActionCodeSettings *)actionCodeSettings
+ requestConfiguration:(FIRAuthRequestConfiguration *)requestConfiguration {
+ return [[self alloc] initWithRequestType:FIRGetOOBConfirmationCodeRequestTypeEmailLink
+ email:email
+ accessToken:nil
+ actionCodeSettings:actionCodeSettings
+ requestConfiguration:requestConfiguration];
+}
+
- (nullable instancetype)initWithRequestType:(FIRGetOOBConfirmationCodeRequestType)requestType
email:(nullable NSString *)email
accessToken:(nullable NSString *)accessToken
@@ -180,6 +198,12 @@ static NSString *const kVerifyEmailRequestTypeValue = @"VERIFY_EMAIL";
body[kIDTokenKey] = _accessToken;
}
+ // For email sign-in link requests, we only need an email address in addition to the already
+ // required fields.
+ if (_requestType == FIRGetOOBConfirmationCodeRequestTypeEmailLink) {
+ body[kEmailKey] = _email;
+ }
+
if (_continueURL) {
body[kContinueURLKey] = _continueURL;
}