When you need to interact with Slack APIs programmatically, the traditional approach requires creating a Slack app, configuring OAuth scopes, and managing bot tokens. But what if you could skip all that bureaucracy and use your existing browser session? This article shows you how to leverage Slack’s browser session tokens with the official Go SDK.
What Are xoxd and xoxc Tokens?
When you use Slack in your web browser, your session is authenticated using two special tokens:
- xoxd token: A session cookie token that proves your browser session is authenticated
- xoxc token: An API token used for making requests on behalf of your user account
Unlike traditional Slack app tokens (xoxb- or xoxp-), these tokens are automatically generated when you log into Slack via your browser and have the same permissions as your user account.
Why Use Browser Session Tokens?
Traditional Approach: Slack App Required
- Create a Slack app in your workspace
- Configure OAuth scopes and permissions
- Go through app installation and approval process
- Manage bot tokens and user tokens
- Limited by app-specific permissions
Browser Token Approach: No App Needed
- ✅ Use your existing user permissions
- ✅ No app creation or approval process
- ✅ Access all APIs your user account can access
- ✅ Works immediately with any Slack workspace you’re a member of
- ✅ Perfect for personal automation and quick prototyping
How to Extract Your Tokens
Step 1: Open Browser Developer Tools
- Open Slack in your web browser
- Press F12 to open Developer Tools
- Go to the Network tab
Step 2: Make a Slack API Request
- Perform any action in Slack (send a message, react to something, etc.)
- Look for API requests in the Network tab
Step 3: Extract Tokens
From any API request, you’ll find:
- xoxd token: In the Cookie header as
d=xoxd-... - xoxc token: In the request payload as
"token":"xoxc-..."
Quick Test with cURL
Before implementing in Go, test your tokens with cURL:
#!/bin/bash
# Your extracted tokens
XOXD_TOKEN="xoxd-YOUR_XOXD_TOKEN_HERE"
XOXC_TOKEN="xoxc-YOUR_XOXC_TOKEN_HERE"
# Test authentication
curl 'https://your-workspace.slack.com/api/auth.test' \
-H 'content-type: application/x-www-form-urlencoded' \
-b "d=${XOXD_TOKEN}" \
--data-raw "token=${XOXC_TOKEN}"
A successful response looks like:
{
"ok": true,
"url": "https://your-workspace.slack.com/",
"team": "Your Team",
"user": "your.username",
"team_id": "T1234567",
"user_id": "U1234567"
}
Go Implementation
Step 1: Install Dependencies
go mod init your-project
go get github.com/slack-go/slack
Step 2: Create Environment File
Create a .env file:
SLACK_XOXD_TOKEN=xoxd-YOUR_XOXD_TOKEN_HERE
SLACK_XOXC_TOKEN=xoxc-YOUR_XOXC_TOKEN_HERE
SLACK_API_URL=https://your-workspace.slack.com/api/
Step 3: Complete Go Implementation
package main
import (
"bufio"
"fmt"
"net/http"
"os"
"strings"
"github.com/slack-go/slack"
)
// Load environment variables from .env file
func loadEnv(filename string) error {
file, err := os.Open(filename)
if err != nil {
return err
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
if line == "" || strings.HasPrefix(line, "#") {
continue
}
parts := strings.SplitN(line, "=", 2)
if len(parts) == 2 {
os.Setenv(parts[0], parts[1])
}
}
return scanner.Err()
}
// Custom HTTP client that adds xoxd token as cookie
type customHTTPClient struct {
client *http.Client
xoxdToken string
}
func (c *customHTTPClient) Do(req *http.Request) (*http.Response, error) {
// Add the xoxd token as a cookie
if c.xoxdToken != "" {
req.Header.Set("Cookie", fmt.Sprintf("d=%s", c.xoxdToken))
}
// Add browser-like headers
req.Header.Set("Origin", "https://app.slack.com")
req.Header.Set("User-Agent", "Mozilla/5.0 (compatible; SlackGoClient/1.0)")
return c.client.Do(req)
}
func main() {
// Load environment variables
if err := loadEnv(".env"); err != nil {
fmt.Printf("Error loading .env file: %v\n", err)
return
}
// Get tokens and API URL from environment
xoxdToken := os.Getenv("SLACK_XOXD_TOKEN")
xoxcToken := os.Getenv("SLACK_XOXC_TOKEN")
apiURL := os.Getenv("SLACK_API_URL")
if xoxdToken == "" || xoxcToken == "" || apiURL == "" {
fmt.Println("Error: SLACK_XOXD_TOKEN, SLACK_XOXC_TOKEN, and SLACK_API_URL must be set in .env file")
return
}
// Create custom HTTP client with cookie support
customClient := &customHTTPClient{
client: &http.Client{},
xoxdToken: xoxdToken,
}
// Create Slack client with custom HTTP client
api := slack.New(xoxcToken,
slack.OptionHTTPClient(customClient),
slack.OptionAPIURL(apiURL),
)
// Test authentication
fmt.Println("Testing authentication...")
authTest, err := api.AuthTest()
if err != nil {
fmt.Printf("Error testing auth: %v\n", err)
return
}
fmt.Printf("✅ Successfully authenticated!\n")
fmt.Printf("User: %s\n", authTest.User)
fmt.Printf("Team: %s\n", authTest.Team)
fmt.Printf("Team ID: %s\n", authTest.TeamID)
fmt.Printf("User ID: %s\n", authTest.UserID)
// Example: Get team info
fmt.Println("\nGetting team info...")
teamInfo, err := api.GetTeamInfo()
if err != nil {
fmt.Printf("Error getting team info: %v\n", err)
return
}
fmt.Printf("✅ Team: %s (Domain: %s)\n", teamInfo.Name, teamInfo.Domain)
// Example: Get emoji list
fmt.Println("\nGetting custom emojis...")
emojis, err := api.GetEmoji()
if err != nil {
fmt.Printf("Error getting emojis: %v\n", err)
return
}
fmt.Printf("✅ Found %d custom emojis\n", len(emojis))
// Example: List channels
fmt.Println("\nListing public channels...")
channels, _, err := api.GetConversations(&slack.GetConversationsParameters{
Types: []string{"public_channel"},
Limit: 5,
})
if err != nil {
fmt.Printf("Error getting channels: %v\n", err)
return
}
fmt.Printf("✅ Found %d channels (showing first 5):\n", len(channels))
for _, channel := range channels {
fmt.Printf(" - #%s (ID: %s)\n", channel.Name, channel.ID)
}
}
Step 4: Run Your Application
go run main.go
Expected output:
Testing authentication...
✅ Successfully authenticated!
User: your.username
Team: Your Team Name
Team ID: T1234567
User ID: U1234567
Getting team info...
✅ Team: Your Team Name (Domain: your-workspace)
Getting custom emojis...
✅ Found 1,234 custom emojis
Listing public channels...
✅ Found 5 channels (showing first 5):
- #general (ID: C1234567)
- #random (ID: C2345678)
...
The Magic: Custom HTTP Client
The key to making this work is implementing a custom HTTP client that satisfies the httpClient interface expected by the Slack Go SDK. This client automatically injects the xoxd token as a cookie for every request:
type customHTTPClient struct {
client *http.Client
xoxdToken string
}
func (c *customHTTPClient) Do(req *http.Request) (*http.Response, error) {
// Add the xoxd token as a cookie
if c.xoxdToken != "" {
req.Header.Set("Cookie", fmt.Sprintf("d=%s", c.xoxdToken))
}
// Add browser-like headers
req.Header.Set("Origin", "https://app.slack.com")
req.Header.Set("User-Agent", "Mozilla/5.0 (compatible; SlackGoClient/1.0)")
return c.client.Do(req)
}
Then we pass it to the Slack client using the OptionHTTPClient() function:
api := slack.New(xoxcToken,
slack.OptionHTTPClient(customClient),
slack.OptionAPIURL(apiURL),
)
Using browser session tokens with the Slack Go SDK provides a powerful way to interact with Slack APIs without the overhead of creating and managing Slack apps. This method leverages your existing user permissions and works immediately with any Slack workspace you have access to.
The combination of xoxd (cookie) and xoxc (API token) provides the same authentication mechanism that your browser uses, making it a reliable approach for automation and integration tasks.
By implementing a custom HTTP client that injects the necessary cookies, we can seamlessly use the official Slack Go SDK with browser session tokens, giving us the best of both worlds: the convenience of browser authentication and the power of a fully-featured SDK.
Remember to keep your tokens secure and be aware that they expire with your browser session!