Extract personal Slack workspace tokens (xoxc-...) and the authentication cookies (d, d-s) from the Slack desktop app's local storage.
Copyright (C) 2026 Hesham Karm. Released under GPL-3.0-or-later.
This is a Go port of hraftery/slacktokens — the original Python implementation by Heath Raftery (2021). The port matches the upstream public surface, ships a CLI, and adds verified-against-current-Chromium crypto plus the test suite the original lacks. Per GPLv3, the upstream copyright is preserved and this port is also distributed under GPLv3.
Not endorsed or authorised by Slack Technologies LLC.
| Platform | Library | CLI |
|---|---|---|
| macOS (Intel + Apple Silicon) | ✅ | ✅ |
| Linux (libsecret) | ✅ | ✅ |
| Windows (DPAPI) | ✅ | ✅ |
Verified against Slack 4.50 / Electron 42 / Chromium 148.
Windows uses a different crypto path: AES-256-GCM with a master key stored in Local State and wrapped with DPAPI. Slack's Electron does not ship Chromium's v20 "app-bound encryption" infrastructure, so cookies remain v10/v11 — extractable from your own user account without elevation. v20 is detected and rejected with a clear error.
Homebrew (macOS / Linux):
brew install hishamkaram/slacktokens/slacktokensDebian / Ubuntu (.deb): download the architecture-matching .deb from the latest release, then:
sudo dpkg -i slacktokens_v*_linux_amd64.deb # or linux_arm64.debPre-built binaries (Windows, or unmanaged Linux/macOS): download from the releases page and extract.
Go (library or CLI):
go get github.com/hishamkaram/slacktokens # library
go install github.com/hishamkaram/slacktokens/cmd/slacktokens@latest # CLI
go install github.com/hishamkaram/slacktokens/cmd/slacktokens-mcp@latest # MCP serverpackage main
import (
"encoding/json"
"fmt"
"os"
"github.com/hishamkaram/slacktokens"
)
func main() {
res, err := slacktokens.GetTokensAndCookie()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
json.NewEncoder(os.Stdout).Encode(res)
}Public API:
func GetTokens() (map[string]Workspace, error)
func GetCookie() (Cookie, error) // parity with Python: returns "d" only
func GetCookies() ([]Cookie, error) // returns d and d-s when present
func GetTokensAndCookie() (Result, error)Sentinel errors (use with errors.Is):
slacktokens.ErrUnsupportedOS
slacktokens.ErrLocalStorageLocked // Slack is still running
slacktokens.ErrLocalConfigMissing
slacktokens.ErrLocalConfigParse
slacktokens.ErrCookieNotFoundslacktokens # full Result as indented JSON
slacktokens -tokens # tokens map only
slacktokens -cookie # the d cookie only (parity with Python)
slacktokens -cookies # d + d-sPipe to jq:
slacktokens -tokens | jq 'keys'
slacktokens -cookie | jq -r .valueUse the credentials with curl (bash):
TOKEN=$(slacktokens -tokens | jq -r '.["https://your-workspace.slack.com"].token')
DCOOKIE=$(slacktokens -cookie | jq -r .value)
curl 'https://slack.com/api/auth.test' \
-d "token=$TOKEN" \
--cookie "d=$DCOOKIE"PowerShell:
$tokens = slacktokens -tokens | ConvertFrom-Json
$token = $tokens.'https://your-workspace.slack.com'.token
$dcookie = (slacktokens -cookie | ConvertFrom-Json).value
curl.exe 'https://slack.com/api/auth.test' -d "token=$token" --cookie "d=$dcookie"A standards-compliant Model Context Protocol server is shipped under cmd/slacktokens-mcp/. It exposes the library to MCP-capable clients (Claude Code, Claude Desktop, Cursor, etc.) over stdio.
go install github.com/hishamkaram/slacktokens/cmd/slacktokens-mcp@latestA Slack token or auth cookie is a live credential. Returning one in a tool result would drop it into the calling model's context window, its transcript, and any provider-side logs — a sensitive-information-disclosure risk. So the MCP server is masked by default:
- The four read tools return only a masked preview (e.g.
xoxc-2…3f9a) — enough for a human to recognise their own credential, useless as a credential itself — plus workspace/cookie metadata. - To hand over real, usable credentials, call
write_credentials_file. It writes them to a freshly created local file readable only by your OS user (mode0600) and returns only the path — the credential values never enter the model context. The file is removed when the server stops.
The credentials file holds the same JSON as slacktokens with no flags — { "tokens": …, "cookie": …, "cookies": … } — so you or a script can consume it directly:
TOKEN=$(jq -r '.tokens["https://your-workspace.slack.com"].token' "$CREDS_FILE")
DCOOKIE=$(jq -r '.cookie.value' "$CREDS_FILE")
curl 'https://slack.com/api/auth.test' -d "token=$TOKEN" --cookie "d=$DCOOKIE"Tools:
| Name | Returns |
|---|---|
get_tokens |
per-workspace name + masked xoxc-* token preview |
get_cookie |
the d auth cookie, masked |
get_cookies |
d and d-s (when present), masked |
get_tokens_and_cookie |
masked tokens + cookies in one call |
write_credentials_file |
path to a 0600 JSON file holding the real credentials |
The four read tools advertise readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false. write_credentials_file advertises readOnlyHint: false and idempotentHint: false (it creates a file) and is otherwise non-destructive and offline. The server opts out of the logging capability so secrets cannot leak via notifications/message.
Built against the official Go SDK (github.com/modelcontextprotocol/go-sdk@v1.6.0) and the MCP 2025-11-25 specification.
Note: file handoff keeps secrets out of the model context, transcript, and logs. An agent that also has shell/file-read tools can still be explicitly instructed to open the file — that is a deliberate user-directed act, not the silent exposure this design prevents.
If you understand the exposure and still want the read tools to inline raw xoxc-* tokens and cookie values (the previous behaviour), start the server with the SLACKTOKENS_MCP_ALLOW_RAW=1 environment variable. Leaving it unset keeps every read tool masked.
To opt in to raw output, add the environment variable:
{
"mcpServers": {
"slacktokens": {
"command": "slacktokens-mcp",
"env": { "SLACKTOKENS_MCP_ALLOW_RAW": "1" }
}
}
}(Or use the absolute path to the binary if it isn't on PATH.)
-
Tokens are read from Slack's Chromium LevelDB localStorage at the OS-specific path; the entry whose key contains
localConfig_v2is parsed as Chromium-encoded localStorage JSON. -
Cookies are read from Slack's Chromium SQLite cookies database. The
dandd-srows for*.slack.comare decrypted with:- macOS: AES-128-CBC, key from PBKDF2-HMAC-SHA1 (1003 iters) of the macOS Keychain item
Slack Safe Storage(accountSlack Keyfor direct download orSlack App Store Keyfor App Store). - Linux: AES-128-CBC, key from libsecret entry
Slack Safe Storagevia D-Bus Secret Service (1 iter PBKDF2). v10 fallback uses Chromium's hardcodedpeanuts-derived key. - Windows: AES-256-GCM, key from
%APPDATA%\Slack\Local State(os_crypt.encrypted_key, base64,DPAPIprefix stripped, thenCryptUnprotectData).
For Chromium ≥ 130 (cookies-DB
meta.version >= 24), a 32-byte SHA-256 of the host_key is prepended to the plaintext and stripped on decrypt. - macOS: AES-128-CBC, key from PBKDF2-HMAC-SHA1 (1003 iters) of the macOS Keychain item
No CGO is required on any platform.
Works whether Slack is running or quit. If the live LevelDB is locked, the directory is snapshot-copied to a temp location and read from there (LevelDB recovery handles partial log records, so the copy is safe to read). Cookies use SQLite mode=ro&immutable=1 and never need a snapshot.
In the rare case the snapshot itself can't be read, ErrLocalStorageLocked is still returned — quit Slack and retry.
go test ./... # unit + mock-pipeline tests; no real keychain access
SLACKTOKENS_LIVE=1 go test -tags=integration -v # end-to-end against your machine's Slack profileLive integration is opt-in: it reads your real tokens and triggers the OS keychain prompt (or DPAPI on Windows). CI runs unit tests + the mock pipeline only.
Install the toolchain and the git hooks.
macOS:
brew install lefthook golangci-lint
go install golang.org/x/vuln/cmd/govulncheck@latest
lefthook installLinux:
# golangci-lint
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b "$(go env GOPATH)/bin" v2.11.3
# lefthook
go install github.com/evilmartians/lefthook@latest
go install golang.org/x/vuln/cmd/govulncheck@latest
lefthook installWindows (PowerShell):
# golangci-lint
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.11.3
# lefthook
go install github.com/evilmartians/lefthook@latest
go install golang.org/x/vuln/cmd/govulncheck@latest
lefthook installHooks:
- pre-commit runs
gofmt -l,go vet, andgolangci-lint. - pre-push runs
go test -raceandgovulncheck.
CI mirrors these checks plus a gosec job, plus a lint matrix across Linux/macOS/Windows targets, plus a test matrix of 2 Go versions × 3 OSes.
GPL-3.0-or-later. The Python source library is GPLv3, so this port must be GPLv3 as well. See LICENSE.
{ "mcpServers": { "slacktokens": { "command": "slacktokens-mcp" } } }