Skip to content

Commit

Permalink
UI Support for Okta Number Challenge (#15998)
Browse files Browse the repository at this point in the history
* Imported uuid library for initial commit to push a clean branch.

* Removed import statement in auth-form file since it was causing UI tests to fail as the import was not being used.

* Added nonce field to payload for okta sign in. (#16001)

* Added nonce field to payload for okta sign in.

* Added missing yarn package for uuid

* Fixed failing ui tests in cluster-test file to take into account of nonce field in the payload of okta login

* Removed uuid library and used crypto.randomUUID() to generate unique uuid values instead

* Fixed indent in package.json

* Removed uuid library since decided to use crypto.randomUUID() instead to generate unique uuid values

* Create polling function for correct answer in okta number challenge (#16070)

* Implemented polling function to get correct answer for okta number challenge.

* Disabled polling function for testing as it was causing acceptance test to fail in auth-test.js

* Changed API call to be the auth mount path instead of being static and created a variable to store the oktaNumberChallengeAnswer to be used later for the display screens

* Create component for okta number challenge screen (#16195)

* Implemented loading screen and display screen for correct answer for Okta Number Challenge

* Fixed linting issues on hbs files

* Added periods to parameter descriptions and made parameters optional

* Removed optional parameters from calling AuthForm component if authMethod is not Okta

* Implement error handling and screens for okta number challenge (#16276)

* Implemented loading screen and display screen for correct answer for Okta Number Challenge

* Fixed linting issues on hbs files

* Temporary changes to include error screen in okta number challenge

* Created error screen tests and made minor fixes

* Fixed error for wrong parameter name being passed in

* Fixed linting issues causing ui tests to fail

* Added periods at the end of param descriptions

* Imported uuid library for initial commit to push a clean branch.

* Removed import statement in auth-form file since it was causing UI tests to fail as the import was not being used.

* Removed uuid library since decided to use crypto.randomUUID() instead to generate unique uuid values

* Added nonce field to payload for okta sign in. (#16001)

* Added nonce field to payload for okta sign in.

* Added missing yarn package for uuid

* Fixed failing ui tests in cluster-test file to take into account of nonce field in the payload of okta login

* Removed uuid library and used crypto.randomUUID() to generate unique uuid values instead

* Fixed indent in package.json

* Create polling function for correct answer in okta number challenge (#16070)

* Implemented polling function to get correct answer for okta number challenge.

* Disabled polling function for testing as it was causing acceptance test to fail in auth-test.js

* Changed API call to be the auth mount path instead of being static and created a variable to store the oktaNumberChallengeAnswer to be used later for the display screens

* Create component for okta number challenge screen (#16195)

* Implemented loading screen and display screen for correct answer for Okta Number Challenge

* Fixed linting issues on hbs files

* Added periods to parameter descriptions and made parameters optional

* Removed optional parameters from calling AuthForm component if authMethod is not Okta

* Implement error handling and screens for okta number challenge (#16276)

* Implemented loading screen and display screen for correct answer for Okta Number Challenge

* Fixed linting issues on hbs files

* Temporary changes to include error screen in okta number challenge

* Created error screen tests and made minor fixes

* Fixed error for wrong parameter name being passed in

* Fixed linting issues causing ui tests to fail

* Added periods at the end of param descriptions

* UI/vault 7312/fix vault enterprise error for okta number challenge (#16568)

* Fixed bug with okta not working when selecting okta tab after being on other tab

* Fixed vault enterprise errors

* Fixed error when logging in with Okta in 'Other' tab

* Removed namespace parameter in option to use the default

* Added changelog
  • Loading branch information
linda9379 committed Aug 10, 2022
1 parent c687f25 commit 19c3e12
Show file tree
Hide file tree
Showing 11 changed files with 396 additions and 168 deletions.
3 changes: 3 additions & 0 deletions changelog/15998.txt
@@ -0,0 +1,3 @@
```release-note:feature
ui: UI support for Okta Number Challenge.
```
4 changes: 3 additions & 1 deletion ui/app/adapters/cluster.js
Expand Up @@ -107,7 +107,7 @@ export default ApplicationAdapter.extend({
},

authenticate({ backend, data }) {
const { role, jwt, token, password, username, path } = data;
const { role, jwt, token, password, username, path, nonce } = data;
const url = this.urlForAuth(backend, username, path);
const verb = backend === 'token' ? 'GET' : 'POST';
let options = {
Expand All @@ -119,6 +119,8 @@ export default ApplicationAdapter.extend({
};
} else if (backend === 'jwt' || backend === 'oidc') {
options.data = { role, jwt };
} else if (backend === 'okta') {
options.data = { password, nonce };
} else {
options.data = token ? { token, password } : { password };
}
Expand Down
59 changes: 55 additions & 4 deletions ui/app/components/auth-form.js
Expand Up @@ -18,13 +18,17 @@ const BACKENDS = supportedAuthBackends();
*
* @example ```js
* // All properties are passed in via query params.
* <AuthForm @wrappedToken={{wrappedToken}} @cluster={{model}} @namespace={{namespaceQueryParam}} @selectedAuth={{authMethod}} @onSuccess={{action this.onSuccess}} />```
* <AuthForm @wrappedToken={{wrappedToken}} @cluster={{model}} @namespace={{namespaceQueryParam}} @selectedAuth={{authMethod}} @onSuccess={{action this.onSuccess}}/>```
*
* @param {string} wrappedToken - The auth method that is currently selected in the dropdown.
* @param {object} cluster - The auth method that is currently selected in the dropdown. This corresponds to an Ember Model.
* @param {string} namespace- The currently active namespace.
* @param {string} selectedAuth - The auth method that is currently selected in the dropdown.
* @param {function} onSuccess - Fired on auth success
* @param {function} onSuccess - Fired on auth success.
* @param {function} [setOktaNumberChallenge] - Sets whether we are waiting for okta number challenge to be used to sign in.
* @param {boolean} [waitingForOktaNumberChallenge=false] - Determines if we are waiting for the Okta Number Challenge to sign in.
* @param {function} [setCancellingAuth] - Sets whether we are cancelling or not the login authentication for Okta Number Challenge.
* @param {boolean} [cancelAuthForOktaNumberChallenge=false] - Determines if we are cancelling the login authentication for the Okta Number Challenge.
*/

const DEFAULTS = {
Expand All @@ -51,6 +55,9 @@ export default Component.extend(DEFAULTS, {
oldNamespace: null,
authMethods: BACKENDS,

// number answer for okta number challenge if applicable
oktaNumberChallengeAnswer: null,

didReceiveAttrs() {
this._super(...arguments);
let {
Expand All @@ -60,8 +67,14 @@ export default Component.extend(DEFAULTS, {
namespace: ns,
selectedAuth: newMethod,
oldSelectedAuth: oldMethod,
cancelAuthForOktaNumberChallenge: cancelAuth,
} = this;

// if we are cancelling the login then we reset the number challenge answer and cancel the current authenticate and polling tasks
if (cancelAuth) {
this.set('oktaNumberChallengeAnswer', null);
this.authenticate.cancelAll();
this.pollForOktaNumberChallenge.cancelAll();
}
next(() => {
if (!token && (oldNS === null || oldNS !== ns)) {
this.fetchMethods.perform();
Expand Down Expand Up @@ -219,7 +232,11 @@ export default Component.extend(DEFAULTS, {
cluster: { id: clusterId },
} = this;
try {
this.delayAuthMessageReminder.perform();
if (backendType === 'okta') {
this.pollForOktaNumberChallenge.perform(data.nonce, data.path);
} else {
this.delayAuthMessageReminder.perform();
}
const authResponse = yield this.auth.authenticate({
clusterId,
backend: backendType,
Expand All @@ -236,6 +253,28 @@ export default Component.extend(DEFAULTS, {
})
),

pollForOktaNumberChallenge: task(function* (nonce, mount) {
// yield for 1s to wait to see if there is a login error before polling
yield timeout(1000);
if (this.error) {
return;
}
let response = null;
this.setOktaNumberChallenge(true);
this.setCancellingAuth(false);
// keep polling /auth/okta/verify/:nonce API every 1s until a response is given with the correct number for the Okta Number Challenge
while (response === null) {
// when testing, the polling loop causes promises to be rejected making acceptance tests fail
// so disable the poll in tests
if (Ember.testing) {
return;
}
yield timeout(1000);
response = yield this.auth.getOktaNumberChallengeAnswer(nonce, mount);
}
this.set('oktaNumberChallengeAnswer', response);
}),

delayAuthMessageReminder: task(function* () {
if (Ember.testing) {
this.showLoading = true;
Expand Down Expand Up @@ -275,6 +314,14 @@ export default Component.extend(DEFAULTS, {
if (this.customPath || backend.id) {
data.path = this.customPath || backend.id;
}
// add nonce field for okta backend
if (backend.type === 'okta') {
data.nonce = crypto.randomUUID();
// add a default path of okta if it doesn't exist to be used for Okta Number Challenge
if (!data.path) {
data.path = 'okta';
}
}
return this.authenticate.unlinked().perform(backend.type, data);
},
handleError(e) {
Expand All @@ -283,5 +330,9 @@ export default Component.extend(DEFAULTS, {
error: e ? this.auth.handleError(e) : null,
});
},
returnToLoginFromOktaNumberChallenge() {
this.setOktaNumberChallenge(false);
this.set('oktaNumberChallengeAnswer', null);
},
},
});
24 changes: 24 additions & 0 deletions ui/app/components/okta-number-challenge.js
@@ -0,0 +1,24 @@
import Component from '@glimmer/component';

/**
* @module OktaNumberChallenge
* OktaNumberChallenge components are used to display loading screen and correct answer for Okta Number Challenge when signing in through Okta
*
* @example
* ```js
* <OktaNumberChallenge @correctAnswer={this.oktaNumberChallengeAnswer} @hasError={this.error} @onReturnToLogin={this.returnToLoginFromOktaNumberChallenge}/>
* ```
* @param {number} correctAnswer - The correct answer to click for the okta number challenge.
* @param {boolean} hasError - Determines if there is an error being thrown.
* @param {function} onReturnToLogin - Sets waitingForOktaNumberChallenge to false if want to return to main login.
*/

export default class OktaNumberChallenge extends Component {
get oktaNumberChallengeCorrectAnswer() {
return this.args.correctAnswer;
}

get errorThrown() {
return this.args.hasError;
}
}
4 changes: 4 additions & 0 deletions ui/app/controllers/vault/cluster/auth.js
Expand Up @@ -85,5 +85,9 @@ export default Controller.extend({
mfaErrors: null,
});
},
cancelAuthentication() {
this.set('cancelAuth', true);
this.set('waitingForOktaNumberChallenge', false);
},
},
});
17 changes: 17 additions & 0 deletions ui/app/services/auth.js
Expand Up @@ -441,4 +441,21 @@ export default Service.extend({
backend: BACKENDS.findBy('type', backend),
});
}),

getOktaNumberChallengeAnswer(nonce, mount) {
const url = `/v1/auth/${mount}/verify/${nonce}`;
return this.ajax(url, 'GET', {}).then(
(resp) => {
return resp.data.correct_answer;
},
(e) => {
// if error status is 404, return and keep polling for a response
if (e.status === 404) {
return null;
} else {
throw e;
}
}
);
},
});

0 comments on commit 19c3e12

Please sign in to comment.