Compare commits
6 commits
846e69fe7b
...
0a270ca31a
Author | SHA1 | Date | |
---|---|---|---|
|
0a270ca31a | ||
|
437d02ef5d | ||
|
2c78c01d57 | ||
|
78af158c30 | ||
|
4687b21d79 | ||
|
7ceaa9c090 |
6 changed files with 25 additions and 62 deletions
|
@ -47,10 +47,6 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
throw new Error("incorrect password");
|
throw new Error("incorrect password");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!profile.twoFactorEnabled) {
|
|
||||||
throw new Error("2fa not enabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
const clientData = JSON.parse(ps.clientDataJSON);
|
const clientData = JSON.parse(ps.clientDataJSON);
|
||||||
|
|
||||||
if (clientData.type !== "webauthn.create") {
|
if (clientData.type !== "webauthn.create") {
|
||||||
|
@ -148,6 +144,8 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
UserProfiles.update(user.id, { securityKeysAvailable: true });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: credentialIdString,
|
id: credentialIdString,
|
||||||
name: ps.name,
|
name: ps.name,
|
||||||
|
|
|
@ -32,10 +32,6 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
throw new Error("incorrect password");
|
throw new Error("incorrect password");
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (!profile.twoFactorEnabled) {
|
|
||||||
// throw new Error("2fa not enabled");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 32 byte challenge
|
// 32 byte challenge
|
||||||
const entropy = await randomBytes(32);
|
const entropy = await randomBytes(32);
|
||||||
const challenge = entropy
|
const challenge = entropy
|
||||||
|
|
|
@ -47,8 +47,9 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (keyCount === 0) {
|
if (keyCount === 0) {
|
||||||
await UserProfiles.update(me.id, {
|
await UserProfiles.update(user.id, {
|
||||||
usePasswordLessLogin: false,
|
usePasswordLessLogin: false,
|
||||||
|
securityKeysAvailable: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -116,7 +116,7 @@ export default async (ctx: Koa.Context) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!profile.twoFactorEnabled) {
|
if (!profile.twoFactorEnabled && !profile.securityKeysAvailable) {
|
||||||
if (same) {
|
if (same) {
|
||||||
signin(ctx, user);
|
signin(ctx, user);
|
||||||
return;
|
return;
|
||||||
|
@ -128,7 +128,7 @@ export default async (ctx: Koa.Context) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token) {
|
if (token && profile.twoFactorEnabled) {
|
||||||
if (!same) {
|
if (!same) {
|
||||||
await fail(403, {
|
await fail(403, {
|
||||||
id: "932c904e-9460-45b7-9ce6-7ed33be7eb2c",
|
id: "932c904e-9460-45b7-9ce6-7ed33be7eb2c",
|
||||||
|
|
|
@ -79,10 +79,10 @@
|
||||||
{{ i18n.ts.retry }}
|
{{ i18n.ts.retry }}
|
||||||
</MkButton>
|
</MkButton>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="user && user.securityKeys" class="or-hr">
|
<div v-if="user && user.securityKeys && user.twoFactorEnabled" class="or-hr">
|
||||||
<p class="or-msg">{{ i18n.ts.or }}</p>
|
<p class="or-msg">{{ i18n.ts.or }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="twofa-group totp-group">
|
<div v-if="user.twoFactorEnabled" class="twofa-group totp-group">
|
||||||
<p style="margin-bottom: 0">
|
<p style="margin-bottom: 0">
|
||||||
{{ i18n.ts.twoStepAuthentication }}
|
{{ i18n.ts.twoStepAuthentication }}
|
||||||
</p>
|
</p>
|
||||||
|
@ -247,25 +247,23 @@ function queryKey() {
|
||||||
function onSubmit() {
|
function onSubmit() {
|
||||||
signing.value = true;
|
signing.value = true;
|
||||||
console.log("submit");
|
console.log("submit");
|
||||||
if (!totpLogin.value && user.value && user.value.twoFactorEnabled) {
|
if (window.PublicKeyCredential && user.value.securityKeys) {
|
||||||
if (window.PublicKeyCredential && user.value.securityKeys) {
|
os.api("signin", {
|
||||||
os.api("signin", {
|
username: username.value,
|
||||||
username: username.value,
|
password: password.value,
|
||||||
password: password.value,
|
"hcaptcha-response": hCaptchaResponse.value,
|
||||||
"hcaptcha-response": hCaptchaResponse.value,
|
"g-recaptcha-response": reCaptchaResponse.value,
|
||||||
"g-recaptcha-response": reCaptchaResponse.value,
|
})
|
||||||
|
.then((res) => {
|
||||||
|
totpLogin.value = true;
|
||||||
|
signing.value = false;
|
||||||
|
challengeData.value = res;
|
||||||
|
return queryKey();
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.catch(loginFailed);
|
||||||
totpLogin.value = true;
|
} else if (!totpLogin.value && user.value && user.value.twoFactorEnabled) {
|
||||||
signing.value = false;
|
totpLogin.value = true;
|
||||||
challengeData.value = res;
|
signing.value = false;
|
||||||
return queryKey();
|
|
||||||
})
|
|
||||||
.catch(loginFailed);
|
|
||||||
} else {
|
|
||||||
totpLogin.value = true;
|
|
||||||
signing.value = false;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
os.api("signin", {
|
os.api("signin", {
|
||||||
username: username.value,
|
username: username.value,
|
||||||
|
|
|
@ -14,17 +14,7 @@
|
||||||
<template #caption>{{ i18n.ts.totpDescription }}</template>
|
<template #caption>{{ i18n.ts.totpDescription }}</template>
|
||||||
<div v-if="$i.twoFactorEnabled" class="_gaps_s">
|
<div v-if="$i.twoFactorEnabled" class="_gaps_s">
|
||||||
<div v-text="i18n.ts._2fa.alreadyRegistered" />
|
<div v-text="i18n.ts._2fa.alreadyRegistered" />
|
||||||
<template v-if="$i.securityKeysList.length > 0">
|
<MkButton @click="unregisterTOTP"
|
||||||
<MkButton @click="renewTOTP"
|
|
||||||
><i
|
|
||||||
:class="icon('ph-shield-check')"
|
|
||||||
style="margin-inline-end: 0.5rem"
|
|
||||||
></i
|
|
||||||
>{{ i18n.ts._2fa.renewTOTP }}</MkButton
|
|
||||||
>
|
|
||||||
<MkInfo>{{ i18n.ts._2fa.whyTOTPOnlyRenew }}</MkInfo>
|
|
||||||
</template>
|
|
||||||
<MkButton v-else @click="unregisterTOTP"
|
|
||||||
><i
|
><i
|
||||||
:class="icon('ph-shield-slash')"
|
:class="icon('ph-shield-slash')"
|
||||||
style="margin-inline-end: 0.5rem"
|
style="margin-inline-end: 0.5rem"
|
||||||
|
@ -59,13 +49,6 @@
|
||||||
{{ i18n.ts._2fa.securityKeyNotSupported }}
|
{{ i18n.ts._2fa.securityKeyNotSupported }}
|
||||||
</MkInfo>
|
</MkInfo>
|
||||||
|
|
||||||
<MkInfo
|
|
||||||
v-else-if="supportsCredentials && !$i.twoFactorEnabled"
|
|
||||||
warn
|
|
||||||
>
|
|
||||||
{{ i18n.ts._2fa.registerTOTPBeforeKey }}
|
|
||||||
</MkInfo>
|
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<MkButton primary @click="addSecurityKey"
|
<MkButton primary @click="addSecurityKey"
|
||||||
><i
|
><i
|
||||||
|
@ -205,19 +188,6 @@ function unregisterTOTP() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function renewTOTP() {
|
|
||||||
os.confirm({
|
|
||||||
type: "question",
|
|
||||||
title: i18n.ts._2fa.renewTOTP,
|
|
||||||
text: i18n.ts._2fa.renewTOTPConfirm,
|
|
||||||
okText: i18n.ts._2fa.renewTOTPOk,
|
|
||||||
cancelText: i18n.ts._2fa.renewTOTPCancel,
|
|
||||||
}).then(({ canceled }) => {
|
|
||||||
if (canceled) return;
|
|
||||||
registerTOTP();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function unregisterKey(key) {
|
async function unregisterKey(key) {
|
||||||
const confirm = await os.confirm({
|
const confirm = await os.confirm({
|
||||||
type: "question",
|
type: "question",
|
||||||
|
|
Loading…
Reference in a new issue