diff --git a/frontend/src/api/users.ts b/frontend/src/api/users.ts index 78096b49..e863bd0a 100644 --- a/frontend/src/api/users.ts +++ b/frontend/src/api/users.ts @@ -41,3 +41,45 @@ export async function remove(id: number) { method: "DELETE", }); } + +export async function enableOTP(id: number, password: string) { + const res = await fetchURL(`/api/users/${id}/otp`, { + method: "POST", + body: JSON.stringify({ + password, + }), + }); + const payload: IOtpSetupKey = await res.json(); + + return payload; +} + +export async function checkOtp(id: number, code: string) { + return fetchURL(`/api/users/${id}/otp/check`, { + method: "POST", + body: JSON.stringify({ + code, + }), + }); +} + +export async function getOtpInfo(id: number, code: string) { + const res = await fetchURL(`/api/users/${id}/otp`, { + method: "GET", + headers: { + "X-TOTP-CODE": code, + }, + }); + const payload: IOtpSetupKey = await res.json(); + + return payload; +} + +export async function disableOtp(id: number, code: string) { + return fetchURL(`/api/users/${id}/otp`, { + method: "DELETE", + headers: { + "X-TOTP-CODE": code, + }, + }); +} diff --git a/frontend/src/types/user.d.ts b/frontend/src/types/user.d.ts index b81806fc..64e10225 100644 --- a/frontend/src/types/user.d.ts +++ b/frontend/src/types/user.d.ts @@ -12,6 +12,7 @@ interface IUser { singleClick: boolean; dateFormat: boolean; viewMode: ViewModeType; + otpEnabled: boolean; sorting?: Sorting; } @@ -64,3 +65,7 @@ interface IRegexp { } type UserTheme = "light" | "dark" | ""; + +interface IOtpSetupKey { + setupKey: string; +} diff --git a/frontend/src/utils/auth.ts b/frontend/src/utils/auth.ts index b868d90f..72e3ad3a 100644 --- a/frontend/src/utils/auth.ts +++ b/frontend/src/utils/auth.ts @@ -33,7 +33,7 @@ export async function login( username: string, password: string, recaptcha: string -) { +): Promise<{ otp: boolean; token: string }> { const data = { username, password, recaptcha }; const res = await fetch(`${baseURL}/api/login`, { @@ -47,7 +47,29 @@ export async function login( const body = await res.text(); if (res.status === 200) { - parseToken(body); + const payload = JSON.parse(body); + return payload; + } else { + throw new StatusError( + body || `${res.status} ${res.statusText}`, + res.status + ); + } +} + +export async function verifyTOTP(code: string, token: string): Promise { + const res = await fetch(`${baseURL}/api/login/otp`, { + method: "POST", + headers: { + "X-TOTP-CODE": code, + "X-TOTP-Auth": token, + }, + }); + const body = await res.text(); + + if (res.status === 200) { + const payload = JSON.parse(body); + parseToken(payload.token); } else { throw new StatusError( body || `${res.status} ${res.statusText}`, @@ -67,7 +89,8 @@ export async function renew(jwt: string) { const body = await res.text(); if (res.status === 200) { - parseToken(body); + const x = JSON.parse(body); + parseToken(x.token); } else { throw new StatusError( body || `${res.status} ${res.statusText}`, diff --git a/frontend/src/views/Login.vue b/frontend/src/views/Login.vue index 5804789a..cf62bfad 100644 --- a/frontend/src/views/Login.vue +++ b/frontend/src/views/Login.vue @@ -1,5 +1,6 @@