flows: make use of oneOf OpenAPI to annotate all challenge types

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer
2021-05-24 14:08:54 +02:00
parent 3b41c662ed
commit 6f6ae7831e
41 changed files with 1102 additions and 335 deletions

View File

@ -8,23 +8,3 @@ export interface Error {
export interface ErrorDict {
[key: string]: Error[];
}
export interface Challenge {
type: ChallengeChoices;
component?: string;
title?: string;
response_errors?: ErrorDict;
}
export interface WithUserInfoChallenge extends Challenge {
pending_user: string;
pending_user_avatar: string;
}
export interface ShellChallenge extends Challenge {
body: string;
}
export interface RedirectChallenge extends Challenge {
to: string;
}

View File

@ -25,29 +25,16 @@ import "./stages/identification/IdentificationStage";
import "./stages/password/PasswordStage";
import "./stages/prompt/PromptStage";
import "./sources/plex/PlexLoginInit";
import { ShellChallenge, RedirectChallenge } from "../api/Flows";
import { IdentificationChallenge } from "./stages/identification/IdentificationStage";
import { PasswordChallenge } from "./stages/password/PasswordStage";
import { ConsentChallenge } from "./stages/consent/ConsentStage";
import { EmailChallenge } from "./stages/email/EmailStage";
import { AutosubmitChallenge } from "./stages/autosubmit/AutosubmitStage";
import { PromptChallenge } from "./stages/prompt/PromptStage";
import { AuthenticatorTOTPChallenge } from "./stages/authenticator_totp/AuthenticatorTOTPStage";
import { AuthenticatorStaticChallenge } from "./stages/authenticator_static/AuthenticatorStaticStage";
import { AuthenticatorValidateStageChallenge } from "./stages/authenticator_validate/AuthenticatorValidateStage";
import { WebAuthnAuthenticatorRegisterChallenge } from "./stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage";
import { CaptchaChallenge } from "./stages/captcha/CaptchaStage";
import { StageHost } from "./stages/base";
import { Challenge, ChallengeChoices, Config, FlowsApi } from "authentik-api";
import { Challenge, ChallengeChoices, Config, FlowsApi, RedirectChallenge, ShellChallenge } from "authentik-api";
import { config, DEFAULT_CONFIG } from "../api/Config";
import { ifDefined } from "lit-html/directives/if-defined";
import { until } from "lit-html/directives/until";
import { AccessDeniedChallenge } from "./access_denied/FlowAccessDenied";
import { PFSize } from "../elements/Spinner";
import { TITLE_DEFAULT } from "../constants";
import { configureSentry } from "../api/Sentry";
import { PlexAuthenticationChallenge } from "./sources/plex/PlexLoginInit";
import { AuthenticatorDuoChallenge } from "./stages/authenticator_duo/AuthenticatorDuoStage";
import { ChallengeResponseRequest } from "authentik-api/dist/models/ChallengeResponseRequest";
@customElement("ak-flow-executor")
export class FlowExecutor extends LitElement implements StageHost {
@ -112,18 +99,18 @@ export class FlowExecutor extends LitElement implements StageHost {
});
}
submit<T>(formData?: T): Promise<void> {
submit(payload: ChallengeResponseRequest): Promise<void> {
payload.component = this.challenge.component;
this.loading = true;
return new FlowsApi(DEFAULT_CONFIG).flowsExecutorSolveRaw({
return new FlowsApi(DEFAULT_CONFIG).flowsExecutorSolve({
flowSlug: this.flowSlug,
requestBody: formData || {},
query: window.location.search.substring(1),
}).then((challengeRaw) => {
return challengeRaw.raw.json();
challengeResponseRequest: payload,
}).then((data) => {
this.challenge = data;
this.postUpdate();
}).catch((e: Response) => {
console.debug(e);
this.errorMessage(e.statusText);
}).finally(() => {
this.loading = false;
@ -135,19 +122,18 @@ export class FlowExecutor extends LitElement implements StageHost {
this.config = config;
});
this.loading = true;
new FlowsApi(DEFAULT_CONFIG).flowsExecutorGetRaw({
new FlowsApi(DEFAULT_CONFIG).flowsExecutorGet({
flowSlug: this.flowSlug,
query: window.location.search.substring(1),
}).then((challengeRaw) => {
return challengeRaw.raw.json();
}).then((challenge) => {
this.challenge = challenge as Challenge;
this.challenge = challenge;
// Only set background on first update, flow won't change throughout execution
if (this.challenge?.background) {
this.setBackground(this.challenge.background);
}
this.postUpdate();
}).catch((e: Response) => {
console.debug(e);
// Catch JSON or Update errors
this.errorMessage(e.statusText);
}).finally(() => {
@ -202,35 +188,35 @@ export class FlowExecutor extends LitElement implements StageHost {
case ChallengeChoices.Native:
switch (this.challenge.component) {
case "ak-stage-access-denied":
return html`<ak-stage-access-denied .host=${this} .challenge=${this.challenge as AccessDeniedChallenge}></ak-stage-access-denied>`;
return html`<ak-stage-access-denied .host=${this} .challenge=${this.challenge}></ak-stage-access-denied>`;
case "ak-stage-identification":
return html`<ak-stage-identification .host=${this} .challenge=${this.challenge as IdentificationChallenge}></ak-stage-identification>`;
return html`<ak-stage-identification .host=${this} .challenge=${this.challenge}></ak-stage-identification>`;
case "ak-stage-password":
return html`<ak-stage-password .host=${this} .challenge=${this.challenge as PasswordChallenge}></ak-stage-password>`;
return html`<ak-stage-password .host=${this} .challenge=${this.challenge}></ak-stage-password>`;
case "ak-stage-captcha":
return html`<ak-stage-captcha .host=${this} .challenge=${this.challenge as CaptchaChallenge}></ak-stage-captcha>`;
return html`<ak-stage-captcha .host=${this} .challenge=${this.challenge}></ak-stage-captcha>`;
case "ak-stage-consent":
return html`<ak-stage-consent .host=${this} .challenge=${this.challenge as ConsentChallenge}></ak-stage-consent>`;
return html`<ak-stage-consent .host=${this} .challenge=${this.challenge}></ak-stage-consent>`;
case "ak-stage-dummy":
return html`<ak-stage-dummy .host=${this} .challenge=${this.challenge as Challenge}></ak-stage-dummy>`;
return html`<ak-stage-dummy .host=${this} .challenge=${this.challenge}></ak-stage-dummy>`;
case "ak-stage-email":
return html`<ak-stage-email .host=${this} .challenge=${this.challenge as EmailChallenge}></ak-stage-email>`;
return html`<ak-stage-email .host=${this} .challenge=${this.challenge}></ak-stage-email>`;
case "ak-stage-autosubmit":
return html`<ak-stage-autosubmit .host=${this} .challenge=${this.challenge as AutosubmitChallenge}></ak-stage-autosubmit>`;
return html`<ak-stage-autosubmit .host=${this} .challenge=${this.challenge}></ak-stage-autosubmit>`;
case "ak-stage-prompt":
return html`<ak-stage-prompt .host=${this} .challenge=${this.challenge as PromptChallenge}></ak-stage-prompt>`;
return html`<ak-stage-prompt .host=${this} .challenge=${this.challenge}></ak-stage-prompt>`;
case "ak-stage-authenticator-totp":
return html`<ak-stage-authenticator-totp .host=${this} .challenge=${this.challenge as AuthenticatorTOTPChallenge}></ak-stage-authenticator-totp>`;
return html`<ak-stage-authenticator-totp .host=${this} .challenge=${this.challenge}></ak-stage-authenticator-totp>`;
case "ak-stage-authenticator-duo":
return html`<ak-stage-authenticator-duo .host=${this} .challenge=${this.challenge as AuthenticatorDuoChallenge}></ak-stage-authenticator-duo>`;
return html`<ak-stage-authenticator-duo .host=${this} .challenge=${this.challenge}></ak-stage-authenticator-duo>`;
case "ak-stage-authenticator-static":
return html`<ak-stage-authenticator-static .host=${this} .challenge=${this.challenge as AuthenticatorStaticChallenge}></ak-stage-authenticator-static>`;
return html`<ak-stage-authenticator-static .host=${this} .challenge=${this.challenge}></ak-stage-authenticator-static>`;
case "ak-stage-authenticator-webauthn":
return html`<ak-stage-authenticator-webauthn .host=${this} .challenge=${this.challenge as WebAuthnAuthenticatorRegisterChallenge}></ak-stage-authenticator-webauthn>`;
return html`<ak-stage-authenticator-webauthn .host=${this} .challenge=${this.challenge}></ak-stage-authenticator-webauthn>`;
case "ak-stage-authenticator-validate":
return html`<ak-stage-authenticator-validate .host=${this} .challenge=${this.challenge as AuthenticatorValidateStageChallenge}></ak-stage-authenticator-validate>`;
return html`<ak-stage-authenticator-validate .host=${this} .challenge=${this.challenge}></ak-stage-authenticator-validate>`;
case "ak-flow-sources-plex":
return html`<ak-flow-sources-plex .host=${this} .challenge=${this.challenge as PlexAuthenticationChallenge}></ak-flow-sources-plex>`;
return html`<ak-flow-sources-plex .host=${this} .challenge=${this.challenge}></ak-flow-sources-plex>`;
default:
break;
}
@ -288,8 +274,7 @@ export class FlowExecutor extends LitElement implements StageHost {
</li>`;
}))}
${this.config?.brandingTitle != "authentik" ? html`
<li><a href="https://goauthentik.io">${t`Powered by authentik`}</a></li>
` : html``}
<li><a href="https://goauthentik.io">${t`Powered by authentik`}</a></li>` : html``}
</ul>
</footer>
</div>

View File

@ -1,4 +1,4 @@
import { Challenge } from "authentik-api";
import { AccessDeniedChallenge } from "authentik-api";
import { CSSResult, customElement, html, property, TemplateResult } from "lit-element";
import { BaseStage } from "../stages/base";
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
@ -12,10 +12,6 @@ import { t } from "@lingui/macro";
import "../../elements/EmptyState";
export interface AccessDeniedChallenge extends Challenge {
error_message?: string;
}
@customElement("ak-stage-access-denied")
export class FlowAccessDenied extends BaseStage {
@ -45,9 +41,9 @@ export class FlowAccessDenied extends BaseStage {
<i class="pf-icon pf-icon-error-circle-o"></i>
${t`Request has been denied.`}
</p>
${this.challenge?.error_message &&
${this.challenge?.errorMessage &&
html`<hr>
<p>${this.challenge.error_message}</p>`}
<p>${this.challenge.errorMessage}</p>`}
</div>
</form>
</div>

View File

@ -1,5 +1,5 @@
import { t } from "@lingui/macro";
import { Challenge } from "authentik-api";
import { PlexAuthenticationChallenge } from "authentik-api";
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
@ -16,12 +16,6 @@ import { SourcesApi } from "authentik-api";
import { showMessage } from "../../../elements/messages/MessageContainer";
import { MessageLevel } from "../../../elements/messages/Message";
export interface PlexAuthenticationChallenge extends Challenge {
client_id: string;
slug: string;
}
@customElement("ak-flow-sources-plex")
export class PlexLoginInit extends BaseStage {
@ -34,9 +28,9 @@ export class PlexLoginInit extends BaseStage {
}
async firstUpdated(): Promise<void> {
const authInfo = await PlexAPIClient.getPin(this.challenge?.client_id || "");
const authInfo = await PlexAPIClient.getPin(this.challenge?.clientId || "");
const authWindow = popupCenterScreen(authInfo.authUrl, "plex auth", 550, 700);
PlexAPIClient.pinPoll(this.challenge?.client_id || "", authInfo.pin.id).then(token => {
PlexAPIClient.pinPoll(this.challenge?.clientId || "", authInfo.pin.id).then(token => {
authWindow?.close();
new SourcesApi(DEFAULT_CONFIG).sourcesPlexRedeemTokenCreate({
plexTokenRedeemRequest: {

View File

@ -1,6 +1,5 @@
import { t } from "@lingui/macro";
import { CSSResult, customElement, html, property, TemplateResult } from "lit-element";
import { WithUserInfoChallenge } from "../../../api/Flows";
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
@ -13,15 +12,9 @@ import "../../../elements/forms/FormElement";
import "../../../elements/EmptyState";
import "../../FormStatic";
import { FlowURLManager } from "../../../api/legacy";
import { StagesApi } from "authentik-api";
import { AuthenticatorDuoChallenge, StagesApi } from "authentik-api";
import { DEFAULT_CONFIG } from "../../../api/Config";
export interface AuthenticatorDuoChallenge extends WithUserInfoChallenge {
activation_barcode: string;
activation_code: string;
stage_uuid: string;
}
@customElement("ak-stage-authenticator-duo")
export class AuthenticatorDuoStage extends BaseStage {
@ -42,10 +35,11 @@ export class AuthenticatorDuoStage extends BaseStage {
checkEnrollStatus(): Promise<void> {
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorDuoEnrollmentStatusCreate({
stageUuid: this.challenge?.stage_uuid || "",
}).then(r => {
stageUuid: this.challenge?.stageUuid || "",
}).then(() => {
this.host?.submit({});
}).catch(e => {
}).catch(() => {
console.debug("authentik/flows/duo: Waiting for auth status");
});
}
@ -65,17 +59,17 @@ export class AuthenticatorDuoStage extends BaseStage {
<form class="pf-c-form" @submit=${(e: Event) => { this.submitForm(e); }}>
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pending_user_avatar}"
user=${this.challenge.pending_user}>
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}>
<div slot="link">
<a href="${FlowURLManager.cancel()}">${t`Not you?`}</a>
</div>
</ak-form-static>
<img src=${this.challenge.activation_barcode} />
<img src=${this.challenge.activationBarcode} />
<p>
${t`Alternatively, if your current device has Duo installed, click on this link:`}
</p>
<a href=${this.challenge.activation_code}>${t`Duo activation`}</a>
<a href=${this.challenge.activationCode}>${t`Duo activation`}</a>
<div class="pf-c-form__group pf-m-action">
<button type="button" class="pf-c-button pf-m-primary pf-m-block" @click=${() => {

View File

@ -1,6 +1,5 @@
import { t } from "@lingui/macro";
import { css, CSSResult, customElement, html, property, TemplateResult } from "lit-element";
import { WithUserInfoChallenge } from "../../../api/Flows";
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
@ -13,6 +12,7 @@ import "../../../elements/forms/FormElement";
import "../../../elements/EmptyState";
import "../../FormStatic";
import { FlowURLManager } from "../../../api/legacy";
import { AuthenticatorStaticChallenge } from "authentik-api";
export const STATIC_TOKEN_STYLE = css`
/* Static OTP Tokens */
@ -29,9 +29,6 @@ export const STATIC_TOKEN_STYLE = css`
}
`;
export interface AuthenticatorStaticChallenge extends WithUserInfoChallenge {
codes: number[];
}
@customElement("ak-stage-authenticator-static")
export class AuthenticatorStaticStage extends BaseStage {
@ -59,8 +56,8 @@ export class AuthenticatorStaticStage extends BaseStage {
<form class="pf-c-form" @submit=${(e: Event) => { this.submitForm(e); }}>
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pending_user_avatar}"
user=${this.challenge.pending_user}>
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}>
<div slot="link">
<a href="${FlowURLManager.cancel()}">${t`Not you?`}</a>
</div>

View File

@ -1,6 +1,5 @@
import { t } from "@lingui/macro";
import { CSSResult, customElement, html, property, TemplateResult } from "lit-element";
import { WithUserInfoChallenge } from "../../../api/Flows";
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
@ -16,10 +15,8 @@ import "../../../elements/EmptyState";
import "../../FormStatic";
import { MessageLevel } from "../../../elements/messages/Message";
import { FlowURLManager } from "../../../api/legacy";
import { AuthenticatorTOTPChallenge } from "authentik-api";
export interface AuthenticatorTOTPChallenge extends WithUserInfoChallenge {
config_url: string;
}
@customElement("ak-stage-authenticator-totp")
export class AuthenticatorTOTPStage extends BaseStage {
@ -47,20 +44,20 @@ export class AuthenticatorTOTPStage extends BaseStage {
<form class="pf-c-form" @submit=${(e: Event) => { this.submitForm(e); }}>
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pending_user_avatar}"
user=${this.challenge.pending_user}>
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}>
<div slot="link">
<a href="${FlowURLManager.cancel()}">${t`Not you?`}</a>
</div>
</ak-form-static>
<input type="hidden" name="otp_uri" value=${this.challenge.config_url} />
<input type="hidden" name="otp_uri" value=${this.challenge.configUrl} />
<ak-form-element>
<!-- @ts-ignore -->
<qr-code data="${this.challenge.config_url}"></qr-code>
<qr-code data="${this.challenge.configUrl}"></qr-code>
<button type="button" class="pf-c-button pf-m-secondary pf-m-progress pf-m-in-progress" @click=${(e: Event) => {
e.preventDefault();
if (!this.challenge?.config_url) return;
navigator.clipboard.writeText(this.challenge?.config_url).then(() => {
if (!this.challenge?.configUrl) return;
navigator.clipboard.writeText(this.challenge?.configUrl).then(() => {
showMessage({
level: MessageLevel.success,
message: t`Successfully copied TOTP Config.`
@ -75,7 +72,7 @@ export class AuthenticatorTOTPStage extends BaseStage {
label="${t`Code`}"
?required="${true}"
class="pf-c-form__group"
.errors=${(this.challenge?.response_errors || {})["code"]}>
.errors=${(this.challenge?.responseErrors || {})["code"]}>
<!-- @ts-ignore -->
<input type="text"
name="code"

View File

@ -1,6 +1,5 @@
import { t } from "@lingui/macro";
import { css, CSSResult, customElement, html, property, TemplateResult } from "lit-element";
import { WithUserInfoChallenge } from "../../../api/Flows";
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
@ -13,6 +12,9 @@ import "./AuthenticatorValidateStageWebAuthn";
import "./AuthenticatorValidateStageCode";
import "./AuthenticatorValidateStageDuo";
import { PasswordManagerPrefill } from "../identification/IdentificationStage";
import { DeviceChallenge } from "authentik-api";
import { AuthenticatorValidationChallenge } from "authentik-api/dist/models/AuthenticatorValidationChallenge";
import { ChallengeResponseRequest } from "authentik-api/dist/models/ChallengeResponseRequest";
export enum DeviceClasses {
STATIC = "static",
@ -21,33 +23,17 @@ export enum DeviceClasses {
DUO = "duo",
}
export interface DeviceChallenge {
device_class: DeviceClasses;
device_uid: string;
challenge: unknown;
}
export interface AuthenticatorValidateStageChallenge extends WithUserInfoChallenge {
device_challenges: DeviceChallenge[];
}
export interface AuthenticatorValidateStageChallengeResponse {
code?: string;
webauthn?: string;
duo?: number;
}
@customElement("ak-stage-authenticator-validate")
export class AuthenticatorValidateStage extends BaseStage implements StageHost {
@property({ attribute: false })
challenge?: AuthenticatorValidateStageChallenge;
challenge?: AuthenticatorValidationChallenge;
@property({attribute: false})
selectedDeviceChallenge?: DeviceChallenge;
submit<T>(formData?: T): Promise<void> {
return this.host?.submit<T>(formData) || Promise.resolve();
submit(payload: ChallengeResponseRequest): Promise<void> {
return this.host?.submit(payload) || Promise.resolve();
}
static get styles(): CSSResult[] {
@ -79,7 +65,7 @@ export class AuthenticatorValidateStage extends BaseStage implements StageHost {
}
renderDevicePickerSingle(deviceChallenge: DeviceChallenge): TemplateResult {
switch (deviceChallenge.device_class) {
switch (deviceChallenge.deviceClass) {
case DeviceClasses.DUO:
return html`<i class="fas fa-mobile-alt"></i>
<div class="right">
@ -124,7 +110,7 @@ export class AuthenticatorValidateStage extends BaseStage implements StageHost {
renderDevicePicker(): TemplateResult {
return html`
<ul>
${this.challenge?.device_challenges.map((challenges) => {
${this.challenge?.deviceChallenges.map((challenges) => {
return html`<li>
<button class="pf-c-button authenticator-button" type="button" @click=${() => {
this.selectedDeviceChallenge = challenges;
@ -140,30 +126,31 @@ export class AuthenticatorValidateStage extends BaseStage implements StageHost {
if (!this.selectedDeviceChallenge) {
return html``;
}
switch (this.selectedDeviceChallenge?.device_class) {
switch (this.selectedDeviceChallenge?.deviceClass) {
case DeviceClasses.STATIC:
case DeviceClasses.TOTP:
return html`<ak-stage-authenticator-validate-code
.host=${this}
.host=${this as StageHost}
.challenge=${this.challenge}
.deviceChallenge=${this.selectedDeviceChallenge}
.showBackButton=${(this.challenge?.device_challenges.length || []) > 1}>
.showBackButton=${(this.challenge?.deviceChallenges.length || []) > 1}>
</ak-stage-authenticator-validate-code>`;
case DeviceClasses.WEBAUTHN:
return html`<ak-stage-authenticator-validate-webauthn
.host=${this}
.host=${this as StageHost}
.challenge=${this.challenge}
.deviceChallenge=${this.selectedDeviceChallenge}
.showBackButton=${(this.challenge?.device_challenges.length || []) > 1}>
.showBackButton=${(this.challenge?.deviceChallenges.length || []) > 1}>
</ak-stage-authenticator-validate-webauthn>`;
case DeviceClasses.DUO:
return html`<ak-stage-authenticator-validate-duo
.host=${this}
.host=${this as StageHost}
.challenge=${this.challenge}
.deviceChallenge=${this.selectedDeviceChallenge}
.showBackButton=${(this.challenge?.device_challenges.length || []) > 1}>
.showBackButton=${(this.challenge?.deviceChallenges.length || []) > 1}>
</ak-stage-authenticator-validate-duo>`;
}
return html``;
}
render(): TemplateResult {
@ -174,8 +161,8 @@ export class AuthenticatorValidateStage extends BaseStage implements StageHost {
</ak-empty-state>`;
}
// User only has a single device class, so we don't show a picker
if (this.challenge?.device_challenges.length === 1) {
this.selectedDeviceChallenge = this.challenge.device_challenges[0];
if (this.challenge?.deviceChallenges.length === 1) {
this.selectedDeviceChallenge = this.challenge.deviceChallenges[0];
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">

View File

@ -8,18 +8,20 @@ import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import AKGlobal from "../../../authentik.css";
import { BaseStage } from "../base";
import { AuthenticatorValidateStage, AuthenticatorValidateStageChallenge, DeviceChallenge } from "./AuthenticatorValidateStage";
import { AuthenticatorValidateStage } from "./AuthenticatorValidateStage";
import "../../../elements/forms/FormElement";
import "../../../elements/EmptyState";
import { PasswordManagerPrefill } from "../identification/IdentificationStage";
import "../../FormStatic";
import { FlowURLManager } from "../../../api/legacy";
import { AuthenticatorValidationChallenge } from "authentik-api/dist/models/AuthenticatorValidationChallenge";
import { DeviceChallenge } from "authentik-api";
@customElement("ak-stage-authenticator-validate-code")
export class AuthenticatorValidateStageWebCode extends BaseStage {
@property({ attribute: false })
challenge?: AuthenticatorValidateStageChallenge;
challenge?: AuthenticatorValidationChallenge;
@property({ attribute: false })
deviceChallenge?: DeviceChallenge;
@ -42,8 +44,8 @@ export class AuthenticatorValidateStageWebCode extends BaseStage {
<form class="pf-c-form" @submit=${(e: Event) => { this.submitForm(e); }}>
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pending_user_avatar}"
user=${this.challenge.pending_user}>
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}>
<div slot="link">
<a href="${FlowURLManager.cancel()}">${t`Not you?`}</a>
</div>
@ -52,7 +54,7 @@ export class AuthenticatorValidateStageWebCode extends BaseStage {
label="${t`Code`}"
?required="${true}"
class="pf-c-form__group"
.errors=${(this.challenge?.response_errors || {})["code"]}>
.errors=${(this.challenge?.responseErrors || {})["code"]}>
<!-- @ts-ignore -->
<input type="text"
name="code"

View File

@ -8,18 +8,19 @@ import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import AKGlobal from "../../../authentik.css";
import { BaseStage } from "../base";
import { AuthenticatorValidateStage, AuthenticatorValidateStageChallenge, DeviceChallenge } from "./AuthenticatorValidateStage";
import { AuthenticatorValidateStage } from "./AuthenticatorValidateStage";
import "../../../elements/forms/FormElement";
import "../../../elements/EmptyState";
import { PasswordManagerPrefill } from "../identification/IdentificationStage";
import "../../FormStatic";
import { FlowURLManager } from "../../../api/legacy";
import { AuthenticatorValidationChallenge } from "authentik-api/dist/models/AuthenticatorValidationChallenge";
import { DeviceChallenge } from "authentik-api";
@customElement("ak-stage-authenticator-validate-duo")
export class AuthenticatorValidateStageWebDuo extends BaseStage {
@property({ attribute: false })
challenge?: AuthenticatorValidateStageChallenge;
challenge?: AuthenticatorValidationChallenge;
@property({ attribute: false })
deviceChallenge?: DeviceChallenge;
@ -33,7 +34,7 @@ export class AuthenticatorValidateStageWebDuo extends BaseStage {
firstUpdated(): void {
this.host?.submit({
"duo": this.deviceChallenge?.device_uid
"duo": this.deviceChallenge?.deviceUid
});
}
@ -48,8 +49,8 @@ export class AuthenticatorValidateStageWebDuo extends BaseStage {
<form class="pf-c-form" @submit=${(e: Event) => { this.submitForm(e); }}>
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pending_user_avatar}"
user=${this.challenge.pending_user}>
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}>
<div slot="link">
<a href="${FlowURLManager.cancel()}">${t`Not you?`}</a>
</div>

View File

@ -10,13 +10,15 @@ import AKGlobal from "../../../authentik.css";
import { PFSize } from "../../../elements/Spinner";
import { transformAssertionForServer, transformCredentialRequestOptions } from "../authenticator_webauthn/utils";
import { BaseStage } from "../base";
import { AuthenticatorValidateStage, AuthenticatorValidateStageChallenge, DeviceChallenge } from "./AuthenticatorValidateStage";
import { AuthenticatorValidateStage } from "./AuthenticatorValidateStage";
import { AuthenticatorValidationChallenge } from "authentik-api/dist/models/AuthenticatorValidationChallenge";
import { DeviceChallenge } from "authentik-api";
@customElement("ak-stage-authenticator-validate-webauthn")
export class AuthenticatorValidateStageWebAuthn extends BaseStage {
@property({attribute: false})
challenge?: AuthenticatorValidateStageChallenge;
challenge?: AuthenticatorValidationChallenge;
@property({attribute: false})
deviceChallenge?: DeviceChallenge;

View File

@ -1,6 +1,5 @@
import { t } from "@lingui/macro";
import { CSSResult, customElement, html, property, TemplateResult } from "lit-element";
import { WithUserInfoChallenge } from "../../../api/Flows";
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
@ -11,10 +10,7 @@ import AKGlobal from "../../../authentik.css";
import { PFSize } from "../../../elements/Spinner";
import { BaseStage } from "../base";
import { Assertion, transformCredentialCreateOptions, transformNewAssertionForServer } from "./utils";
export interface WebAuthnAuthenticatorRegisterChallenge extends WithUserInfoChallenge {
registration: PublicKeyCredentialCreationOptions;
}
import { AuthenticatorWebAuthnChallenge } from "authentik-api";
export interface WebAuthnAuthenticatorRegisterChallengeResponse {
response: Assertion;
@ -24,7 +20,7 @@ export interface WebAuthnAuthenticatorRegisterChallengeResponse {
export class WebAuthnAuthenticatorRegisterStage extends BaseStage {
@property({ attribute: false })
challenge?: WebAuthnAuthenticatorRegisterChallenge;
challenge?: AuthenticatorWebAuthnChallenge;
@property({type: Boolean})
registerRunning = false;
@ -42,7 +38,7 @@ export class WebAuthnAuthenticatorRegisterStage extends BaseStage {
}
// convert certain members of the PublicKeyCredentialCreateOptions into
// byte arrays as expected by the spec.
const publicKeyCredentialCreateOptions = transformCredentialCreateOptions(this.challenge?.registration);
const publicKeyCredentialCreateOptions = transformCredentialCreateOptions(this.challenge?.registration as PublicKeyCredentialCreationOptions);
// request the authenticator(s) to create a new credential keypair.
let credential;
@ -106,8 +102,8 @@ export class WebAuthnAuthenticatorRegisterStage extends BaseStage {
</div>`:
html`
<div class="pf-c-form__group pf-m-action">
${this.challenge?.response_errors ?
html`<p class="pf-m-block">${this.challenge.response_errors["response"][0].string}</p>`:
${this.challenge?.responseErrors ?
html`<p class="pf-m-block">${this.challenge.responseErrors["response"][0].string}</p>`:
html``}
<p class="pf-m-block">${this.registerMessage}</p>
<button class="pf-c-button pf-m-primary pf-m-block" @click=${() => {

View File

@ -1,6 +1,5 @@
import { t } from "@lingui/macro";
import { CSSResult, customElement, html, property, TemplateResult } from "lit-element";
import { WithUserInfoChallenge } from "../../../api/Flows";
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
@ -10,11 +9,7 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
import AKGlobal from "../../../authentik.css";
import { BaseStage } from "../base";
import "../../../elements/EmptyState";
export interface AutosubmitChallenge extends WithUserInfoChallenge {
url: string;
attrs: { [key: string]: string };
}
import { AutosubmitChallenge } from "authentik-api";
@customElement("ak-stage-autosubmit")
export class AutosubmitStage extends BaseStage {

View File

@ -1,20 +1,25 @@
import { Challenge } from "authentik-api";
import { ChallengeResponseRequest } from "authentik-api/dist/models/ChallengeResponseRequest";
import { LitElement } from "lit-element";
export interface StageHost {
challenge?: Challenge;
submit<T>(formData?: T): Promise<void>;
submit(payload: ChallengeResponseRequest): Promise<void>;
}
export class BaseStage extends LitElement {
host?: StageHost;
challenge?: Challenge;
submitForm(e: Event): void {
e.preventDefault();
const object: {
component: string;
[key: string]: unknown;
} = {};
} = {
component: this.challenge.component,
};
const form = new FormData(this.shadowRoot?.querySelector("form") || undefined);
form.forEach((value, key) => object[key] = value);
this.host?.submit(object);

View File

@ -1,6 +1,5 @@
import { t } from "@lingui/macro";
import { CSSResult, customElement, html, property, TemplateResult } from "lit-element";
import { WithUserInfoChallenge } from "../../../api/Flows";
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
@ -14,10 +13,7 @@ import "../../../elements/forms/FormElement";
import "../../../elements/EmptyState";
import "../../FormStatic";
import { FlowURLManager } from "../../../api/legacy";
export interface CaptchaChallenge extends WithUserInfoChallenge {
site_key: string;
}
import { CaptchaChallenge } from "authentik-api";
@customElement("ak-stage-captcha")
export class CaptchaStage extends BaseStage {
@ -39,10 +35,10 @@ export class CaptchaStage extends BaseStage {
script.onload = () => {
console.debug("authentik/stages/captcha: script loaded");
grecaptcha.ready(() => {
if (!this.challenge?.site_key) return;
if (!this.challenge?.siteKey) return;
console.debug("authentik/stages/captcha: ready");
const captchaId = grecaptcha.render(captchaContainer, {
sitekey: this.challenge.site_key,
sitekey: this.challenge.siteKey,
callback: (token) => {
this.host?.submit({
"token": token,
@ -72,8 +68,8 @@ export class CaptchaStage extends BaseStage {
<form class="pf-c-form">
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pending_user_avatar}"
user=${this.challenge.pending_user}>
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}>
<div slot="link">
<a href="${FlowURLManager.cancel()}">${t`Not you?`}</a>
</div>

View File

@ -1,6 +1,5 @@
import { t } from "@lingui/macro";
import { CSSResult, customElement, html, property, TemplateResult } from "lit-element";
import { WithUserInfoChallenge } from "../../../api/Flows";
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
@ -12,18 +11,8 @@ import { BaseStage } from "../base";
import "../../../elements/EmptyState";
import "../../FormStatic";
import { FlowURLManager } from "../../../api/legacy";
import { ConsentChallenge } from "authentik-api";
export interface Permission {
name: string;
id: string;
}
export interface ConsentChallenge extends WithUserInfoChallenge {
header_text: string;
permissions?: Permission[];
}
@customElement("ak-stage-consent")
export class ConsentStage extends BaseStage {
@ -51,15 +40,15 @@ export class ConsentStage extends BaseStage {
<form class="pf-c-form" @submit=${(e: Event) => { this.submitForm(e); }}>
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pending_user_avatar}"
user=${this.challenge.pending_user}>
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}>
<div slot="link">
<a href="${FlowURLManager.cancel()}">${t`Not you?`}</a>
</div>
</ak-form-static>
<div class="pf-c-form__group">
<p id="header-text">
${this.challenge.header_text}
${this.challenge.headerText}
</p>
<p>${t`Application requires following permissions`}</p>
<ul class="pf-c-list" id="permmissions">

View File

@ -1,6 +1,5 @@
import { t } from "@lingui/macro";
import { CSSResult, customElement, html, property, TemplateResult } from "lit-element";
import { Challenge } from "../../../api/Flows";
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
@ -11,12 +10,13 @@ import AKGlobal from "../../../authentik.css";
import { BaseStage } from "../base";
import "../../../elements/EmptyState";
import "../../FormStatic";
import { DummyChallenge } from "authentik-api";
@customElement("ak-stage-dummy")
export class DummyStage extends BaseStage {
@property({ attribute: false })
challenge?: Challenge;
challenge?: DummyChallenge;
static get styles(): CSSResult[] {
return [PFBase, PFLogin, PFForm, PFFormControl, PFTitle, PFButton, AKGlobal];

View File

@ -1,6 +1,5 @@
import { t } from "@lingui/macro";
import { CSSResult, customElement, html, property, TemplateResult } from "lit-element";
import { Challenge } from "authentik-api";
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
@ -10,8 +9,7 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
import AKGlobal from "../../../authentik.css";
import { BaseStage } from "../base";
import "../../../elements/EmptyState";
export type EmailChallenge = Challenge;
import { EmailChallenge } from "authentik-api";
@customElement("ak-stage-email")
export class EmailStage extends BaseStage {

View File

@ -10,7 +10,7 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
import AKGlobal from "../../../authentik.css";
import "../../../elements/forms/FormElement";
import "../../../elements/EmptyState";
import { Challenge } from "../../../api/Flows";
import { IdentificationChallenge, UILoginButton } from "authentik-api";
export const PasswordManagerPrefill: {
password: string | undefined;
@ -20,24 +20,6 @@ export const PasswordManagerPrefill: {
totp: undefined,
};
export interface IdentificationChallenge extends Challenge {
user_fields?: string[];
primary_action: string;
sources?: UILoginButton[];
application_pre?: string;
enroll_url?: string;
recovery_url?: string;
}
export interface UILoginButton {
name: string;
challenge: Challenge;
icon_url?: string;
}
@customElement("ak-stage-identification")
export class IdentificationStage extends BaseStage {
@ -131,8 +113,8 @@ export class IdentificationStage extends BaseStage {
renderSource(source: UILoginButton): TemplateResult {
let icon = html`<i class="fas fas fa-share-square" title="${source.name}"></i>`;
if (source.icon_url) {
icon = html`<img src="${source.icon_url}" alt="${source.name}">`;
if (source.iconUrl) {
icon = html`<img src="${source.iconUrl}" alt="${source.name}">`;
}
return html`<li class="pf-c-login__main-footer-links-item">
<button type="button" @click=${() => {
@ -145,18 +127,18 @@ export class IdentificationStage extends BaseStage {
}
renderFooter(): TemplateResult {
if (!this.challenge?.enroll_url && !this.challenge?.recovery_url) {
if (!this.challenge?.enrollUrl && !this.challenge?.recoveryUrl) {
return html``;
}
return html`<div class="pf-c-login__main-footer-band">
${this.challenge.enroll_url ? html`
${this.challenge.enrollUrl ? html`
<p class="pf-c-login__main-footer-band-item">
${t`Need an account?`}
<a id="enroll" href="${this.challenge.enroll_url}">${t`Sign up.`}</a>
<a id="enroll" href="${this.challenge.enrollUrl}">${t`Sign up.`}</a>
</p>` : html``}
${this.challenge.recovery_url ? html`
${this.challenge.recoveryUrl ? html`
<p class="pf-c-login__main-footer-band-item">
<a id="recovery" href="${this.challenge.recovery_url}">${t`Forgot username or password?`}</a>
<a id="recovery" href="${this.challenge.recoveryUrl}">${t`Forgot username or password?`}</a>
</p>` : html``}
</div>`;
}
@ -164,15 +146,15 @@ export class IdentificationStage extends BaseStage {
renderInput(): TemplateResult {
let label = "";
let type = "text";
if (!this.challenge?.user_fields) {
if (!this.challenge?.userFields) {
return html`<p>
${t`Select one of the sources below to login.`}
</p>`;
}
if (this.challenge?.user_fields === ["email"]) {
if (this.challenge?.userFields === ["email"]) {
label = t`Email`;
type = "email";
} else if (this.challenge?.user_fields === ["username"]) {
} else if (this.challenge?.userFields === ["username"]) {
label = t`Username`;
} else {
label = t`Email or username`;
@ -181,10 +163,10 @@ export class IdentificationStage extends BaseStage {
label=${label}
?required="${true}"
class="pf-c-form__group"
.errors=${(this.challenge?.response_errors || {})["uid_field"]}>
.errors=${(this.challenge?.responseErrors || {})["uidField"]}>
<!-- @ts-ignore -->
<input type=${type}
name="uid_field"
name="uidField"
placeholder="Email or Username"
autofocus=""
autocomplete="username"
@ -193,7 +175,7 @@ export class IdentificationStage extends BaseStage {
</ak-form-element>
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
${this.challenge.primary_action}
${this.challenge.primaryAction}
</button>
</div>`;
}
@ -212,9 +194,9 @@ export class IdentificationStage extends BaseStage {
</header>
<div class="pf-c-login__main-body">
<form class="pf-c-form" @submit=${(e: Event) => {this.submitForm(e);}}>
${this.challenge.application_pre ?
${this.challenge.applicationPre ?
html`<p>
${t`Login to continue to ${this.challenge.application_pre}.`}
${t`Login to continue to ${this.challenge.applicationPre}.`}
</p>`:
html``}
${this.renderInput()}

View File

@ -1,6 +1,5 @@
import { t } from "@lingui/macro";
import { CSSResult, customElement, html, property, TemplateResult } from "lit-element";
import { WithUserInfoChallenge } from "../../../api/Flows";
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
@ -14,10 +13,7 @@ import "../../../elements/EmptyState";
import { PasswordManagerPrefill } from "../identification/IdentificationStage";
import "../../FormStatic";
import { FlowURLManager } from "../../../api/legacy";
export interface PasswordChallenge extends WithUserInfoChallenge {
recovery_url?: string;
}
import { PasswordChallenge } from "authentik-api";
@customElement("ak-stage-password")
export class PasswordStage extends BaseStage {
@ -45,18 +41,18 @@ export class PasswordStage extends BaseStage {
<form class="pf-c-form" @submit=${(e: Event) => {this.submitForm(e);}}>
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pending_user_avatar}"
user=${this.challenge.pending_user}>
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}>
<div slot="link">
<a href="${FlowURLManager.cancel()}">${t`Not you?`}</a>
</div>
</ak-form-static>
<input name="username" autocomplete="username" type="hidden" value="${this.challenge.pending_user}">
<input name="username" autocomplete="username" type="hidden" value="${this.challenge.pendingUser}">
<ak-form-element
label="${t`Password`}"
?required="${true}"
class="pf-c-form__group"
.errors=${(this.challenge?.response_errors || {})["password"]}>
.errors=${(this.challenge?.responseErrors || {})["password"]}>
<input type="password"
name="password"
placeholder="${t`Please enter your password`}"
@ -67,8 +63,8 @@ export class PasswordStage extends BaseStage {
value=${PasswordManagerPrefill.password || ""}>
</ak-form-element>
${this.challenge.recovery_url ?
html`<a href="${this.challenge.recovery_url}">
${this.challenge.recoveryUrl ?
html`<a href="${this.challenge.recoveryUrl}">
${t`Forgot password?`}</a>` : ""}
<div class="pf-c-form__group pf-m-action">

View File

@ -13,20 +13,9 @@ import { BaseStage } from "../base";
import "../../../elements/forms/FormElement";
import "../../../elements/EmptyState";
import "../../../elements/Divider";
import { Challenge, Error } from "../../../api/Flows";
import { Error } from "../../../api/Flows";
import { Prompt, PromptChallenge } from "authentik-api";
export interface Prompt {
field_key: string;
label: string;
type: string;
required: boolean;
placeholder: string;
order: number;
}
export interface PromptChallenge extends Challenge {
fields: Prompt[];
}
@customElement("ak-stage-prompt")
export class PromptStage extends BaseStage {
@ -43,7 +32,7 @@ export class PromptStage extends BaseStage {
case "text":
return `<input
type="text"
name="${prompt.field_key}"
name="${prompt.fieldKey}"
placeholder="${prompt.placeholder}"
autocomplete="off"
class="pf-c-form-control"
@ -52,7 +41,7 @@ export class PromptStage extends BaseStage {
case "username":
return `<input
type="text"
name="${prompt.field_key}"
name="${prompt.fieldKey}"
placeholder="${prompt.placeholder}"
autocomplete="username"
class="pf-c-form-control"
@ -61,7 +50,7 @@ export class PromptStage extends BaseStage {
case "email":
return `<input
type="email"
name="${prompt.field_key}"
name="${prompt.fieldKey}"
placeholder="${prompt.placeholder}"
class="pf-c-form-control"
?required=${prompt.required}
@ -69,7 +58,7 @@ export class PromptStage extends BaseStage {
case "password":
return `<input
type="password"
name="${prompt.field_key}"
name="${prompt.fieldKey}"
placeholder="${prompt.placeholder}"
autocomplete="new-password"
class="pf-c-form-control"
@ -77,28 +66,28 @@ export class PromptStage extends BaseStage {
case "number":
return `<input
type="number"
name="${prompt.field_key}"
name="${prompt.fieldKey}"
placeholder="${prompt.placeholder}"
class="pf-c-form-control"
?required=${prompt.required}>`;
case "checkbox":
return `<input
type="checkbox"
name="${prompt.field_key}"
name="${prompt.fieldKey}"
placeholder="${prompt.placeholder}"
class="pf-c-form-control"
?required=${prompt.required}>`;
case "date":
return `<input
type="date"
name="${prompt.field_key}"
name="${prompt.fieldKey}"
placeholder="${prompt.placeholder}"
class="pf-c-form-control"
?required=${prompt.required}>`;
case "date-time":
return `<input
type="datetime"
name="${prompt.field_key}"
name="${prompt.fieldKey}"
placeholder="${prompt.placeholder}"
class="pf-c-form-control"
?required=${prompt.required}>`;
@ -107,7 +96,7 @@ export class PromptStage extends BaseStage {
case "hidden":
return `<input
type="hidden"
name="${prompt.field_key}"
name="${prompt.fieldKey}"
value="${prompt.placeholder}"
class="pf-c-form-control"
?required=${prompt.required}>`;
@ -158,12 +147,12 @@ export class PromptStage extends BaseStage {
label="${prompt.label}"
?required="${prompt.required}"
class="pf-c-form__group"
.errors=${(this.challenge?.response_errors || {})[prompt.field_key]}>
.errors=${(this.challenge?.responseErrors || {})[prompt.fieldKey]}>
${unsafeHTML(this.renderPromptInner(prompt))}
</ak-form-element>`;
})}
${"non_field_errors" in (this.challenge?.response_errors || {}) ?
this.renderNonFieldErrors(this.challenge?.response_errors?.non_field_errors || []):
${"non_field_errors" in (this.challenge?.responseErrors || {}) ?
this.renderNonFieldErrors(this.challenge?.responseErrors?.non_field_errors || []):
html``}
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">

View File

@ -63,6 +63,10 @@ msgstr "ANY, any policy must match to grant access."
msgid "ANY, any policy must match to include this stage access."
msgstr "ANY, any policy must match to include this stage access."
#: src/pages/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
msgid "API Hostname"
msgstr "API Hostname"
#: src/elements/notifications/APIDrawer.ts
msgid "API Requests"
msgstr "API Requests"
@ -180,6 +184,10 @@ msgstr "Allows/denys requests based on the users and/or the IPs reputation."
msgid "Also known as Entity ID. Defaults the Metadata URL."
msgstr "Also known as Entity ID. Defaults the Metadata URL."
#: src/flows/stages/authenticator_duo/AuthenticatorDuoStage.ts
msgid "Alternatively, if your current device has Duo installed, click on this link:"
msgstr "Alternatively, if your current device has Duo installed, click on this link:"
#: src/pages/stages/consent/ConsentStageForm.ts
msgid "Always require consent"
msgstr "Always require consent"
@ -522,6 +530,10 @@ msgstr "Check IP"
msgid "Check Username"
msgstr "Check Username"
#: src/flows/stages/authenticator_duo/AuthenticatorDuoStage.ts
msgid "Check status"
msgstr "Check status"
#: src/flows/stages/email/EmailStage.ts
msgid "Check your Emails for a password reset link."
msgstr "Check your Emails for a password reset link."
@ -572,6 +584,7 @@ msgstr "Click to copy token"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderViewPage.ts
#: src/pages/sources/plex/PlexSourceForm.ts
#: src/pages/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
msgid "Client ID"
msgstr "Client ID"
@ -583,6 +596,7 @@ msgid "Client IP"
msgstr "Client IP"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
msgid "Client Secret"
msgstr "Client Secret"
@ -616,6 +630,7 @@ msgstr "Confidential clients are capable of maintaining the confidentiality of t
msgid "Configuration"
msgstr "Configuration"
#: src/pages/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
#: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
@ -715,6 +730,7 @@ msgstr "Context"
#: src/flows/stages/authenticator_static/AuthenticatorStaticStage.ts
#: src/flows/stages/authenticator_totp/AuthenticatorTOTPStage.ts
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStageCode.ts
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStageDuo.ts
#: src/flows/stages/autosubmit/AutosubmitStage.ts
#: src/flows/stages/consent/ConsentStage.ts
#: src/flows/stages/dummy/DummyStage.ts
@ -1010,6 +1026,10 @@ msgstr "Determines how authentik sends the response back to the Service Provider
msgid "Determines how long a session lasts. Default of 0 seconds means that the sessions lasts until the browser is closed."
msgstr "Determines how long a session lasts. Default of 0 seconds means that the sessions lasts until the browser is closed."
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
msgid "Device classes"
msgstr "Device classes"
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
msgid "Device classes which can be used to authenticate."
msgstr "Device classes which can be used to authenticate."
@ -1032,6 +1052,7 @@ msgstr "Digits"
msgid "Disable"
msgstr "Disable"
#: src/pages/user-settings/settings/UserSettingsAuthenticatorDuo.ts
#: src/pages/user-settings/settings/UserSettingsAuthenticatorStatic.ts
msgid "Disable Static Tokens"
msgstr "Disable Static Tokens"
@ -1070,6 +1091,22 @@ msgstr "Download Private key"
msgid "Dummy stage used for testing. Shows a simple continue button and always passes."
msgstr "Dummy stage used for testing. Shows a simple continue button and always passes."
#: src/pages/user-settings/settings/UserSettingsAuthenticatorDuo.ts
msgid "Duo"
msgstr "Duo"
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
msgid "Duo Authenticators"
msgstr "Duo Authenticators"
#: src/flows/stages/authenticator_duo/AuthenticatorDuoStage.ts
msgid "Duo activation"
msgstr "Duo activation"
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStage.ts
msgid "Duo push-notifications"
msgstr "Duo push-notifications"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "Each provider has a different issuer, based on the application slug."
msgstr "Each provider has a different issuer, based on the application slug."
@ -1159,6 +1196,7 @@ msgstr "Enable"
msgid "Enable StartTLS"
msgstr "Enable StartTLS"
#: src/pages/user-settings/settings/UserSettingsAuthenticatorDuo.ts
#: src/pages/user-settings/settings/UserSettingsAuthenticatorStatic.ts
msgid "Enable Static Tokens"
msgstr "Enable Static Tokens"
@ -1431,6 +1469,7 @@ msgstr "Flow used before authentication."
msgid "Flow used by an authenticated user to configure their password. If empty, user will not be able to configure change their password."
msgstr "Flow used by an authenticated user to configure their password. If empty, user will not be able to configure change their password."
#: src/pages/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
#: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
msgid "Flow used by an authenticated user to configure this Stage. If empty, user will not be able to configure this stage."
@ -1803,10 +1842,12 @@ msgstr "Load servers"
#: src/flows/FlowExecutor.ts
#: src/flows/FlowExecutor.ts
#: src/flows/access_denied/FlowAccessDenied.ts
#: src/flows/stages/authenticator_duo/AuthenticatorDuoStage.ts
#: src/flows/stages/authenticator_static/AuthenticatorStaticStage.ts
#: src/flows/stages/authenticator_totp/AuthenticatorTOTPStage.ts
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStage.ts
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStageCode.ts
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStageDuo.ts
#: src/flows/stages/autosubmit/AutosubmitStage.ts
#: src/flows/stages/captcha/CaptchaStage.ts
#: src/flows/stages/consent/ConsentStage.ts
@ -1866,6 +1907,7 @@ msgstr "Loading"
#: src/pages/sources/saml/SAMLSourceForm.ts
#: src/pages/sources/saml/SAMLSourceForm.ts
#: src/pages/sources/saml/SAMLSourceForm.ts
#: src/pages/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
#: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
@ -2041,6 +2083,7 @@ msgstr "My Applications"
#: src/pages/sources/saml/SAMLSourceForm.ts
#: src/pages/sources/saml/SAMLSourceViewPage.ts
#: src/pages/stages/StageListPage.ts
#: src/pages/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
#: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
@ -2173,9 +2216,11 @@ msgstr "Not found"
msgid "Not synced yet."
msgstr "Not synced yet."
#: src/flows/stages/authenticator_duo/AuthenticatorDuoStage.ts
#: src/flows/stages/authenticator_static/AuthenticatorStaticStage.ts
#: src/flows/stages/authenticator_totp/AuthenticatorTOTPStage.ts
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStageCode.ts
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStageDuo.ts
#: src/flows/stages/captcha/CaptchaStage.ts
#: src/flows/stages/consent/ConsentStage.ts
#: src/flows/stages/password/PasswordStage.ts
@ -2617,6 +2662,10 @@ msgstr "RSA-SHA512"
msgid "Re-evaluate policies"
msgstr "Re-evaluate policies"
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStage.ts
msgid "Receive a push notification on your phone to prove your identity."
msgstr "Receive a push notification on your phone to prove your identity."
#: src/pages/flows/FlowForm.ts
msgid "Recovery"
msgstr "Recovery"
@ -2728,6 +2777,7 @@ msgid "Return home"
msgstr "Return home"
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStageCode.ts
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStageDuo.ts
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStageWebAuthn.ts
msgid "Return to device picker"
msgstr "Return to device picker"
@ -3033,6 +3083,10 @@ msgstr "Stage used to configure a TOTP authenticator (i.e. Authy/Google Authenti
msgid "Stage used to configure a WebAutnn authenticator (i.e. Yubikey, FaceID/Windows Hello)."
msgstr "Stage used to configure a WebAutnn authenticator (i.e. Yubikey, FaceID/Windows Hello)."
#: src/pages/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
msgid "Stage used to configure a duo-based authenticator. This stage should be used for configuration flows."
msgstr "Stage used to configure a duo-based authenticator. This stage should be used for configuration flows."
#: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts
msgid "Stage used to configure a static authenticator (i.e. static tokens). This stage should be used for configuration flows."
msgstr "Stage used to configure a static authenticator (i.e. static tokens). This stage should be used for configuration flows."
@ -3041,6 +3095,7 @@ msgstr "Stage used to configure a static authenticator (i.e. static tokens). Thi
msgid "Stage used to validate any authenticator. This stage should be used during authentication or authorization flows."
msgstr "Stage used to validate any authenticator. This stage should be used during authentication or authorization flows."
#: src/pages/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
#: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
@ -3074,7 +3129,7 @@ msgstr "State"
msgid "Static Tokens"
msgstr "Static Tokens"
#: src/pages/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
#: src/pages/user-settings/settings/UserSettingsAuthenticatorStatic.ts
msgid "Static tokens"
msgstr "Static tokens"
@ -3090,11 +3145,13 @@ msgstr "Statically deny the flow. To use this stage effectively, disable *Evalua
msgid "Status"
msgstr "Status"
#: src/pages/user-settings/settings/UserSettingsAuthenticatorDuo.ts
#: src/pages/user-settings/settings/UserSettingsAuthenticatorStatic.ts
#: src/pages/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
msgid "Status: Disabled"
msgstr "Status: Disabled"
#: src/pages/user-settings/settings/UserSettingsAuthenticatorDuo.ts
#: src/pages/user-settings/settings/UserSettingsAuthenticatorStatic.ts
#: src/pages/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
msgid "Status: Enabled"
@ -3204,6 +3261,7 @@ msgstr "Successfully created service-connection."
msgid "Successfully created source."
msgstr "Successfully created source."
#: src/pages/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
#: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
@ -3342,6 +3400,7 @@ msgstr "Successfully updated service-connection."
msgid "Successfully updated source."
msgstr "Successfully updated source."
#: src/pages/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
#: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
@ -3533,7 +3592,7 @@ msgstr "Time in minutes the token sent is valid."
msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3)."
msgstr "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3)."
#: src/pages/user-settings/settings/UserSettingsAuthenticatorStatic.ts
#: src/pages/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
msgid "Time-based One-Time Passwords"
msgstr "Time-based One-Time Passwords"
@ -3893,7 +3952,6 @@ msgstr "User details"
msgid "User events"
msgstr "User events"
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
#: src/pages/stages/identification/IdentificationStageForm.ts
msgid "User fields"
msgstr "User fields"

View File

@ -63,6 +63,10 @@ msgstr ""
msgid "ANY, any policy must match to include this stage access."
msgstr ""
#:
msgid "API Hostname"
msgstr ""
#:
msgid "API Requests"
msgstr ""
@ -180,6 +184,10 @@ msgstr ""
msgid "Also known as Entity ID. Defaults the Metadata URL."
msgstr ""
#:
msgid "Alternatively, if your current device has Duo installed, click on this link:"
msgstr ""
#:
msgid "Always require consent"
msgstr ""
@ -518,6 +526,10 @@ msgstr ""
msgid "Check Username"
msgstr ""
#:
msgid "Check status"
msgstr ""
#:
msgid "Check your Emails for a password reset link."
msgstr ""
@ -566,6 +578,7 @@ msgstr ""
#:
#:
#:
#:
msgid "Client ID"
msgstr ""
@ -576,6 +589,7 @@ msgstr ""
msgid "Client IP"
msgstr ""
#:
#:
msgid "Client Secret"
msgstr ""
@ -614,6 +628,7 @@ msgstr ""
#:
#:
#:
#:
msgid "Configuration flow"
msgstr ""
@ -715,6 +730,7 @@ msgstr ""
#:
#:
#:
#:
msgid "Continue"
msgstr ""
@ -1002,6 +1018,10 @@ msgstr ""
msgid "Determines how long a session lasts. Default of 0 seconds means that the sessions lasts until the browser is closed."
msgstr ""
#:
msgid "Device classes"
msgstr ""
#:
msgid "Device classes which can be used to authenticate."
msgstr ""
@ -1024,6 +1044,7 @@ msgstr ""
msgid "Disable"
msgstr ""
#:
#:
msgid "Disable Static Tokens"
msgstr ""
@ -1062,6 +1083,22 @@ msgstr ""
msgid "Dummy stage used for testing. Shows a simple continue button and always passes."
msgstr ""
#:
msgid "Duo"
msgstr ""
#:
msgid "Duo Authenticators"
msgstr ""
#:
msgid "Duo activation"
msgstr ""
#:
msgid "Duo push-notifications"
msgstr ""
#:
msgid "Each provider has a different issuer, based on the application slug."
msgstr ""
@ -1151,6 +1188,7 @@ msgstr ""
msgid "Enable StartTLS"
msgstr ""
#:
#:
msgid "Enable Static Tokens"
msgstr ""
@ -1423,6 +1461,7 @@ msgstr ""
msgid "Flow used by an authenticated user to configure their password. If empty, user will not be able to configure change their password."
msgstr ""
#:
#:
#:
msgid "Flow used by an authenticated user to configure this Stage. If empty, user will not be able to configure this stage."
@ -1811,6 +1850,8 @@ msgstr ""
#:
#:
#:
#:
#:
msgid "Loading"
msgstr ""
@ -1867,6 +1908,7 @@ msgstr ""
#:
#:
#:
#:
msgid "Loading..."
msgstr ""
@ -2054,6 +2096,7 @@ msgstr ""
#:
#:
#:
#:
msgid "Name"
msgstr ""
@ -2171,6 +2214,8 @@ msgstr ""
#:
#:
#:
#:
#:
msgid "Not you?"
msgstr ""
@ -2609,6 +2654,10 @@ msgstr ""
msgid "Re-evaluate policies"
msgstr ""
#:
msgid "Receive a push notification on your phone to prove your identity."
msgstr ""
#:
msgid "Recovery"
msgstr ""
@ -2719,6 +2768,7 @@ msgstr ""
msgid "Return home"
msgstr ""
#:
#:
#:
msgid "Return to device picker"
@ -3025,6 +3075,10 @@ msgstr ""
msgid "Stage used to configure a WebAutnn authenticator (i.e. Yubikey, FaceID/Windows Hello)."
msgstr ""
#:
msgid "Stage used to configure a duo-based authenticator. This stage should be used for configuration flows."
msgstr ""
#:
msgid "Stage used to configure a static authenticator (i.e. static tokens). This stage should be used for configuration flows."
msgstr ""
@ -3044,6 +3098,7 @@ msgstr ""
#:
#:
#:
#:
msgid "Stage-specific settings"
msgstr ""
@ -3082,11 +3137,13 @@ msgstr ""
msgid "Status"
msgstr ""
#:
#:
#:
msgid "Status: Disabled"
msgstr ""
#:
#:
#:
msgid "Status: Enabled"
@ -3213,6 +3270,7 @@ msgstr ""
#:
#:
#:
#:
msgid "Successfully created stage."
msgstr ""
@ -3351,6 +3409,7 @@ msgstr ""
#:
#:
#:
#:
msgid "Successfully updated stage."
msgstr ""
@ -3881,7 +3940,6 @@ msgstr ""
msgid "User events"
msgstr ""
#:
#:
msgid "User fields"
msgstr ""