cchat-discord/internal/discord/authenticate/authenticator.go

118 lines
2.2 KiB
Go

package authenticate
import (
"errors"
"github.com/diamondburned/cchat"
"github.com/diamondburned/cchat-discord/internal/discord/session"
"github.com/diamondburned/cchat-discord/internal/discord/state"
)
var (
ErrMalformed = errors.New("malformed authentication form")
EnterPassword = errors.New("enter your password")
)
type Authenticator struct {
username string
password string
}
func New() cchat.Authenticator {
return &Authenticator{}
}
func (a *Authenticator) stage() int {
switch {
// Stage 1: Prompt for the token OR username.
case a.username == "" && a.password == "":
return 0
// Stage 2: Prompt for the password.
case a.password == "":
return 1
// Stage 3: Prompt for the TOTP token.
default:
return 2
}
}
func (a *Authenticator) AuthenticateForm() []cchat.AuthenticateEntry {
switch a.stage() {
case 0:
return []cchat.AuthenticateEntry{
{Name: "Token", Secret: true},
{Name: "Username", Description: "Fill either Token or Username only."},
}
case 1:
return []cchat.AuthenticateEntry{
{Name: "Password", Secret: true},
}
case 2:
return []cchat.AuthenticateEntry{
{Name: "Auth Code", Description: "6-digit code for Two-factor Authentication."},
}
default:
return nil
}
}
func (a *Authenticator) Authenticate(form []string) (cchat.Session, error) {
switch a.stage() {
case 0:
if len(form) != 2 {
return nil, ErrMalformed
}
switch {
case form[0] != "": // Token
i, err := state.NewFromToken(form[0])
if err != nil {
return nil, err
}
return session.NewFromInstance(i)
case form[1] != "": // Username
// Move to a new stage.
a.username = form[1]
return nil, EnterPassword
}
case 1:
if len(form) != 1 {
return nil, ErrMalformed
}
a.password = form[0]
i, err := state.Login(a.username, a.password, "")
if err != nil {
// If the error is not ErrMFA, then we should reset password to
// empty.
if !errors.Is(err, session.ErrMFA) {
a.password = ""
}
return nil, err
}
return session.NewFromInstance(i)
case 2:
if len(form) != 1 {
return nil, ErrMalformed
}
i, err := state.Login(a.username, a.password, form[0])
if err != nil {
return nil, err
}
return session.NewFromInstance(i)
}
return nil, ErrMalformed
}