Skip to content

Commit

Permalink
Added remediation ReEnrollAuthenticatorWarning (#1472)
Browse files Browse the repository at this point in the history
OKTA-644785 Added remediation ReEnrollAuthenticatorWarning
  • Loading branch information
denysoblohin-okta authored Nov 1, 2023
1 parent a9f092d commit eeac1d1
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Bug Fix

- [#1462](https://github.com/okta/okta-auth-js/pull/1462) Fixes ESM build for Node.js
- [#1472](https://github.com/okta/okta-auth-js/pull/1472) Added missing remediator `ReEnrollAuthenticatorWarning`

### Other

Expand Down
4 changes: 3 additions & 1 deletion lib/idx/flow/AccountUnlockFlow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import {
SelectAuthenticatorAuthenticate,
ChallengeAuthenticator,
ChallengePoll,
AuthenticatorVerificationData
AuthenticatorVerificationData,
ReEnrollAuthenticatorWarning
} from '../remediators';

export const AccountUnlockFlow: RemediationFlow = {
Expand All @@ -31,4 +32,5 @@ export const AccountUnlockFlow: RemediationFlow = {
'challenge-authenticator': ChallengeAuthenticator,
'challenge-poll': ChallengePoll,
'authenticator-verification-data': AuthenticatorVerificationData,
'reenroll-authenticator-warning': ReEnrollAuthenticatorWarning,
};
2 changes: 2 additions & 0 deletions lib/idx/flow/AuthenticationFlow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
SelectAuthenticatorAuthenticate,
ChallengeAuthenticator,
ReEnrollAuthenticator,
ReEnrollAuthenticatorWarning,
RedirectIdp,
AuthenticatorEnrollmentData,
SelectAuthenticatorEnroll,
Expand All @@ -39,6 +40,7 @@ export const AuthenticationFlow: RemediationFlow = {
'challenge-authenticator': ChallengeAuthenticator,
'challenge-poll': ChallengePoll,
'reenroll-authenticator': ReEnrollAuthenticator,
'reenroll-authenticator-warning': ReEnrollAuthenticatorWarning,
'enroll-poll': EnrollPoll,
'select-enrollment-channel': SelectEnrollmentChannel,
'enrollment-channel-data': EnrollmentChannelData,
Expand Down
2 changes: 2 additions & 0 deletions lib/idx/flow/PasswordRecoveryFlow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
AuthenticatorVerificationData,
ResetAuthenticator,
ReEnrollAuthenticator,
ReEnrollAuthenticatorWarning,
SelectAuthenticatorEnroll,
AuthenticatorEnrollmentData,
EnrollPoll
Expand All @@ -34,5 +35,6 @@ export const PasswordRecoveryFlow: RemediationFlow = {
'authenticator-enrollment-data': AuthenticatorEnrollmentData,
'reset-authenticator': ResetAuthenticator,
'reenroll-authenticator': ReEnrollAuthenticator,
'reenroll-authenticator-warning': ReEnrollAuthenticatorWarning,
'enroll-poll': EnrollPoll,
};
18 changes: 18 additions & 0 deletions lib/idx/remediators/ReEnrollAuthenticatorWarning.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*!
* Copyright (c) 2015-present, Okta, Inc. and/or its affiliates. All rights reserved.
* The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.")
*
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and limitations under the License.
*/


import { ReEnrollAuthenticator } from './ReEnrollAuthenticator';

export class ReEnrollAuthenticatorWarning extends ReEnrollAuthenticator {
static remediationName = 'reenroll-authenticator-warning';
}
1 change: 1 addition & 0 deletions lib/idx/remediators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export * from './ResetAuthenticator';
export * from './EnrollProfile';
export * from './Identify';
export * from './ReEnrollAuthenticator';
export * from './ReEnrollAuthenticatorWarning';
export * from './RedirectIdp';
export * from './SelectAuthenticatorAuthenticate';
export * from './SelectAuthenticatorEnroll';
Expand Down
143 changes: 143 additions & 0 deletions test/spec/idx/authenticate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import {
IdxAuthenticatorFactory,
SelectEnrollmentChannelRemediationFactory,
EnrollmentChannelDataSmsRemediationFactory,
ReEnrollPasswordAuthenticatorRemediationFactory,
} from '@okta/test.support/idx';
import { IdxMessagesFactory } from '@okta/test.support/idx/factories/messages';

Expand Down Expand Up @@ -614,6 +615,148 @@ describe('idx/authenticate', () => {

});


describe('authentication with optional password change', () => {
beforeEach(() => {
const { successResponse } = testContext;
const identifyResponse = IdentifyResponseFactory.build();
const selectAuthenticatorResponse = IdxResponseFactory.build({
neededToProceed: [
SelectAuthenticatorAuthenticateRemediationFactory.build({
value: [
AuthenticatorValueFactory.build({
options: [
PasswordAuthenticatorOptionFactory.build(),
]
})
]
})
]
});
const verifyPasswordResponse = VerifyPasswordResponseFactory.build();
const changePasswordResponse = IdxResponseFactory.build({
neededToProceed: [
ReEnrollPasswordAuthenticatorRemediationFactory.build({
name: 'reenroll-authenticator-warning'
}),
SkipRemediationFactory.build()
]
});
chainResponses([
identifyResponse,
selectAuthenticatorResponse,
verifyPasswordResponse,
changePasswordResponse,
successResponse,
]);
jest.spyOn(mocked.introspect, 'introspect')
.mockResolvedValueOnce(identifyResponse)
.mockResolvedValueOnce(changePasswordResponse);
jest.spyOn(identifyResponse, 'proceed');
jest.spyOn(selectAuthenticatorResponse, 'proceed');
jest.spyOn(verifyPasswordResponse, 'proceed');
jest.spyOn(changePasswordResponse, 'proceed');
Object.assign(testContext, {
identifyResponse,
selectAuthenticatorResponse,
verifyPasswordResponse,
changePasswordResponse,
});
});

it('can authenticate with current password and then change password to new one', async () => {
const {
authClient,
identifyResponse,
selectAuthenticatorResponse,
verifyPasswordResponse,
changePasswordResponse,
tokenResponse
} = testContext;
// authenticate
jest.spyOn(mocked.introspect, 'introspect').mockResolvedValue(identifyResponse);
let res = await authenticate(authClient, { username: 'fakeuser', password: 'fakepass' });
expect(res).toMatchObject({
status: IdxStatus.PENDING,
neededToProceed: [{
name: 'reenroll-authenticator-warning'
}, {
name: 'skip'
}]
});
expect(identifyResponse.proceed).toHaveBeenCalledWith('identify', { identifier: 'fakeuser' });
expect(selectAuthenticatorResponse.proceed).toHaveBeenCalledWith('select-authenticator-authenticate', {
authenticator: { id: 'id-password' }
});
expect(verifyPasswordResponse.proceed).toHaveBeenCalledWith('challenge-authenticator', { credentials: { passcode: 'fakepass' } });
expect(res.nextStep).toMatchObject({
name: 'reenroll-authenticator-warning',
type: 'password',
authenticator: {
key: 'okta_password',
},
inputs: [{
name: 'newPassword',
label: 'New password',
secret: true,
}],
});
// proceed
res = await proceed(authClient, { newPassword: 'newpass' });
expect(changePasswordResponse.proceed).toHaveBeenCalledWith('reenroll-authenticator-warning', {
credentials: { passcode: 'newpass' }
});
expect(res).toMatchObject({
status: IdxStatus.SUCCESS,
tokens: tokenResponse.tokens,
});
});

it('can authenticate with current password and skip optional password change', async () => {
const {
authClient,
identifyResponse,
selectAuthenticatorResponse,
verifyPasswordResponse,
tokenResponse
} = testContext;
// authenticate
jest.spyOn(mocked.introspect, 'introspect').mockResolvedValue(identifyResponse);
let res = await authenticate(authClient, { username: 'fakeuser', password: 'fakepass' });
expect(res).toMatchObject({
status: IdxStatus.PENDING,
neededToProceed: [{
name: 'reenroll-authenticator-warning'
}, {
name: 'skip'
}]
});
expect(identifyResponse.proceed).toHaveBeenCalledWith('identify', { identifier: 'fakeuser' });
expect(selectAuthenticatorResponse.proceed).toHaveBeenCalledWith('select-authenticator-authenticate', {
authenticator: { id: 'id-password' }
});
expect(verifyPasswordResponse.proceed).toHaveBeenCalledWith('challenge-authenticator', { credentials: { passcode: 'fakepass' } });
expect(res.nextStep).toMatchObject({
name: 'reenroll-authenticator-warning',
type: 'password',
authenticator: {
key: 'okta_password',
},
inputs: [{
name: 'newPassword',
label: 'New password',
secret: true,
}],
});
// proceed
res = await proceed(authClient, { skip: true });
expect(res).toMatchObject({
status: IdxStatus.SUCCESS,
tokens: tokenResponse.tokens,
});
});
});

describe('mfa authentication', () => {

describe('phone', () => {
Expand Down

0 comments on commit eeac1d1

Please sign in to comment.