web/flows: fix webauthn retry (#8599)
* web/flows: fix retry button on webauthn device stage Signed-off-by: Jens Langhammer <jens@goauthentik.io> * web/flows: rework webauth register design to match Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
@ -89,8 +89,8 @@ export class AuthenticatorValidateStageWebAuthn extends BaseDeviceStage<
|
|||||||
this.authenticating = true;
|
this.authenticating = true;
|
||||||
this.authenticate()
|
this.authenticate()
|
||||||
.catch((e: Error) => {
|
.catch((e: Error) => {
|
||||||
console.warn(`authentik/flows/authenticator_validate/webauthn: ${e.toString()}`);
|
console.warn("authentik/flows/authenticator_validate/webauthn: failed to auth", e);
|
||||||
this.errorMessage = msg("Authentication failed.");
|
this.errorMessage = msg("Authentication failed. Please try again.");
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.authenticating = false;
|
this.authenticating = false;
|
||||||
@ -110,12 +110,13 @@ export class AuthenticatorValidateStageWebAuthn extends BaseDeviceStage<
|
|||||||
>
|
>
|
||||||
</ak-empty-state>
|
</ak-empty-state>
|
||||||
<div class="pf-c-form__group pf-m-action">
|
<div class="pf-c-form__group pf-m-action">
|
||||||
${this.errorMessage
|
${!this.authenticating
|
||||||
? html` <button
|
? html` <button
|
||||||
class="pf-c-button pf-m-primary pf-m-block"
|
class="pf-c-button pf-m-primary pf-m-block"
|
||||||
@click=${() => {
|
@click=${() => {
|
||||||
this.authenticateWrapper();
|
this.authenticateWrapper();
|
||||||
}}
|
}}
|
||||||
|
type="button"
|
||||||
>
|
>
|
||||||
${msg("Retry authentication")}
|
${msg("Retry authentication")}
|
||||||
</button>`
|
</button>`
|
||||||
|
@ -4,11 +4,11 @@ import {
|
|||||||
transformCredentialCreateOptions,
|
transformCredentialCreateOptions,
|
||||||
transformNewAssertionForServer,
|
transformNewAssertionForServer,
|
||||||
} from "@goauthentik/common/helpers/webauthn";
|
} from "@goauthentik/common/helpers/webauthn";
|
||||||
import { PFSize } from "@goauthentik/elements/Spinner";
|
import "@goauthentik/elements/EmptyState";
|
||||||
import { BaseStage } from "@goauthentik/flow/stages/base";
|
import { BaseStage } from "@goauthentik/flow/stages/base";
|
||||||
|
|
||||||
import { msg, str } from "@lit/localize";
|
import { msg, str } from "@lit/localize";
|
||||||
import { CSSResult, TemplateResult, html } from "lit";
|
import { CSSResult, TemplateResult, css, html, nothing } from "lit";
|
||||||
import { customElement, property } from "lit/decorators.js";
|
import { customElement, property } from "lit/decorators.js";
|
||||||
|
|
||||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||||
@ -41,7 +41,24 @@ export class WebAuthnAuthenticatorRegisterStage extends BaseStage<
|
|||||||
publicKeyCredentialCreateOptions?: PublicKeyCredentialCreationOptions;
|
publicKeyCredentialCreateOptions?: PublicKeyCredentialCreationOptions;
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult[] {
|
||||||
return [PFBase, PFLogin, PFFormControl, PFForm, PFTitle, PFButton];
|
return [
|
||||||
|
PFBase,
|
||||||
|
PFLogin,
|
||||||
|
PFFormControl,
|
||||||
|
PFForm,
|
||||||
|
PFTitle,
|
||||||
|
PFButton,
|
||||||
|
// FIXME: this is technically duplicate with ../authenticator_validate/base.ts
|
||||||
|
css`
|
||||||
|
.pf-c-form__group.pf-m-action {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: calc(var(--pf-c-form__group--m-action--MarginTop) / 2);
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
async register(): Promise<void> {
|
async register(): Promise<void> {
|
||||||
@ -69,9 +86,14 @@ export class WebAuthnAuthenticatorRegisterStage extends BaseStage<
|
|||||||
// post the transformed credential data to the server for validation
|
// post the transformed credential data to the server for validation
|
||||||
// and storing the public key
|
// and storing the public key
|
||||||
try {
|
try {
|
||||||
await this.host?.submit({
|
await this.host?.submit(
|
||||||
|
{
|
||||||
response: newAssertionForServer,
|
response: newAssertionForServer,
|
||||||
});
|
},
|
||||||
|
{
|
||||||
|
invisible: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new Error(msg(str`Server validation of credential failed: ${err}`));
|
throw new Error(msg(str`Server validation of credential failed: ${err}`));
|
||||||
}
|
}
|
||||||
@ -84,8 +106,8 @@ export class WebAuthnAuthenticatorRegisterStage extends BaseStage<
|
|||||||
this.registerRunning = true;
|
this.registerRunning = true;
|
||||||
this.register()
|
this.register()
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
console.error(e);
|
console.warn("authentik/flows/authenticator_webauthn: failed to register", e);
|
||||||
this.registerMessage = e.toString();
|
this.registerMessage = msg("Failed to register. Please try again.");
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.registerRunning = false;
|
this.registerRunning = false;
|
||||||
@ -104,42 +126,37 @@ export class WebAuthnAuthenticatorRegisterStage extends BaseStage<
|
|||||||
|
|
||||||
render(): TemplateResult {
|
render(): TemplateResult {
|
||||||
return html`<header class="pf-c-login__main-header">
|
return html`<header class="pf-c-login__main-header">
|
||||||
<h1 class="pf-c-title pf-m-3xl">
|
<h1 class="pf-c-title pf-m-3xl">${this.challenge?.flowInfo?.title}</h1>
|
||||||
${this.challenge?.flowInfo?.title}
|
|
||||||
</h1>
|
|
||||||
</header>
|
</header>
|
||||||
<div class="pf-c-login__main-body">
|
<div class="pf-c-login__main-body">
|
||||||
${
|
<form class="pf-c-form">
|
||||||
this.registerRunning
|
<ak-empty-state
|
||||||
? html`<div class="pf-c-empty-state__content">
|
?loading="${this.registerRunning}"
|
||||||
<div class="pf-l-bullseye">
|
header=${this.registerRunning
|
||||||
<div class="pf-l-bullseye__item">
|
? msg("Registering...")
|
||||||
<ak-spinner size="${PFSize.XLarge}"></ak-spinner>
|
: this.registerMessage || msg("Failed to register")}
|
||||||
</div>
|
icon="fa-times"
|
||||||
</div>
|
>
|
||||||
</div>`
|
</ak-empty-state>
|
||||||
: html` <div class="pf-c-form__group pf-m-action">
|
|
||||||
${this.challenge?.responseErrors
|
${this.challenge?.responseErrors
|
||||||
? html`<p class="pf-m-block">
|
? html`<p class="pf-m-block">
|
||||||
${this.challenge.responseErrors["response"][0].string}
|
${this.challenge.responseErrors["response"][0].string}
|
||||||
</p>`
|
</p>`
|
||||||
: html``}
|
: html``}
|
||||||
<p class="pf-m-block">${this.registerMessage}</p>
|
<div class="pf-c-form__group pf-m-action">
|
||||||
<button
|
${!this.registerRunning
|
||||||
|
? html` <button
|
||||||
class="pf-c-button pf-m-primary pf-m-block"
|
class="pf-c-button pf-m-primary pf-m-block"
|
||||||
@click=${() => {
|
@click=${() => {
|
||||||
this.registerWrapper();
|
this.registerWrapper();
|
||||||
}}
|
}}
|
||||||
|
type="button"
|
||||||
>
|
>
|
||||||
${msg("Register device")}
|
${msg("Retry registration")}
|
||||||
</button>
|
</button>`
|
||||||
</div>`
|
: nothing}
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
<footer class="pf-c-login__main-footer">
|
</div>`;
|
||||||
<ul class="pf-c-login__main-footer-links">
|
|
||||||
</ul>
|
|
||||||
</footer>`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user