stages/authenticator_webauthn: migrate to SPA
This commit is contained in:
@ -1,10 +1,23 @@
|
||||
import { gettext } from "django";
|
||||
import { customElement, html, LitElement, property, TemplateResult } from "lit-element";
|
||||
import { customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { WithUserInfoChallenge } from "../../../api/Flows";
|
||||
import { SpinnerSize } from "../../Spinner";
|
||||
import { getCredentialCreateOptionsFromServer, postNewAssertionToServer, transformCredentialCreateOptions, transformNewAssertionForServer } from "./utils";
|
||||
import { BaseStage } from "../base";
|
||||
import { Assertion, transformCredentialCreateOptions, transformNewAssertionForServer } from "./utils";
|
||||
|
||||
@customElement("ak-stage-webauthn-register")
|
||||
export class WebAuthnRegister extends LitElement {
|
||||
export interface WebAuthnAuthenticatorRegisterChallenge extends WithUserInfoChallenge {
|
||||
registration: PublicKeyCredentialCreationOptions;
|
||||
}
|
||||
|
||||
export interface WebAuthnAuthenticatorRegisterChallengeResponse {
|
||||
response: Assertion;
|
||||
}
|
||||
|
||||
@customElement("ak-stage-authenticator-webauthn-register")
|
||||
export class WebAuthnAuthenticatorRegisterStage extends BaseStage {
|
||||
|
||||
@property({ attribute: false })
|
||||
challenge?: WebAuthnAuthenticatorRegisterChallenge;
|
||||
|
||||
@property({type: Boolean})
|
||||
registerRunning = false;
|
||||
@ -17,17 +30,12 @@ export class WebAuthnRegister extends LitElement {
|
||||
}
|
||||
|
||||
async register(): Promise<void> {
|
||||
// post the data to the server to generate the PublicKeyCredentialCreateOptions
|
||||
let credentialCreateOptionsFromServer;
|
||||
try {
|
||||
credentialCreateOptionsFromServer = await getCredentialCreateOptionsFromServer();
|
||||
} catch (err) {
|
||||
throw new Error(gettext(`Failed to generate credential request options: ${err}`));
|
||||
if (!this.challenge) {
|
||||
return;
|
||||
}
|
||||
|
||||
// convert certain members of the PublicKeyCredentialCreateOptions into
|
||||
// byte arrays as expected by the spec.
|
||||
const publicKeyCredentialCreateOptions = transformCredentialCreateOptions(credentialCreateOptionsFromServer);
|
||||
const publicKeyCredentialCreateOptions = transformCredentialCreateOptions(this.challenge?.registration);
|
||||
|
||||
// request the authenticator(s) to create a new credential keypair.
|
||||
let credential;
|
||||
@ -49,7 +57,10 @@ export class WebAuthnRegister extends LitElement {
|
||||
// post the transformed credential data to the server for validation
|
||||
// and storing the public key
|
||||
try {
|
||||
await postNewAssertionToServer(newAssertionForServer);
|
||||
const response = <WebAuthnAuthenticatorRegisterChallengeResponse>{
|
||||
response: newAssertionForServer
|
||||
};
|
||||
await this.host?.submit(JSON.stringify(response));
|
||||
} catch (err) {
|
||||
throw new Error(gettext(`Server validation of credential failed: ${err}`));
|
||||
}
|
@ -84,38 +84,6 @@ export function transformNewAssertionForServer(newAssertion: PublicKeyCredential
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Post the assertion to the server for validation and logging the user in.
|
||||
* @param {Object} assertionDataForServer
|
||||
*/
|
||||
export async function postNewAssertionToServer(assertionDataForServer: Assertion): Promise<GenericResponse> {
|
||||
const formData = new FormData();
|
||||
Object.entries(assertionDataForServer).forEach(([key, value]) => {
|
||||
formData.set(key, value);
|
||||
});
|
||||
|
||||
return await fetchJSON(
|
||||
"/-/user/authenticator/webauthn/verify-credential-info/", {
|
||||
method: "POST",
|
||||
body: formData
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get PublicKeyCredentialRequestOptions for this user from the server
|
||||
* formData of the registration form
|
||||
* @param {FormData} formData
|
||||
*/
|
||||
export async function getCredentialCreateOptionsFromServer(): Promise<GenericResponse> {
|
||||
return await fetchJSON(
|
||||
"/-/user/authenticator/webauthn/begin-activate/",
|
||||
{
|
||||
method: "POST",
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get PublicKeyCredentialRequestOptions for this user from the server
|
||||
* formData of the registration form
|
||||
|
@ -31,9 +31,4 @@ import "./pages/applications/ApplicationViewPage";
|
||||
import "./pages/tokens/UserTokenList";
|
||||
import "./pages/LibraryPage";
|
||||
|
||||
import "./elements/stages/authenticator_webauthn/WebAuthnRegister";
|
||||
import "./elements/stages/authenticator_webauthn/WebAuthnAuth";
|
||||
import "./elements/stages/authenticator_validate/AuthenticatorValidateStage";
|
||||
import "./elements/stages/identification/IdentificationStage";
|
||||
|
||||
import "./interfaces/AdminInterface";
|
||||
|
@ -10,6 +10,7 @@ import "../../elements/stages/autosubmit/AutosubmitStage";
|
||||
import "../../elements/stages/prompt/PromptStage";
|
||||
import "../../elements/stages/authenticator_totp/AuthenticatorTOTPStage";
|
||||
import "../../elements/stages/authenticator_static/AuthenticatorStaticStage";
|
||||
import "../../elements/stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage";
|
||||
import { ShellChallenge, Challenge, ChallengeTypes, Flow, RedirectChallenge } from "../../api/Flows";
|
||||
import { DefaultClient } from "../../api/Client";
|
||||
import { IdentificationChallenge } from "../../elements/stages/identification/IdentificationStage";
|
||||
@ -20,6 +21,7 @@ import { AutosubmitChallenge } from "../../elements/stages/autosubmit/Autosubmit
|
||||
import { PromptChallenge } from "../../elements/stages/prompt/PromptStage";
|
||||
import { AuthenticatorTOTPChallenge } from "../../elements/stages/authenticator_totp/AuthenticatorTOTPStage";
|
||||
import { AuthenticatorStaticChallenge } from "../../elements/stages/authenticator_static/AuthenticatorStaticStage";
|
||||
import { WebAuthnAuthenticatorRegisterChallenge } from "../../elements/stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage";
|
||||
|
||||
@customElement("ak-flow-executor")
|
||||
export class FlowExecutor extends LitElement {
|
||||
@ -40,14 +42,14 @@ export class FlowExecutor extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
submit(formData?: FormData): void {
|
||||
submit(formData?: string | FormData): Promise<void> {
|
||||
const csrftoken = getCookie("authentik_csrf");
|
||||
const request = new Request(DefaultClient.makeUrl(["flows", "executor", this.flowSlug]), {
|
||||
headers: {
|
||||
"X-CSRFToken": csrftoken,
|
||||
},
|
||||
});
|
||||
fetch(request, {
|
||||
return fetch(request, {
|
||||
method: "POST",
|
||||
mode: "same-origin",
|
||||
body: formData,
|
||||
@ -132,6 +134,8 @@ export class FlowExecutor extends LitElement {
|
||||
return html`<ak-stage-authenticator-totp .host=${this} .challenge=${this.challenge as AuthenticatorTOTPChallenge}></ak-stage-authenticator-totp>`;
|
||||
case "ak-stage-authenticator-static":
|
||||
return html`<ak-stage-authenticator-static .host=${this} .challenge=${this.challenge as AuthenticatorStaticChallenge}></ak-stage-authenticator-static>`;
|
||||
case "ak-stage-authenticator-webauthn-register":
|
||||
return html`<ak-stage-authenticator-webauthn-register .host=${this} .challenge=${this.challenge as WebAuthnAuthenticatorRegisterChallenge}></ak-stage-authenticator-webauthn-register>`;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
Reference in New Issue
Block a user