V14: The login page does not respect certain error codes (#16244)
* handle 403 and unknown error codes from the server * resolve 2fa errors in repository error handling was never being activated because this specific endpoint did not return api errors as it works exactly like the "authorize" endpoint, which is being called directly * chore: add obsolete message to unused `SetupViewPath` * chore: remove unused events * add missing labels * fix: send only 'error' back if the response is not ok * chore: remove duplicate error handling for 500 errors * fix: add hack to allow to submit the form on enter click
This commit is contained in:
@@ -8,5 +8,6 @@ public class TwoFactorLoginViewOptions
|
||||
/// <summary>
|
||||
/// Gets or sets the path of the view to show when setting up this 2fa provider
|
||||
/// </summary>
|
||||
[Obsolete("Register the view in the backoffice instead. This will be removed in version 15.")]
|
||||
public string? SetupViewPath { get; set; }
|
||||
}
|
||||
|
||||
@@ -42,6 +42,12 @@ export default class UmbLoginPageElement extends UmbLitElement {
|
||||
|
||||
if (!this.#formElement) return;
|
||||
|
||||
// We need to listen for the enter key to submit the form, because the uui-button does not support the native input fields submit event
|
||||
this.#formElement.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
this.#onSubmitClick();
|
||||
}
|
||||
});
|
||||
this.#formElement.onsubmit = this.#handleSubmit;
|
||||
}
|
||||
|
||||
@@ -91,7 +97,6 @@ export default class UmbLoginPageElement extends UmbLitElement {
|
||||
}
|
||||
|
||||
if (response.error) {
|
||||
this.dispatchEvent(new CustomEvent('umb-login-failed', {bubbles: true, composed: true}));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -100,8 +105,6 @@ export default class UmbLoginPageElement extends UmbLitElement {
|
||||
if (returnPath) {
|
||||
location.href = returnPath;
|
||||
}
|
||||
|
||||
this.dispatchEvent(new CustomEvent('umb-login-success', {bubbles: true, composed: true, detail: response.data}));
|
||||
};
|
||||
|
||||
get #greetingLocalizationKey() {
|
||||
|
||||
@@ -56,6 +56,7 @@ export default class UmbMfaPageElement extends UmbLitElement {
|
||||
if (codeInput) {
|
||||
codeInput.error = false;
|
||||
codeInput.errorMessage = '';
|
||||
codeInput.setCustomValidity('');
|
||||
}
|
||||
|
||||
if (!form.checkValidity()) return;
|
||||
@@ -84,44 +85,30 @@ export default class UmbMfaPageElement extends UmbLitElement {
|
||||
|
||||
this.buttonState = 'waiting';
|
||||
|
||||
try {
|
||||
const response = await this.#authContext.validateMfaCode(code, provider);
|
||||
if (response.error) {
|
||||
if (codeInput) {
|
||||
codeInput.error = true;
|
||||
codeInput.errorMessage = response.error;
|
||||
} else {
|
||||
this.error = response.error;
|
||||
}
|
||||
this.buttonState = 'failed';
|
||||
return;
|
||||
}
|
||||
|
||||
this.buttonState = 'success';
|
||||
|
||||
const returnPath = this.#authContext.returnPath;
|
||||
if (returnPath) {
|
||||
location.href = returnPath;
|
||||
}
|
||||
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('umb-login-success', {bubbles: true, composed: true})
|
||||
);
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
this.error = e.message ?? 'Unknown error';
|
||||
const response = await this.#authContext.validateMfaCode(code, provider);
|
||||
if (response.error) {
|
||||
if (codeInput) {
|
||||
codeInput.error = true;
|
||||
codeInput.errorMessage = response.error;
|
||||
} else {
|
||||
this.error = 'Unknown error';
|
||||
this.error = response.error;
|
||||
}
|
||||
this.buttonState = 'failed';
|
||||
this.dispatchEvent(new CustomEvent('umb-login-failed', {bubbles: true, composed: true}));
|
||||
return;
|
||||
}
|
||||
|
||||
this.buttonState = 'success';
|
||||
|
||||
const returnPath = this.#authContext.returnPath;
|
||||
if (returnPath) {
|
||||
location.href = returnPath;
|
||||
}
|
||||
}
|
||||
|
||||
protected renderDefaultView() {
|
||||
return html`
|
||||
<uui-form>
|
||||
<form id="LoginForm" @submit=${this.#handleSubmit}>
|
||||
<form id="LoginForm" @submit=${this.#handleSubmit} novalidate>
|
||||
<header id="header">
|
||||
<h1>
|
||||
<umb-localize key="auth_mfaTitle">One last step</umb-localize>
|
||||
@@ -141,7 +128,7 @@ export default class UmbMfaPageElement extends UmbLitElement {
|
||||
<uui-label id="providerLabel" for="provider" slot="label" required>
|
||||
<umb-localize key="auth_mfaMultipleText">Please choose a 2-factor provider</umb-localize>
|
||||
</uui-label>
|
||||
<uui-select id="provider" name="provider" .options=${this.providers} aria-required="true" required></uui-select>
|
||||
<uui-select label=${this.localize.term('auth_mfaMultipleText')} id="provider" name="provider" .options=${this.providers} aria-required="true" required></uui-select>
|
||||
</uui-form-layout-item>
|
||||
`
|
||||
: nothing}
|
||||
@@ -162,6 +149,7 @@ export default class UmbMfaPageElement extends UmbLitElement {
|
||||
aria-required="true"
|
||||
required
|
||||
required-message=${this.localize.term('auth_mfaCodeInputHelp')}
|
||||
label=${this.localize.term('auth_mfaCodeInput')}
|
||||
style="width:100%;">
|
||||
</uui-input>
|
||||
</uui-form-layout-item>
|
||||
|
||||
@@ -36,13 +36,21 @@ export class UmbAuthRepository extends UmbRepositoryBase {
|
||||
|
||||
const response = await fetch(request);
|
||||
|
||||
// If the response code is 402, it means that the user has enabled 2-factor authentication
|
||||
let twoFactorView = '';
|
||||
let twoFactorProviders: Array<string> = [];
|
||||
if (response.status === 402) {
|
||||
const responseData = await response.json();
|
||||
twoFactorView = responseData.twoFactorLoginView ?? '';
|
||||
twoFactorProviders = responseData.enabledTwoFactorProviderNames ?? [];
|
||||
if (!response.ok) {
|
||||
// If the response code is 402, it means that the user has enabled 2-factor authentication
|
||||
if (response.status === 402) {
|
||||
const responseData = await response.json();
|
||||
return {
|
||||
status: response.status,
|
||||
twoFactorView: responseData.twoFactorLoginView ?? '',
|
||||
twoFactorProviders: responseData.enabledTwoFactorProviderNames ?? [],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
status: response.status,
|
||||
error: await this.#getErrorText(response),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -50,41 +58,42 @@ export class UmbAuthRepository extends UmbRepositoryBase {
|
||||
data: {
|
||||
username: data.username,
|
||||
},
|
||||
error: await this.#getErrorText(response),
|
||||
twoFactorView,
|
||||
twoFactorProviders,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
status: 500,
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
error: error instanceof Error ? error.message : this.#localize.term('auth_receivedErrorFromServer'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public async validateMfaCode(code: string, provider: string): Promise<MfaCodeResponse> {
|
||||
const requestData = new Request('management/api/v1/security/back-office/verify-2fa', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
code,
|
||||
provider,
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
try {
|
||||
const requestData = new Request('management/api/v1/security/back-office/verify-2fa', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
code,
|
||||
provider,
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
const request = fetch(requestData);
|
||||
const response = await fetch(requestData);
|
||||
|
||||
const response = await tryExecute(request);
|
||||
if (!response.ok) {
|
||||
return {
|
||||
error: response.status === 400 ? this.#localize.term('auth_mfaInvalidCode') : await this.#getErrorText(response),
|
||||
};
|
||||
}
|
||||
|
||||
if (response.error) {
|
||||
return {};
|
||||
} catch (error) {
|
||||
return {
|
||||
error: this.#getApiErrorDetailText(response.error, 'Could not validate the MFA code'),
|
||||
error: error instanceof Error ? error.message : this.#localize.term('auth_receivedErrorFromServer'),
|
||||
};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
public async resetPassword(email: string): Promise<ResetPasswordResponse> {
|
||||
@@ -208,12 +217,11 @@ export class UmbAuthRepository extends UmbRepositoryBase {
|
||||
case 402:
|
||||
return this.#localize.term('auth_mfaText');
|
||||
|
||||
case 500:
|
||||
return this.#localize.term('auth_receivedErrorFromServer');
|
||||
case 403:
|
||||
return this.#localize.term('auth_userLockedOut');
|
||||
|
||||
default:
|
||||
return (
|
||||
response.statusText ??
|
||||
this.#localize.term('auth_receivedErrorFromServer')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ export default {
|
||||
passwordMinLength: 'Adgangskoden skal være mindst {0} tegn lang.',
|
||||
passwordIsBlank: 'Din nye adgangskode kan ikke være tom.',
|
||||
userFailedLogin: 'Ups! Vi kunne ikke logge dig ind. Tjek at dit brugernavn og adgangskode er korrekt og prøv igen.',
|
||||
userLockedOut: 'Din konto er blevet låst. Prøv igen senere.',
|
||||
receivedErrorFromServer: 'Der skete en fejl på serveren',
|
||||
resetCodeExpired: 'Det link, du har klikket på, er ugyldigt eller udløbet',
|
||||
userInviteWelcomeMessage: 'Hej og velkommen til Umbraco! På bare 1 minut vil du være klar til at komme i gang, vi skal bare have dig til at oprette en adgangskode.',
|
||||
@@ -41,6 +42,7 @@ export default {
|
||||
mfaText: 'Du har aktiveret multi-faktor godkendelse. Du skal nu bekræfte din identitet.',
|
||||
mfaMultipleText: 'Vælg venligst en godkendelsesmetode',
|
||||
mfaCodeInput: 'Kode',
|
||||
mfaInvalidCode: 'Forkert kode indtastet',
|
||||
signInWith: 'Log ind med {0}',
|
||||
returnToLogin: 'Tilbage til log ind',
|
||||
localLoginDisabled: 'Desværre er det ikke muligt at logge ind direkte. Det er blevet deaktiveret af en login-udbyder.',
|
||||
|
||||
@@ -23,6 +23,7 @@ export default {
|
||||
passwordMinLength: 'Ihr Kennwort muss mindestens {0} Zeichen lang sein.',
|
||||
passwordIsBlank: 'Ihr neues Kennwort darf nicht leer sein!',
|
||||
userFailedLogin: 'Hoppla! Wir konnten Sie nicht anmelden. Bitte überprüfen Sie Ihre Anmeldeinformationen und versuchen Sie es erneut.',
|
||||
userLockedOut: 'Ihr Konto wurde gesperrt. Bitte versuchen Sie es später erneut.',
|
||||
receivedErrorFromServer: 'Der Server hat einen Fehler gemeldet',
|
||||
resetCodeExpired: 'Der aufgerufene Link ist ungültig oder abgelaufen',
|
||||
userInviteWelcomeMessage: 'Hallo und Willkommen bei Umbraco! In nur einer Minute sind Sie bereit loszulegen, Sie müssen nur ein Kennwort festlegen.',
|
||||
@@ -41,6 +42,7 @@ export default {
|
||||
mfaText: 'Sie haben die Multi-Faktor-Authentifizierung aktiviert und müssen Ihre Identität bestätigen.',
|
||||
mfaMultipleText: 'Bitte wählen Sie einen Multi-Faktor-Anbieter',
|
||||
mfaCodeInput: 'Bestätigungscode',
|
||||
mfaInvalidCode: 'Ungültiger Code eingegeben',
|
||||
signInWith: 'Anmelden mit {0}',
|
||||
returnToLogin: 'Zurück zur Anmeldung',
|
||||
localLoginDisabled: 'Leider ist eine direkte Anmeldung nicht möglich. Sie wurde von einem Anbieter deaktiviert.',
|
||||
|
||||
@@ -23,6 +23,7 @@ export default {
|
||||
passwordMinLength: 'The password must be at least {0} characters long.',
|
||||
passwordIsBlank: 'The password cannot be blank.',
|
||||
userFailedLogin: 'Oops! We couldn\'t log you in. Please check your credentials and try again.',
|
||||
userLockedOut: 'Your account has been locked out. Please try again later.',
|
||||
receivedErrorFromServer: 'Received an error from the server',
|
||||
resetCodeExpired: 'The link you have clicked on is invalid or has expired',
|
||||
userInviteWelcomeMessage: 'Hello there and welcome to Umbraco! In just 1 minute you’ll be good to go, we just need you to setup a password.',
|
||||
@@ -41,6 +42,7 @@ export default {
|
||||
mfaText: 'You have enabled 2-factor authentication and must verify your identity.',
|
||||
mfaMultipleText: 'Please choose a 2-factor provider',
|
||||
mfaCodeInput: 'Verification code',
|
||||
mfaInvalidCode: 'Invalid code entered',
|
||||
signInWith: 'Sign in with {0}',
|
||||
returnToLogin: 'Return to login',
|
||||
localLoginDisabled: 'Unfortunately, direct login is not possible. It has been disabled by a provider.',
|
||||
|
||||
@@ -23,6 +23,7 @@ export default {
|
||||
passwordMinLength: 'The password must be at least {0} characters long.',
|
||||
passwordIsBlank: 'The password cannot be blank.',
|
||||
userFailedLogin: 'Oops! We couldn\'t log you in. Please check your credentials and try again.',
|
||||
userLockedOut: 'Your account has been locked out. Please try again later.',
|
||||
receivedErrorFromServer: 'Received an error from the server',
|
||||
resetCodeExpired: 'The link you have clicked on is invalid or has expired',
|
||||
userInviteWelcomeMessage: 'Hello there and welcome to Umbraco! In just 1 minute you’ll be good to go, we just need you to setup a password.',
|
||||
@@ -41,6 +42,7 @@ export default {
|
||||
mfaText: 'You have enabled 2-factor authentication and must verify your identity.',
|
||||
mfaMultipleText: 'Please choose a 2-factor provider',
|
||||
mfaCodeInput: 'Verification code',
|
||||
mfaInvalidCode: 'Invalid code entered',
|
||||
signInWith: 'Sign in with {0}',
|
||||
returnToLogin: 'Return to login',
|
||||
localLoginDisabled: 'Unfortunately, direct login is not possible. It has been disabled by a provider.',
|
||||
|
||||
@@ -23,6 +23,7 @@ export default {
|
||||
passwordMinLength: 'Passordet må være minst {0} tegn langt.',
|
||||
passwordIsBlank: 'Det nye passordet kan ikke være tomt!',
|
||||
userFailedLogin: 'Oops! Vi kunne ikke logge deg inn. Vennligst sjekk legitimasjonen din og prøv igjen.',
|
||||
userLockedOut: 'Kontoen din er låst. Vennligst prøv igjen senere.',
|
||||
receivedErrorFromServer: 'Mottok en feil fra serveren',
|
||||
resetCodeExpired: 'Lenken du har klikket på er ugyldig eller har utløpt',
|
||||
userInviteWelcomeMessage: 'Hei og velkommen til Umbraco! Om bare 1 minutt er du klar til å starte, vi trenger bare at du setter et passord.',
|
||||
@@ -41,6 +42,7 @@ export default {
|
||||
mfaText: 'Du har aktivert tofaktorautentisering og må bekrefte identiteten din.',
|
||||
mfaMultipleText: 'Velg en tofaktorleverandør',
|
||||
mfaCodeInput: 'Bekreftelseskode',
|
||||
mfaInvalidCode: 'Ugyldig kode angitt',
|
||||
signInWith: 'Logg inn med {0}',
|
||||
returnToLogin: 'Tilbake til innlogging',
|
||||
localLoginDisabled: 'Dessverre er direkte innlogging ikke mulig. Den er deaktivert av en leverandør.',
|
||||
|
||||
@@ -23,6 +23,7 @@ export default {
|
||||
passwordMinLength: 'Je wachtwoord moet minstens {0} tekens lang zijn.',
|
||||
passwordIsBlank: 'Je nieuwe wachtwoord mag niet leeg zijn!',
|
||||
userFailedLogin: 'Oeps! We konden je niet inloggen. Controleer je inloggegevens en probeer het opnieuw.',
|
||||
userLockedOut: 'Je account is geblokkeerd. Probeer het later opnieuw.',
|
||||
receivedErrorFromServer: 'Een error ontvangen van de server',
|
||||
resetCodeExpired: 'De link die je hebt aangeklikt is niet (meer) geldig.',
|
||||
userInviteWelcomeMessage: 'Hallo en welkom in Umbraco! Binnen ongeveer één minuut kan je aan de slag. Je moet enkel je wachtwoord instellen.',
|
||||
@@ -41,6 +42,7 @@ export default {
|
||||
mfaText: 'Je hebt tweestapsverificatie ingeschakeld en moet je identiteit verifiëren.',
|
||||
mfaMultipleText: 'Kies een tweestapsverificatie aanbieder',
|
||||
mfaCodeInput: 'Verificatiecode',
|
||||
mfaInvalidCode: 'Ongeldige code ingevoerd',
|
||||
signInWith: 'Inloggen met {0}',
|
||||
returnToLogin: 'Terug naar loginformulier',
|
||||
localLoginDisabled: 'Helaas is direct inloggen niet mogelijk. Het is uitgeschakeld door een aanbieder.',
|
||||
|
||||
@@ -23,6 +23,7 @@ export default {
|
||||
passwordMinLength: 'Lösenordet måste vara minst {0} tecken långt.',
|
||||
passwordIsBlank: 'Ditt nya lösenord kan inte vara tomt!',
|
||||
userFailedLogin: 'Hoppsan! Vi kunde inte logga in dig. Vänligen kontrollera dina uppgifter och försök igen.',
|
||||
userLockedOut: 'Ditt konto har låsts. Vänligen försök igen senare.',
|
||||
receivedErrorFromServer: 'Ett fel uppstod på servern',
|
||||
resetCodeExpired: 'Länken du har klickat på är ogiltig eller har gått ut',
|
||||
userInviteWelcomeMessage: 'Hej och välkommen till Umbraco! Inom bara 1 minut är du redo att börja, vi behöver bara att du ställer in ett lösenord.',
|
||||
@@ -41,6 +42,7 @@ export default {
|
||||
mfaText: 'Du har aktiverat tvåfaktorsautentisering och måste verifiera din identitet.',
|
||||
mfaMultipleText: 'Vänligen välj en tvåfaktorsleverantör',
|
||||
mfaCodeInput: 'Verifieringskod',
|
||||
mfaInvalidCode: 'Ogiltig kod angiven',
|
||||
signInWith: 'Logga in med {0}',
|
||||
returnToLogin: 'Återgå till inloggning',
|
||||
localLoginDisabled: 'Tyvärr är direktinloggning inte möjlig. Det har inaktiverats av en leverantör.',
|
||||
|
||||
Reference in New Issue
Block a user