Skip to content

Optimistic one leg flow with fallback #7488

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions common/api-review/auth.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,11 @@ export const AuthErrorCodes: {
readonly MISSING_RECAPTCHA_VERSION: "auth/missing-recaptcha-version";
readonly INVALID_RECAPTCHA_VERSION: "auth/invalid-recaptcha-version";
readonly INVALID_REQ_TYPE: "auth/invalid-req-type";
readonly WEB_OTP_TIMEOUT: "auth/web-otp-timeout";
readonly WEB_OTP_BROKEN: "auth/web-otp-broken";
readonly WEB_OTP_NOT_RETRIEVED: "auth/web-otp-not-retrieved";
readonly WEB_OTP_NOT_SUPPORTED: "auth/web-otp-not-supported";
readonly WEB_OTP_NOT_DEFINED: "auth/web-otp-not-defined";
};

// @public
Expand Down Expand Up @@ -281,6 +286,7 @@ export interface Config {
// @public
export interface ConfirmationResult {
confirm(verificationCode: string): Promise<UserCredential>;
confirmWithWebOTP(webOTPTimeout: number): Promise<UserCredential>;
readonly verificationId: string;
}

Expand Down Expand Up @@ -597,7 +603,7 @@ export class PhoneAuthProvider {
static readonly PHONE_SIGN_IN_METHOD: 'phone';
static readonly PROVIDER_ID: 'phone';
readonly providerId: "phone";
verifyPhoneNumber(phoneOptions: PhoneInfoOptions | string, applicationVerifier: ApplicationVerifier): Promise<string>;
verifyPhoneNumber(phoneOptions: PhoneInfoOptions | string, applicationVerifier: ApplicationVerifier): Promise<string | UserCredential>;
}

// @public
Expand Down Expand Up @@ -747,7 +753,7 @@ export function signInWithEmailAndPassword(auth: Auth, email: string, password:
export function signInWithEmailLink(auth: Auth, email: string, emailLink?: string): Promise<UserCredential>;

// @public
export function signInWithPhoneNumber(auth: Auth, phoneNumber: string, appVerifier: ApplicationVerifier): Promise<ConfirmationResult>;
export function signInWithPhoneNumber(auth: Auth, phoneNumber: string, appVerifier: ApplicationVerifier, useWebOTP?: boolean, webOTPTimeout?: number): Promise<ConfirmationResult | UserCredential>;

// @public
export function signInWithPopup(auth: Auth, provider: AuthProvider, resolver?: PopupRedirectResolver): Promise<UserCredential>;
Expand Down
21 changes: 21 additions & 0 deletions docs-devsite/auth.confirmationresult.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export interface ConfirmationResult
| Method | Description |
| --- | --- |
| [confirm(verificationCode)](./auth.confirmationresult.md#confirmationresultconfirm) | Finishes a phone number sign-in, link, or reauthentication. |
| [confirmWithWebOTP(webOTPTimeout)](./auth.confirmationresult.md#confirmationresultconfirmwithwebotp) | Automatically fetches the verification code from SMS message. Then, it calls confirm(verificationCode) to finish a phone number sign-in, link, or reauthentication. |

## ConfirmationResult.verificationId

Expand Down Expand Up @@ -72,3 +73,23 @@ const userCredential = await confirmationResult.confirm(verificationCode);

```

## ConfirmationResult.confirmWithWebOTP()

Automatically fetches the verification code from SMS message. Then, it calls confirm(verificationCode) to finish a phone number sign-in, link, or reauthentication.

<b>Signature:</b>

```typescript
confirmWithWebOTP(webOTPTimeout: number): Promise<UserCredential>;
```

### Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| webOTPTimeout | number | Error would be thrown if WebOTP does not resolve within this specified timeout parameter (milliseconds). |

<b>Returns:</b>

Promise&lt;[UserCredential](./auth.usercredential.md#usercredential_interface)<!-- -->&gt;

19 changes: 14 additions & 5 deletions docs-devsite/auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Firebase Authentication
| [signInWithCustomToken(auth, customToken)](./auth.md#signinwithcustomtoken) | Asynchronously signs in using a custom token. |
| [signInWithEmailAndPassword(auth, email, password)](./auth.md#signinwithemailandpassword) | Asynchronously signs in using an email and password. |
| [signInWithEmailLink(auth, email, emailLink)](./auth.md#signinwithemaillink) | Asynchronously signs in using an email and sign-in email link. |
| [signInWithPhoneNumber(auth, phoneNumber, appVerifier)](./auth.md#signinwithphonenumber) | Asynchronously signs in using a phone number. |
| [signInWithPhoneNumber(auth, phoneNumber, appVerifier, useWebOTP, webOTPTimeout)](./auth.md#signinwithphonenumber) | Asynchronously signs in using a phone number. |
| [signInWithPopup(auth, provider, resolver)](./auth.md#signinwithpopup) | Authenticates a Firebase client using a popup-based OAuth authentication flow. |
| [signInWithRedirect(auth, provider, resolver)](./auth.md#signinwithredirect) | Authenticates a Firebase client using a full-page redirect flow. |
| [signOut(auth)](./auth.md#signout) | Signs out the current user. |
Expand Down Expand Up @@ -878,7 +878,11 @@ if(isSignInWithEmailLink(auth, emailLink)) {

Asynchronously signs in using a phone number.

This method sends a code via SMS to the given phone number, and returns a [ConfirmationResult](./auth.confirmationresult.md#confirmationresult_interface)<!-- -->. After the user provides the code sent to their phone, call [ConfirmationResult.confirm()](./auth.confirmationresult.md#confirmationresultconfirm) with the code to sign the user in.
This method sends a code via SMS to the given phone number. It returns a [ConfirmationResult](./auth.confirmationresult.md#confirmationresult_interface) if is set to false. If is set to true, the method will return a [UserCredential](./auth.usercredential.md#usercredential_interface)<!-- -->.

If is set to `false`<!-- -->, webOTP autofill is disabled. After the user provides the code sent to their phone, call [ConfirmationResult.confirm()](./auth.confirmationresult.md#confirmationresultconfirm) with the code to sign the user in. [ConfirmationResult](./auth.confirmationresult.md#confirmationresult_interface) is returned.

If is set to `true`<!-- -->, webOTP autofill is enabled and the verfication code is automatically fetched, then [ConfirmationResult.confirm()](./auth.confirmationresult.md#confirmationresultconfirm) will be called with the code to sign the user in. [UserCredential](./auth.usercredential.md#usercredential_interface) is returned.

For abuse prevention, this method also requires a [ApplicationVerifier](./auth.applicationverifier.md#applicationverifier_interface)<!-- -->. This SDK includes a reCAPTCHA-based implementation, [RecaptchaVerifier](./auth.recaptchaverifier.md#recaptchaverifier_class)<!-- -->. This function can work on other platforms that do not support the [RecaptchaVerifier](./auth.recaptchaverifier.md#recaptchaverifier_class) (like React Native), but you need to use a third-party [ApplicationVerifier](./auth.applicationverifier.md#applicationverifier_interface) implementation.

Expand All @@ -887,7 +891,7 @@ This method does not work in a Node.js environment.
<b>Signature:</b>

```typescript
export declare function signInWithPhoneNumber(auth: Auth, phoneNumber: string, appVerifier: ApplicationVerifier): Promise<ConfirmationResult>;
export declare function signInWithPhoneNumber(auth: Auth, phoneNumber: string, appVerifier: ApplicationVerifier, useWebOTP?: boolean, webOTPTimeout?: number): Promise<ConfirmationResult | UserCredential>;
```

### Parameters
Expand All @@ -897,18 +901,23 @@ export declare function signInWithPhoneNumber(auth: Auth, phoneNumber: string, a
| auth | [Auth](./auth.auth.md#auth_interface) | The [Auth](./auth.auth.md#auth_interface) instance. |
| phoneNumber | string | The user's phone number in E.164 format (e.g. +16505550101). |
| appVerifier | [ApplicationVerifier](./auth.applicationverifier.md#applicationverifier_interface) | The [ApplicationVerifier](./auth.applicationverifier.md#applicationverifier_interface)<!-- -->. |
| useWebOTP | boolean | Specifies whether to use WebOTP autofill or not in the sign in |
| webOTPTimeout | number | Error would be thrown if WebOTP autofill is used and does not resolve within this specified timeout parameter (milliseconds). |

<b>Returns:</b>

Promise&lt;[ConfirmationResult](./auth.confirmationresult.md#confirmationresult_interface)<!-- -->&gt;
Promise&lt;[ConfirmationResult](./auth.confirmationresult.md#confirmationresult_interface) \| [UserCredential](./auth.usercredential.md#usercredential_interface)<!-- -->&gt;

### Example


```javascript
// 'recaptcha-container' is the ID of an element in the DOM.
const applicationVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container');
const confirmationResult = await signInWithPhoneNumber(auth, phoneNumber, applicationVerifier);
const credential = await signInWithPhoneNumber(auth, phoneNumber, applicationVerifier, true);

const applicationVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container');
const confirmationResult = await signInWithPhoneNumber(auth, phoneNumber, applicationVerifier, false);
// Obtain a verificationCode from the user.
const credential = await confirmationResult.confirm(verificationCode);

Expand Down
14 changes: 9 additions & 5 deletions packages/auth-compat/src/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { _isPopupRedirectSupported } from './platform';
import { CompatPopupRedirectResolver } from './popup_redirect';
import { User } from './user';
import {
convertConfirmationResult,
convertPhoneSignInPromiseResult,
convertCredential
} from './user_credential';
import { ReverseWrapper, Wrapper } from './wrap';
Expand Down Expand Up @@ -280,14 +280,18 @@ export class Auth
}
signInWithPhoneNumber(
phoneNumber: string,
applicationVerifier: compat.ApplicationVerifier
): Promise<compat.ConfirmationResult> {
return convertConfirmationResult(
applicationVerifier: compat.ApplicationVerifier,
useWebOTP = false,
webOTPTimeout = 30
): Promise<compat.ConfirmationResult | compat.UserCredential> {
return convertPhoneSignInPromiseResult(
this._delegate,
exp.signInWithPhoneNumber(
this._delegate,
phoneNumber,
applicationVerifier
applicationVerifier,
useWebOTP,
webOTPTimeout
)
);
}
Expand Down
32 changes: 32 additions & 0 deletions packages/auth-compat/src/user_credential.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,38 @@ export async function convertConfirmationResult(
};
}

export async function convertPhoneSignInPromiseResult(
auth: exp.Auth,
phoneSignInPromise: Promise<exp.ConfirmationResult | exp.UserCredential>
): Promise<compat.ConfirmationResult | compat.UserCredential> {
const result = await phoneSignInPromise;
const resultAsConfirmationResult = result as exp.ConfirmationResult;
if (resultAsConfirmationResult) {
return {
verificationId: resultAsConfirmationResult.verificationId,
confirm: (verificationCode: string) =>
convertCredential(
auth,
resultAsConfirmationResult.confirm(verificationCode)
)
};
} else {
const resultAsUserCredential = result as exp.UserCredential;
const operationType = resultAsUserCredential.operationType;
const user = resultAsUserCredential.user;
return {
operationType,
credential: credentialFromResponse(
resultAsUserCredential as exp.UserCredentialInternal
),
additionalUserInfo: exp.getAdditionalUserInfo(
resultAsUserCredential as exp.UserCredential
),
user: User.getOrCreate(user)
};
}
}

class MultiFactorResolver implements compat.MultiFactorResolver {
readonly auth: Auth;
constructor(
Expand Down
Loading