diff --git a/.changeset/eleven-hats-happen.md b/.changeset/eleven-hats-happen.md new file mode 100644 index 00000000..ea54a01e --- /dev/null +++ b/.changeset/eleven-hats-happen.md @@ -0,0 +1,5 @@ +--- +"@capacitor-firebase/authentication": minor +--- + +feat(web): support sign-in with redirect diff --git a/packages/authentication/README.md b/packages/authentication/README.md index 965ac386..858f1c81 100644 --- a/packages/authentication/README.md +++ b/packages/authentication/README.md @@ -356,10 +356,11 @@ const useEmulator = async () => { * [`applyActionCode(...)`](#applyactioncode) -* [`createUserWithEmailAndPassword(...)`](#createuserwithemailandpassword) * [`confirmPasswordReset(...)`](#confirmpasswordreset) +* [`createUserWithEmailAndPassword(...)`](#createuserwithemailandpassword) * [`getCurrentUser()`](#getcurrentuser) * [`getIdToken(...)`](#getidtoken) +* [`getRedirectResult()`](#getredirectresult) * [`getTenantId()`](#gettenantid) * [`isSignInWithEmailLink(...)`](#issigninwithemaillink) * [`linkWithApple(...)`](#linkwithapple) @@ -427,37 +428,37 @@ Applies a verification code sent to the user by email. -------------------- -### createUserWithEmailAndPassword(...) +### confirmPasswordReset(...) ```typescript -createUserWithEmailAndPassword(options: CreateUserWithEmailAndPasswordOptions) => Promise +confirmPasswordReset(options: ConfirmPasswordResetOptions) => Promise ``` -Creates a new user account with email and password. -If the new account was created, the user is signed in automatically. - -| Param | Type | -| ------------- | ------------------------------------------------------------------------------------------------------- | -| **`options`** | CreateUserWithEmailAndPasswordOptions | +Completes the password reset process. -**Returns:** Promise<SignInResult> +| Param | Type | +| ------------- | ----------------------------------------------------------------------------------- | +| **`options`** | ConfirmPasswordResetOptions | **Since:** 0.2.2 -------------------- -### confirmPasswordReset(...) +### createUserWithEmailAndPassword(...) ```typescript -confirmPasswordReset(options: ConfirmPasswordResetOptions) => Promise +createUserWithEmailAndPassword(options: CreateUserWithEmailAndPasswordOptions) => Promise ``` -Completes the password reset process. +Creates a new user account with email and password. +If the new account was created, the user is signed in automatically. -| Param | Type | -| ------------- | ----------------------------------------------------------------------------------- | -| **`options`** | ConfirmPasswordResetOptions | +| Param | Type | +| ------------- | ------------------------------------------------------------------------------------------------------- | +| **`options`** | CreateUserWithEmailAndPasswordOptions | + +**Returns:** Promise<SignInResult> **Since:** 0.2.2 @@ -498,6 +499,26 @@ Fetches the Firebase Auth ID Token for the currently signed-in user. -------------------- +### getRedirectResult() + +```typescript +getRedirectResult() => Promise +``` + +Returns the `SignInResult` from the redirect-based sign-in flow. + +If sign-in was unsuccessful, fails with an error. +If no redirect operation was called, returns a `SignInResult` with a null user. + +Only available for Web. + +**Returns:** Promise<SignInResult> + +**Since:** 1.3.0 + +-------------------- + + ### getTenantId() ```typescript @@ -901,14 +922,14 @@ Signs in as an anonymous user. ### signInWithApple(...) ```typescript -signInWithApple(options?: SignInWithOAuthOptions | SignInOptions | undefined) => Promise +signInWithApple(options?: SignInWithOAuthOptions | undefined) => Promise ``` Starts the Apple sign-in flow. -| Param | Type | -| ------------- | ----------------------------------------------------------------------------------------------------------------------- | -| **`options`** | SignInWithOAuthOptions \| SignInOptions | +| Param | Type | +| ------------- | ------------------------------------------------------------------------- | +| **`options`** | SignInWithOAuthOptions | **Returns:** Promise<SignInResult> @@ -980,14 +1001,14 @@ Signs in using an email and sign-in email link. ### signInWithFacebook(...) ```typescript -signInWithFacebook(options?: SignInWithOAuthOptions | SignInOptions | undefined) => Promise +signInWithFacebook(options?: SignInWithOAuthOptions | undefined) => Promise ``` Starts the Facebook sign-in flow. -| Param | Type | -| ------------- | ----------------------------------------------------------------------------------------------------------------------- | -| **`options`** | SignInWithOAuthOptions \| SignInOptions | +| Param | Type | +| ------------- | ------------------------------------------------------------------------- | +| **`options`** | SignInWithOAuthOptions | **Returns:** Promise<SignInResult> @@ -1020,14 +1041,14 @@ Only available for iOS. ### signInWithGithub(...) ```typescript -signInWithGithub(options?: SignInWithOAuthOptions | SignInOptions | undefined) => Promise +signInWithGithub(options?: SignInWithOAuthOptions | undefined) => Promise ``` Starts the GitHub sign-in flow. -| Param | Type | -| ------------- | ----------------------------------------------------------------------------------------------------------------------- | -| **`options`** | SignInWithOAuthOptions \| SignInOptions | +| Param | Type | +| ------------- | ------------------------------------------------------------------------- | +| **`options`** | SignInWithOAuthOptions | **Returns:** Promise<SignInResult> @@ -1039,14 +1060,14 @@ Starts the GitHub sign-in flow. ### signInWithGoogle(...) ```typescript -signInWithGoogle(options?: SignInWithOAuthOptions | SignInOptions | undefined) => Promise +signInWithGoogle(options?: SignInWithOAuthOptions | undefined) => Promise ``` Starts the Google sign-in flow. -| Param | Type | -| ------------- | ----------------------------------------------------------------------------------------------------------------------- | -| **`options`** | SignInWithOAuthOptions \| SignInOptions | +| Param | Type | +| ------------- | ------------------------------------------------------------------------- | +| **`options`** | SignInWithOAuthOptions | **Returns:** Promise<SignInResult> @@ -1058,14 +1079,14 @@ Starts the Google sign-in flow. ### signInWithMicrosoft(...) ```typescript -signInWithMicrosoft(options?: SignInWithOAuthOptions | SignInOptions | undefined) => Promise +signInWithMicrosoft(options?: SignInWithOAuthOptions | undefined) => Promise ``` Starts the Microsoft sign-in flow. -| Param | Type | -| ------------- | ----------------------------------------------------------------------------------------------------------------------- | -| **`options`** | SignInWithOAuthOptions \| SignInOptions | +| Param | Type | +| ------------- | ------------------------------------------------------------------------- | +| **`options`** | SignInWithOAuthOptions | **Returns:** Promise<SignInResult> @@ -1100,16 +1121,16 @@ Only available for Android and iOS. ### signInWithPlayGames(...) ```typescript -signInWithPlayGames(options?: SignInWithOAuthOptions | SignInOptions | undefined) => Promise +signInWithPlayGames(options?: SignInWithOAuthOptions | undefined) => Promise ``` Starts the Play Games sign-in flow. Only available for Android. -| Param | Type | -| ------------- | ----------------------------------------------------------------------------------------------------------------------- | -| **`options`** | SignInWithOAuthOptions \| SignInOptions | +| Param | Type | +| ------------- | ------------------------------------------------------------------------- | +| **`options`** | SignInWithOAuthOptions | **Returns:** Promise<SignInResult> @@ -1121,14 +1142,14 @@ Only available for Android. ### signInWithTwitter(...) ```typescript -signInWithTwitter(options?: SignInWithOAuthOptions | SignInOptions | undefined) => Promise +signInWithTwitter(options?: SignInWithOAuthOptions | undefined) => Promise ``` Starts the Twitter sign-in flow. -| Param | Type | -| ------------- | ----------------------------------------------------------------------------------------------------------------------- | -| **`options`** | SignInWithOAuthOptions \| SignInOptions | +| Param | Type | +| ------------- | ------------------------------------------------------------------------- | +| **`options`** | SignInWithOAuthOptions | **Returns:** Promise<SignInResult> @@ -1140,14 +1161,14 @@ Starts the Twitter sign-in flow. ### signInWithYahoo(...) ```typescript -signInWithYahoo(options?: SignInWithOAuthOptions | SignInOptions | undefined) => Promise +signInWithYahoo(options?: SignInWithOAuthOptions | undefined) => Promise ``` Starts the Yahoo sign-in flow. -| Param | Type | -| ------------- | ----------------------------------------------------------------------------------------------------------------------- | -| **`options`** | SignInWithOAuthOptions \| SignInOptions | +| Param | Type | +| ------------- | ------------------------------------------------------------------------- | +| **`options`** | SignInWithOAuthOptions | **Returns:** Promise<SignInResult> @@ -1295,6 +1316,14 @@ Remove all listeners for this plugin. | **`oobCode`** | string | A verification code sent to the user. | 0.2.2 | +#### ConfirmPasswordResetOptions + +| Prop | Type | Description | Since | +| ----------------- | ------------------- | ------------------------------------- | ----- | +| **`oobCode`** | string | A verification code sent to the user. | 0.2.2 | +| **`newPassword`** | string | The new password. | 0.2.2 | + + #### SignInResult | Prop | Type | Description | Since | @@ -1349,14 +1378,6 @@ Remove all listeners for this plugin. | **`password`** | string | 0.2.2 | -#### ConfirmPasswordResetOptions - -| Prop | Type | Description | Since | -| ----------------- | ------------------- | ------------------------------------- | ----- | -| **`oobCode`** | string | A verification code sent to the user. | 0.2.2 | -| **`newPassword`** | string | The new password. | 0.2.2 | - - #### GetCurrentUserResult | Prop | Type | Description | Since | @@ -1401,10 +1422,11 @@ Remove all listeners for this plugin. #### SignInWithOAuthOptions -| Prop | Type | Description | Since | -| ---------------------- | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | -| **`customParameters`** | SignInCustomParameter[] | Configures custom parameters to be passed to the identity provider during the OAuth sign-in flow. Supports Apple, Facebook, GitHub, Google, Microsoft, Twitter and Yahoo on Web. Supports Apple, GitHub, Microsoft, Twitter and Yahoo on Android. Supports Facebook, GitHub, Microsoft, Twitter and Yahoo on iOS. | 1.1.0 | -| **`scopes`** | string[] | Scopes to request from provider. Supports Apple, Facebook, GitHub, Google, Microsoft, Twitter and Yahoo on Web. Supports Apple, GitHub, Microsoft, Twitter, Yahoo and Play Games on Android. Supports Facebook, GitHub, Google, Microsoft, Twitter and Yahoo on iOS. | 1.1.0 | +| Prop | Type | Description | Default | Since | +| ---------------------- | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | ----- | +| **`customParameters`** | SignInCustomParameter[] | Configures custom parameters to be passed to the identity provider during the OAuth sign-in flow. Supports Apple, Facebook, GitHub, Google, Microsoft, Twitter and Yahoo on Web. Supports Apple, GitHub, Microsoft, Twitter and Yahoo on Android. Supports Facebook, GitHub, Microsoft, Twitter and Yahoo on iOS. | | 1.1.0 | +| **`mode`** | 'popup' \| 'redirect' | Whether to use the popup-based OAuth authentication flow or the full-page redirect flow. If you choose `redirect`, you will get the result of the call via the `authStateChange` listener after the redirect. | 'popup' | 1.3.0 | +| **`scopes`** | string[] | Scopes to request from provider. Supports Apple, Facebook, GitHub, Google, Microsoft, Twitter and Yahoo on Web. Supports Apple, GitHub, Microsoft, Twitter, Yahoo and Play Games on Android. Supports Facebook, GitHub, Google, Microsoft, Twitter and Yahoo on iOS. | | 1.1.0 | #### SignInCustomParameter @@ -1481,15 +1503,6 @@ bundle identifiers. | **`tenantId`** | string | The tenant id. | 1.1.0 | -#### SignInOptions - -| Prop | Type | Description | Since | -| ---------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----- | -| **`customParameters`** | SignInCustomParameter[] | Configures custom parameters to be passed to the identity provider during the OAuth sign-in flow. | 0.1.0 | -| **`scopes`** | string[] | Scopes to request from provider. | 0.3.1 | -| **`skipNativeAuth`** | boolean | Whether the plugin should skip the native authentication or not. Only needed if you want to use the Firebase JavaScript SDK. This value overwrites the configrations value of the `skipNativeAuth` option. If no value is set, the configuration value is used. **Note that the plugin may behave differently across the platforms.** `skipNativeAuth` cannot be used in combination with `signInWithCustomToken`, `createUserWithEmailAndPassword` or `signInWithEmailAndPassword`. Only available for Android and iOS. | 1.1.0 | - - #### SignInWithCustomTokenOptions | Prop | Type | Description | Since | @@ -1513,6 +1526,15 @@ bundle identifiers. | **`emailLink`** | string | The link sent to the user's email address. | 1.1.0 | +#### SignInOptions + +| Prop | Type | Description | Since | +| ---------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----- | +| **`customParameters`** | SignInCustomParameter[] | Configures custom parameters to be passed to the identity provider during the OAuth sign-in flow. | 0.1.0 | +| **`scopes`** | string[] | Scopes to request from provider. | 0.3.1 | +| **`skipNativeAuth`** | boolean | Whether the plugin should skip the native authentication or not. Only needed if you want to use the Firebase JavaScript SDK. This value overwrites the configrations value of the `skipNativeAuth` option. If no value is set, the configuration value is used. **Note that the plugin may behave differently across the platforms.** `skipNativeAuth` cannot be used in combination with `signInWithCustomToken`, `createUserWithEmailAndPassword` or `signInWithEmailAndPassword`. Only available for Android and iOS. | 1.1.0 | + + #### SignInWithPhoneNumberResult | Prop | Type | Description | Since | diff --git a/packages/authentication/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/authentication/FirebaseAuthenticationPlugin.java b/packages/authentication/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/authentication/FirebaseAuthenticationPlugin.java index 37e461be..3058c3f2 100644 --- a/packages/authentication/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/authentication/FirebaseAuthenticationPlugin.java +++ b/packages/authentication/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/authentication/FirebaseAuthenticationPlugin.java @@ -66,15 +66,6 @@ public void applyActionCode(PluginCall call) { } } - @PluginMethod - public void createUserWithEmailAndPassword(PluginCall call) { - try { - implementation.createUserWithEmailAndPassword(call); - } catch (Exception ex) { - call.reject(ex.getLocalizedMessage()); - } - } - @PluginMethod public void confirmPasswordReset(PluginCall call) { try { @@ -94,6 +85,15 @@ public void confirmPasswordReset(PluginCall call) { } } + @PluginMethod + public void createUserWithEmailAndPassword(PluginCall call) { + try { + implementation.createUserWithEmailAndPassword(call); + } catch (Exception ex) { + call.reject(ex.getLocalizedMessage()); + } + } + @PluginMethod public void getCurrentUser(PluginCall call) { try { @@ -133,6 +133,11 @@ public void error(String message) { } } + @PluginMethod + public void getRedirectResult(PluginCall call) { + call.reject("Not available on Android."); + } + @PluginMethod public void getTenantId(PluginCall call) { try { diff --git a/packages/authentication/ios/Plugin/FirebaseAuthentication.swift b/packages/authentication/ios/Plugin/FirebaseAuthentication.swift index 73295906..8adb89f5 100644 --- a/packages/authentication/ios/Plugin/FirebaseAuthentication.swift +++ b/packages/authentication/ios/Plugin/FirebaseAuthentication.swift @@ -5,6 +5,7 @@ import FirebaseAuth public typealias AuthStateChangedObserver = () -> Void +// swiftlint:disable type_body_length @objc public class FirebaseAuthentication: NSObject { public var authStateObserver: AuthStateChangedObserver? private let plugin: FirebaseAuthenticationPlugin diff --git a/packages/authentication/ios/Plugin/FirebaseAuthenticationPlugin.m b/packages/authentication/ios/Plugin/FirebaseAuthenticationPlugin.m index cdb8b1db..d935e756 100644 --- a/packages/authentication/ios/Plugin/FirebaseAuthenticationPlugin.m +++ b/packages/authentication/ios/Plugin/FirebaseAuthenticationPlugin.m @@ -5,10 +5,11 @@ // each method the plugin supports using the CAP_PLUGIN_METHOD macro. CAP_PLUGIN(FirebaseAuthenticationPlugin, "FirebaseAuthentication", CAP_PLUGIN_METHOD(applyActionCode, CAPPluginReturnPromise); - CAP_PLUGIN_METHOD(createUserWithEmailAndPassword, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(confirmPasswordReset, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(createUserWithEmailAndPassword, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(getCurrentUser, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(getIdToken, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(getRedirectResult, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(getTenantId, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(isSignInWithEmailLink, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(linkWithApple, CAPPluginReturnPromise); diff --git a/packages/authentication/ios/Plugin/FirebaseAuthenticationPlugin.swift b/packages/authentication/ios/Plugin/FirebaseAuthenticationPlugin.swift index 6bc7bfdc..3a6b547e 100644 --- a/packages/authentication/ios/Plugin/FirebaseAuthenticationPlugin.swift +++ b/packages/authentication/ios/Plugin/FirebaseAuthenticationPlugin.swift @@ -57,10 +57,6 @@ public class FirebaseAuthenticationPlugin: CAPPlugin { }) } - @objc func createUserWithEmailAndPassword(_ call: CAPPluginCall) { - implementation?.createUserWithEmailAndPassword(call) - } - @objc func confirmPasswordReset(_ call: CAPPluginCall) { guard let oobCode = call.getString("oobCode") else { call.reject(errorOobCodeMissing) @@ -80,6 +76,10 @@ public class FirebaseAuthenticationPlugin: CAPPlugin { }) } + @objc func createUserWithEmailAndPassword(_ call: CAPPluginCall) { + implementation?.createUserWithEmailAndPassword(call) + } + @objc func getCurrentUser(_ call: CAPPluginCall) { let user = implementation?.getCurrentUser() let userResult = FirebaseAuthenticationHelper.createUserResult(user) @@ -102,6 +102,10 @@ public class FirebaseAuthenticationPlugin: CAPPlugin { }) } + @objc func getRedirectResult(_ call: CAPPluginCall) { + call.reject("Not available on iOS.") + } + @objc func getTenantId(_ call: CAPPluginCall) { var result = JSObject() result["tenantId"] = implementation?.getTenantId() diff --git a/packages/authentication/ios/Plugin/Handlers/FacebookAuthProviderHandler.swift b/packages/authentication/ios/Plugin/Handlers/FacebookAuthProviderHandler.swift index f849517a..be41b942 100644 --- a/packages/authentication/ios/Plugin/Handlers/FacebookAuthProviderHandler.swift +++ b/packages/authentication/ios/Plugin/Handlers/FacebookAuthProviderHandler.swift @@ -6,8 +6,8 @@ import FBSDKLoginKit #endif class FacebookAuthProviderHandler: NSObject { - public let errorSignInCanceled = "Sign in canceled." - public let errorLinkCanceled = "Link canceled." + let errorSignInCanceled = "Sign in canceled." + let errorLinkCanceled = "Link canceled." private var pluginImplementation: FirebaseAuthentication #if RGCFA_INCLUDE_FACEBOOK private var loginManager: LoginManager diff --git a/packages/authentication/ios/Plugin/Handlers/GameCenterAuthProviderHandler.swift b/packages/authentication/ios/Plugin/Handlers/GameCenterAuthProviderHandler.swift index 8323de6a..35d61dc1 100644 --- a/packages/authentication/ios/Plugin/Handlers/GameCenterAuthProviderHandler.swift +++ b/packages/authentication/ios/Plugin/Handlers/GameCenterAuthProviderHandler.swift @@ -6,7 +6,7 @@ import AuthenticationServices import GameKit class GameCenterAuthProviderHandler: NSObject { - public let errorGetCredentialFailed = "getCredential failed." + let errorGetCredentialFailed = "getCredential failed." private var pluginImplementation: FirebaseAuthentication init(_ pluginImplementation: FirebaseAuthentication) { diff --git a/packages/authentication/src/definitions.ts b/packages/authentication/src/definitions.ts index 1b562824..e54f4e98 100644 --- a/packages/authentication/src/definitions.ts +++ b/packages/authentication/src/definitions.ts @@ -47,6 +47,12 @@ export interface FirebaseAuthenticationPlugin { * @since 0.2.2 */ applyActionCode(options: ApplyActionCodeOptions): Promise; + /** + * Completes the password reset process. + * + * @since 0.2.2 + */ + confirmPasswordReset(options: ConfirmPasswordResetOptions): Promise; /** * Creates a new user account with email and password. * If the new account was created, the user is signed in automatically. @@ -56,12 +62,6 @@ export interface FirebaseAuthenticationPlugin { createUserWithEmailAndPassword( options: CreateUserWithEmailAndPasswordOptions, ): Promise; - /** - * Completes the password reset process. - * - * @since 0.2.2 - */ - confirmPasswordReset(options: ConfirmPasswordResetOptions): Promise; /** * Fetches the currently signed-in user. * @@ -74,6 +74,17 @@ export interface FirebaseAuthenticationPlugin { * @since 0.1.0 */ getIdToken(options?: GetIdTokenOptions): Promise; + /** + * Returns the `SignInResult` from the redirect-based sign-in flow. + * + * If sign-in was unsuccessful, fails with an error. + * If no redirect operation was called, returns a `SignInResult` with a null user. + * + * Only available for Web. + * + * @since 1.3.0 + */ + getRedirectResult(): Promise; /** * Get the tenant id. * @@ -245,9 +256,7 @@ export interface FirebaseAuthenticationPlugin { * * @since 0.1.0 */ - signInWithApple( - options?: SignInOptions | SignInWithOAuthOptions, - ): Promise; + signInWithApple(options?: SignInWithOAuthOptions): Promise; /** * Starts the Custom Token sign-in flow. * @@ -280,9 +289,7 @@ export interface FirebaseAuthenticationPlugin { * * @since 0.1.0 */ - signInWithFacebook( - options?: SignInOptions | SignInWithOAuthOptions, - ): Promise; + signInWithFacebook(options?: SignInWithOAuthOptions): Promise; /** * Starts the Game Center sign-in flow. * @@ -298,25 +305,19 @@ export interface FirebaseAuthenticationPlugin { * * @since 0.1.0 */ - signInWithGithub( - options?: SignInOptions | SignInWithOAuthOptions, - ): Promise; + signInWithGithub(options?: SignInWithOAuthOptions): Promise; /** * Starts the Google sign-in flow. * * @since 0.1.0 */ - signInWithGoogle( - options?: SignInOptions | SignInWithOAuthOptions, - ): Promise; + signInWithGoogle(options?: SignInWithOAuthOptions): Promise; /** * Starts the Microsoft sign-in flow. * * @since 0.1.0 */ - signInWithMicrosoft( - options?: SignInOptions | SignInWithOAuthOptions, - ): Promise; + signInWithMicrosoft(options?: SignInWithOAuthOptions): Promise; /** * Starts the sign-in flow using a phone number. * @@ -336,25 +337,19 @@ export interface FirebaseAuthenticationPlugin { * * @since 0.1.0 */ - signInWithPlayGames( - options?: SignInOptions | SignInWithOAuthOptions, - ): Promise; + signInWithPlayGames(options?: SignInWithOAuthOptions): Promise; /** * Starts the Twitter sign-in flow. * * @since 0.1.0 */ - signInWithTwitter( - options?: SignInOptions | SignInWithOAuthOptions, - ): Promise; + signInWithTwitter(options?: SignInWithOAuthOptions): Promise; /** * Starts the Yahoo sign-in flow. * * @since 0.1.0 */ - signInWithYahoo( - options?: SignInOptions | SignInWithOAuthOptions, - ): Promise; + signInWithYahoo(options?: SignInWithOAuthOptions): Promise; /** * Starts the sign-out flow. * @@ -678,6 +673,14 @@ export interface SignInWithOAuthOptions extends SignInOptions { * @since 1.1.0 */ customParameters?: SignInCustomParameter[]; + /** + * Whether to use the popup-based OAuth authentication flow or the full-page redirect flow. + * If you choose `redirect`, you will get the result of the call via the `authStateChange` listener after the redirect. + * + * @default 'popup' + * @since 1.3.0 + */ + mode?: 'popup' | 'redirect'; /** * Scopes to request from provider. * diff --git a/packages/authentication/src/web.ts b/packages/authentication/src/web.ts index e9d95044..75935ea7 100644 --- a/packages/authentication/src/web.ts +++ b/packages/authentication/src/web.ts @@ -1,11 +1,17 @@ import { WebPlugin } from '@capacitor/core'; import type { AuthCredential as FirebaseAuthCredential, - CustomParameters, + AuthProvider as FirebaseAuthProvider, + CustomParameters as FirebaseCustomParameters, User as FirebaseUser, UserCredential as FirebaseUserCredential, } from 'firebase/auth'; import { + getRedirectResult, + linkWithPopup, + linkWithRedirect, + signInWithPopup, + signInWithRedirect, applyActionCode, confirmPasswordReset, connectAuthEmulator, @@ -18,7 +24,6 @@ import { GoogleAuthProvider, isSignInWithEmailLink, linkWithCredential, - linkWithPopup, OAuthCredential, OAuthProvider, sendEmailVerification, @@ -28,7 +33,6 @@ import { signInWithCustomToken, signInWithEmailAndPassword, signInWithEmailLink, - signInWithPopup, TwitterAuthProvider, unlink, updateEmail, @@ -63,6 +67,7 @@ import type { SignInWithCustomTokenOptions, SignInWithEmailAndPasswordOptions, SignInWithEmailLinkOptions, + SignInWithOAuthOptions, SignInWithPhoneNumberOptions, UnlinkOptions, UnlinkResult, @@ -132,6 +137,15 @@ export class FirebaseAuthenticationWeb return result; } + public async getRedirectResult(): Promise { + const auth = getAuth(); + const userCredential = await getRedirectResult(auth); + const authCredential = userCredential + ? OAuthProvider.credentialFromResult(userCredential) + : null; + return this.createSignInResult(userCredential, authCredential); + } + public async getTenantId(): Promise { const auth = getAuth(); return { @@ -151,13 +165,12 @@ export class FirebaseAuthenticationWeb public async linkWithApple( options?: LinkWithOAuthOptions, ): Promise { - const auth = getAuth(); - if (!auth.currentUser) { - throw new Error(FirebaseAuthenticationWeb.ERROR_NO_USER_SIGNED_IN); - } const provider = new OAuthProvider(ProviderId.APPLE); this.applySignInOptions(options || {}, provider); - const userCredential = await linkWithPopup(auth.currentUser, provider); + const userCredential = await this.linkCurrentUserWithPopupOrRedirect( + provider, + options?.mode, + ); const authCredential = OAuthProvider.credentialFromResult(userCredential); return this.createSignInResult(userCredential, authCredential); } @@ -165,16 +178,11 @@ export class FirebaseAuthenticationWeb public async linkWithEmailAndPassword( options: LinkWithEmailAndPasswordOptions, ): Promise { - const auth = getAuth(); - if (!auth.currentUser) { - throw new Error(FirebaseAuthenticationWeb.ERROR_NO_USER_SIGNED_IN); - } const authCredential = EmailAuthProvider.credential( options.email, options.password, ); - const userCredential = await linkWithCredential( - auth.currentUser, + const userCredential = await this.linkCurrentUserWithCredential( authCredential, ); return this.createSignInResult(userCredential, authCredential); @@ -183,16 +191,11 @@ export class FirebaseAuthenticationWeb public async linkWithEmailLink( options: LinkWithEmailLinkOptions, ): Promise { - const auth = getAuth(); - if (!auth.currentUser) { - throw new Error(FirebaseAuthenticationWeb.ERROR_NO_USER_SIGNED_IN); - } const authCredential = EmailAuthProvider.credentialWithLink( options.email, options.emailLink, ); - const userCredential = await linkWithCredential( - auth.currentUser, + const userCredential = await this.linkCurrentUserWithCredential( authCredential, ); return this.createSignInResult(userCredential, authCredential); @@ -201,13 +204,12 @@ export class FirebaseAuthenticationWeb public async linkWithFacebook( options?: LinkWithOAuthOptions, ): Promise { - const auth = getAuth(); - if (!auth.currentUser) { - throw new Error(FirebaseAuthenticationWeb.ERROR_NO_USER_SIGNED_IN); - } const provider = new FacebookAuthProvider(); this.applySignInOptions(options || {}, provider); - const userCredential = await linkWithPopup(auth.currentUser, provider); + const userCredential = await this.linkCurrentUserWithPopupOrRedirect( + provider, + options?.mode, + ); const authCredential = FacebookAuthProvider.credentialFromResult(userCredential); return this.createSignInResult(userCredential, authCredential); @@ -220,13 +222,12 @@ export class FirebaseAuthenticationWeb public async linkWithGithub( options?: LinkWithOAuthOptions, ): Promise { - const auth = getAuth(); - if (!auth.currentUser) { - throw new Error(FirebaseAuthenticationWeb.ERROR_NO_USER_SIGNED_IN); - } const provider = new GithubAuthProvider(); this.applySignInOptions(options || {}, provider); - const userCredential = await linkWithPopup(auth.currentUser, provider); + const userCredential = await this.linkCurrentUserWithPopupOrRedirect( + provider, + options?.mode, + ); const authCredential = GithubAuthProvider.credentialFromResult(userCredential); return this.createSignInResult(userCredential, authCredential); @@ -235,13 +236,12 @@ export class FirebaseAuthenticationWeb public async linkWithGoogle( options?: LinkWithOAuthOptions, ): Promise { - const auth = getAuth(); - if (!auth.currentUser) { - throw new Error(FirebaseAuthenticationWeb.ERROR_NO_USER_SIGNED_IN); - } const provider = new GoogleAuthProvider(); this.applySignInOptions(options || {}, provider); - const userCredential = await linkWithPopup(auth.currentUser, provider); + const userCredential = await this.linkCurrentUserWithPopupOrRedirect( + provider, + options?.mode, + ); const authCredential = GoogleAuthProvider.credentialFromResult(userCredential); return this.createSignInResult(userCredential, authCredential); @@ -250,13 +250,12 @@ export class FirebaseAuthenticationWeb public async linkWithMicrosoft( options?: LinkWithOAuthOptions, ): Promise { - const auth = getAuth(); - if (!auth.currentUser) { - throw new Error(FirebaseAuthenticationWeb.ERROR_NO_USER_SIGNED_IN); - } const provider = new OAuthProvider(ProviderId.MICROSOFT); this.applySignInOptions(options || {}, provider); - const userCredential = await linkWithPopup(auth.currentUser, provider); + const userCredential = await this.linkCurrentUserWithPopupOrRedirect( + provider, + options?.mode, + ); const authCredential = OAuthProvider.credentialFromResult(userCredential); return this.createSignInResult(userCredential, authCredential); } @@ -274,13 +273,12 @@ export class FirebaseAuthenticationWeb public async linkWithTwitter( options?: LinkWithOAuthOptions, ): Promise { - const auth = getAuth(); - if (!auth.currentUser) { - throw new Error(FirebaseAuthenticationWeb.ERROR_NO_USER_SIGNED_IN); - } const provider = new TwitterAuthProvider(); this.applySignInOptions(options || {}, provider); - const userCredential = await linkWithPopup(auth.currentUser, provider); + const userCredential = await this.linkCurrentUserWithPopupOrRedirect( + provider, + options?.mode, + ); const authCredential = TwitterAuthProvider.credentialFromResult(userCredential); return this.createSignInResult(userCredential, authCredential); @@ -289,13 +287,12 @@ export class FirebaseAuthenticationWeb public async linkWithYahoo( options?: LinkWithOAuthOptions, ): Promise { - const auth = getAuth(); - if (!auth.currentUser) { - throw new Error(FirebaseAuthenticationWeb.ERROR_NO_USER_SIGNED_IN); - } const provider = new OAuthProvider(ProviderId.YAHOO); this.applySignInOptions(options || {}, provider); - const userCredential = await linkWithPopup(auth.currentUser, provider); + const userCredential = await this.linkCurrentUserWithPopupOrRedirect( + provider, + options?.mode, + ); const authCredential = OAuthProvider.credentialFromResult(userCredential); return this.createSignInResult(userCredential, authCredential); } @@ -343,11 +340,15 @@ export class FirebaseAuthenticationWeb auth.tenantId = options.tenantId; } - public async signInWithApple(options?: SignInOptions): Promise { + public async signInWithApple( + options?: SignInWithOAuthOptions, + ): Promise { const provider = new OAuthProvider(ProviderId.APPLE); this.applySignInOptions(options || {}, provider); - const auth = getAuth(); - const userCredential = await signInWithPopup(auth, provider); + const userCredential = await this.signInWithPopupOrRedirect( + provider, + options?.mode, + ); const authCredential = OAuthProvider.credentialFromResult(userCredential); return this.createSignInResult(userCredential, authCredential); } @@ -385,48 +386,56 @@ export class FirebaseAuthenticationWeb } public async signInWithFacebook( - options?: SignInOptions, + options?: SignInWithOAuthOptions, ): Promise { const provider = new FacebookAuthProvider(); this.applySignInOptions(options || {}, provider); - const auth = getAuth(); - const userCredential = await signInWithPopup(auth, provider); + const userCredential = await this.signInWithPopupOrRedirect( + provider, + options?.mode, + ); const authCredential = FacebookAuthProvider.credentialFromResult(userCredential); return this.createSignInResult(userCredential, authCredential); } public async signInWithGithub( - options?: SignInOptions, + options?: SignInWithOAuthOptions, ): Promise { const provider = new GithubAuthProvider(); this.applySignInOptions(options || {}, provider); - const auth = getAuth(); - const userCredential = await signInWithPopup(auth, provider); + const userCredential = await this.signInWithPopupOrRedirect( + provider, + options?.mode, + ); const authCredential = GithubAuthProvider.credentialFromResult(userCredential); return this.createSignInResult(userCredential, authCredential); } public async signInWithGoogle( - options?: SignInOptions, + options?: SignInWithOAuthOptions, ): Promise { const provider = new GoogleAuthProvider(); this.applySignInOptions(options || {}, provider); - const auth = getAuth(); - const userCredential = await signInWithPopup(auth, provider); + const userCredential = await this.signInWithPopupOrRedirect( + provider, + options?.mode, + ); const authCredential = GoogleAuthProvider.credentialFromResult(userCredential); return this.createSignInResult(userCredential, authCredential); } public async signInWithMicrosoft( - options?: SignInOptions, + options?: SignInWithOAuthOptions, ): Promise { const provider = new OAuthProvider(ProviderId.MICROSOFT); this.applySignInOptions(options || {}, provider); - const auth = getAuth(); - const userCredential = await signInWithPopup(auth, provider); + const userCredential = await this.signInWithPopupOrRedirect( + provider, + options?.mode, + ); const authCredential = OAuthProvider.credentialFromResult(userCredential); return this.createSignInResult(userCredential, authCredential); } @@ -446,22 +455,28 @@ export class FirebaseAuthenticationWeb } public async signInWithTwitter( - options?: SignInOptions, + options?: SignInWithOAuthOptions, ): Promise { const provider = new TwitterAuthProvider(); this.applySignInOptions(options || {}, provider); - const auth = getAuth(); - const userCredential = await signInWithPopup(auth, provider); + const userCredential = await this.signInWithPopupOrRedirect( + provider, + options?.mode, + ); const authCredential = TwitterAuthProvider.credentialFromResult(userCredential); return this.createSignInResult(userCredential, authCredential); } - public async signInWithYahoo(options?: SignInOptions): Promise { + public async signInWithYahoo( + options?: SignInWithOAuthOptions, + ): Promise { const provider = new OAuthProvider(ProviderId.YAHOO); this.applySignInOptions(options || {}, provider); - const auth = getAuth(); - const userCredential = await signInWithPopup(auth, provider); + const userCredential = await this.signInWithPopupOrRedirect( + provider, + options?.mode, + ); const authCredential = OAuthProvider.credentialFromResult(userCredential); return this.createSignInResult(userCredential, authCredential); } @@ -526,7 +541,7 @@ export class FirebaseAuthenticationWeb provider: OAuthProvider | GoogleAuthProvider | FacebookAuthProvider, ) { if (options.customParameters) { - const customParameters: CustomParameters = {}; + const customParameters: FirebaseCustomParameters = {}; options.customParameters.map(parameter => { customParameters[parameter.key] = parameter.value; }); @@ -539,11 +554,48 @@ export class FirebaseAuthenticationWeb } } + public signInWithPopupOrRedirect( + provider: FirebaseAuthProvider, + mode?: 'popup' | 'redirect', + ): Promise { + const auth = getAuth(); + if (mode === 'redirect') { + return signInWithRedirect(auth, provider); + } else { + return signInWithPopup(auth, provider); + } + } + + public linkCurrentUserWithPopupOrRedirect( + provider: FirebaseAuthProvider, + mode?: 'popup' | 'redirect', + ): Promise { + const auth = getAuth(); + if (!auth.currentUser) { + throw new Error(FirebaseAuthenticationWeb.ERROR_NO_USER_SIGNED_IN); + } + if (mode === 'redirect') { + return linkWithRedirect(auth.currentUser, provider); + } else { + return linkWithPopup(auth.currentUser, provider); + } + } + + public linkCurrentUserWithCredential( + credential: FirebaseAuthCredential, + ): Promise { + const auth = getAuth(); + if (!auth.currentUser) { + throw new Error(FirebaseAuthenticationWeb.ERROR_NO_USER_SIGNED_IN); + } + return linkWithCredential(auth.currentUser, credential); + } + private createSignInResult( - userCredential: FirebaseUserCredential, + userCredential: FirebaseUserCredential | null, authCredential: FirebaseAuthCredential | null, ): SignInResult { - const userResult = this.createUserResult(userCredential.user); + const userResult = this.createUserResult(userCredential?.user || null); const credentialResult = this.createCredentialResult(authCredential); const additionalUserInfoResult = this.createAdditionalUserInfoResult(userCredential); @@ -591,8 +643,11 @@ export class FirebaseAuthenticationWeb } private createAdditionalUserInfoResult( - credential: FirebaseUserCredential, + credential: FirebaseUserCredential | null, ): AdditionalUserInfo | null { + if (!credential) { + return null; + } const additionalUserInfo = getAdditionalUserInfo(credential); if (!additionalUserInfo) { return null;