mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-24 16:11:08 +02:00
* updates api client vars to snake_case for custom messages * updates api client vars to snake_case for tools * updates api client vars to snake_case for sync * updates api client vars to snake_case for secrets engine * updates api client vars to snake_case for auth * updates api client vars to snake_case for usage * updates api client dep to point to gh repo * fixes custom-messages service unit tests * fixes configure-ssh test * fixes configure-ssh test...again
94 lines
2.9 KiB
TypeScript
94 lines
2.9 KiB
TypeScript
/**
|
|
* Copyright (c) HashiCorp, Inc.
|
|
* SPDX-License-Identifier: BUSL-1.1
|
|
*/
|
|
|
|
import AuthBase from './base';
|
|
import { action } from '@ember/object';
|
|
import { task, timeout } from 'ember-concurrency';
|
|
import { tracked } from '@glimmer/tracking';
|
|
import { waitFor } from '@ember/test-waiters';
|
|
import uuid from 'core/utils/uuid';
|
|
|
|
import type { OktaVerifyApiResponse, UsernameLoginResponse } from 'vault/vault/auth/methods';
|
|
|
|
/**
|
|
* @module Auth::Form::Okta
|
|
* see Auth::Base
|
|
* */
|
|
|
|
export default class AuthFormOkta extends AuthBase {
|
|
@tracked challengeAnswer = '';
|
|
@tracked oktaVerifyError = '';
|
|
@tracked showNumberChallenge = false;
|
|
|
|
loginFields = [{ name: 'username' }, { name: 'password' }];
|
|
|
|
async loginRequest(formData: { path: string; username: string; password: string }) {
|
|
const { path, username, password } = formData;
|
|
// wait for 1s to wait to see if there is a login error before polling
|
|
await timeout(1000);
|
|
|
|
const nonce = uuid();
|
|
this.pollForOktaNumberChallenge.perform(nonce, path);
|
|
|
|
// If an Okta MFA challenge is configured for the end user this request resolves when it is completed.
|
|
// If a user fails the MFA challenge (e.g. Okta number challenge) this POST login request fails.
|
|
const { auth } = (await this.api.auth.oktaLogin(username, path, {
|
|
nonce,
|
|
password,
|
|
})) as UsernameLoginResponse;
|
|
|
|
return this.normalizeAuthResponse(auth, {
|
|
authMountPath: path,
|
|
displayName: auth?.metadata?.username,
|
|
token: auth.client_token,
|
|
ttl: auth.lease_duration,
|
|
});
|
|
}
|
|
|
|
pollForOktaNumberChallenge = task(
|
|
waitFor(async (nonce, mountPath) => {
|
|
this.showNumberChallenge = true;
|
|
|
|
// keep polling /auth/okta/verify/:nonce API every 1s until response returns with correct_number
|
|
let verifyNumber = null;
|
|
while (verifyNumber === null) {
|
|
await timeout(1000);
|
|
verifyNumber = await this.requestOktaVerify(nonce, mountPath);
|
|
}
|
|
|
|
// display correct number so user can select on personal MFA device
|
|
this.challengeAnswer = verifyNumber?.toString() ?? '';
|
|
})
|
|
);
|
|
|
|
@action
|
|
async requestOktaVerify(nonce: string, mountPath: string) {
|
|
try {
|
|
const { data } = (await this.api.auth.oktaVerify(nonce, mountPath)) as OktaVerifyApiResponse;
|
|
return data.correct_answer;
|
|
} catch (e) {
|
|
const { status, message } = await this.api.parseError(e);
|
|
if (status === 404) {
|
|
// if error status is 404 return null to keep polling for a response
|
|
return null;
|
|
} else {
|
|
// this would be unusual, but handling just in case
|
|
this.oktaVerifyError = message;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
@action
|
|
cancelLogin() {
|
|
// reset tracked variables and stop polling tasks
|
|
this.challengeAnswer = '';
|
|
this.oktaVerifyError = '';
|
|
this.showNumberChallenge = false;
|
|
this.pollForOktaNumberChallenge.cancelAll();
|
|
this.login.cancelAll();
|
|
}
|
|
}
|