Skip to content

Commit

Permalink
feat(idx): auto select authenticator data method
Browse files Browse the repository at this point in the history
OKTA-472634
<<<Jenkins Check-In of Tested SHA: 29f1217 for eng_productivity_ci_bot_okta@okta.com>>>
Artifact: okta-auth-js
Files changed count: 6
PR Link: "#1125"
  • Loading branch information
shuowu authored and eng-prod-CI-bot-okta committed Feb 28, 2022
1 parent 5c53f02 commit 2adda80
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 4 deletions.
1 change: 1 addition & 0 deletions .watchmanconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Features

- [#1113](https://github.com/okta/okta-auth-js/pull/1113) Updates types for `SigninWithCredentialsOptions` and `SignInOptions` to support `SP Initiated Auth`
- [#1125](https://github.com/okta/okta-auth-js/pull/1125) IDX - Supports auto select methodType (when only one selection is available) for `authenticator-verification-data` remediation

## 6.1.0

Expand Down
37 changes: 36 additions & 1 deletion lib/idx/remediators/AuthenticatorVerificationData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,59 @@
*/


import { AuthSdkError } from '../../errors';
import { AuthenticatorData, AuthenticatorDataValues } from './Base/AuthenticatorData';
import { IdxRemediation } from '../types/idx-js';

export type AuthenticatorVerificationDataValues = AuthenticatorDataValues;

export class AuthenticatorVerificationData extends AuthenticatorData {
static remediationName = 'authenticator-verification-data';

values!: AuthenticatorVerificationDataValues;
shouldProceedWithEmailAuthenticator: boolean;

constructor(remediation: IdxRemediation, values: AuthenticatorDataValues = {}) {
super(remediation, values);

// TODO: extend this feature to all authenticators
this.shouldProceedWithEmailAuthenticator = this.authenticator.methods.length === 1
&& this.authenticator.methods[0].type === 'email';
}

canRemediate() {
// auto proceed if there is only one method
if (this.shouldProceedWithEmailAuthenticator) {
return true;
}
return super.canRemediate();
}

mapAuthenticator() {
const authenticatorData = this.getAuthenticatorData();
const authenticatorFromRemediation = this.getAuthenticatorFromRemediation();

// auto proceed with the only methodType option
if (this.shouldProceedWithEmailAuthenticator) {
return authenticatorFromRemediation.form?.value.reduce((acc, curr) => {
if (curr.value) {
acc[curr.name] = curr.value;
} else if (curr.options) {
acc[curr.name] = curr.options![0].value;
} else {
throw new AuthSdkError(`Unsupported authenticator data type: ${curr}`);
}
return acc;
}, {});
}

// return based on user selection
return {
id: authenticatorFromRemediation.form!.value
.find(({ name }) => name === 'id')!.value,
enrollmentId: authenticatorFromRemediation.form!.value
.find(({ name }) => name === 'enrollmentId')?.value,
methodType: authenticatorData!.methodType,
methodType: authenticatorData?.methodType,
};
}

Expand Down
5 changes: 2 additions & 3 deletions lib/idx/remediators/Base/AuthenticatorData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@


import { Remediator, RemediationValues } from './Remediator';
import { IdxRemediationValue, IdxOption, IdxRemediation } from '../../types/idx-js';
import { Authenticator } from '../../types';
import { IdxRemediationValue, IdxOption, IdxRemediation, IdxAuthenticator } from '../../types/idx-js';

export type AuthenticatorDataValues = RemediationValues & {
methodType?: string;
Expand All @@ -28,7 +27,7 @@ export class AuthenticatorData extends Remediator {
};

values!: AuthenticatorDataValues;
authenticator: Authenticator;
authenticator: IdxAuthenticator;

constructor(remediation: IdxRemediation, values: AuthenticatorDataValues = {}) {
super(remediation, values);
Expand Down
30 changes: 30 additions & 0 deletions test/spec/idx/authenticate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
IdxErrorPasscodeInvalidFactory,
IdxErrorEnrollmentInvalidPhoneFactory,
IdxErrorGoogleAuthenticatorPasscodeInvalidFactory,
EmailAuthenticatorVerificationDataRemediationFactory,
PhoneAuthenticatorVerificationDataRemediationFactory,
VerifyEmailRemediationFactory,
VerifyGoogleAuthenticatorRemediationFactory,
Expand Down Expand Up @@ -1097,6 +1098,11 @@ describe('idx/authenticate', () => {
})
]
});
const verificationDataResponse = IdxResponseFactory.build({
neededToProceed: [
EmailAuthenticatorVerificationDataRemediationFactory.build()
]
});
const verifyEmailResponse = IdxResponseFactory.build({
neededToProceed: [
VerifyEmailRemediationFactory.build()
Expand Down Expand Up @@ -1138,6 +1144,7 @@ describe('idx/authenticate', () => {

Object.assign(testContext, {
selectAuthenticatorResponse,
verificationDataResponse,
verifyEmailResponse,
errorInvalidCodeResponse
});
Expand Down Expand Up @@ -1169,6 +1176,29 @@ describe('idx/authenticate', () => {
});
});

it('can auto select email methodType as authenticator verification data', async () => {
const {
authClient,
verificationDataResponse,
verifyEmailResponse
} = testContext;

jest.spyOn(verificationDataResponse, 'proceed').mockResolvedValue(verifyEmailResponse);
jest.spyOn(mocked.introspect, 'introspect').mockResolvedValue(verificationDataResponse);
const res = await authenticate(authClient);
expect(verificationDataResponse.proceed).toHaveBeenCalledWith('authenticator-verification-data', {
authenticator: {
id: 'id-email'
}
});
expect(res).toMatchObject({
status: IdxStatus.PENDING,
nextStep: {
name: 'challenge-authenticator'
}
});
});

it('returns a PENDING error if an invalid code is provided', async () => {
const {
authClient,
Expand Down
13 changes: 13 additions & 0 deletions test/support/idx/factories/remediations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,19 @@ export const PasswordAuthenticatorVerificationDataRemediationFactory = Authentic
]
});

export const EmailAuthenticatorVerificationDataRemediationFactory = AuthenticatorVerificationDataRemediationFactory.params({
relatesTo: {
type: 'object',
value: EmailAuthenticatorFactory.build()
},
value: [
AuthenticatorValueFactory.build({
label: 'Email',
form: EmailAuthenticatorFormFactory.build()
})
]
});

export const PhoneAuthenticatorVerificationDataRemediationFactory = AuthenticatorVerificationDataRemediationFactory.params({
relatesTo: {
type: 'object',
Expand Down

0 comments on commit 2adda80

Please sign in to comment.