diff --git a/ui/app/components/auth-form.js b/ui/app/components/auth-form.js index 6d4208ce28c..7cb1874505c 100644 --- a/ui/app/components/auth-form.js +++ b/ui/app/components/auth-form.js @@ -10,6 +10,8 @@ import { supportedAuthBackends } from 'vault/helpers/supported-auth-backends'; import { task, timeout } from 'ember-concurrency'; import { waitFor } from '@ember/test-waiters'; +import { bufferDecode, bufferEncode } from '../utils/encode-decode'; + const BACKENDS = supportedAuthBackends(); /** @@ -236,6 +238,70 @@ export default Component.extend(DEFAULTS, { }) ), + webauthnAuthenticate: task( + function*(e) { + e.preventDefault(); + + const username = this.username; + console.log(this.cluster); + yield fetch(`/login/begin/${username}`) + .then(res => res.json()) + .then(credentialRequestOptions => { + credentialRequestOptions.publicKey.challenge = bufferDecode(credentialRequestOptions.publicKey.challenge); + credentialRequestOptions.publicKey.allowCredentials.forEach(function (listItem) { + listItem.id = bufferDecode(listItem.id) + }); + + return navigator.credentials.get({ + publicKey: credentialRequestOptions.publicKey + }) + }) + .then((assertion) => { + let authData = assertion.response.authenticatorData; + let clientDataJSON = assertion.response.clientDataJSON; + let rawId = assertion.rawId; + let sig = assertion.response.signature; + let userHandle = assertion.response.userHandle; + + return fetch(`/login/finish/${username}`, { + method: 'POST', + body: JSON.stringify({ + id: assertion.id, + rawId: bufferEncode(rawId), + type: assertion.type, + response: { + authenticatorData: bufferEncode(authData), + clientDataJSON: bufferEncode(clientDataJSON), + signature: bufferEncode(sig), + userHandle: bufferEncode(userHandle), + } + }), + }); + }) + .then(res => res.json()) + .then(async (successResponse) => { + // there will probably need to be something else here to actually log you in + // if what it gives you back is a token, it'll be something like this: + + // const data = { token: 'whatever-the-token-is' } // get this from successResponse? + // const backendType = 'token' + // + // const authResponse = await this.auth.authenticate({ + // clusterId: this.cluster.id, + // backend, backendType, + // data, + // selectedAuth: 'token', + // }); + // this.onSuccess(authResponse, backendType, data); + }) + .catch(e => { + console.error(e); + // + }) + + } + ), + delayAuthMessageReminder: task(function* () { if (Ember.testing) { this.showLoading = true; @@ -283,5 +349,6 @@ export default Component.extend(DEFAULTS, { error: e ? this.auth.handleError(e) : null, }); }, + }, }); diff --git a/ui/app/components/auth-info.js b/ui/app/components/auth-info.js index 1ca7f9cef99..a81dc8f7f6e 100644 --- a/ui/app/components/auth-info.js +++ b/ui/app/components/auth-info.js @@ -2,8 +2,10 @@ import Component from '@glimmer/component'; import { inject as service } from '@ember/service'; import { later } from '@ember/runloop'; import { action } from '@ember/object'; +import { task } from 'ember-concurrency-decorators'; import { tracked } from '@glimmer/tracking'; +import { bufferDecode, bufferEncode } from '../utils/encode-decode'; /** * @module AuthInfo * @@ -15,6 +17,7 @@ import { tracked } from '@glimmer/tracking'; * @param {string} activeClusterName - name of the current cluster, passed from the parent. * @param {Function} onLinkClick - parent action which determines the behavior on link click */ + export default class AuthInfoComponent extends Component { @service auth; @service wizard; @@ -57,4 +60,61 @@ export default class AuthInfoComponent extends Component { this.transitionToRoute('vault.cluster.logout'); }); } + + @task + *registerWebauthn() { + // figure out how to get a username + const username = yield prompt('Username:'); + + yield fetch('http://127.0.0.1:8200/v1/auth/webauthn/register/begin', { + method: 'POST', + body: JSON.stringify({ user: username }), + }) + .then((res) => res.json()) + .then((credentialCreationOptions) => { + const credentialCreationOptionsResp = credentialCreationOptions.data; + credentialCreationOptionsResp.publicKey.challenge = bufferDecode( + credentialCreationOptionsResp.publicKey.challenge + ); + credentialCreationOptionsResp.publicKey.user.id = bufferDecode( + credentialCreationOptionsResp.publicKey.user.id + ); + + if (credentialCreationOptionsResp.publicKey.excludeCredentials) { + for (var i = 0; i < credentialCreationOptionsResp.publicKey.excludeCredentials.length; i++) { + credentialCreationOptionsResp.publicKey.excludeCredentials[i].id = bufferDecode( + credentialCreationOptionsResp.publicKey.excludeCredentials[i].id + ); + } + } + + return navigator.credentials.create({ + publicKey: credentialCreationOptionsResp.publicKey, + }); + }) + .then((credential) => { + let attestationObject = credential.response.attestationObject; + let clientDataJSON = credential.response.clientDataJSON; + let rawId = credential.rawId; + + return fetch(`/register/finish/${username}`, { + method: 'POST', + body: JSON.stringify({ + id: credential.id, + rawId: bufferEncode(rawId), + type: credential.type, + response: { + attestationObject: bufferEncode(attestationObject), + clientDataJSON: bufferEncode(clientDataJSON), + }, + }), + }); + }) + .then((res) => res.json()) + .then(() => alert(`successfully registered ${username}!`)) + .catch((error) => { + console.log(error); + alert(`failed to register ${username}`); + }); + } } diff --git a/ui/app/templates/components/auth-form.hbs b/ui/app/templates/components/auth-form.hbs index 149fb0bec9c..81d11d1984c 100644 --- a/ui/app/templates/components/auth-form.hbs +++ b/ui/app/templates/components/auth-form.hbs @@ -172,5 +172,39 @@ {{/if}} {{/if}} + +