Slack Integration¶
Connect WhiteLotusAI to Slack to discover accessible channels and retrieve recent conversations through registered integration tools.
Warning
Release status: Not approved for production read-only use.
The repository contains channel-reading and message-posting capabilities. The initial release must enforce a server-side read-only policy, remove unnecessary write scopes, and resolve shared-token fallback behavior before production approval.
Slack API documentation · Slack OAuth documentation · Slack permissions
1. Overview¶
The Slack integration connects a user's Slack workspace to WhiteLotusAI through OAuth 2.0. The chat service selects registered tools, while the integrations service resolves credentials and communicates with the Slack Web API.
The initial release is intended to be read-only.
Supported capabilities¶
- List public and private channels accessible to the connected Slack identity.
- Retrieve recent messages from a specified channel.
- Establish a Slack connection through OAuth 2.0.
- Normalize provider responses for consumption by the chat service.
Message posting is present in the source code but must remain unavailable in the initial release.
Implementation status¶
| Capability | Source-level status | Release requirement |
|---|---|---|
| OAuth connection | Implemented | Verify state validation, identity binding, and token storage |
| Channel listing | Implemented | Enforce user isolation and verify pagination behavior |
| Channel history | Implemented | Validate channel access and bound request limits |
| Message posting | Implemented | Block before provider invocation |
| Per-user token resolution | Present | Remove unsafe shared-token fallback in per-user mode |
| Rate-limit handling | Incomplete or requires verification | Implement and test provider-aware retry behavior |
Note
This page documents the implementation findings supplied from the repository review. It does not certify successful end-to-end tests, deployment configuration, or production security controls.
2. Registered tools¶
The chat plugin is located at chat/package/app/plugins/slack/plugin.py.
| Tool | Category | Purpose | Initial release |
|---|---|---|---|
list_channels |
READ |
List accessible Slack channels | Allowed after authorization |
get_channel_history |
READ |
Retrieve recent channel messages | Allowed after authorization |
send_message |
WRITE |
Post a message to a Slack channel | Blocked |
The send_message tool has a confirmation requirement and a configured rate limit of 10. Neither mechanism establishes a read-only security boundary.
The plugin's intent-routing logic recognizes Slack-related phrases and channel names such as #general. Requests containing words such as “send,” “post,” “notify,” or “message” may be routed to the write tool.
Security rule: conversational instructions, tool descriptions, confirmation prompts, and rate limits must never substitute for server-side authorization.
3. Architecture and request flow¶
The integration spans the chat plugin, tool registry, integrations service, credential resolver, and Slack Web API client.
flowchart TD
U["User request"] --> C["Chat service"]
C --> P["Intent and tool selection"]
P --> R{"Registered tool allowed?"}
R -->|No| D["Reject operation"]
R -->|Yes| A["Authenticate internal request"]
A --> I["Integrations service"]
I --> V{"Valid user connection and token?"}
V -->|No| X["Fail closed"]
V -->|Yes| S["Slack API client"]
S --> W["Slack Web API"]
W --> N["Normalize response"]
N --> C
R -.->|Write tool| B["Read-only policy gate"]
B --> D
classDef process fill:#E8F0FE,stroke:#315DA8,color:#172B4D,stroke-width:1.5px
classDef decision fill:#FFF0CC,stroke:#9A6700,color:#513600,stroke-width:1.5px
classDef safe fill:#DDF5E8,stroke:#238454,color:#12452D,stroke-width:1.5px
classDef blocked fill:#FCE8E6,stroke:#B42318,color:#7A271A,stroke-width:1.5px
class U,C,P,A,I,S,W,N process
class R,V decision
class D,X,B blocked
The diagram describes the intended secure request flow, including controls that still require verification. It should not be interpreted as proof that every gate is currently enforced.
Source components¶
| Component | Repository path | Responsibility |
|---|---|---|
| Chat plugin | chat/package/app/plugins/slack/plugin.py |
Tool definitions, intent routing, dispatch, response formatting |
| Tool registry | chat/package/app/plugins/registry.py |
Tool registration, dispatch, permission checks |
| Permission policy | chat/package/app/plugins/permissions.py |
Default tool-category permissions |
| OAuth and API routes | integrations/package/app/handlers/integration_handlers.py |
OAuth callbacks, token resolution, Slack routes |
| Slack API client | integrations/package/app/managers/slack_client.py |
Slack Web API requests and response handling |
| Alternate handlers | integrations/package/app/handlers/slack_handlers.py |
Additional or legacy Slack operations; reachability requires verification |
| Settings | integrations/package/app/settings.py |
Slack and service configuration |
The chat plugin sends X-User-Id and X-Internal-Secret headers when a user ID is available. Every protected route must authenticate the internal caller, validate the user identity, and reject untrusted external requests.
4. OAuth and configuration¶
4.1 Configuration variables¶
| Variable | Purpose | Handling requirement |
|---|---|---|
SLACK_CLIENT_ID |
OAuth client identifier | Configuration; not a password |
SLACK_CLIENT_SECRET |
OAuth client secret | Store in a secret manager or protected environment |
SLACK_REDIRECT_URI |
Registered OAuth callback | Must match the configured Slack app |
SLACK_BOT_TOKEN |
Environment-configured bot token and current fallback credential | Disable as an implicit fallback in per-user mode |
SLACK_DEFAULT_CHANNEL |
Default channel setting | Validate before use |
SLACK_SIGNING_SECRET |
Slack request-signature verification setting | Verify actual implementation before relying on it |
JWT_SECRET_KEY |
Application JWT validation secret | Keep secret; rotate under a controlled process |
JWT_ALGORITHM |
Configured JWT validation algorithm | Use an explicitly approved algorithm |
INTERNAL_SECRET |
Internal service authentication secret | Restrict access and rotate securely |
FRONTEND_URL |
Frontend redirect destination | Allowlist trusted destinations |
The plugin's env_vars declaration lists SLACK_BOT_TOKEN and SLACK_DEFAULT_CHANNEL. OAuth initiation separately uses the Slack OAuth client settings.
Danger
Never commit real access tokens, refresh tokens, client secrets, signing secrets, JWT secrets, or internal-service secrets. Use placeholders in examples and redact credentials from logs, traces, and error responses.
4.2 Example environment configuration¶
The following is a template, not a complete deployment configuration.
# Slack OAuth
SLACK_CLIENT_ID=your_slack_client_id
SLACK_CLIENT_SECRET=replace_with_secret
SLACK_REDIRECT_URI=https://your-domain.example/integrations/slack/callback
# Optional environment-token mode.
# Do not use this as an implicit per-user fallback.
SLACK_BOT_TOKEN=
# Optional defaults
SLACK_DEFAULT_CHANNEL=
# Application security
JWT_SECRET_KEY=replace_with_secret
JWT_ALGORITHM=HS256
INTERNAL_SECRET=replace_with_internal_secret
# Frontend
FRONTEND_URL=https://your-domain.example
Do not copy placeholder values directly into production. Provision secrets through the deployment's approved secret-management mechanism.
4.3 OAuth flow¶
The handlers are implemented in integrations/package/app/handlers/integration_handlers.py.
sequenceDiagram
autonumber
actor U as User
participant F as WhiteLotusAI frontend
participant I as Integrations service
participant S as Slack OAuth
participant DB as Connection storage
U->>F: Start Slack connection
F->>I: Initiate OAuth
I->>I: Validate application identity
I->>DB: Store user-bound, single-use state
I-->>F: Redirect to Slack authorization
F->>S: Authorize requested scopes
S-->>F: Redirect with code and state
F->>I: OAuth callback
I->>DB: Validate and consume state
I->>S: Exchange authorization code
S-->>I: OAuth response
I->>DB: Store credentials for initiating user
I-->>F: Redirect with connection result
The sequence illustrates the intended security properties. Verify the actual callback routing, state storage, and frontend redirect behavior against the implementation and tests.
The documented source flow is:
- The OAuth-start endpoint receives the application JWT in a query parameter.
- The handler validates the JWT and extracts the user ID.
- A random OAuth state is stored with the user ID.
- The browser is redirected to Slack's authorization endpoint.
- The callback validates and consumes the stored state.
- The authorization code is exchanged for a Slack OAuth response.
- Access-token and scope information are stored server-side.
- The user's Slack connection is marked active.
- The browser is redirected to the frontend with a connection result.
Warning
OAuth-start credential exposure: accepting an application JWT in a URL query parameter can expose it through browser history, proxy logs, monitoring, and URL-handling infrastructure. Review a safer authenticated initiation flow before production deployment.
Required OAuth safeguards:
- Validate JWT signatures, expiry, and expected claims.
- Generate unpredictable OAuth state values.
- Bind state to the initiating user and consume it only once.
- Apply state expiration and safe cleanup.
- Validate the callback and redirect destination.
- Store tokens server-side against the correct user and workspace.
- Keep tokens out of frontend redirect URLs and application logs.
- Verify that disconnecting a connection prevents further use of its credentials.
4.4 Requested OAuth scopes¶
The current authorization handler requests:
| Scope | Purpose | Initial read-only release |
|---|---|---|
channels:read |
Discover public channels | Retain if required |
channels:history |
Read public-channel history | Retain if required |
groups:read |
Discover accessible private channels | Retain if required |
groups:history |
Read accessible private-channel history | Retain if required |
chat:write |
Post messages | Remove unless explicitly approved |
Confirm the exact scope requirements against the Slack API methods and token type used by the implementation.
Important
Removing chat:write from the OAuth request is necessary for least privilege, but it is not sufficient by itself. The server-side tool policy and message-posting route must also deny write operations. Existing grants may need to be revoked or reauthorized, depending on the approved migration procedure.
The presence of SLACK_SIGNING_SECRET in configuration does not establish that inbound request-signature verification exists. If Slack events, commands, or webhooks are enabled, verify signature validation, timestamp freshness checks, and replay protection separately.
5. API operations¶
5.1 List channels¶
The Slack client calls conversations.list with:
| Parameter | Current behavior |
|---|---|
exclude_archived |
true |
limit |
20 |
types |
public_channel,private_channel |
Each returned channel includes its ID, name, and private-channel flag.
Known limitation: the current implementation does not paginate through all results. Do not describe channel discovery as exhaustive for larger workspaces.
Channel visibility depends on the Slack identity's granted permissions and accessible conversations. Listing a channel does not grant permission to read its history.
5.2 Retrieve channel history¶
The client calls conversations.history using the supplied channel identifier and requested limit.
The returned message fields include:
- Message text.
- Timestamp.
- User ID.
| Behavior | Current implementation |
|---|---|
| Default result limit | 10 |
| Maximum server-side limit | Not explicitly declared in the reviewed route |
| Full-history pagination | Not implemented |
| Chat display limit | At most 10 returned messages |
| Channel-name normalization | Requires verification |
The integrations route does not declare an explicit upper bound for the limit query parameter, and the client forwards the value to Slack. Add server-side validation before production use.
The tool description permits a channel name such as #general, but the client forwards the supplied value as Slack's channel parameter. Verify and normalize channel-name-to-ID behavior rather than assuming that names and IDs are interchangeable.
A recommended validation policy is:
DEFAULT_HISTORY_LIMIT = 10
MAX_HISTORY_LIMIT = 100 # Example policy; validate against provider limits.
def validate_history_limit(limit: int | None) -> int:
if limit is None:
return DEFAULT_HISTORY_LIMIT
if limit < 1 or limit > MAX_HISTORY_LIMIT:
raise ValueError("History limit is outside the permitted range")
return limit
This is an illustrative validation pattern, not a claim that the repository already contains this function. Select the actual maximum based on provider constraints, application requirements, and tested behavior.
5.3 Post a message¶
The Slack client calls chat.postMessage with a channel and message body.
This is a state-changing operation and is outside the initial release scope.
The required policy is to reject the operation before invoking the integrations client or Slack API. Hiding the tool from the user interface or relying on conversational instructions is insufficient.
6. Critical finding: credential isolation¶
The _resolve_token helper in integrations/package/app/handlers/integration_handlers.py attempts to resolve a per-user Slack token when valid internal headers are available.
The reviewed behavior can fall back to SLACK_BOT_TOKEN when:
- The user has not explicitly connected Slack.
- A connection lookup fails.
- A user-specific token cannot be retrieved.
- Valid user and internal-secret headers are absent.
This is a significant authorization-design concern. A shared environment token may allow requests to operate under a common Slack identity rather than the initiating user's authorized connection.
Required remediation¶
- Fail closed in per-user mode. Missing connections and failed token lookups must reject the request.
- Remove implicit fallback. A failed per-user lookup must never silently switch to a shared token.
- Separate operating modes. If single-account environment-token mode is required, enable it explicitly with a documented trust boundary.
- Validate identity on every route. Never trust a caller-supplied user ID without authenticating the caller.
- Isolate caches. Cache keys must reflect the authenticated user and appropriate workspace or connection identity.
- Test cross-user access. Verify that one user cannot retrieve another user's data or unintentionally use a shared account.
Caution
Treat token fallback as a release-blocking issue for multi-user production until the behavior is corrected and authorization tests demonstrate isolation.
Recommended credential-resolution policy¶
flowchart TD
A["Incoming Slack request"] --> B{"Explicit operating mode?"}
B -->|Per-user| C["Authenticate internal caller"]
C --> D{"Valid user and active connection?"}
D -->|Yes| E{"User token available?"}
E -->|Yes| F["Use that user's token"]
D -->|No| X["Reject request"]
E -->|No| X
B -->|Single-account| G{"Mode explicitly enabled?"}
G -->|No| X
G -->|Yes| H["Validate configured shared credential"]
H --> I{"Credential valid?"}
I -->|No| X
I -->|Yes| J["Use shared identity under restricted policy"]
classDef process fill:#E8F0FE,stroke:#315DA8,color:#172B4D,stroke-width:1.5px
classDef decision fill:#FFF0CC,stroke:#9A6700,color:#513600,stroke-width:1.5px
classDef safe fill:#DDF5E8,stroke:#238454,color:#12452D,stroke-width:1.5px
classDef blocked fill:#FCE8E6,stroke:#B42318,color:#7A271A,stroke-width:1.5px
class A,C,F,H,J process
class B,D,E,G,I decision
class X blocked
In shared-account mode, document that provider access occurs under the configured shared identity. Do not represent this mode as equivalent to per-user OAuth authorization.
7. Caching, errors, and rate limits¶
| Area | Observed behavior | Required review |
|---|---|---|
| Channel-list cache | TTL of 120 seconds | Verify user and workspace isolation |
| Cache key | Includes user ID when supplied, or env in environment-token mode |
Ensure the mode cannot be confused or bypassed |
| History cache | Not present in the primary handler | Reassess only if caching is introduced |
| HTTP timeout | 10 seconds in the Slack client | Test timeouts and cancellation |
| Provider errors | Slack JSON ok field is checked |
Normalize failures safely |
| HTTP error mapping | Many failures become HTTP 502 responses | Avoid leaking internal details |
| Pagination | Not implemented | Document limits or implement pagination |
| Rate limiting | Explicit Retry-After handling not implemented |
Add provider-aware throttling and retry behavior |
Do not retry every failed request indiscriminately. Respect Slack's rate-limit responses, including Retry-After where applicable, and use bounded retries with backoff for operations that are safe to repeat.
Review error handling so provider responses, request details, access tokens, and internal exceptions are not exposed unnecessarily to callers.
8. Read-only security policy¶
The following controls are mandatory for the initial release.
| Control | Expected behavior |
|---|---|
| Tool allowlist | Only approved read tools can be dispatched |
| Write-tool enforcement | send_message is denied server-side |
| Message route | POST /slack/message cannot perform a write in read-only mode |
| Internal authentication | Protected routes reject unauthenticated external callers |
| User isolation | Tokens and data remain bound to the authorized connection |
| Shared-token mode | Disabled by default and explicitly configured if supported |
| Alternate handlers | Every reachable handler follows the same security policy |
| Provider invocation | Denied write operations never call chat.postMessage |
The repository also contains integrations/package/app/handlers/slack_handlers.py. Verify whether these alternate handlers are registered or otherwise reachable. Any reachable route must enforce the same identity, credential-isolation, and read-only requirements.
Recommended release gate¶
READ_ONLY_SLACK_TOOLS = {
"list_channels",
"get_channel_history",
}
def is_slack_tool_allowed(tool_name: str, read_only: bool = True) -> bool:
if read_only:
return tool_name in READ_ONLY_SLACK_TOOLS
# Non-read-only operation requires a separately approved policy.
return False
This illustrates an explicit allowlist. It is not a drop-in replacement for the repository's authorization system; integrate the rule into the actual server-side dispatch and route enforcement layers.
9. Security and privacy requirements¶
Slack conversations may contain confidential business information, customer data, personal information, and operational details.
Apply the following safeguards:
- Identity: authenticate the initiating user and validate the active Slack connection.
- Least privilege: request only the scopes needed for approved read operations.
- Authorization: verify access to the requested conversation through Slack and application policy.
- Input validation: validate channel identifiers, result limits, and request payloads.
- Credential protection: keep tokens and secrets out of logs, URLs, and error responses.
- Cache isolation: separate cached results by the correct user and authentication mode.
- Resilience: use bounded retries, timeouts, and provider-aware rate-limit handling.
- Data minimization: return only the fields needed to answer the request.
- Untrusted content: treat retrieved Slack messages as data, not as instructions that override system policy or authorization.
- Read-only enforcement: prevent all unapproved state-changing operations.
10. Troubleshooting¶
| Symptom | Investigation |
|---|---|
| Slack cannot be connected | Check OAuth client settings, callback URI, state handling, and Slack app installation |
| OAuth returns an error | Inspect the provider response, code exchange, and granted scopes without logging credentials |
| Channel listing fails | Verify the user's connection, token resolution, scopes, and channel access |
| A private channel is missing | Confirm that the connected identity can access the channel |
| History returns no messages | Verify the channel ID, required scope, channel membership, and requested limit |
| The wrong account or workspace is accessed | Investigate shared-token fallback and per-user credential resolution immediately |
| Rate-limit errors occur | Review provider limits, Retry-After, and bounded retry behavior |
| Slack requests time out | Check integrations-service health, network access, and the HTTP timeout |
| A message is posted in read-only mode | Treat as a release-blocking authorization defect |
| Cached channel data appears incorrect | Inspect cache keys, TTL, connection status, and workspace association |
Never troubleshoot OAuth by copying real tokens or secrets into source code, query strings, tickets, or documentation.
11. Testing checklist¶
Use these checklists as release acceptance criteria. Mark an item complete only after its behavior has been verified.
Functional tests¶
- List channels in a connected workspace.
- Retrieve recent messages from a permitted channel.
- Handle empty channel lists and empty history results.
- Validate channel identifiers and requested history limits.
- Test channel names as input and verify normalization behavior.
- Test workspaces with more than 20 channels and document pagination behavior.
- Handle missing scopes, inaccessible channels, expired tokens, and revoked access.
- Test rate limits,
Retry-After, timeouts, and temporary outages. - Confirm that message contents and credentials are not unnecessarily logged.
Authorization tests¶
- An unconnected user cannot access Slack in per-user mode.
- One user cannot access another user's token or workspace data.
- Connection lookup failures fail closed in per-user mode.
- A shared-token fallback cannot bypass per-user authorization.
- Only approved read tools are callable in the initial release.
-
send_messageis rejected before provider invocation. - Direct requests to
POST /slack/messagecannot post in read-only mode. - Alternate handlers cannot bypass authentication or read-only controls.
- Denied operations never invoke
chat.postMessage.
OAuth tests¶
- Invalid or expired application JWTs fail safely.
- OAuth state is unpredictable, user-bound, single-use, and time-limited.
- Tokens are stored against the correct user and workspace.
- Access tokens are absent from redirect URLs and application logs.
- Granted scopes match the approved release policy.
- Disconnecting Slack prevents subsequent use of the connection.
Operational tests¶
- Secrets are supplied through the approved configuration mechanism.
- Cache keys separate users and authentication modes.
- Provider errors do not expose sensitive internal details.
- Retries are bounded and respect provider rate limits.
- Logs and monitoring avoid unnecessary message-content retention.
12. Source references¶
| File | Review scope |
|---|---|
chat/package/app/plugins/slack/plugin.py |
Registered tools, intent routing, dispatch, and response formatting |
integrations/package/app/handlers/integration_handlers.py |
OAuth, token resolution, channel/history routes, and posting route |
integrations/package/app/managers/slack_client.py |
Slack API calls, request limits, and timeout behavior |
integrations/package/app/handlers/slack_handlers.py |
Alternate handlers and reachability |
chat/package/app/plugins/registry.py |
Tool registration, dispatch, and permission checks |
chat/package/app/plugins/permissions.py |
Default tool-category permissions |
integrations/package/app/settings.py |
Slack and service configuration |
13. Production readiness decision¶
Danger
Do not approve the Slack integration for multi-user production until the release-blocking security requirements are verified.
The reviewed implementation registers send_message, requests chat:write, exposes a message-posting route, and may fall back to a shared environment token.
Production approval requires evidence that:
- Read-only access is enforced at the tool and HTTP-route layers.
- Per-user credential resolution fails closed.
- OAuth scopes follow least privilege.
- OAuth state and token storage are correctly bound to the initiating user.
- Channel-history requests have validated, bounded limits.
- Internal routes and alternate handlers cannot bypass authorization.
- Cross-user isolation and denied-write tests pass.
Update this readiness decision only when the relevant code changes, tests, and deployment configuration have been reviewed.
Documentation scope: source-inspected implementation baseline. End-to-end behavior and production security remain subject to verification.