mirror of
https://github.com/diamondburned/cchat-discord.git
synced 2024-11-01 12:24:15 +00:00
118 lines
2.2 KiB
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
|
||
|
}
|