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

(V2) google auth support #336

Merged
merged 2 commits into from
Oct 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
2 changes: 2 additions & 0 deletions auth/auth_ui/auth_ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ const (
LInteractive LoginType = iota
// LHeadless is the email/password login type.
LHeadless
// LGoogleAuth is the google auth option
LGoogleAuth
// LCancel should be returned if the user cancels the login intent.
LCancel
)
Expand Down
15 changes: 8 additions & 7 deletions auth/auth_ui/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,14 @@ func (cl *CLI) RequestWorkspace(w io.Writer) (string, error) {
return Sanitize(workspace)
}

func (cl *CLI) RequestEmail(w io.Writer) (string, error) {
return prompt(w, "Enter Email: ", readln)
}

func (cl *CLI) RequestPassword(w io.Writer, account string) (string, error) {
func (*CLI) RequestCreds(w io.Writer, workspace string) (email string, passwd string, err error) {
email, err = prompt(w, "Enter Email: ", readln)
if err != nil {
return
}
defer fmt.Fprintln(w)
return prompt(w, fmt.Sprintf("Enter Password for %s (won't be visible): ", account), readpwd)
passwd, err = prompt(w, fmt.Sprintf("Enter Password for %s (won't be visible): ", email), readpwd)
return
}

func (cl *CLI) RequestLoginType(w io.Writer) (LoginType, error) {
Expand All @@ -48,7 +49,7 @@ func (cl *CLI) RequestLoginType(w io.Writer) (LoginType, error) {
value LoginType
}{
{"Email", LHeadless},
{"Google", LInteractive},
{"Google", LGoogleAuth},
{"Apple", LInteractive},
{"Login with Single-Sign-On (SSO)", LInteractive},
{"Other/Manual", LInteractive},
Expand Down
6 changes: 3 additions & 3 deletions auth/auth_ui/huh.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func (*Huh) RequestCreds(w io.Writer, workspace string) (email string, passwd st
huh.NewInput().
Title("Password").Value(&passwd).
Placeholder("your slack password").
Validate(valRequired).Password(true),
Validate(valRequired).EchoMode(huh.EchoModePassword),
),
)
err = f.Run()
Expand All @@ -54,8 +54,8 @@ func (*Huh) RequestLoginType(w io.Writer) (LoginType, error) {
err := huh.NewSelect[LoginType]().Title("Select login type").
Options(
huh.NewOption("Email (manual)", LInteractive),
huh.NewOption("Email (automatic, experimental)", LHeadless),
huh.NewOption("Google", LInteractive),
huh.NewOption("Email (automatic)", LHeadless),
huh.NewOption("Google", LGoogleAuth),
huh.NewOption("Apple", LInteractive),
huh.NewOption("Login with Single-Sign-On (SSO)", LInteractive),
huh.NewOption("Other/Manual", LInteractive),
Expand Down
57 changes: 48 additions & 9 deletions auth/rod.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,19 @@ import (
"fmt"
"io"
"os"
"time"

"github.com/rusq/slackauth"

"github.com/rusq/slackdump/v2/auth/auth_ui"
"github.com/rusq/slackdump/v2/logger"
)

// RODHeadlessTimeout is the default timeout for the headless login flow.
// It is a net time of headless browser interaction, without the browser
// starting time.
const RODHeadlessTimeout = 40 * time.Second

// RodAuth is an authentication provider that uses a headless or interactive
// browser to authenticate with Slack, depending on the user's choice. It uses
// rod library to drive the browser via the CDP protocol.
Expand All @@ -26,12 +34,29 @@ type RodAuth struct {
opts options
}

func (p RodAuth) Type() Type {
return TypeRod
type rodOpts struct {
ui browserAuthUIExt
autoTimeout time.Duration
userAgent string
usermode bool
bundledBrowser bool
}

type rodOpts struct {
ui browserAuthUIExt
func (ro rodOpts) slackauthOpts() []slackauth.Option {
sopts := []slackauth.Option{
slackauth.WithChallengeFunc(ro.ui.ConfirmationCode),
slackauth.WithAutologinTimeout(ro.autoTimeout),
}
if ro.userAgent != "" {
sopts = append(sopts, slackauth.WithUserAgent(ro.userAgent))
}
if ro.usermode {
sopts = append(sopts, slackauth.WithForceUser())
}
if ro.bundledBrowser {
sopts = append(sopts, slackauth.WithBundledBrowser())
}
return sopts
}

type browserAuthUIExt interface {
Expand All @@ -53,7 +78,11 @@ func NewRODAuth(ctx context.Context, opts ...Option) (RodAuth, error) {
r := RodAuth{
opts: options{
rodOpts: rodOpts{
ui: &auth_ui.Huh{},
ui: &auth_ui.Huh{},
autoTimeout: RODHeadlessTimeout,
userAgent: "", // slackauth default user agent.
usermode: false,
bundledBrowser: false,
},
},
}
Expand All @@ -80,19 +109,28 @@ func NewRODAuth(ctx context.Context, opts ...Option) (RodAuth, error) {
if err != nil {
return r, err
}
sopts := r.opts.slackauthOpts()
if resp == auth_ui.LGoogleAuth {
// it doesn't need to know that this browser is just a puppet in the
// masterful hands.
sopts = append(sopts, slackauth.WithForceUser())
}

cl, err := slackauth.New(
r.opts.workspace,
slackauth.WithChallengeFunc(r.opts.ui.ConfirmationCode),
sopts...,
)
if err != nil {
return r, err
}
defer cl.Close()

lg := logger.FromContext(ctx)
t := time.Now()
var sp simpleProvider
switch resp {
case auth_ui.LInteractive:
case auth_ui.LInteractive, auth_ui.LGoogleAuth:
lg.Printf("ℹ️ Initialising browser, once the browser appears, login as usual")
var err error
sp.Token, sp.Cookie, err = cl.Manual(ctx)
if err != nil {
Expand All @@ -107,7 +145,7 @@ func NewRODAuth(ctx context.Context, opts ...Option) (RodAuth, error) {
return r, ErrCancelled
}

fmt.Fprintln(os.Stderr, "authenticated.")
lg.Printf("✅ authenticated (time taken: %s)", time.Since(t))

return RodAuth{
simpleProvider: sp,
Expand All @@ -125,7 +163,8 @@ func headlessFlow(ctx context.Context, cl *slackauth.Client, workspace string, u
if password == "" {
return sp, fmt.Errorf("password cannot be empty")
}
fmt.Println("Logging in to Slack, depending on your connection speed, it will take 25-40 seconds...")
logger.FromContext(ctx).Println("⏳ Logging in to Slack, depending on your connection speed, it will take 25-40 seconds...")

var loginErr error
sp.Token, sp.Cookie, loginErr = cl.Headless(ctx, username, password)
if loginErr != nil {
Expand Down
7 changes: 7 additions & 0 deletions auth/rod_v2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package auth

// things specific to v2 (to ease backporting)

func (p RodAuth) Type() Type {
return TypeRod
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,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.3.1
github.com/rusq/slackauth v0.4.0
github.com/rusq/tracer v1.0.1
github.com/schollz/progressbar/v3 v3.13.0
github.com/slack-go/slack v0.14.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ github.com/rusq/slackauth v0.3.0 h1:5ieWjlQHhmqMO4qhTsVZzXEsIt8AD6ADc2hhaxwERmY=
github.com/rusq/slackauth v0.3.0/go.mod h1:/+6JZULYSpWk7/Dc232132wF6UbFOLV8DhSz55zBcnw=
github.com/rusq/slackauth v0.3.1 h1:lAFuMQMVfe2VGZVCd8Hrwhqfrd8cy6nOzG0mhX9BvSg=
github.com/rusq/slackauth v0.3.1/go.mod h1:/+6JZULYSpWk7/Dc232132wF6UbFOLV8DhSz55zBcnw=
github.com/rusq/slackauth v0.4.0 h1:hNOEWw6Tji24MRJ8rsepgb4w47v0wPGxjhGpvkQJQJU=
github.com/rusq/slackauth v0.4.0/go.mod h1:wAtNCbeKH0pnaZnqJjG5RKY3e5BF9F2L/YTzhOjBIb0=
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