stages/authenticator_validate: send challenge for each device
This commit is contained in:
@ -42,7 +42,7 @@ export class AuthenticatorStaticStage extends BaseStage {
|
||||
</h1>
|
||||
</header>
|
||||
<div class="pf-c-login__main-body">
|
||||
<form class="pf-c-form" @submit=${(e: Event) => { this.submit(e); }}>
|
||||
<form class="pf-c-form" @submit=${(e: Event) => { this.submitForm(e); }}>
|
||||
<div class="pf-c-form__group">
|
||||
<div class="form-control-static">
|
||||
<div class="left">
|
||||
|
||||
@ -30,7 +30,7 @@ export class AuthenticatorTOTPStage extends BaseStage {
|
||||
</h1>
|
||||
</header>
|
||||
<div class="pf-c-login__main-body">
|
||||
<form class="pf-c-form" @submit=${(e: Event) => { this.submit(e); }}>
|
||||
<form class="pf-c-form" @submit=${(e: Event) => { this.submitForm(e); }}>
|
||||
<div class="pf-c-form__group">
|
||||
<div class="form-control-static">
|
||||
<div class="left">
|
||||
|
||||
@ -9,13 +9,18 @@ export enum DeviceClasses {
|
||||
WEBAUTHN = "webauthn",
|
||||
}
|
||||
|
||||
export interface DeviceChallenge {
|
||||
device_class: DeviceClasses;
|
||||
device_uid: string;
|
||||
challenge: unknown;
|
||||
}
|
||||
|
||||
export interface AuthenticatorValidateStageChallenge extends WithUserInfoChallenge {
|
||||
users_device_classes: DeviceClasses[];
|
||||
class_challenges: { [key in DeviceClasses]: unknown };
|
||||
device_challenges: DeviceChallenge[];
|
||||
}
|
||||
|
||||
export interface AuthenticatorValidateStageChallengeResponse {
|
||||
device_challenges: { [key in DeviceClasses]: unknown} ;
|
||||
response: DeviceChallenge;
|
||||
}
|
||||
|
||||
@customElement("ak-stage-authenticator-validate")
|
||||
@ -24,13 +29,24 @@ export class AuthenticatorValidateStage extends BaseStage implements StageHost {
|
||||
@property({ attribute: false })
|
||||
challenge?: AuthenticatorValidateStageChallenge;
|
||||
|
||||
renderDeviceClass(deviceClass: DeviceClasses): TemplateResult {
|
||||
switch (deviceClass) {
|
||||
@property({attribute: false})
|
||||
selectedDeviceChallenge?: DeviceChallenge;
|
||||
|
||||
renderDeviceChallenge(): TemplateResult {
|
||||
if (!this.selectedDeviceChallenge) {
|
||||
return html``;
|
||||
}
|
||||
switch (this.selectedDeviceChallenge?.device_class) {
|
||||
case DeviceClasses.STATIC:
|
||||
case DeviceClasses.TOTP:
|
||||
// TODO: Create input for code
|
||||
return html``;
|
||||
case DeviceClasses.WEBAUTHN:
|
||||
return html`<ak-stage-authenticator-validate-webauthn .host=${this} .challenge=${this.challenge}></ak-stage-authenticator-validate-webauthn>`;
|
||||
return html`<ak-stage-authenticator-validate-webauthn
|
||||
.host=${this}
|
||||
.challenge=${this.challenge}
|
||||
.deviceChallenge=${this.selectedDeviceChallenge}>
|
||||
</ak-stage-authenticator-validate-webauthn>`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,9 +56,13 @@ export class AuthenticatorValidateStage extends BaseStage implements StageHost {
|
||||
|
||||
render(): TemplateResult {
|
||||
// User only has a single device class, so we don't show a picker
|
||||
if (this.challenge?.users_device_classes.length === 1) {
|
||||
return this.renderDeviceClass(this.challenge.users_device_classes[0]);
|
||||
if (this.challenge?.device_challenges.length === 1) {
|
||||
this.selectedDeviceChallenge = this.challenge.device_challenges[0];
|
||||
}
|
||||
if (this.selectedDeviceChallenge) {
|
||||
return this.renderDeviceChallenge();
|
||||
}
|
||||
// TODO: Create picker between challenges
|
||||
return html`ak-stage-authenticator-validate`;
|
||||
}
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ import { customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { SpinnerSize } from "../../Spinner";
|
||||
import { transformAssertionForServer, transformCredentialRequestOptions } from "../authenticator_webauthn/utils";
|
||||
import { BaseStage } from "../base";
|
||||
import { AuthenticatorValidateStageChallenge, DeviceClasses } from "./AuthenticatorValidateStage";
|
||||
import { AuthenticatorValidateStageChallenge, DeviceChallenge } from "./AuthenticatorValidateStage";
|
||||
|
||||
@customElement("ak-stage-authenticator-validate-webauthn")
|
||||
export class AuthenticatorValidateStageWebAuthn extends BaseStage {
|
||||
@ -11,6 +11,9 @@ export class AuthenticatorValidateStageWebAuthn extends BaseStage {
|
||||
@property({attribute: false})
|
||||
challenge?: AuthenticatorValidateStageChallenge;
|
||||
|
||||
@property({attribute: false})
|
||||
deviceChallenge?: DeviceChallenge;
|
||||
|
||||
@property({ type: Boolean })
|
||||
authenticateRunning = false;
|
||||
|
||||
@ -20,7 +23,7 @@ export class AuthenticatorValidateStageWebAuthn extends BaseStage {
|
||||
async authenticate(): Promise<void> {
|
||||
// convert certain members of the PublicKeyCredentialRequestOptions into
|
||||
// byte arrays as expected by the spec.
|
||||
const credentialRequestOptions = <PublicKeyCredentialRequestOptions>this.challenge?.class_challenges[DeviceClasses.WEBAUTHN];
|
||||
const credentialRequestOptions = <PublicKeyCredentialRequestOptions>this.deviceChallenge?.challenge;
|
||||
const transformedCredentialRequestOptions = transformCredentialRequestOptions(credentialRequestOptions);
|
||||
|
||||
// request the authenticator to create an assertion signature using the
|
||||
@ -44,7 +47,11 @@ export class AuthenticatorValidateStageWebAuthn extends BaseStage {
|
||||
// post the assertion to the server for verification.
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.set(`response[${DeviceClasses.WEBAUTHN}]`, JSON.stringify(transformedAssertionForServer));
|
||||
formData.set("response", JSON.stringify(<DeviceChallenge>{
|
||||
device_class: this.deviceChallenge?.device_class,
|
||||
device_uid: this.deviceChallenge?.device_uid,
|
||||
challenge: transformedAssertionForServer,
|
||||
}));
|
||||
await this.host?.submit(formData);
|
||||
} catch (err) {
|
||||
throw new Error(gettext(`Error when validating assertion on server: ${err}`));
|
||||
|
||||
@ -36,7 +36,7 @@ export class ConsentStage extends BaseStage {
|
||||
</h1>
|
||||
</header>
|
||||
<div class="pf-c-login__main-body">
|
||||
<form class="pf-c-form" @submit=${(e: Event) => { this.submit(e); }}>
|
||||
<form class="pf-c-form" @submit=${(e: Event) => { this.submitForm(e); }}>
|
||||
<div class="pf-c-form__group">
|
||||
<div class="form-control-static">
|
||||
<div class="left">
|
||||
|
||||
@ -26,7 +26,7 @@ export class EmailStage extends BaseStage {
|
||||
</h1>
|
||||
</header>
|
||||
<div class="pf-c-login__main-body">
|
||||
<form class="pf-c-form" @submit=${(e: Event) => { this.submit(e); }}>
|
||||
<form class="pf-c-form" @submit=${(e: Event) => { this.submitForm(e); }}>
|
||||
<div class="pf-c-form__group">
|
||||
<p>
|
||||
${gettext("Check your Emails for a password reset link.")}
|
||||
|
||||
@ -74,7 +74,7 @@ export class IdentificationStage extends BaseStage {
|
||||
</h1>
|
||||
</header>
|
||||
<div class="pf-c-login__main-body">
|
||||
<form class="pf-c-form" @submit=${(e: Event) => {this.submit(e);}}>
|
||||
<form class="pf-c-form" @submit=${(e: Event) => {this.submitForm(e);}}>
|
||||
${this.challenge.application_pre ?
|
||||
html`<p>
|
||||
${gettext(`Login to continue to ${this.challenge.application_pre}.`)}
|
||||
|
||||
@ -29,7 +29,7 @@ export class PasswordStage extends BaseStage {
|
||||
</h1>
|
||||
</header>
|
||||
<div class="pf-c-login__main-body">
|
||||
<form class="pf-c-form" @submit=${(e: Event) => {this.submit(e);}}>
|
||||
<form class="pf-c-form" @submit=${(e: Event) => {this.submitForm(e);}}>
|
||||
<div class="pf-c-form__group">
|
||||
<div class="form-control-static">
|
||||
<div class="left">
|
||||
|
||||
@ -119,7 +119,7 @@ export class PromptStage extends BaseStage {
|
||||
</h1>
|
||||
</header>
|
||||
<div class="pf-c-login__main-body">
|
||||
<form class="pf-c-form" @submit=${(e: Event) => {this.submit(e);}}>
|
||||
<form class="pf-c-form" @submit=${(e: Event) => {this.submitForm(e);}}>
|
||||
${this.challenge.fields.map((prompt) => {
|
||||
return html`<ak-form-element
|
||||
label="${prompt.label}"
|
||||
|
||||
Reference in New Issue
Block a user