feat(auth): totp form

This commit is contained in:
Denis Gukov 2025-01-03 02:22:38 +05:00
parent 54533f3fcb
commit b957254247
No known key found for this signature in database
GPG Key ID: 044381366A5D4731
3 changed files with 94 additions and 51 deletions

View File

@ -84,7 +84,12 @@ func verifySession(w http.ResponseWriter, r *http.Request) {
return return
} }
user := context.Get(r, "user").(*db.User) user, err := helpers.Store(r).GetUser(session.UserID)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
key, err := otp.NewKeyFromURL(user.Totp.URL) key, err := otp.NewKeyFromURL(user.Totp.URL)
if err != nil { if err != nil {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)

View File

@ -82,7 +82,7 @@ func Route() *mux.Router {
publicAPIRouter.Use(StoreMiddleware, JSONMiddleware) publicAPIRouter.Use(StoreMiddleware, JSONMiddleware)
publicAPIRouter.HandleFunc("/auth/login", login).Methods("GET", "POST") publicAPIRouter.HandleFunc("/auth/login", login).Methods("GET", "POST")
publicAPIRouter.HandleFunc("/auth/verify", verifySession).Methods("GET", "POST") publicAPIRouter.HandleFunc("/auth/verify", verifySession).Methods("POST")
publicAPIRouter.HandleFunc("/auth/logout", logout).Methods("POST") publicAPIRouter.HandleFunc("/auth/logout", logout).Methods("POST")
publicAPIRouter.HandleFunc("/auth/oidc/{provider}/login", oidcLogin).Methods("GET") publicAPIRouter.HandleFunc("/auth/oidc/{provider}/login", oidcLogin).Methods("GET")

View File

@ -98,6 +98,16 @@
>{{ signInError }} >{{ signInError }}
</v-alert> </v-alert>
<div v-if="verification">
<v-otp-input
v-model="verificationCode"
length="6"
type="number"
@finish="verify()"
></v-otp-input>
</div>
<div v-else>
<v-text-field <v-text-field
v-model="username" v-model="username"
v-bind:label='$t("username")' v-bind:label='$t("username")'
@ -152,6 +162,8 @@
<div class="text-center mt-6" v-if="loginWithPassword"> <div class="text-center mt-6" v-if="loginWithPassword">
<a @click="loginHelpDialog = true">{{ $t('dontHaveAccountOrCantSignIn') }}</a> <a @click="loginHelpDialog = true">{{ $t('dontHaveAccountOrCantSignIn') }}</a>
</div> </div>
</div>
</v-form> </v-form>
</v-container> </v-container>
</div> </div>
@ -181,6 +193,7 @@ export default {
loginWithPassword: null, loginWithPassword: null,
verification: null, verification: null,
verificationCode: null,
verificationMethod: null, verificationMethod: null,
}; };
}, },
@ -231,7 +244,7 @@ export default {
}); });
} catch (err) { } catch (err) {
if (err.response.status === 401) { if (err.response.status === 401) {
switch (err.response.body.error) { switch (err.response.data.error) {
case 'TOTP_REQUIRED': case 'TOTP_REQUIRED':
return { return {
status: 'unverified', status: 'unverified',
@ -248,7 +261,32 @@ export default {
}, },
async verify() { async verify() {
// TODO: verify this.signInError = null;
if (!this.$refs.signInForm.validate()) {
return;
}
this.signInProcess = true;
try {
await axios({
method: 'post',
url: '/api/auth/verify',
responseType: 'json',
data: {
passcode: this.verificationCode,
},
});
document.location = document.baseURI + window.location.search;
} catch (err) {
if (err.response.status === 401) {
this.signInError = this.$t('incorrectUsrPwd');
} else {
this.signInError = getErrorMessage(err);
}
} finally {
this.signInProcess = false;
}
}, },
async signIn() { async signIn() {