stages/authenticator_webauthn: add MDS support (#9114)
* web: align style to show current user for webauthn enroll
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* ask for aaguid
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* initial MDS import
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* add API
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* add restriction
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* fix api, add actual restriction
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* default authenticator name based on aaguid
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* connect device with device type
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* fix typo in webauthn stage name
this typo has been around for 3 years 8708e487ae (diff-bb4aee4a37f4b95c8daa7beb6bf6251d8d2b6deb8c16dce0cd7cb0d6cd71900aR16)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* add fido2 dep
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* add CI pipeline to automate updating blob
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* fix tests, include device type
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* add tests
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* exclude icon for now
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* add passkeys aaguid
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* make special unknown device type work, add docs
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
---------
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
@ -5,7 +5,7 @@ import "@goauthentik/admin/stages/authenticator_sms/AuthenticatorSMSStageForm";
|
||||
import "@goauthentik/admin/stages/authenticator_static/AuthenticatorStaticStageForm";
|
||||
import "@goauthentik/admin/stages/authenticator_totp/AuthenticatorTOTPStageForm";
|
||||
import "@goauthentik/admin/stages/authenticator_validate/AuthenticatorValidateStageForm";
|
||||
import "@goauthentik/admin/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm";
|
||||
import "@goauthentik/admin/stages/authenticator_webauthn/AuthenticatorWebAuthnStageForm";
|
||||
import "@goauthentik/admin/stages/captcha/CaptchaStageForm";
|
||||
import "@goauthentik/admin/stages/consent/ConsentStageForm";
|
||||
import "@goauthentik/admin/stages/deny/DenyStageForm";
|
||||
|
@ -5,7 +5,7 @@ import "@goauthentik/admin/stages/authenticator_sms/AuthenticatorSMSStageForm";
|
||||
import "@goauthentik/admin/stages/authenticator_static/AuthenticatorStaticStageForm";
|
||||
import "@goauthentik/admin/stages/authenticator_totp/AuthenticatorTOTPStageForm";
|
||||
import "@goauthentik/admin/stages/authenticator_validate/AuthenticatorValidateStageForm";
|
||||
import "@goauthentik/admin/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm";
|
||||
import "@goauthentik/admin/stages/authenticator_webauthn/AuthenticatorWebAuthnStageForm";
|
||||
import "@goauthentik/admin/stages/captcha/CaptchaStageForm";
|
||||
import "@goauthentik/admin/stages/consent/ConsentStageForm";
|
||||
import "@goauthentik/admin/stages/deny/DenyStageForm";
|
||||
|
@ -1,7 +1,12 @@
|
||||
import { RenderFlowOption } from "@goauthentik/admin/flows/utils";
|
||||
import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
|
||||
import {
|
||||
DataProvision,
|
||||
DualSelectPair,
|
||||
} from "@goauthentik/authentik/elements/ak-dual-select/types";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/elements/ak-dual-select/ak-dual-select-provider";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import "@goauthentik/elements/forms/Radio";
|
||||
import "@goauthentik/elements/forms/SearchSelect";
|
||||
@ -11,8 +16,8 @@ import { TemplateResult, html } from "lit";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
|
||||
import {
|
||||
AuthenticateWebAuthnStage,
|
||||
AuthenticatorAttachmentEnum,
|
||||
AuthenticatorWebAuthnStage,
|
||||
Flow,
|
||||
FlowsApi,
|
||||
FlowsInstancesListDesignationEnum,
|
||||
@ -20,28 +25,39 @@ import {
|
||||
ResidentKeyRequirementEnum,
|
||||
StagesApi,
|
||||
UserVerificationEnum,
|
||||
WebAuthnDeviceType,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-stage-authenticator-webauthn-form")
|
||||
export class AuthenticateWebAuthnStageForm extends BaseStageForm<AuthenticateWebAuthnStage> {
|
||||
loadInstance(pk: string): Promise<AuthenticateWebAuthnStage> {
|
||||
export class AuthenticatorWebAuthnStageForm extends BaseStageForm<AuthenticatorWebAuthnStage> {
|
||||
deviceTypeRestrictionPair(item: WebAuthnDeviceType): DualSelectPair {
|
||||
const label = item.description ? item.description : item.aaguid;
|
||||
return [
|
||||
item.aaguid,
|
||||
html`<div class="selection-main">${label}</div>
|
||||
<div class="selection-desc">${item.aaguid}</div>`,
|
||||
label,
|
||||
];
|
||||
}
|
||||
|
||||
loadInstance(pk: string): Promise<AuthenticatorWebAuthnStage> {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorWebauthnRetrieve({
|
||||
stageUuid: pk,
|
||||
});
|
||||
}
|
||||
|
||||
async send(data: AuthenticateWebAuthnStage): Promise<AuthenticateWebAuthnStage> {
|
||||
async send(data: AuthenticatorWebAuthnStage): Promise<AuthenticatorWebAuthnStage> {
|
||||
if (data.authenticatorAttachment?.toString() === "") {
|
||||
data.authenticatorAttachment = null;
|
||||
}
|
||||
if (this.instance) {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorWebauthnUpdate({
|
||||
stageUuid: this.instance.pk || "",
|
||||
authenticateWebAuthnStageRequest: data,
|
||||
authenticatorWebAuthnStageRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorWebauthnCreate({
|
||||
authenticateWebAuthnStageRequest: data,
|
||||
authenticatorWebAuthnStageRequest: data,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -164,6 +180,38 @@ export class AuthenticateWebAuthnStageForm extends BaseStageForm<AuthenticateWeb
|
||||
>
|
||||
</ak-radio>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Device type restrictions")}
|
||||
name="deviceTypeRestrictions"
|
||||
>
|
||||
<ak-dual-select-provider
|
||||
.provider=${(page: number, search?: string): Promise<DataProvision> => {
|
||||
return new StagesApi(DEFAULT_CONFIG)
|
||||
.stagesAuthenticatorWebauthnDeviceTypesList({
|
||||
page: page,
|
||||
search: search,
|
||||
})
|
||||
.then((results) => {
|
||||
return {
|
||||
pagination: results.pagination,
|
||||
options: results.results.map(
|
||||
this.deviceTypeRestrictionPair,
|
||||
),
|
||||
};
|
||||
});
|
||||
}}
|
||||
.selected=${(this.instance?.deviceTypeRestrictionsObj ?? []).map(
|
||||
this.deviceTypeRestrictionPair,
|
||||
)}
|
||||
available-label="${msg("Available Device types")}"
|
||||
selected-label="${msg("Selected Device types")}"
|
||||
></ak-dual-select-provider>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
"Optionally restrict which WebAuthn device types may be used. When no device types are selected, all devices are allowed.",
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Configuration flow")}
|
||||
name="configureFlow"
|
@ -10,6 +10,7 @@ import { BaseStage } from "@goauthentik/flow/stages/base";
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, css, html, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
@ -130,6 +131,17 @@ export class WebAuthnAuthenticatorRegisterStage extends BaseStage<
|
||||
</header>
|
||||
<div class="pf-c-login__main-body">
|
||||
<form class="pf-c-form">
|
||||
<ak-form-static
|
||||
class="pf-c-form__group"
|
||||
userAvatar="${this.challenge.pendingUserAvatar}"
|
||||
user=${this.challenge.pendingUser}
|
||||
>
|
||||
<div slot="link">
|
||||
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}"
|
||||
>${msg("Not you?")}</a
|
||||
>
|
||||
</div>
|
||||
</ak-form-static>
|
||||
<ak-empty-state
|
||||
?loading="${this.registerRunning}"
|
||||
header=${this.registerRunning
|
||||
|
Reference in New Issue
Block a user