Skip to content
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

UI Support for Okta Number Challenge #15998

Merged
merged 19 commits into from Aug 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
8f37494
Imported uuid library for initial commit to push a clean branch.
linda9379 Jun 15, 2022
6a76020
Removed import statement in auth-form file since it was causing UI te…
linda9379 Jun 15, 2022
75185d6
Added nonce field to payload for okta sign in. (#16001)
linda9379 Jun 16, 2022
a64f326
Removed uuid library since decided to use crypto.randomUUID() instead…
linda9379 Jun 16, 2022
449b6cd
Merge branch 'ui/VAULT-5417/ui_support_for_okta_number_challenge' of …
linda9379 Jun 16, 2022
189bedd
Create polling function for correct answer in okta number challenge (…
linda9379 Jun 22, 2022
61bd21b
Create component for okta number challenge screen (#16195)
linda9379 Jul 12, 2022
9d1246c
Implement error handling and screens for okta number challenge (#16276)
linda9379 Jul 12, 2022
77e49b0
Imported uuid library for initial commit to push a clean branch.
linda9379 Jun 15, 2022
47a165b
Removed import statement in auth-form file since it was causing UI te…
linda9379 Jun 15, 2022
0432eec
Removed uuid library since decided to use crypto.randomUUID() instead…
linda9379 Jun 16, 2022
20012eb
Added nonce field to payload for okta sign in. (#16001)
linda9379 Jun 16, 2022
3b074e4
Create polling function for correct answer in okta number challenge (…
linda9379 Jun 22, 2022
b58c0e4
Create component for okta number challenge screen (#16195)
linda9379 Jul 12, 2022
ec43431
Implement error handling and screens for okta number challenge (#16276)
linda9379 Jul 12, 2022
1f0faff
Merge branch main branch to ui/VAULT-5471/ui_support_for_okta_number_…
linda9379 Aug 3, 2022
88d87e4
Merge branch 'main' into ui/VAULT-5417/ui_support_for_okta_number_cha…
linda9379 Aug 3, 2022
5b474c3
UI/vault 7312/fix vault enterprise error for okta number challenge (#…
linda9379 Aug 4, 2022
368f64e
Added changelog
linda9379 Aug 10, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog/15998.txt
@@ -0,0 +1,3 @@
```release-note:feature
Copy link
Collaborator

Choose a reason for hiding this comment

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

👋🏻 (@cinthiaconti maybe?) This changelog entry isn't correctly formatted for new features.

**UI Okta Number Challenge**: <text>

You can check out the 1.11 changelog for a reference, and there's also a wiki page about this. Could someone please update this file on main so that it formats correctly in the final version of the 1.12 changelog?

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;
}
}
);
},
});