mirror of
https://github.com/discourse/discourse.git
synced 2025-01-18 10:42:45 +08:00
DEV: Convert security-key-form to glimmer/gjs (#27364)
(`allowedCredentialIds` and `challenge` args were unused)
This commit is contained in:
parent
42a529f9ae
commit
c18e5d1698
|
@ -78,11 +78,9 @@
|
|||
>
|
||||
{{#if @showSecurityKey}}
|
||||
<SecurityKeyForm
|
||||
@allowedCredentialIds={{@securityKeyAllowedCredentialIds}}
|
||||
@challenge={{@securityKeyChallenge}}
|
||||
@showSecurityKey={{@showSecurityKey}}
|
||||
@showSecondFactor={{@showSecondFactor}}
|
||||
@secondFactorMethod={{@secondFactorMethod}}
|
||||
@setShowSecurityKey={{fn (mut @showSecurityKey)}}
|
||||
@setShowSecondFactor={{fn (mut @showSecondFactor)}}
|
||||
@setSecondFactorMethod={{fn (mut @secondFactorMethod)}}
|
||||
@backupEnabled={{@backupEnabled}}
|
||||
@totpEnabled={{@totpEnabled}}
|
||||
@otherMethodAllowed={{@otherMethodAllowed}}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { on } from "@ember/modifier";
|
||||
import { action } from "@ember/object";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import { SECOND_FACTOR_METHODS } from "discourse/models/user";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
|
||||
export default class SecurityKeyForm extends Component {
|
||||
@action
|
||||
useAnotherMethod(event) {
|
||||
event.preventDefault();
|
||||
this.args.setShowSecurityKey?.(false);
|
||||
this.args.setShowSecondFactor?.(true);
|
||||
|
||||
if (this.args.totpEnabled) {
|
||||
this.args.setSecondFactorMethod?.(SECOND_FACTOR_METHODS.TOTP);
|
||||
} else if (this.args.backupEnabled) {
|
||||
this.args.setSecondFactorMethod?.(SECOND_FACTOR_METHODS.BACKUP_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
<template>
|
||||
<div id="security-key">
|
||||
<DButton
|
||||
@action={{@action}}
|
||||
@icon="key"
|
||||
@label="login.security_key_authenticate"
|
||||
id="security-key-authenticate-button"
|
||||
class="btn-large btn-primary"
|
||||
/>
|
||||
<p>
|
||||
{{#if @otherMethodAllowed}}
|
||||
<a
|
||||
{{on "click" this.useAnotherMethod}}
|
||||
href
|
||||
class="toggle-second-factor-method"
|
||||
>{{i18n "login.security_key_alternative"}}</a>
|
||||
{{/if}}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
<div id="security-key">
|
||||
<DButton
|
||||
@action={{this.action}}
|
||||
@icon="key"
|
||||
@label="login.security_key_authenticate"
|
||||
id="security-key-authenticate-button"
|
||||
class="btn-large btn-primary"
|
||||
/>
|
||||
<p>
|
||||
{{#if this.otherMethodAllowed}}
|
||||
<a
|
||||
href
|
||||
class="toggle-second-factor-method"
|
||||
{{on "click" this.useAnotherMethod}}
|
||||
>{{i18n "login.security_key_alternative"}}</a>
|
||||
{{/if}}
|
||||
</p>
|
||||
</div>
|
|
@ -1,18 +0,0 @@
|
|||
import Component from "@ember/component";
|
||||
import { action } from "@ember/object";
|
||||
import { SECOND_FACTOR_METHODS } from "discourse/models/user";
|
||||
|
||||
export default Component.extend({
|
||||
@action
|
||||
useAnotherMethod(event) {
|
||||
event?.preventDefault();
|
||||
this.set("showSecurityKey", false);
|
||||
this.set("showSecondFactor", true);
|
||||
|
||||
if (this.totpEnabled) {
|
||||
this.set("secondFactorMethod", SECOND_FACTOR_METHODS.TOTP);
|
||||
} else if (this.backupEnabled) {
|
||||
this.set("secondFactorMethod", SECOND_FACTOR_METHODS.BACKUP_CODE);
|
||||
}
|
||||
},
|
||||
});
|
|
@ -1,4 +1,5 @@
|
|||
import Controller from "@ember/controller";
|
||||
import { action } from "@ember/object";
|
||||
import { service } from "@ember/service";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
|
@ -11,9 +12,7 @@ export default Controller.extend({
|
|||
router: service(),
|
||||
|
||||
secondFactorMethod: null,
|
||||
|
||||
secondFactorToken: null,
|
||||
|
||||
lockImageUrl: getURL("/images/lock.svg"),
|
||||
|
||||
@discourseComputed("model")
|
||||
|
@ -21,57 +20,62 @@ export default Controller.extend({
|
|||
return model.security_key_required || model.second_factor_required;
|
||||
},
|
||||
|
||||
actions: {
|
||||
finishLogin() {
|
||||
let data = {
|
||||
second_factor_method: this.secondFactorMethod,
|
||||
timezone: moment.tz.guess(),
|
||||
};
|
||||
if (this.securityKeyCredential) {
|
||||
data.second_factor_token = this.securityKeyCredential;
|
||||
} else {
|
||||
data.second_factor_token = this.secondFactorToken;
|
||||
}
|
||||
@action
|
||||
async finishLogin() {
|
||||
let data = {
|
||||
second_factor_method: this.secondFactorMethod,
|
||||
timezone: moment.tz.guess(),
|
||||
};
|
||||
|
||||
ajax({
|
||||
if (this.securityKeyCredential) {
|
||||
data.second_factor_token = this.securityKeyCredential;
|
||||
} else {
|
||||
data.second_factor_token = this.secondFactorToken;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await ajax({
|
||||
url: `/session/email-login/${this.model.token}`,
|
||||
type: "POST",
|
||||
data,
|
||||
})
|
||||
.then((result) => {
|
||||
if (result.success) {
|
||||
let destination = "/";
|
||||
});
|
||||
|
||||
const safeMode = new URL(
|
||||
this.router.currentURL,
|
||||
window.location.origin
|
||||
).searchParams.get("safe_mode");
|
||||
if (!result.success) {
|
||||
this.set("model.error", result.error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (safeMode) {
|
||||
const params = new URLSearchParams();
|
||||
params.set("safe_mode", safeMode);
|
||||
destination += `?${params.toString()}`;
|
||||
}
|
||||
let destination = "/";
|
||||
|
||||
DiscourseURL.redirectTo(destination);
|
||||
} else {
|
||||
this.set("model.error", result.error);
|
||||
}
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
},
|
||||
authenticateSecurityKey() {
|
||||
getWebauthnCredential(
|
||||
this.model.challenge,
|
||||
this.model.allowed_credential_ids,
|
||||
(credentialData) => {
|
||||
this.set("securityKeyCredential", credentialData);
|
||||
this.send("finishLogin");
|
||||
},
|
||||
(errorMessage) => {
|
||||
this.set("model.error", errorMessage);
|
||||
}
|
||||
);
|
||||
},
|
||||
const safeMode = new URL(
|
||||
this.router.currentURL,
|
||||
window.location.origin
|
||||
).searchParams.get("safe_mode");
|
||||
|
||||
if (safeMode) {
|
||||
const params = new URLSearchParams();
|
||||
params.set("safe_mode", safeMode);
|
||||
destination += `?${params.toString()}`;
|
||||
}
|
||||
|
||||
DiscourseURL.redirectTo(destination);
|
||||
} catch (e) {
|
||||
popupAjaxError(e);
|
||||
}
|
||||
},
|
||||
|
||||
@action
|
||||
authenticateSecurityKey() {
|
||||
getWebauthnCredential(
|
||||
this.model.challenge,
|
||||
this.model.allowed_credential_ids,
|
||||
(credentialData) => {
|
||||
this.set("securityKeyCredential", credentialData);
|
||||
this.send("finishLogin");
|
||||
},
|
||||
(errorMessage) => {
|
||||
this.set("model.error", errorMessage);
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -79,9 +79,10 @@ export default Controller.extend(PasswordValidation, {
|
|||
this.toggleProperty("maskPassword");
|
||||
},
|
||||
|
||||
actions: {
|
||||
submit() {
|
||||
ajax({
|
||||
@action
|
||||
async submit() {
|
||||
try {
|
||||
const result = await ajax({
|
||||
url: userPath(`password-reset/${this.get("model.token")}.json`),
|
||||
type: "PUT",
|
||||
data: {
|
||||
|
@ -91,81 +92,71 @@ export default Controller.extend(PasswordValidation, {
|
|||
second_factor_method: this.selectedSecondFactorMethod,
|
||||
timezone: moment.tz.guess(),
|
||||
},
|
||||
})
|
||||
.then((result) => {
|
||||
if (result.success) {
|
||||
this.set("successMessage", result.message);
|
||||
this.set("redirectTo", result.redirect_to);
|
||||
if (result.requires_approval) {
|
||||
this.set("requiresApproval", true);
|
||||
} else {
|
||||
this.set("redirected", true);
|
||||
DiscourseURL.redirectTo(result.redirect_to || "/");
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
result.errors.security_keys ||
|
||||
result.errors.user_second_factors
|
||||
) {
|
||||
this.setProperties({
|
||||
secondFactorRequired: this.secondFactorRequired,
|
||||
securityKeyRequired: this.securityKeyRequired,
|
||||
password: null,
|
||||
errorMessage: result.message,
|
||||
});
|
||||
} else if (this.secondFactorRequired || this.securityKeyRequired) {
|
||||
this.setProperties({
|
||||
secondFactorRequired: false,
|
||||
securityKeyRequired: false,
|
||||
errorMessage: null,
|
||||
});
|
||||
} else if (
|
||||
result.errors &&
|
||||
result.errors.password &&
|
||||
result.errors.password.length > 0
|
||||
) {
|
||||
this.rejectedPasswords.pushObject(this.accountPassword);
|
||||
this.rejectedPasswordsMessages.set(
|
||||
this.accountPassword,
|
||||
(result.friendly_messages || []).join("\n")
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
if (result.message) {
|
||||
this.set("errorMessage", result.message);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e.jqXHR && e.jqXHR.status === 429) {
|
||||
this.set("errorMessage", I18n.t("user.second_factor.rate_limit"));
|
||||
} else {
|
||||
throw new Error(e);
|
||||
}
|
||||
});
|
||||
},
|
||||
if (result.success) {
|
||||
this.set("successMessage", result.message);
|
||||
this.set("redirectTo", result.redirect_to);
|
||||
|
||||
authenticateSecurityKey() {
|
||||
this.set(
|
||||
"selectedSecondFactorMethod",
|
||||
SECOND_FACTOR_METHODS.SECURITY_KEY
|
||||
);
|
||||
|
||||
getWebauthnCredential(
|
||||
this.model.challenge,
|
||||
this.model.allowed_credential_ids,
|
||||
(credentialData) => {
|
||||
this.set("securityKeyCredential", credentialData);
|
||||
this.send("submit");
|
||||
},
|
||||
(errorMessage) => {
|
||||
this.setProperties({
|
||||
securityKeyRequired: true,
|
||||
password: null,
|
||||
errorMessage,
|
||||
});
|
||||
if (result.requires_approval) {
|
||||
this.set("requiresApproval", true);
|
||||
} else {
|
||||
this.set("redirected", true);
|
||||
DiscourseURL.redirectTo(result.redirect_to || "/");
|
||||
}
|
||||
);
|
||||
},
|
||||
} else {
|
||||
if (result.errors.security_keys || result.errors.user_second_factors) {
|
||||
this.setProperties({
|
||||
secondFactorRequired: this.secondFactorRequired,
|
||||
securityKeyRequired: this.securityKeyRequired,
|
||||
password: null,
|
||||
errorMessage: result.message,
|
||||
});
|
||||
} else if (this.secondFactorRequired || this.securityKeyRequired) {
|
||||
this.setProperties({
|
||||
secondFactorRequired: false,
|
||||
securityKeyRequired: false,
|
||||
errorMessage: null,
|
||||
});
|
||||
} else if (result.errors?.password?.length > 0) {
|
||||
this.rejectedPasswords.pushObject(this.accountPassword);
|
||||
this.rejectedPasswordsMessages.set(
|
||||
this.accountPassword,
|
||||
(result.friendly_messages || []).join("\n")
|
||||
);
|
||||
}
|
||||
|
||||
if (result.message) {
|
||||
this.set("errorMessage", result.message);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.jqXHR?.status === 429) {
|
||||
this.set("errorMessage", I18n.t("user.second_factor.rate_limit"));
|
||||
} else {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@action
|
||||
authenticateSecurityKey() {
|
||||
this.set("selectedSecondFactorMethod", SECOND_FACTOR_METHODS.SECURITY_KEY);
|
||||
|
||||
getWebauthnCredential(
|
||||
this.model.challenge,
|
||||
this.model.allowed_credential_ids,
|
||||
(credentialData) => {
|
||||
this.set("securityKeyCredential", credentialData);
|
||||
this.send("submit");
|
||||
},
|
||||
(errorMessage) => {
|
||||
this.setProperties({
|
||||
securityKeyRequired: true,
|
||||
password: null,
|
||||
errorMessage,
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -16,15 +16,14 @@
|
|||
{{#if this.secondFactorRequired}}
|
||||
{{#if this.model.security_key_required}}
|
||||
<SecurityKeyForm
|
||||
@allowedCredentialIds={{this.model.allowed_credential_ids}}
|
||||
@challenge={{this.model.security_key_challenge}}
|
||||
@showSecurityKey={{this.model.security_key_required}}
|
||||
@showSecondFactor={{false}}
|
||||
@secondFactorMethod={{this.secondFactorMethod}}
|
||||
@setShowSecurityKey={{fn
|
||||
(mut this.model.security_key_required)
|
||||
}}
|
||||
@setSecondFactorMethod={{fn (mut this.secondFactorMethod)}}
|
||||
@backupEnabled={{this.model.backup_codes_enabled}}
|
||||
@totpEnabled={{this.model.totp_enabled}}
|
||||
@otherMethodAllowed={{this.secondFactorRequired}}
|
||||
@action={{action "authenticateSecurityKey"}}
|
||||
@action={{this.authenticateSecurityKey}}
|
||||
/>
|
||||
{{else}}
|
||||
<SecondFactorForm
|
||||
|
|
|
@ -35,15 +35,13 @@
|
|||
|
||||
{{#if this.displaySecurityKeyForm}}
|
||||
<SecurityKeyForm
|
||||
@allowedCredentialIds={{this.model.allowed_credential_ids}}
|
||||
@challenge={{this.model.security_key_challenge}}
|
||||
@showSecurityKey={{false}}
|
||||
@showSecondFactor={{false}}
|
||||
@secondFactorMethod={{this.selectedSecondFactorMethod}}
|
||||
@setSecondFactorMethod={{fn
|
||||
(mut this.selectedSecondFactorMethod)
|
||||
}}
|
||||
@backupEnabled={{this.backupEnabled}}
|
||||
@totpEnabled={{this.secondFactorRequired}}
|
||||
@otherMethodAllowed={{this.otherMethodAllowed}}
|
||||
@action={{action "authenticateSecurityKey"}}
|
||||
@action={{this.authenticateSecurityKey}}
|
||||
/>
|
||||
{{else}}
|
||||
<SecondFactorForm
|
||||
|
|
Loading…
Reference in New Issue
Block a user