From 2092a32f46e2d4219c6aa5ac02c7a4073df615ca Mon Sep 17 00:00:00 2001 From: Salakar Date: Wed, 22 Aug 2018 17:55:53 +0100 Subject: [PATCH] [auth][wip] improve error codes, messages and types --- ios/RNFirebase/auth/RNFirebaseAuth.h | 91 ++++++++-- ios/RNFirebase/auth/RNFirebaseAuth.m | 18 +- src/modules/auth/AuthError.js | 33 ++++ src/modules/auth/User.js | 5 +- src/modules/auth/index.js | 25 +-- src/modules/auth/phone/ConfirmationResult.js | 1 + src/modules/auth/phone/PhoneAuthListener.js | 18 +- .../auth/providers/EmailAuthProvider.js | 2 +- .../auth/providers/FacebookAuthProvider.js | 2 +- .../auth/providers/GithubAuthProvider.js | 2 +- .../auth/providers/GoogleAuthProvider.js | 2 +- src/modules/auth/providers/OAuthProvider.js | 2 +- .../auth/providers/PhoneAuthProvider.js | 2 +- .../auth/providers/TwitterAuthProvider.js | 2 +- src/modules/auth/types.flow.js | 162 ++++++++++++++++++ src/modules/auth/types.js | 75 -------- 16 files changed, 318 insertions(+), 124 deletions(-) create mode 100644 src/modules/auth/AuthError.js create mode 100644 src/modules/auth/types.flow.js delete mode 100644 src/modules/auth/types.js diff --git a/ios/RNFirebase/auth/RNFirebaseAuth.h b/ios/RNFirebase/auth/RNFirebaseAuth.h index 05975067..d5f24634 100644 --- a/ios/RNFirebase/auth/RNFirebaseAuth.h +++ b/ios/RNFirebase/auth/RNFirebaseAuth.h @@ -1,21 +1,78 @@ #ifndef RNFirebaseAuth_h -#define RNFirebaseAuth_h -#import + #define RNFirebaseAuth_h + #import + #if __has_include() + #import + #import + #import -#if __has_include() -#import -#import -#import - -@interface RNFirebaseAuth : RCTEventEmitter {}; -@property NSMutableDictionary *authStateHandlers; -@property NSMutableDictionary *idTokenHandlers; - -@end - -#else -@interface RNFirebaseAuth : NSObject -@end -#endif + @interface RNFirebaseAuth : RCTEventEmitter {}; + @property NSMutableDictionary *authStateHandlers; + @property NSMutableDictionary *idTokenHandlers; + @end + extern NSString * const AuthErrorCode_toJSErrorCode[]; + NSString * const AuthErrorCode_toJSErrorCode[] = { + [FIRAuthErrorCodeInvalidCustomToken] = @"invalid-custom-token", + [FIRAuthErrorCodeCustomTokenMismatch] = @"custom-token-mismatch", + [FIRAuthErrorCodeInvalidCredential] = @"invalid-credential", + [FIRAuthErrorCodeUserDisabled] = @"user-disabled", + [FIRAuthErrorCodeOperationNotAllowed] = @"operation-not-allowed", + [FIRAuthErrorCodeEmailAlreadyInUse] = @"email-already-in-use", + [FIRAuthErrorCodeInvalidEmail] = @"invalid-email", + [FIRAuthErrorCodeWrongPassword] = @"wrong-password", + [FIRAuthErrorCodeTooManyRequests] = @"too-many-requests", + [FIRAuthErrorCodeUserNotFound] = @"user-not-found", + [FIRAuthErrorCodeAccountExistsWithDifferentCredential] = @"account-exists-with-different-credential", + [FIRAuthErrorCodeRequiresRecentLogin] = @"requires-recent-login", + [FIRAuthErrorCodeProviderAlreadyLinked] = @"provider-already-linked", + [FIRAuthErrorCodeNoSuchProvider] = @"no-such-provider", + [FIRAuthErrorCodeInvalidUserToken] = @"invalid-user-token", + [FIRAuthErrorCodeNetworkError] = @"network-request-failed", + [FIRAuthErrorCodeUserTokenExpired] = @"user-token-expired", + [FIRAuthErrorCodeInvalidAPIKey] = @"invalid-api-key", + [FIRAuthErrorCodeUserMismatch] = @"user-mismatch", + [FIRAuthErrorCodeCredentialAlreadyInUse] = @"credential-already-in-use", + [FIRAuthErrorCodeWeakPassword] = @"weak-password", + [FIRAuthErrorCodeAppNotAuthorized] = @"app-not-authorized", + [FIRAuthErrorCodeExpiredActionCode] = @"expired-action-code", + [FIRAuthErrorCodeInvalidActionCode] = @"invalid-action-code", + [FIRAuthErrorCodeInvalidMessagePayload] = @"invalid-message-payload", + [FIRAuthErrorCodeInvalidSender] = @"invalid-sender", + [FIRAuthErrorCodeInvalidRecipientEmail] = @"invalid-recipient-email", + [FIRAuthErrorCodeMissingEmail] = @"invalid-email", + [FIRAuthErrorCodeMissingIosBundleID] = @"missing-ios-bundle-id", + [FIRAuthErrorCodeMissingAndroidPackageName] = @"missing-android-pkg-name", + [FIRAuthErrorCodeUnauthorizedDomain] = @"unauthorized-domain", + [FIRAuthErrorCodeInvalidContinueURI] = @"invalid-continue-uri", + [FIRAuthErrorCodeMissingContinueURI] = @"missing-continue-uri", + [FIRAuthErrorCodeMissingPhoneNumber] = @"missing-phone-number", + [FIRAuthErrorCodeInvalidPhoneNumber] = @"invalid-phone-number", + [FIRAuthErrorCodeMissingVerificationCode] = @"missing-verification-code", + [FIRAuthErrorCodeInvalidVerificationCode] = @"invalid-verification-code", + [FIRAuthErrorCodeMissingVerificationID] = @"missing-verification-id", + [FIRAuthErrorCodeInvalidVerificationID] = @"invalid-verification-id", + [FIRAuthErrorCodeMissingAppCredential] = @"missing-app-credential", + [FIRAuthErrorCodeInvalidAppCredential] = @"invalid-app-credential", + [FIRAuthErrorCodeSessionExpired] = @"code-expired", + [FIRAuthErrorCodeQuotaExceeded] = @"quota-exceeded", + [FIRAuthErrorCodeMissingAppToken] = @"missing-apns-token", + [FIRAuthErrorCodeNotificationNotForwarded] = @"notification-not-forwarded", + [FIRAuthErrorCodeAppNotVerified] = @"app-not-verified", + [FIRAuthErrorCodeCaptchaCheckFailed] = @"captcha-check-failed", + [FIRAuthErrorCodeWebContextAlreadyPresented] = @"cancelled-popup-request", + [FIRAuthErrorCodeWebContextCancelled] = @"popup-closed-by-user", + [FIRAuthErrorCodeAppVerificationUserInteractionFailure] = @"app-verification-user-interaction-failure", + [FIRAuthErrorCodeInvalidClientID] = @"invalid-oauth-client-id", + [FIRAuthErrorCodeWebNetworkRequestFailed] = @"network-request-failed", + [FIRAuthErrorCodeWebInternalError] = @"internal-error", + [FIRAuthErrorCodeNullUser] = @"null-user", + [FIRAuthErrorCodeKeychainError] = @"keychain-error", + [FIRAuthErrorCodeInternalError] = @"internal-error", + [FIRAuthErrorCodeMalformedJWT] = @"malformed-jwt" + }; + #else + @interface RNFirebaseAuth : NSObject + @end + #endif #endif diff --git a/ios/RNFirebase/auth/RNFirebaseAuth.m b/ios/RNFirebase/auth/RNFirebaseAuth.m index 97e6e970..c0a22b41 100644 --- a/ios/RNFirebase/auth/RNFirebaseAuth.m +++ b/ios/RNFirebase/auth/RNFirebaseAuth.m @@ -1224,7 +1224,9 @@ RCT_EXPORT_METHOD(verifyPasswordResetCode:(NSString *) appDisplayName NSString *code = @"auth/unknown"; NSString *message = [error localizedDescription]; NSString *nativeErrorMessage = [error localizedDescription]; - + + code = AuthErrorCode_toJSErrorCode[error.code]; + // TODO remove switch case after testing AuthErrorCode_toJSErrorCode switch (error.code) { case FIRAuthErrorCodeInvalidCustomToken: code = @"auth/invalid-custom-token"; @@ -1298,8 +1300,6 @@ RCT_EXPORT_METHOD(verifyPasswordResetCode:(NSString *) appDisplayName code = @"auth/internal-error"; message = @"An internal error has occurred, please try again."; break; - - // unsure of the below codes so leaving them as the default error message case FIRAuthErrorCodeTooManyRequests: code = @"auth/too-many-requests"; break; @@ -1330,6 +1330,18 @@ RCT_EXPORT_METHOD(verifyPasswordResetCode:(NSString *) appDisplayName case FIRAuthErrorCodeKeychainError: code = @"auth/keychain-error"; break; + case FIRAuthErrorCodeInvalidVerificationCode: + code = @"auth/invalid-verification-code"; + message = @"The verification code used to create the phone auth credential is invalid"; + break; + case FIRAuthErrorCodeInvalidVerificationID: + code = @"auth/invalid-verification-id"; + message = @"The verification ID used to create the phone auth credential is invalid"; + break; + case FIRAuthErrorCodeInvalidActionCode: + code = @"auth/invalid-action-code"; + message = @"The action code is invalid. This can happen if the code is malformed, expired, or has already been used."; + break; default: break; } diff --git a/src/modules/auth/AuthError.js b/src/modules/auth/AuthError.js new file mode 100644 index 00000000..79a1952a --- /dev/null +++ b/src/modules/auth/AuthError.js @@ -0,0 +1,33 @@ +import type { AuthErrorCode } from './types.flow'; + +export default class AuthError { + +code: AuthErrorCode; + + +message: string; + + +nativeMessage: string; + + +credential: ?any; + + /** + * + * @param code + * @param nativeMessage + * @param credential + */ + constructor( + code: AuthErrorCode, + nativeMessage: string, + credential?: any // TODO AuthCredential type + ) { + // this.code = code; + // this.details = details; + // this.message = message; + // TODO babel 7 issue... can't extend builtin classes properly. + this._error = new Error(''); // TODO code to message lookup or revert to nativeMessage + this._error.code = code; + this._error.credential = credential; + this._error.constructor = AuthError; + return this._error; + } +} diff --git a/src/modules/auth/User.js b/src/modules/auth/User.js index 7cb30787..4fd4bdee 100644 --- a/src/modules/auth/User.js +++ b/src/modules/auth/User.js @@ -5,7 +5,6 @@ import INTERNALS from '../../utils/internals'; import { getNativeModule } from '../../utils/native'; -import type Auth from '.'; import type { ActionCodeSettings, AuthCredential, @@ -13,7 +12,9 @@ import type { UserCredential, UserInfo, UserMetadata, -} from './types'; +} from './types.flow'; + +import type Auth from '.'; type UpdateProfile = { displayName?: string, diff --git a/src/modules/auth/index.js b/src/modules/auth/index.js index cd80de13..e54d91bd 100644 --- a/src/modules/auth/index.js +++ b/src/modules/auth/index.js @@ -3,32 +3,33 @@ * Auth representation wrapper */ import User from './User'; -import ModuleBase from '../../utils/ModuleBase'; -import { getAppEventName, SharedEventEmitter } from '../../utils/events'; -import { isAndroid, isBoolean } from '../../utils'; import { getLogger } from '../../utils/log'; -import { getNativeModule } from '../../utils/native'; import INTERNALS from '../../utils/internals'; -import ConfirmationResult from './phone/ConfirmationResult'; +import ModuleBase from '../../utils/ModuleBase'; +import { isAndroid, isBoolean } from '../../utils'; +import { getNativeModule } from '../../utils/native'; import PhoneAuthListener from './phone/PhoneAuthListener'; +import ConfirmationResult from './phone/ConfirmationResult'; +import { getAppEventName, SharedEventEmitter } from '../../utils/events'; // providers +import OAuthProvider from './providers/OAuthProvider'; import EmailAuthProvider from './providers/EmailAuthProvider'; import PhoneAuthProvider from './providers/PhoneAuthProvider'; import GoogleAuthProvider from './providers/GoogleAuthProvider'; import GithubAuthProvider from './providers/GithubAuthProvider'; -import OAuthProvider from './providers/OAuthProvider'; import TwitterAuthProvider from './providers/TwitterAuthProvider'; import FacebookAuthProvider from './providers/FacebookAuthProvider'; import type { - ActionCodeInfo, - ActionCodeSettings, - AuthCredential, NativeUser, - NativeUserCredential, + AuthCredential, + ActionCodeInfo, UserCredential, -} from './types'; + ActionCodeSettings, + NativeUserCredential, +} from './types.flow'; + import type App from '../core/app'; type AuthState = { @@ -526,7 +527,7 @@ export default class Auth extends ModuleBase { * @param code * @returns {*} */ - set languageCode(code: string) { + set languageCode(code: string): void { this._languageCode = code; getNativeModule(this).setLanguageCode(code); } diff --git a/src/modules/auth/phone/ConfirmationResult.js b/src/modules/auth/phone/ConfirmationResult.js index 94ef56a2..0e8cd0e6 100644 --- a/src/modules/auth/phone/ConfirmationResult.js +++ b/src/modules/auth/phone/ConfirmationResult.js @@ -3,6 +3,7 @@ * ConfirmationResult representation wrapper */ import { getNativeModule } from '../../../utils/native'; + import type Auth from '..'; import type User from '../User'; diff --git a/src/modules/auth/phone/PhoneAuthListener.js b/src/modules/auth/phone/PhoneAuthListener.js index e1940601..c0a7f39b 100644 --- a/src/modules/auth/phone/PhoneAuthListener.js +++ b/src/modules/auth/phone/PhoneAuthListener.js @@ -1,23 +1,23 @@ // @flow -import INTERNALS from '../../../utils/internals'; -import { SharedEventEmitter } from '../../../utils/events'; import { - generatePushID, - isFunction, - isAndroid, isIOS, isString, + isAndroid, + isFunction, + generatePushID, nativeToJSError, } from '../../../utils'; +import INTERNALS from '../../../utils/internals'; import { getNativeModule } from '../../../utils/native'; +import { SharedEventEmitter } from '../../../utils/events'; import type Auth from '..'; type PhoneAuthSnapshot = { - state: 'sent' | 'timeout' | 'verified' | 'error', - verificationId: string, - code: string | null, error: Error | null, + code: string | null, + verificationId: string, + state: 'sent' | 'timeout' | 'verified' | 'error', }; type PhoneAuthError = { @@ -194,6 +194,7 @@ export default class PhoneAuthListener { /** * Create a new internal deferred promise, if not already created * @private + * TODO use promise deferred util */ _promiseDeferred() { if (!this._promise) { @@ -341,6 +342,7 @@ export default class PhoneAuthListener { return this; } + // TODO have these inherit from extending a ThenableClass util/helper? /** * Promise .then proxy * @param fn diff --git a/src/modules/auth/providers/EmailAuthProvider.js b/src/modules/auth/providers/EmailAuthProvider.js index fc013a47..1a398022 100644 --- a/src/modules/auth/providers/EmailAuthProvider.js +++ b/src/modules/auth/providers/EmailAuthProvider.js @@ -2,7 +2,7 @@ * @flow * EmailAuthProvider representation wrapper */ -import type { AuthCredential } from '../types'; +import type { AuthCredential } from '../types.flow'; const linkProviderId = 'emailLink'; const passwordProviderId = 'password'; diff --git a/src/modules/auth/providers/FacebookAuthProvider.js b/src/modules/auth/providers/FacebookAuthProvider.js index 67fa957b..2fbb7b01 100644 --- a/src/modules/auth/providers/FacebookAuthProvider.js +++ b/src/modules/auth/providers/FacebookAuthProvider.js @@ -2,7 +2,7 @@ * @flow * FacebookAuthProvider representation wrapper */ -import type { AuthCredential } from '../types'; +import type { AuthCredential } from '../types.flow'; const providerId = 'facebook.com'; diff --git a/src/modules/auth/providers/GithubAuthProvider.js b/src/modules/auth/providers/GithubAuthProvider.js index a6e8c13c..d88af83b 100644 --- a/src/modules/auth/providers/GithubAuthProvider.js +++ b/src/modules/auth/providers/GithubAuthProvider.js @@ -2,7 +2,7 @@ * @flow * GithubAuthProvider representation wrapper */ -import type { AuthCredential } from '../types'; +import type { AuthCredential } from '../types.flow'; const providerId = 'github.com'; diff --git a/src/modules/auth/providers/GoogleAuthProvider.js b/src/modules/auth/providers/GoogleAuthProvider.js index 25c81a21..f9bcbb08 100644 --- a/src/modules/auth/providers/GoogleAuthProvider.js +++ b/src/modules/auth/providers/GoogleAuthProvider.js @@ -2,7 +2,7 @@ * @flow * EmailAuthProvider representation wrapper */ -import type { AuthCredential } from '../types'; +import type { AuthCredential } from '../types.flow'; const providerId = 'google.com'; diff --git a/src/modules/auth/providers/OAuthProvider.js b/src/modules/auth/providers/OAuthProvider.js index 2bd28ed2..57f9cd1c 100644 --- a/src/modules/auth/providers/OAuthProvider.js +++ b/src/modules/auth/providers/OAuthProvider.js @@ -2,7 +2,7 @@ * @flow * OAuthProvider representation wrapper */ -import type { AuthCredential } from '../types'; +import type { AuthCredential } from '../types.flow'; const providerId = 'oauth'; diff --git a/src/modules/auth/providers/PhoneAuthProvider.js b/src/modules/auth/providers/PhoneAuthProvider.js index 57ce6558..9a17c097 100644 --- a/src/modules/auth/providers/PhoneAuthProvider.js +++ b/src/modules/auth/providers/PhoneAuthProvider.js @@ -2,7 +2,7 @@ * @flow * PhoneAuthProvider representation wrapper */ -import type { AuthCredential } from '../types'; +import type { AuthCredential } from '../types.flow'; const providerId = 'phone'; diff --git a/src/modules/auth/providers/TwitterAuthProvider.js b/src/modules/auth/providers/TwitterAuthProvider.js index 150926bb..c6851525 100644 --- a/src/modules/auth/providers/TwitterAuthProvider.js +++ b/src/modules/auth/providers/TwitterAuthProvider.js @@ -2,7 +2,7 @@ * @flow * TwitterAuthProvider representation wrapper */ -import type { AuthCredential } from '../types'; +import type { AuthCredential } from '../types.flow'; const providerId = 'twitter.com'; diff --git a/src/modules/auth/types.flow.js b/src/modules/auth/types.flow.js new file mode 100644 index 00000000..20fc7f55 --- /dev/null +++ b/src/modules/auth/types.flow.js @@ -0,0 +1,162 @@ +/** + * @flow + */ +import type User from './User'; + +export type ActionCodeInfo = { + data: { + email?: string, + fromEmail?: string, + }, + operation: 'PASSWORD_RESET' | 'VERIFY_EMAIL' | 'RECOVER_EMAIL', +}; + +export type ActionCodeSettings = { + android: { + installApp?: boolean, + minimumVersion?: string, + packageName: string, + }, + handleCodeInApp?: boolean, + iOS: { + bundleId?: string, + }, + url: string, +}; + +export type AdditionalUserInfo = { + isNewUser: boolean, + profile?: Object, + providerId: string, + username?: string, +}; + +export type AuthCredential = { + providerId: string, + token: string, + secret: string, +}; + +export type UserCredential = {| + additionalUserInfo?: AdditionalUserInfo, + user: User, +|}; + +export type UserInfo = { + displayName?: string, + email?: string, + phoneNumber?: string, + photoURL?: string, + providerId: string, + uid: string, +}; + +export type UserMetadata = { + creationTime?: string, + lastSignInTime?: string, +}; + +export type NativeUser = { + displayName?: string, + email?: string, + emailVerified?: boolean, + isAnonymous?: boolean, + metadata: UserMetadata, + phoneNumber?: string, + photoURL?: string, + providerData: UserInfo[], + providerId: string, + uid: string, +}; + +export type NativeUserCredential = {| + additionalUserInfo?: AdditionalUserInfo, + user: NativeUser, +|}; + +export type AuthErrorCode = + | 'account-exists-with-different-credential' + | 'app-deleted' + | 'app-not-authorized' + | 'app-not-installed' + | 'app-not-verified' + | 'app-verification-user-interaction-failure' + | 'argument-error' + | 'auth-domain-config-required' + | 'cancelled-popup-request' + | 'cancelled-popup-request' + | 'captcha-check-failed' + | 'code-expired' + | 'code-expired' + | 'credential-already-in-use' + | 'custom-token-mismatch' + | 'dynamic-link-not-activated' + | 'email-already-in-use' + | 'expired-action-code' + | 'internal-error' + | 'internal-error' + | 'invalid-action-code' + | 'invalid-api-key' + | 'invalid-app-credential' + | 'invalid-app-id' + | 'invalid-auth-event' + | 'invalid-cert-hash' + | 'invalid-continue-uri' + | 'invalid-credential' + | 'invalid-custom-token' + | 'invalid-email' + | 'invalid-email' + | 'invalid-message-payload' + | 'invalid-oauth-client-id' + | 'invalid-oauth-client-id' + | 'invalid-oauth-provider' + | 'invalid-persistence-type' + | 'invalid-phone-number' + | 'invalid-provider-id' + | 'invalid-recipient-email' + | 'invalid-sender' + | 'invalid-user-token' + | 'invalid-verification-code' + | 'invalid-verification-id' + | 'keychain-error' + | 'malformed-jwt' + | 'missing-android-pkg-name' + | 'missing-android-pkg-name' + | 'missing-apns-token' + | 'missing-app-credential' + | 'missing-continue-uri' + | 'missing-ios-bundle-id' + | 'missing-phone-number' + | 'missing-verification-code' + | 'missing-verification-id' + | 'network-request-failed' + | 'network-request-failed' + | 'network-request-failed' + | 'no-auth-event' + | 'no-such-provider' + | 'notification-not-forwarded' + | 'null-user' + | 'operation-not-allowed' + | 'operation-not-supported-in-this-environment' + | 'popup-blocked' + | 'popup-closed-by-user' + | 'popup-closed-by-user' + | 'provider-already-linked' + | 'quota-exceeded' + | 'redirect-cancelled-by-user' + | 'redirect-operation-pending' + | 'requires-recent-login' + | 'timeout' + | 'too-many-requests' + | 'unauthorized-continue-uri' + | 'unauthorized-domain' + | 'unsupported-persistence-type' + | 'user-cancelled' + | 'user-disabled' + | 'user-mismatch' + | 'user-not-found' + | 'user-signed-out' + | 'user-token-expired' + | 'weak-password' + | 'web-storage-unsupported' + | 'wrong-password'; diff --git a/src/modules/auth/types.js b/src/modules/auth/types.js deleted file mode 100644 index 64655509..00000000 --- a/src/modules/auth/types.js +++ /dev/null @@ -1,75 +0,0 @@ -/** - * @flow - */ -import type User from './User'; - -export type ActionCodeInfo = { - data: { - email?: string, - fromEmail?: string, - }, - operation: 'PASSWORD_RESET' | 'VERIFY_EMAIL' | 'RECOVER_EMAIL', -}; - -export type ActionCodeSettings = { - android: { - installApp?: boolean, - minimumVersion?: string, - packageName: string, - }, - handleCodeInApp?: boolean, - iOS: { - bundleId?: string, - }, - url: string, -}; - -export type AdditionalUserInfo = { - isNewUser: boolean, - profile?: Object, - providerId: string, - username?: string, -}; - -export type AuthCredential = { - providerId: string, - token: string, - secret: string, -}; - -export type UserCredential = {| - additionalUserInfo?: AdditionalUserInfo, - user: User, -|}; - -export type UserInfo = { - displayName?: string, - email?: string, - phoneNumber?: string, - photoURL?: string, - providerId: string, - uid: string, -}; - -export type UserMetadata = { - creationTime?: string, - lastSignInTime?: string, -}; - -export type NativeUser = { - displayName?: string, - email?: string, - emailVerified?: boolean, - isAnonymous?: boolean, - metadata: UserMetadata, - phoneNumber?: string, - photoURL?: string, - providerData: UserInfo[], - providerId: string, - uid: string, -}; - -export type NativeUserCredential = {| - additionalUserInfo?: AdditionalUserInfo, - user: NativeUser, -|};