mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2025-10-28 04:35:40 +00:00
Feat/totp (#45)
* wip * feat: finalize totp gen code * refactor: split login screen and forms * feat: add totp logic and ui * refactor: make totp pending expiry time fixed * refactor: skip all checks when disable continue is enabled * fix: fix cli not exiting on invalid input
This commit is contained in:
@@ -60,7 +60,7 @@ var CreateCmd = &cobra.Command{
|
||||
|
||||
// Do we have username and password?
|
||||
if iUsername == "" || iPassword == "" {
|
||||
log.Error().Msg("Username and password cannot be empty")
|
||||
log.Fatal().Err(errors.New("error invalid input")).Msg("Username and password cannot be empty")
|
||||
}
|
||||
|
||||
log.Info().Str("username", iUsername).Str("password", iPassword).Bool("docker", docker).Msg("Creating user")
|
||||
|
||||
@@ -2,9 +2,10 @@ package verify
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"tinyauth/internal/utils"
|
||||
|
||||
"github.com/charmbracelet/huh"
|
||||
"github.com/pquerna/otp/totp"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -17,22 +18,26 @@ var docker bool
|
||||
// i stands for input
|
||||
var iUsername string
|
||||
var iPassword string
|
||||
var iTotp string
|
||||
var iUser string
|
||||
|
||||
var VerifyCmd = &cobra.Command{
|
||||
Use: "verify",
|
||||
Short: "Verify a user is set up correctly",
|
||||
Long: `Verify a user is set up correctly meaning that it has a correct username and password.`,
|
||||
Long: `Verify a user is set up correctly meaning that it has a correct username, password and totp code.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// Setup logger
|
||||
log.Logger = log.Level(zerolog.InfoLevel)
|
||||
|
||||
// Use simple theme
|
||||
var baseTheme *huh.Theme = huh.ThemeBase()
|
||||
|
||||
// Check if interactive
|
||||
if interactive {
|
||||
// Create huh form
|
||||
form := huh.NewForm(
|
||||
huh.NewGroup(
|
||||
huh.NewInput().Title("User (username:hash)").Value(&iUser).Validate((func(s string) error {
|
||||
huh.NewInput().Title("User (username:hash:totp)").Value(&iUser).Validate((func(s string) error {
|
||||
if s == "" {
|
||||
return errors.New("user cannot be empty")
|
||||
}
|
||||
@@ -50,13 +55,11 @@ var VerifyCmd = &cobra.Command{
|
||||
}
|
||||
return nil
|
||||
})),
|
||||
huh.NewSelect[bool]().Title("Is the user formatted for docker?").Options(huh.NewOption("Yes", true), huh.NewOption("No", false)).Value(&docker),
|
||||
huh.NewInput().Title("Totp Code (if setup)").Value(&iTotp),
|
||||
),
|
||||
)
|
||||
|
||||
// Use simple theme
|
||||
var baseTheme *huh.Theme = huh.ThemeBase()
|
||||
|
||||
// Run form
|
||||
formErr := form.WithTheme(baseTheme).Run()
|
||||
|
||||
if formErr != nil {
|
||||
@@ -64,33 +67,44 @@ var VerifyCmd = &cobra.Command{
|
||||
}
|
||||
}
|
||||
|
||||
// Do we have username, password and user?
|
||||
if iUsername == "" || iPassword == "" || iUser == "" {
|
||||
log.Fatal().Msg("Username, password and user cannot be empty")
|
||||
// Parse user
|
||||
user, userErr := utils.ParseUser(iUser)
|
||||
|
||||
if userErr != nil {
|
||||
log.Fatal().Err(userErr).Msg("Failed to parse user")
|
||||
}
|
||||
|
||||
log.Info().Str("user", iUser).Str("username", iUsername).Str("password", iPassword).Bool("docker", docker).Msg("Verifying user")
|
||||
|
||||
// Split username and password hash
|
||||
username, hash, ok := strings.Cut(iUser, ":")
|
||||
|
||||
if !ok {
|
||||
log.Fatal().Msg("User is not formatted correctly")
|
||||
// Compare username
|
||||
if user.Username != iUsername {
|
||||
log.Fatal().Msg("Username is incorrect")
|
||||
}
|
||||
|
||||
// Replace $$ with $ if formatted for docker
|
||||
if docker {
|
||||
hash = strings.ReplaceAll(hash, "$$", "$")
|
||||
// Compare password
|
||||
verifyErr := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(iPassword))
|
||||
|
||||
if verifyErr != nil {
|
||||
log.Fatal().Msg("Ppassword is incorrect")
|
||||
}
|
||||
|
||||
// Compare username and password
|
||||
verifyErr := bcrypt.CompareHashAndPassword([]byte(hash), []byte(iPassword))
|
||||
|
||||
if verifyErr != nil || username != iUsername {
|
||||
log.Fatal().Msg("Username or password incorrect")
|
||||
} else {
|
||||
log.Info().Msg("Verification successful")
|
||||
// Check if user has 2fa code
|
||||
if user.TotpSecret == "" {
|
||||
if iTotp != "" {
|
||||
log.Warn().Msg("User does not have 2fa secret")
|
||||
}
|
||||
log.Info().Msg("User verified")
|
||||
return
|
||||
}
|
||||
|
||||
// Check totp code
|
||||
totpOk := totp.Validate(iTotp, user.TotpSecret)
|
||||
|
||||
if !totpOk {
|
||||
log.Fatal().Msg("Totp code incorrect")
|
||||
|
||||
}
|
||||
|
||||
// Done
|
||||
log.Info().Msg("User verified")
|
||||
},
|
||||
}
|
||||
|
||||
@@ -100,5 +114,6 @@ func init() {
|
||||
VerifyCmd.Flags().BoolVar(&docker, "docker", false, "Is the user formatted for docker?")
|
||||
VerifyCmd.Flags().StringVar(&iUsername, "username", "", "Username")
|
||||
VerifyCmd.Flags().StringVar(&iPassword, "password", "", "Password")
|
||||
VerifyCmd.Flags().StringVar(&iUser, "user", "", "Hash (username:hash combination)")
|
||||
VerifyCmd.Flags().StringVar(&iTotp, "totp", "", "Totp code")
|
||||
VerifyCmd.Flags().StringVar(&iUser, "user", "", "Hash (username:hash:totp combination)")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user