2021-01-06 18:44:29 +00:00
package main
import (
"context"
"errors"
"fmt"
"log"
"net/http"
2021-01-07 17:19:56 +00:00
"os/exec"
2021-01-06 18:44:29 +00:00
"strings"
2021-01-07 17:19:56 +00:00
"time"
2021-01-06 18:44:29 +00:00
"github.com/slack-go/slack"
)
var (
// These are set via build flags but can be overriden via environment variables.
defaultClientID = ""
defaultClientSecret = ""
)
type slackApp struct {
clientID , clientSecret , redirectURI string
scopes , userScopes [ ] string
listenHost , listenPath string
}
func ( app slackApp ) getAuthURL ( ) string {
scopes := strings . Join ( app . scopes , "," )
userScopes := strings . Join ( app . userScopes , "," )
return fmt . Sprintf (
"https://slack.com/oauth/authorize?scope=%s&user_scope=%s&client_id=%s&redirect_uri=%s" ,
scopes ,
userScopes ,
app . clientID ,
app . redirectURI ,
)
}
func ( app slackApp ) listenForCode ( ) ( string , error ) {
// start an http listener and listen for the redirect and return the code from params
var code string
// Also, should generate TLS certificate to use since https is a required scheme
2021-01-07 17:19:56 +00:00
server := http . Server {
Addr : app . listenHost ,
ReadTimeout : 5 * time . Second ,
WriteTimeout : 10 * time . Second ,
IdleTimeout : 120 * time . Second ,
}
2021-01-06 18:44:29 +00:00
http . HandleFunc ( app . listenPath , func ( w http . ResponseWriter , r * http . Request ) {
codes := r . URL . Query ( ) [ "code" ]
if len ( codes ) == 0 {
log . Fatal ( "no oauth code found in response" )
}
code = codes [ 0 ]
fmt . Fprintf ( w , "Got code %s" , code )
// Shutdown after response
go func ( ) {
if err := server . Shutdown ( context . Background ( ) ) ; err != nil {
fmt . Println ( "Fatal?" )
log . Fatal ( err )
}
} ( )
} )
2021-01-07 17:19:56 +00:00
certPath := getConfigFilePath ( "cert.pem" )
keyPath := getConfigFilePath ( "key.pem" )
if ! fileExists ( certPath ) || ! fileExists ( keyPath ) {
if err := generateSelfSignedCertificates ( certPath , keyPath ) ; err != nil {
return "" , err
}
}
if err := server . ListenAndServeTLS ( certPath , keyPath ) ; err != nil && ! errors . Is ( err , http . ErrServerClosed ) {
2021-01-06 18:44:29 +00:00
return "" , err
}
return code , nil
}
2021-01-07 17:19:56 +00:00
func generateSelfSignedCertificates ( certPath , keyPath string ) error {
command := exec . Command (
"openssl" ,
"req" ,
"-x509" ,
"-subj" ,
"/C=US/O=Slack Status CLI/CN=localhost:8888" ,
"-nodes" ,
"-days" ,
"365" ,
"-newkey" ,
"rsa:2048" ,
"-keyout" ,
keyPath ,
"-out" ,
certPath ,
)
return command . Run ( )
}
2021-01-06 18:44:29 +00:00
func authenticate ( ) ( string , error ) {
app := slackApp {
2021-02-02 19:57:39 +00:00
userScopes : [ ] string { "dnd:write" , "users.profile:write" , "team:read" } ,
scopes : [ ] string { "dnd:write" , "users.profile:write" , "team:read" } ,
2021-01-06 18:44:29 +00:00
clientID : getEnvOrDefault ( "CLIENT_ID" , defaultClientID ) ,
clientSecret : getEnvOrDefault ( "CLIENT_SECRET" , defaultClientSecret ) ,
2021-01-07 17:19:56 +00:00
redirectURI : "https://localhost:8888/auth" ,
2021-01-06 18:44:29 +00:00
listenHost : "localhost:8888" ,
listenPath : "/auth" ,
}
fmt . Println ( "To authenticate, go to the following URL:" )
2021-01-07 17:19:56 +00:00
fmt . Println ( "NOTE: After you authenticate with Slack, it will redirect you to a server running on your local computer. Your browser will present a security error because it cann't verify the server. You will need to manually add an exception or tell your browser to proceed anyway." )
2021-01-06 18:44:29 +00:00
fmt . Println ( app . getAuthURL ( ) )
code , err := app . listenForCode ( )
if err != nil {
return "" , err
}
accessToken , _ , err := slack . GetOAuthToken ( & http . Client { } , app . clientID , app . clientSecret , code , app . redirectURI )
if err != nil {
return "" , err
}
return accessToken , nil
}