Skip to content

Instantly share code, notes, and snippets.

@dmulder
Created September 9, 2021 16:27
Show Gist options
  • Save dmulder/129a386abfe43d44ceffaaae188ddebd to your computer and use it in GitHub Desktop.
Save dmulder/129a386abfe43d44ceffaaae188ddebd to your computer and use it in GitHub Desktop.
Slack autotoken modified to simply load a browser for authentication (for complicated auth situations). Taken from https://github.com/insomniacslk/irc-slack
// autotoken retrieves a Slack token and cookie using your Slack team
// credentials.
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"os"
"strings"
"time"
"github.com/chromedp/cdproto/network"
"github.com/chromedp/cdproto/runtime"
"github.com/chromedp/chromedp"
"github.com/spf13/pflag"
)
var (
flagDebug = pflag.BoolP("debug", "d", false, "Enable debug log")
flagShowBrowser = pflag.BoolP("show-browser", "b", true, "show browser, useful for debugging")
flagMFA = pflag.StringP("mfa", "m", "", "Provide a multi-factor authentication token (necessary if MFA is enabled on your account)")
flagWaitGDPRNotice = pflag.BoolP("gdpr", "g", false, "Wait for Slack's GDPR notice pop-up before inserting username and password. Use this to work around login failures")
flagTimeout = pflag.UintP("timeout", "t", 120, "Timeout in seconds")
)
func main() {
usage := func() {
fmt.Fprintf(os.Stderr, "autotoken: log into slack team and get token and cookie.\n\n")
fmt.Fprintf(os.Stderr, "Usage: %s [-d] teamname[.slack.com]\n\n", os.Args[0])
pflag.PrintDefaults()
os.Exit(1)
}
pflag.Usage = usage
pflag.Parse()
if len(pflag.Args()) < 1 {
usage()
}
team := pflag.Arg(0)
email := ""
var password string
password = ""
timeout := time.Duration(*flagTimeout) * time.Second
token, cookie, err := fetchCredentials(context.TODO(), team, email, password, *flagMFA, *flagWaitGDPRNotice, timeout, *flagShowBrowser, *flagDebug)
if err != nil {
log.Fatalf("Failed to fetch credentials for team `%s`: %v", team, err)
}
fmt.Printf("%s|%s\n", token, cookie)
}
// fetchCredentials fetches Slack token and cookie for a given team.
func fetchCredentials(ctx context.Context, team, email, password, mfa string, waitGDPRNotice bool, timeout time.Duration, showBrowser, doDebug bool) (string, string, error) {
if !strings.HasSuffix(team, ".slack.com") {
team += ".slack.com"
}
teamURL := "https://" + team
var cancel func()
ctx, cancel = context.WithTimeout(ctx, timeout)
defer cancel()
// show browser
if showBrowser {
ctx, cancel = chromedp.NewExecAllocator(ctx, chromedp.NoFirstRun, chromedp.NoDefaultBrowserCheck)
defer cancel()
}
var opts []chromedp.ContextOption
if doDebug {
opts = append(opts, chromedp.WithDebugf(log.Printf))
}
ctx, cancel = chromedp.NewContext(ctx, opts...)
defer cancel()
fmt.Fprintf(os.Stderr, "Fetching token and cookie for %s on %s\n", email, team)
// run chromedp tasks
return submit(ctx, teamURL, `//input[@id="email"]`, email, `//input[@id="password"]`, password, mfa, waitGDPRNotice)
}
// submit authenticates through Slack and returns token and cookie, or an error.
func submit(ctx context.Context, urlstr, selEmail, email, selPassword, password, mfa string, waitGDPRNotice bool) (string, string, error) {
tasks := chromedp.Tasks{
chromedp.Navigate(urlstr),
}
if err := chromedp.Run(ctx, tasks); err != nil {
return "", "", fmt.Errorf("failed to send credentials: %w", err)
}
return extractTokenAndCookie(ctx)
}
// extractTokenAndCookie extracts Slack token and cookie from an existing
// context.
func extractTokenAndCookie(ctx context.Context) (string, string, error) {
var token, cookie string
tasks := chromedp.Tasks{
chromedp.WaitVisible(".p-workspace__primary_view_contents"),
chromedp.ActionFunc(func(ctx context.Context) error {
v, exp, err := runtime.Evaluate(`q=JSON.parse(localStorage.localConfig_v2)["teams"]; q[Object.keys(q)[0]]["token"]`).Do(ctx)
if err != nil {
return err
}
if exp != nil {
return exp
}
if err := json.Unmarshal(v.Value, &token); err != nil {
return fmt.Errorf("failed to unmarshal token: %v", err)
}
return nil
}),
chromedp.ActionFunc(func(ctx context.Context) error {
cookies, err := network.GetAllCookies().Do(ctx)
if err != nil {
return err
}
for _, c := range cookies {
if c.Name == "d" {
cookie = fmt.Sprintf("d=%s;", c.Value)
}
}
return nil
}),
}
if err := chromedp.Run(ctx, tasks); err != nil {
return "", "", err
}
return token, cookie, nil
}
@dmulder
Copy link
Author

dmulder commented Sep 9, 2021

Just run go mod init then go build to make the autotoken executable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment