Skip to content
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

[WIP] WebOTP #7757

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
5 changes: 5 additions & 0 deletions common/api-review/auth.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ 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_NOT_RETRIEVED: "auth/web-otp-not-retrieved";
};

// @public
Expand Down Expand Up @@ -282,6 +283,9 @@ export interface Config {
// @public
export interface ConfirmationResult {
confirm(verificationCode: string): Promise<UserCredential>;
// (undocumented)
confirmed(auth: Auth): Promise<UserCredential>;
confirmWithWebOTP(auth: Auth, webOTPTimeoutSeconds: number): Promise<UserCredential | undefined>;
readonly verificationId: string;
}

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

// @public
Expand Down
22 changes: 22 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(auth, webOTPTimeoutSeconds)](./auth.confirmationresult.md#confirmationresultconfirmwithwebotp) | Automatically fetches a verification code from an SMS message. Then, calls (<!-- -->@<!-- -->link confirm(verificationCode)<!-- -->} to finish a phone number sign-in, link, or reauthentication. |

## ConfirmationResult.verificationId

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

```

## ConfirmationResult.confirmWithWebOTP()

Automatically fetches a verification code from an SMS message. Then, calls (<!-- -->@<!-- -->link confirm(verificationCode)<!-- -->} to finish a phone number sign-in, link, or reauthentication.

<b>Signature:</b>

```typescript
confirmWithWebOTP(auth: Auth, webOTPTimeoutSeconds: number): Promise<UserCredential | undefined>;
```

### Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| auth | [Auth](./auth.auth.md#auth_interface) | the current [Auth](./auth.auth.md#auth_interface) instance |
| webOTPTimeoutSeconds | number | Error would be thrown if WebOTP does not resolve within this specified timeout parameter (in seconds). |

<b>Returns:</b>

Promise&lt;[UserCredential](./auth.usercredential.md#usercredential_interface) \| undefined&gt;

41 changes: 41 additions & 0 deletions docs-devsite/auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Firebase Authentication
| [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, webOTPTimeoutSeconds)](./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 @@ -939,6 +940,45 @@ const credential = await confirmationResult.confirm(verificationCode);

```

## signInWithPhoneNumber()

Asynchronously signs in using a phone number.

This method sends a code via SMS to the given phone number. Then, the method will try to autofill the SMS code for the user and sign the user in. A [UserCredential](./auth.usercredential.md#usercredential_interface) is then returned if the process is successful. If the process failed, is thrown.

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.

This method does not work in a Node.js environment.

<b>Signature:</b>

```typescript
export declare function signInWithPhoneNumber(auth: Auth, phoneNumber: string, appVerifier: ApplicationVerifier, webOTPTimeoutSeconds: number): Promise<UserCredential>;
```

### Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| 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)<!-- -->. |
| webOTPTimeoutSeconds | number | |

<b>Returns:</b>

Promise&lt;[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 userCredential = await signInWithPhoneNumber(auth, phoneNumber, applicationVerifier, 10);

```

## signInWithPopup()

Authenticates a Firebase client using a popup-based OAuth authentication flow.
Expand Down Expand Up @@ -1922,6 +1962,7 @@ AUTH_ERROR_CODES_MAP_DO_NOT_USE_INTERNALLY: {
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_NOT_RETRIEVED: "auth/web-otp-not-retrieved";
}
```

Expand Down
49 changes: 46 additions & 3 deletions docs-devsite/auth.phoneauthprovider.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@ export declare class PhoneAuthProvider

| Method | Modifiers | Description |
| --- | --- | --- |
| [credential(verificationId, verificationCode)](./auth.phoneauthprovider.md#phoneauthprovidercredential) | <code>static</code> | Creates a phone auth credential, given the verification ID from [PhoneAuthProvider.verifyPhoneNumber()](./auth.phoneauthprovider.md#phoneauthproviderverifyphonenumber) and the code that was sent to the user's mobile device. |
| [credential(verificationId, verificationCode)](./auth.phoneauthprovider.md#phoneauthprovidercredential) | <code>static</code> | Creates a phone auth credential, given the verification ID from and the code that was sent to the user's mobile device. |
| [credentialFromError(error)](./auth.phoneauthprovider.md#phoneauthprovidercredentialfromerror) | <code>static</code> | Returns an [AuthCredential](./auth.authcredential.md#authcredential_class) when passed an error. |
| [credentialFromResult(userCredential)](./auth.phoneauthprovider.md#phoneauthprovidercredentialfromresult) | <code>static</code> | Generates an [AuthCredential](./auth.authcredential.md#authcredential_class) from a [UserCredential](./auth.usercredential.md#usercredential_interface)<!-- -->. |
| [verifyPhoneNumber(phoneOptions, applicationVerifier)](./auth.phoneauthprovider.md#phoneauthproviderverifyphonenumber) | | Starts a phone number authentication flow by sending a verification code to the given phone number. |
| [verifyPhoneNumber(phoneOptions, applicationVerifier, webOTPTimeoutSeconds)](./auth.phoneauthprovider.md#phoneauthproviderverifyphonenumber) | | Completes the phone number authentication flow by sending a verification code to the given phone number, automatically retrieving the verification code from the SMS message, and signing the user in. |

## PhoneAuthProvider.(constructor)

Expand Down Expand Up @@ -91,7 +92,7 @@ readonly providerId: "phone";

## PhoneAuthProvider.credential()

Creates a phone auth credential, given the verification ID from [PhoneAuthProvider.verifyPhoneNumber()](./auth.phoneauthprovider.md#phoneauthproviderverifyphonenumber) and the code that was sent to the user's mobile device.
Creates a phone auth credential, given the verification ID from and the code that was sent to the user's mobile device.

<b>Signature:</b>

Expand All @@ -103,7 +104,7 @@ static credential(verificationId: string, verificationCode: string): PhoneAuthCr

| Parameter | Type | Description |
| --- | --- | --- |
| verificationId | string | The verification ID returned from [PhoneAuthProvider.verifyPhoneNumber()](./auth.phoneauthprovider.md#phoneauthproviderverifyphonenumber)<!-- -->. |
| verificationId | string | The verification ID returned from . |
| verificationCode | string | The verification code sent to the user's mobile device. |

<b>Returns:</b>
Expand Down Expand Up @@ -242,6 +243,48 @@ const userCredential = confirmationResult.confirm(verificationCode);

```

## PhoneAuthProvider.verifyPhoneNumber()

Completes the phone number authentication flow by sending a verification code to the given phone number, automatically retrieving the verification code from the SMS message, and signing the user in.

<b>Signature:</b>

```typescript
verifyPhoneNumber(phoneOptions: PhoneInfoOptions | string, applicationVerifier: ApplicationVerifier, webOTPTimeoutSeconds: number): Promise<UserCredential>;
```

### Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| phoneOptions | [PhoneInfoOptions](./auth.md#phoneinfooptions) \| string | |
| applicationVerifier | [ApplicationVerifier](./auth.applicationverifier.md#applicationverifier_interface) | 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)<!-- -->. |
| webOTPTimeoutSeconds | number | Error would be thrown if WebOTP does not resolve within this specified timeout parameter (in seconds). |

<b>Returns:</b>

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

A Promise for a UserCredential.

### Example 1


```javascript
const provider = new PhoneAuthProvider(auth);
const userCredential = await provider.verifyPhoneNumber(phoneNumber, applicationVerifier, 10);

```

### Example 2

An alternative flow is provided using the `signInWithPhoneNumber` method.

```javascript
const userCredential = signInWithPhoneNumber(auth, phoneNumber, applicationVerifier, 10);

```

### Example


Expand Down
24 changes: 20 additions & 4 deletions packages/auth/demo/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -386,14 +386,30 @@
<input type="tel" id="signin-phone-number"
class="form-control" placeholder="Phone Number" />
<button class="btn btn-block btn-primary" id="signin-verify-phone-number">
Verify Phone Number
Sign in with Phone Number
</button>
<button class="btn btn-block btn-primary" id="signin-verify-phone-number-webotp">
Sign in with Phone Number (WebOTP)
</button>
<input type="text" id="signin-phone-verification-id"
class="form-control" placeholder="Phone Verification ID" />
<input type="text" id="signin-phone-verification-code"
class="form-control" placeholder="Phone Verification Code" />
<button class="btn btn-block btn-primary" id="signin-confirm-phone-verification">
Sign in by Confirming Phone Verification
Confirm confirmation result
</button>
</form>

<form class="form form-bordered no-submit">
<input type="tel" id="signin-phone-number-auth-provider"
class="form-control" placeholder="Phone Number" />
<button class="btn btn-block btn-primary" id="signin-verify-phone-number-auth-provider">
Verify Phone Number (PhoneAuthProvider)
</button>
<input type="text" id="signin-phone-verification-id-auth-provider"
class="form-control" placeholder="Phone Verification ID" />
<input type="text" id="signin-phone-verification-code-auth-provider"
class="form-control" placeholder="Phone Verification Code" />
<button class="btn btn-block btn-primary" id="signin-confirm-phone-verification-auth-provider">
Sign in (PhoneAuthProvider)
</button>
</form>

Expand Down
71 changes: 63 additions & 8 deletions packages/auth/demo/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,10 @@ import {
browserPopupRedirectResolver,
connectAuthEmulator,
initializeRecaptchaConfig,
revokeAccessToken,
AuthErrorCodes,
validatePassword,
revokeAccessToken
signInWithPhoneNumber
} from '@firebase/auth';

import { config } from './config';
Expand Down Expand Up @@ -119,6 +121,7 @@ const providersIcons = {
* Returns active user (currentUser or lastUser).
* @return {!firebase.User}
*/

function activeUser() {
const type = $('input[name=toggle-user-selection]:checked').val();
if (type === 'lastUser') {
Expand Down Expand Up @@ -714,21 +717,60 @@ function clearApplicationVerifier() {
}
}

async function onSignInVerifyPhoneNumber() {
const phoneNumber = $('#signin-phone-number').val();
// Clear existing reCAPTCHA as an existing reCAPTCHA could be targeted for a
// link/re-auth operation.
clearApplicationVerifier();
// Initialize a reCAPTCHA application verifier.
makeApplicationVerifier('signin-verify-phone-number');
signInWithPhoneNumber(auth, phoneNumber, applicationVerifier).then(
confirmationResult => {
window.confirmationResult = confirmationResult;
}
);
}

async function onSignInVerifyPhoneNumberWebOTP() {
const phoneNumber = $('#signin-phone-number').val();
// Clear existing reCAPTCHA as an existing reCAPTCHA could be targeted for a
// link/re-auth operation.
clearApplicationVerifier();
// Initialize a reCAPTCHA application verifier.
makeApplicationVerifier('signin-verify-phone-number');
await signInWithPhoneNumber(auth, phoneNumber, applicationVerifier).then(
confirmationResult => {
window.confirmationResult = confirmationResult;
confirmationResult
.confirmed(auth)
.then(onAuthUserCredentialSuccess, onAuthError);
}
);
}

function onSignInConfirmPhoneVerification() {
const verificationCode = $('#signin-phone-verification-code').val();
const confirmationResult = window.confirmationResult;
confirmationResult
.confirm(verificationCode)
.then(onAuthUserCredentialSuccess, onAuthError);
}

/**
* Sends a phone number verification code for sign-in.
*/
function onSignInVerifyPhoneNumber() {
const phoneNumber = $('#signin-phone-number').val();
async function onSignInVerifyPhoneNumberAuthProvider() {
const phoneNumber = $('#signin-phone-number-auth-provider').val();
const provider = new PhoneAuthProvider(auth);
// Clear existing reCAPTCHA as an existing reCAPTCHA could be targeted for a
// link/re-auth operation.
clearApplicationVerifier();
// Initialize a reCAPTCHA application verifier.
makeApplicationVerifier('signin-verify-phone-number');
makeApplicationVerifier('signin-verify-phone-number-auth-provider');
provider.verifyPhoneNumber(phoneNumber, applicationVerifier).then(
verificationId => {
clearApplicationVerifier();
$('#signin-phone-verification-id').val(verificationId);
$('#signin-phone-verification-id-auth-provider').val(verificationId);
alertSuccess('Phone verification sent!');
},
error => {
Expand All @@ -741,9 +783,11 @@ function onSignInVerifyPhoneNumber() {
/**
* Confirms a phone number verification for sign-in.
*/
function onSignInConfirmPhoneVerification() {
const verificationId = $('#signin-phone-verification-id').val();
const verificationCode = $('#signin-phone-verification-code').val();
function onSignInConfirmPhoneVerificationAuthProvider() {
const verificationId = $('#signin-phone-verification-id-auth-provider').val();
const verificationCode = $(
'#signin-phone-verification-code-auth-provider'
).val();
const credential = PhoneAuthProvider.credential(
verificationId,
verificationCode
Expand Down Expand Up @@ -2274,10 +2318,21 @@ function initApp() {
$('#sign-in-with-generic-idp-credential').click(
onSignInWithGenericIdPCredential
);

$('#signin-verify-phone-number').click(onSignInVerifyPhoneNumber);
$('#signin-verify-phone-number-webotp').click(
onSignInVerifyPhoneNumberWebOTP
);
$('#signin-confirm-phone-verification').click(
onSignInConfirmPhoneVerification
);
$('#signin-verify-phone-number-auth-provider').click(
onSignInVerifyPhoneNumberAuthProvider
);
$('#signin-confirm-phone-verification-auth-provider').click(
onSignInConfirmPhoneVerificationAuthProvider
);

// On enter click in verification code, complete phone sign-in. This prevents
// reCAPTCHA from being re-rendered (default behavior on enter).
$('#signin-phone-verification-code').keypress(e => {
Expand Down
Loading
Loading