Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for login email challenge #263

Merged
merged 3 commits into from
Jan 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions auth/auth_ui/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,9 @@ func prompt(w io.Writer, prompt string, readlnFn func(*os.File) (string, error))
fmt.Fprintln(w, "input cannot be empty")
}
}

func (*CLI) ConfirmationCode(email string) (code int, err error) {
fmt.Printf("Enter confirmation code sent to %s: ", email)
_, err = fmt.Fscanf(os.Stdin, "%d", &code)
return
}
29 changes: 29 additions & 0 deletions auth/auth_ui/huh.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"errors"
"fmt"
"io"
"regexp"
"strconv"

"github.com/charmbracelet/huh"
)
Expand Down Expand Up @@ -102,3 +104,30 @@ func valSepEaster() func(v int) error {
return nil
}
}

func (*Huh) ConfirmationCode(email string) (int, error) {
var strCode string
q := huh.NewInput().
CharLimit(6).
Title(fmt.Sprintf("Enter confirmation code sent to %s", email)).
Description("Slack did not recognise the browser, and sent a confirmation code. Please enter the confirmation code below.").
Value(&strCode).
Validate(valSixDigits)
if err := q.Run(); err != nil {
return 0, err
}
code, err := strconv.Atoi(strCode)
if err != nil {
return 0, err
}
return code, nil
}

var numChlgRE = regexp.MustCompile(`^\d{6}$`)

func valSixDigits(s string) error {
if numChlgRE.MatchString(s) {
return nil
}
return errors.New("confirmation code must be a sequence of six digits")
}
47 changes: 47 additions & 0 deletions auth/auth_ui/huh_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package auth_ui

import "testing"

func Test_valSixDigits(t *testing.T) {
type args struct {
s string
}
tests := []struct {
name string
args args
wantErr bool
}{
{
"empty",
args{""},
true,
},
{
"too short",
args{"12345"},
true,
},
{
"too long",
args{"1234567"},
true,
},
{
"not a number",
args{"123456a"},
true,
},
{
"valid",
args{"123456"},
false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := valSixDigits(tt.args.s); (err != nil) != tt.wantErr {
t.Errorf("valSixDigits() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
52 changes: 31 additions & 21 deletions auth/rod.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import (
"fmt"
"io"
"os"
"time"

"github.com/charmbracelet/huh/spinner"
"github.com/rusq/slackauth"
"github.com/rusq/slackdump/v2/auth/auth_ui"
)
Expand All @@ -29,10 +27,9 @@ type browserAuthUIExt interface {
BrowserAuthUI
RequestLoginType(w io.Writer) (int, error)
RequestCreds(w io.Writer, workspace string) (email string, passwd string, err error)
ConfirmationCode(email string) (code int, err error)
}

const expectedLoginDuration = 16 * time.Second

func NewRODAuth(ctx context.Context, opts ...Option) (RodAuth, error) {
r := RodAuth{
opts: options{
Expand Down Expand Up @@ -74,26 +71,10 @@ func NewRODAuth(ctx context.Context, opts ...Option) (RodAuth, error) {
return r, err
}
case auth_ui.LoginEmail:
username, password, err := r.opts.ui.RequestCreds(os.Stdout, r.opts.workspace)
sp, err = headlessFlow(ctx, r.opts.workspace, r.opts.ui)
if err != nil {
return r, err
}
if username == "" {
return r, fmt.Errorf("email cannot be empty")
}
if password == "" {
return r, fmt.Errorf("password cannot be empty")
}
var loginErr error
spin := spinner.New().Title("Logging in...").Action(func() {
sp.Token, sp.Cookie, loginErr = slackauth.Headless(ctx, r.opts.workspace, username, password)
})
if err := spin.Run(); err != nil {
return r, err
}
if loginErr != nil {
return r, loginErr
}
fmt.Fprintln(os.Stderr, "authenticated.")
case auth_ui.LoginCancel:
return r, ErrCancelled
Expand All @@ -103,3 +84,32 @@ func NewRODAuth(ctx context.Context, opts ...Option) (RodAuth, error) {
simpleProvider: sp,
}, nil
}

func headlessFlow(ctx context.Context, workspace string, ui browserAuthUIExt) (sp simpleProvider, err error) {
username, password, err := ui.RequestCreds(os.Stdout, workspace)
if err != nil {
return sp, err
}
if username == "" {
return sp, fmt.Errorf("email cannot be empty")
}
if password == "" {
return sp, fmt.Errorf("password cannot be empty")
}
fmt.Println("Logging in to Slack, depending on your connection speed, it will take 15-30 seconds...")

var loginErr error
sp.Token, sp.Cookie, loginErr = slackauth.Headless(
ctx,
workspace,
username,
password,
slackauth.WithChallengeFunc(ui.ConfirmationCode),
)
if loginErr != nil {
return sp, loginErr
}

fmt.Fprintln(os.Stderr, "authenticated.")
return
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ require (
github.com/rusq/dlog v1.4.0
github.com/rusq/osenv/v2 v2.0.1
github.com/rusq/secure v0.0.4
github.com/rusq/slackauth v0.0.5
github.com/rusq/slackauth v0.0.6
github.com/rusq/tracer v1.0.1
github.com/schollz/progressbar/v3 v3.13.0
github.com/slack-go/slack v0.12.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ github.com/rusq/slackauth v0.0.4 h1:AMR0bRTHM7Kg2N14bPWm8bG+J5hXOoLrgiGUmFaBsMU=
github.com/rusq/slackauth v0.0.4/go.mod h1:zb1PJY2+8uEqn0RiWuRjnd+ZFwwfnvA5xrGoooVUgNY=
github.com/rusq/slackauth v0.0.5 h1:qhRkhLa+tS60OGPhzlO/mXbQiWxnzPkoVixqy18n7eI=
github.com/rusq/slackauth v0.0.5/go.mod h1:zb1PJY2+8uEqn0RiWuRjnd+ZFwwfnvA5xrGoooVUgNY=
github.com/rusq/slackauth v0.0.6 h1:vV4kg3lRKV+oiHVAWxyKXa9aoRU4XwT5pSQ0mlo9OSM=
github.com/rusq/slackauth v0.0.6/go.mod h1:zb1PJY2+8uEqn0RiWuRjnd+ZFwwfnvA5xrGoooVUgNY=
github.com/rusq/tracer v1.0.1 h1:5u4PCV8NGO97VuAINQA4gOVRkPoqHimLE2jpezRVNMU=
github.com/rusq/tracer v1.0.1/go.mod h1:Rqu48C3/K8bA5NPmF20Hft73v431MQIdM+Co+113pME=
github.com/schollz/progressbar/v3 v3.13.0 h1:9TeeWRcjW2qd05I8Kf9knPkW4vLM/hYoa6z9ABvxje8=
Expand Down
Loading