Skip to content

sign-in flow for totp #6626

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 12 commits into from
Oct 13, 2022
11 changes: 11 additions & 0 deletions packages/auth/demo/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,17 @@ <h4 class="modal-title">Select a second factor to sign in with</h4>
</button>
</div>
</form>
<!-- For handling sign-in with TOTP 2nd factor. -->
<form class="form form-bordered no-submit hidden" id="multi-factor-totp">
<div class="form">
<input type="text" id="multi-factor-totp-sign-in-verification-code"
class="form-control" placeholder="Totp Verification code" />
<button class="btn btn-block btn-primary"
id="sign-in-with-totp-multi-factor">
Complete sign In
</button>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
Expand Down
39 changes: 38 additions & 1 deletion packages/auth/demo/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,7 @@ function handleMultiFactorSignIn(resolver) {
);
// Hide phone form (other second factor types could be supported).
$('#multi-factor-phone').addClass('hidden');
$('#multi-factor-totp').addClass('hidden');
// Show second factor recovery dialog.
$('#multiFactorModal').modal();
}
Expand Down Expand Up @@ -1134,6 +1135,7 @@ function onSelectMultiFactorHint(index) {
// Hide all forms for handling each type of second factors.
// Currently only phone is supported.
$('#multi-factor-phone').addClass('hidden');
$('#multi-factor-totp').addClass('hidden');
if (
!multiFactorErrorResolver ||
typeof multiFactorErrorResolver.hints[index] === 'undefined'
Expand All @@ -1153,6 +1155,14 @@ function onSelectMultiFactorHint(index) {
// Clear all input.
$('#multi-factor-sign-in-verification-id').val('');
$('#multi-factor-sign-in-verification-code').val('');
} else if (multiFactorErrorResolver.hints[index].factorId === 'totp') {
// Save selected second factor.
selectedMultiFactorHint = multiFactorErrorResolver.hints[index];

// Show sign-in with totp second factor menu.
$('#multi-factor-totp').removeClass('hidden');
// Clear all input.
$('#multi-factor-totp-sign-in-verification-code').val('');
} else {
// 2nd factor not found or not supported by app.
alertError('Selected 2nd factor is not supported!');
Expand Down Expand Up @@ -1207,6 +1217,28 @@ function onFinalizeSignInWithPhoneMultiFactor(event) {
}, onAuthError);
}

/**
* Completes sign-in with the 2nd factor totp assertion.
* @param {!jQuery.Event} event The jQuery event object.
*/
function onFinalizeSignInWithTotpMultiFactor(event) {
event.preventDefault();
// Make sure a second factor is selected.
const otp = $('#multi-factor-totp-sign-in-verification-code').val();
if (!otp || !selectedMultiFactorHint || !multiFactorErrorResolver) {
return;
}

const assertion = TotpMultiFactorGenerator.assertionForSignIn(
selectedMultiFactorHint.uid,
otp
);
multiFactorErrorResolver.resolveSignIn(assertion).then(userCredential => {
onAuthUserCredentialSuccess(userCredential);
$('#multiFactorModal').modal('hide');
}, onAuthError);
}

/**
* Adds a new row to insert an OAuth custom parameter key/value pair.
* @param {!jQuery.Event} _event The jQuery event object.
Expand Down Expand Up @@ -1336,7 +1368,6 @@ function signInWithPopupRedirect(provider) {
customParameters[key] = value;
}
});
console.log('customParameters: ', customParameters);
// For older jscore versions that do not support this.
if (provider.setCustomParameters) {
// Set custom parameters on current provider.
Expand Down Expand Up @@ -1985,6 +2016,12 @@ function initApp() {
$('#sign-in-with-phone-multi-factor').click(
onFinalizeSignInWithPhoneMultiFactor
);

// Completes multi-factor sign-in with supplied OTP(One-Time Password).
$('#sign-in-with-totp-multi-factor').click(
onFinalizeSignInWithTotpMultiFactor
);

// Starts multi-factor enrollment with phone number.
$('#enroll-mfa-verify-phone-number').click(onStartEnrollWithPhoneMultiFactor);
// Completes multi-factor enrollment with supplied SMS code.
Expand Down
53 changes: 53 additions & 0 deletions packages/auth/src/api/authentication/mfa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,27 @@ export interface StartPhoneMfaSignInRequest {
};
tenantId?: string;
}
export interface StartTotpMfaSignInRequest {
mfaPendingCredential: string;
mfaEnrollmentId: string;
totpSignInInfo: {
verificationCode: string;
};
tenantId?: string;
}

export interface StartPhoneMfaSignInResponse {
phoneResponseInfo: {
sessionInfo: string;
};
}

export interface StartTotpMfaSignInResponse {
totpSignInInfo: {
verificationCode: string;
};
}

export function startSignInPhoneMfa(
auth: Auth,
request: StartPhoneMfaSignInRequest
Expand All @@ -73,14 +87,38 @@ export function startSignInPhoneMfa(
);
}

export function startSignInTotpMfa(
auth: Auth,
request: StartTotpMfaSignInRequest
): Promise<StartTotpMfaSignInResponse> {
return _performApiRequest<
StartTotpMfaSignInRequest,
StartTotpMfaSignInResponse
>(
auth,
HttpMethod.POST,
Endpoint.START_MFA_SIGN_IN,
_addTidIfNecessary(auth, request)
);
}

export interface FinalizePhoneMfaSignInRequest {
mfaPendingCredential: string;
phoneVerificationInfo: SignInWithPhoneNumberRequest;
tenantId?: string;
}

export interface FinalizeTotpMfaSignInRequest {
mfaPendingCredential: string;
totpVerificationInfo: { verificationCode: string };
tenantId?: string;
mfaEnrollmentId: string;
}

export interface FinalizePhoneMfaSignInResponse extends FinalizeMfaResponse {}

export interface FinalizeTotpMfaSignInResponse extends FinalizeMfaResponse {}

export function finalizeSignInPhoneMfa(
auth: Auth,
request: FinalizePhoneMfaSignInRequest
Expand All @@ -96,6 +134,21 @@ export function finalizeSignInPhoneMfa(
);
}

export function finalizeSignInTotpMfa(
auth: Auth,
request: FinalizeTotpMfaSignInRequest
): Promise<FinalizeTotpMfaSignInResponse> {
return _performApiRequest<
FinalizeTotpMfaSignInRequest,
FinalizeTotpMfaSignInResponse
>(
auth,
HttpMethod.POST,
Endpoint.FINALIZE_MFA_SIGN_IN,
_addTidIfNecessary(auth, request)
);
}

/**
* @internal
*/
Expand Down
25 changes: 19 additions & 6 deletions packages/auth/src/mfa/assertions/totp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ import {
StartTotpMfaEnrollmentResponse,
TotpVerificationInfo
} from '../../api/account_management/mfa';
import { FinalizeMfaResponse } from '../../api/authentication/mfa';
import {
FinalizeMfaResponse,
finalizeSignInTotpMfa
} from '../../api/authentication/mfa';
import { MultiFactorAssertionImpl } from '../../mfa/mfa_assertion';
import { MultiFactorSessionImpl } from '../mfa_session';
import { AuthErrorCode } from '../../core/errors';
Expand Down Expand Up @@ -136,7 +139,7 @@ export class TotpMultiFactorAssertionImpl
}

/** @internal */
_finalizeEnroll(
async _finalizeEnroll(
auth: AuthInternal,
idToken: string,
displayName?: string | null
Expand All @@ -154,11 +157,21 @@ export class TotpMultiFactorAssertionImpl
}

/** @internal */
_finalizeSignIn(
_auth: AuthInternal,
_mfaPendingCredential: string
async _finalizeSignIn(
auth: AuthInternal,
mfaPendingCredential: string
): Promise<FinalizeMfaResponse> {
throw new Error('method not implemented');
_assert(
this.enrollmentId !== undefined && this.otp !== undefined,
auth,
AuthErrorCode.ARGUMENT_ERROR
);
const totpVerificationInfo = { verificationCode: this.otp };
return finalizeSignInTotpMfa(auth, {
mfaPendingCredential,
mfaEnrollmentId: this.enrollmentId,
totpVerificationInfo
});
}
}

Expand Down