Skip to content

Mfa totp demoapp #6629

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

Merged
merged 2 commits into from
Oct 5, 2022
Merged
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
26 changes: 26 additions & 0 deletions common/api-review/auth.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -750,10 +750,36 @@ export function signOut(auth: Auth): Promise<void>;
export interface TotpMultiFactorAssertion extends MultiFactorAssertion {
}

// @public
export class TotpMultiFactorGenerator {
static assertionForEnrollment(secret: TotpSecret, oneTimePassword: string): TotpMultiFactorAssertion;
static assertionForSignIn(enrollmentId: string, oneTimePassword: string): TotpMultiFactorAssertion;
// Warning: (ae-forgotten-export) The symbol "FactorId" needs to be exported by the entry point index.d.ts
static FACTOR_ID: FactorId_2;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FactorId_2 is fishy, the public type might be using the wrong enum

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This uses FactorId from public_types - https://github.com/firebase/firebase-js-sdk/blob/mfa-totp/packages/auth/src/mfa/assertions/totp.ts#L21, which is using the enum type -

export const enum FactorId {

This looks right to me.. maybe we are running into something like - microsoft/rushstack#3581

The other option is to define FACTOR_ID as string in

static FACTOR_ID = FactorId.TOTP;
, similar to what phone mfa does - https://github.com/firebase/firebase-js-sdk/blob/mfa-totp/packages/auth/src/platform_browser/mfa/assertions/phone.ts#L98

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Synced offline that we will leave this as-is for now and switch to String if we see the need for it.

static generateSecret(session: MultiFactorSession): Promise<TotpSecret>;
}

// @public
export interface TotpMultiFactorInfo extends MultiFactorInfo {
}

// @public
export class TotpSecret {
readonly codeIntervalSeconds: number;
readonly codeLength: number;
// Warning: (ae-forgotten-export) The symbol "StartTotpMfaEnrollmentResponse" needs to be exported by the entry point index.d.ts
//
// @internal (undocumented)
static _fromStartTotpMfaEnrollmentResponse(response: StartTotpMfaEnrollmentResponse, auth: AuthInternal): TotpSecret;
generateQrCodeUrl(accountName?: string, issuer?: string): string;
readonly hashingAlgorithm: string;
// Warning: (ae-forgotten-export) The symbol "TotpVerificationInfo" needs to be exported by the entry point index.d.ts
//
// @internal (undocumented)
_makeTotpVerificationInfo(otp: string): TotpVerificationInfo;
readonly secretKey: string;
}

// @public
export class TwitterAuthProvider extends BaseOAuthProvider {
constructor();
Expand Down
2 changes: 1 addition & 1 deletion packages/auth/demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
},
"dependencies": {
"@firebase/app": "0.7.24",
"@firebase/auth": "0.20.1",
"@firebase/auth": "file:..",
"@firebase/logger": "0.3.2",
"@firebase/util": "1.6.0",
"tslib": "^2.1.0"
Expand Down
28 changes: 27 additions & 1 deletion packages/auth/demo/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,12 @@
Phone
</a>
</li>
<li role="presentation">
<a href="#mfa-totp-section" aria-controls="mfa-totp-section"
data-toggle="tab" role="tab">
TOTP
</a>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="mfa-phone-section">
Expand All @@ -500,7 +506,27 @@
class="form-control" placeholder="Display Name" />
<button class="btn btn-block btn-primary"
id="enroll-mfa-confirm-phone-verification">
Complete Enrollment
Complete Phone Enrollment
</button>

</form>
</div>
<div class="tab-pane" id="mfa-totp-section">
<form class="form form-bordered no-submit">
<button class="btn btn-block btn-primary" id="enroll-mfa-totp-start">
Start TOTP Enrollment
</button>
<br>
<p hidden class="totp-text" id="totp-text"> Please scan the QR code below in a TOTP app </p>
<br>
<img hidden class="totp-qr-image" id="totp-qr-image"/>
<br>
<input type="text" id="enroll-mfa-totp-verification-code"
class="form-control" placeholder="TOTP Verification Code(from App)" />
<input type="text" id="enroll-mfa-totp-display-name"
class="form-control" placeholder="Display Name" />
<button class="btn btn-block btn-primary" id="enroll-mfa-totp-finalize">
Complete TOTP Enrollment
</button>
</form>
</div>
Expand Down
11 changes: 11 additions & 0 deletions packages/auth/demo/public/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,17 @@ input + .form,
margin-right: 10px;
}

.totp-text {
font-family: 'Courier New', Courier;
}
.totp-qr-image {
height: 120px;
margin-right: 10px;
border: 1px solid #ddd;
border-radius: 4px;
width: 120px;
}

.profile-email-not-verified {
color: #d9534f;
}
Expand Down
51 changes: 51 additions & 0 deletions packages/auth/demo/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ import {
signInWithCredential,
signInWithCustomToken,
signInWithEmailAndPassword,
TotpMultiFactorGenerator,
TotpSecret,
unlink,
updateEmail,
updatePassword,
Expand Down Expand Up @@ -97,6 +99,7 @@ let multiFactorErrorResolver = null;
let selectedMultiFactorHint = null;
let recaptchaSize = 'normal';
let webWorker = null;
let totpSecret = null;

// The corresponding Font Awesome icons for each provider.
const providersIcons = {
Expand Down Expand Up @@ -652,6 +655,50 @@ function onFinalizeEnrollWithPhoneMultiFactor() {
}, onAuthError);
}

async function onStartEnrollWithTotpMultiFactor() {
console.log('Starting TOTP enrollment!');
if (!activeUser()) {
alertError('No active user found.');
return;
}
try {
multiFactorSession = await multiFactor(activeUser()).getSession();
totpSecret = await TotpMultiFactorGenerator.generateSecret(
multiFactorSession
);
const url = totpSecret.generateQrCodeUrl('test', 'testissuer');
console.log('TOTP URL is ' + url);
// Use the QRServer API documented at https://goqr.me/api/doc/
const qrCodeUrl = `https://api.qrserver.com/v1/create-qr-code/?data=${url}&amp;size=30x30`;
$('img.totp-qr-image').attr('src', qrCodeUrl).show();
$('p.totp-text').show();
} catch (e) {
onAuthError(e);
}
}

async function onFinalizeEnrollWithTotpMultiFactor() {
const verificationCode = $('#enroll-mfa-totp-verification-code').val();
if (!activeUser() || !totpSecret || !verificationCode) {
alertError(' Missing active user OR TOTP secret OR verification code.');
return;
}

const multiFactorAssertion = TotpMultiFactorGenerator.assertionForEnrollment(
totpSecret,
verificationCode
);
const displayName = $('#enroll-mfa-totp-display-name').val() || undefined;

try {
await multiFactor(activeUser()).enroll(multiFactorAssertion, displayName);
refreshUserData();
alertSuccess('TOTP MFA enrolled!');
} catch (e) {
onAuthError(e);
}
}

/**
* Signs in or links a provider's credential, based on current tab opened.
* @param {!AuthCredential} credential The provider's credential.
Expand Down Expand Up @@ -1944,6 +1991,10 @@ function initApp() {
$('#enroll-mfa-confirm-phone-verification').click(
onFinalizeEnrollWithPhoneMultiFactor
);
// Starts multi-factor enrollment with TOTP.
$('#enroll-mfa-totp-start').click(onStartEnrollWithTotpMultiFactor);
// Completes multi-factor enrollment with supplied OTP(One-Time Password).
$('#enroll-mfa-totp-finalize').click(onFinalizeEnrollWithTotpMultiFactor);
}

$(initApp);
6 changes: 6 additions & 0 deletions packages/auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ import { browserPopupRedirectResolver } from './src/platform_browser/popup_redir

// MFA
import { PhoneMultiFactorGenerator } from './src/platform_browser/mfa/assertions/phone';
import {
TotpMultiFactorGenerator,
TotpSecret
} from './src/mfa/assertions/totp';

// Initialization and registration of Auth
import { getAuth } from './src/platform_browser';
Expand All @@ -96,5 +100,7 @@ export {
RecaptchaVerifier,
browserPopupRedirectResolver,
PhoneMultiFactorGenerator,
TotpMultiFactorGenerator,
TotpSecret,
getAuth
};
44 changes: 31 additions & 13 deletions packages/auth/src/mfa/assertions/totp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,23 +171,41 @@ export class TotpMultiFactorAssertionImpl
*/
export class TotpSecret {
/**
* Constructor for TotpSecret.
* @param secretKey - Shared secret key/seed used for enrolling in TOTP MFA and generating otps.
* @param hashingAlgorithm - Hashing algorithm used.
* @param codeLength - Length of the one-time passwords to be generated.
* @param codeIntervalSeconds - The interval (in seconds) when the OTP codes should change.
* Shared secret key/seed used for enrolling in TOTP MFA and generating otps.
*/
readonly secretKey: string;
/**
* Hashing algorithm used.
*/
readonly hashingAlgorithm: string;
/**
* Length of the one-time passwords to be generated.
*/
readonly codeLength: number;
/**
* The interval (in seconds) when the OTP codes should change.
*/
readonly codeIntervalSeconds: number;
// TODO(prameshj) - make this public after API review.
// This can be used by callers to show a countdown of when to enter OTP code by.
private readonly finalizeEnrollmentBy: string;

// The public members are declared outside the constructor so the docs can be generated.
private constructor(
readonly secretKey: string,
readonly hashingAlgorithm: string,
readonly codeLength: number,
readonly codeIntervalSeconds: number,
// TODO(prameshj) - make this public after API review.
// This can be used by callers to show a countdown of when to enter OTP code by.
private readonly finalizeEnrollmentBy: string,
secretKey: string,
hashingAlgorithm: string,
codeLength: number,
codeIntervalSeconds: number,
finalizeEnrollmentBy: string,
private readonly sessionInfo: string,
private readonly auth: AuthInternal
) {}
) {
this.secretKey = secretKey;
this.hashingAlgorithm = hashingAlgorithm;
this.codeLength = codeLength;
this.codeIntervalSeconds = codeIntervalSeconds;
this.finalizeEnrollmentBy = finalizeEnrollmentBy;
}

/** @internal */
static _fromStartTotpMfaEnrollmentResponse(
Expand Down