Pre-Implementation QA Testing Plan
| Ticket | PAZ-297 โ Change how users connect Slack |
| Scope | Complete overhaul of Slack connection flow for new users using Slack App Configuration Tokens |
| Components | API (new endpoints, models, token storage, webhooks), Frontend (new setup UI), Agent Repo (Connect Slack tool, auth hook) |
| Environment | Staging (pazi.dev) |
| Test Account | qa-agent@pazi.ai / PaziQA2026! |
apps.manifest.create, handles token rotation (12h expiry), generates OAuth authorize URLs, registers per-user webhooks for channel events, and enforces user-level DM authorization where only the agent creator and channel co-members can talk to the agent.
api.slack.com/apps โ "Your App Configuration Tokens" โ "Generate Token"| ๐ด Token Security | Config tokens must be encrypted at rest. Plaintext storage = critical vulnerability. Access tokens expire in 12h โ refresh logic must work transparently. |
| ๐ด Cross-user Isolation | User A must never be able to create apps with User B's tokens or access User B's agents. Webhook events must be scoped per-user. |
| ๐ด Backward Compatibility | Existing Slack connections (old bot+app token flow) must continue working. No silent migration or data loss. |
| ๐ Token Rotation | When the 12h access token expires, tooling.tokens.rotate must be called with the refresh token. Both new tokens must be stored. Failure = all Slack operations break. |
| ๐ Per-agent App Creation | Each agent needs its own Slack app with correct name/avatar. Manifest must include all required scopes and event subscriptions. |
| ๐ Authorization Model | Only agent creator + channel co-members should be authorized. Webhook must correctly process member_joined_channel events. Unauthorized users must get rejection. |
| ๐ก Error Handling | Slack API failures, network timeouts, invalid manifests โ all need clear user-facing errors with recovery steps. |
| ๐ก State Persistence | Authorization lists, Slack connection state, and webhook registrations must survive agent restarts (recurring bug pattern). |
iv, authTag, and ciphertext separately.
apps.manifest.create returns app_id, credentials (client_id, client_secret, signing_secret), and oauth_authorize_url. After app creation, the bot token + app-level token still need to be obtained โ the user installs via OAuth, then those tokens come back via the OAuth callback. The API needs an OAuth callback handler.
apps.manifest.update or manual upload). The apps.manifest.create manifest schema doesn't have an icon field. The developer should use POST https://slack.com/api/apps.icon.upload (undocumented but functional with config token) or note this as a limitation.
request_url field, but since socket mode is enabled, event delivery is via WebSocket, not HTTP. The webhook in the ticket description likely refers to an internal API endpoint that processes Slack events received via socket mode.
/api/slack/config-tokens (or actual endpoint) with valid access and refresh tokensxoxe.xoxp-... plaintext.
curl -X POST ${API_URL}/slack/config-tokens \
-H 'Cookie: <session>' \
-H 'Content-Type: application/json' \
-d '{"accessToken":"xoxe.xoxp-VALID","refreshToken":"xoxe-VALID"}'
refreshToken field (no accessToken)curl -X POST ${API_URL}/slack/config-tokens \
-H 'Cookie: <session>' \
-H 'Content-Type: application/json' \
-d '{"refreshToken":"xoxe-VALID"}'
accessToken field (no refreshToken)curl -X POST ${API_URL}/slack/config-tokens \
-H 'Cookie: <session>' \
-H 'Content-Type: application/json' \
-d '{"accessToken":"xoxe.xoxp-VALID"}'
xoxe.xoxp- for access, xoxe- for refresh)xoxe.xoxp-, refresh tokens with xoxe-.
curl -X POST ${API_URL}/slack/config-tokens \
-H 'Cookie: <session>' \
-H 'Content-Type: application/json' \
-d '{"accessToken":"invalid-format","refreshToken":"also-invalid"}'
curl -X POST ${API_URL}/slack/config-tokens \
-H 'Content-Type: application/json' \
-d '{"accessToken":"xoxe.xoxp-VALID","refreshToken":"xoxe-VALID"}'
curl -X POST ${API_URL}/slack/config-tokens \
-H 'Cookie: <session>' \
-H 'Content-Type: application/json' \
-d '{"accessToken":" ","refreshToken":" "}'
xoxe.xoxp-... strings. Encryption key stored in env vars (e.g., SLACK_TOKEN_ENCRYPTION_KEY).
mongosh --eval 'db.users.findOne({email:"qa-agent@pazi.ai"}, {slackConfigTokens:1})'
# OR check a dedicated collection:
mongosh --eval 'db.slackconfigtokens.findOne({userId: ObjectId("...")})'
curl -X GET ${API_URL}/slack/config-tokens -H 'Cookie: <session_user_B>'
for i in $(seq 1 20); do
curl -s -o /dev/null -w '%{http_code}\n' -X POST ${API_URL}/slack/config-tokens \
-H 'Cookie: <session>' -H 'Content-Type: application/json' \
-d '{"accessToken":"xoxe.xoxp-TOKEN","refreshToken":"xoxe-REFRESH"}'
done
app_id, credentials, and oauth_authorize_urlapp_id, client_id, client_secret, signing_secret, and oauth_authorize_url. App visible at api.slack.com/apps with correct agent name and manifest configuration.
curl -X POST ${API_URL}/slack/create-app \
-H 'Cookie: <session>' \
-H 'Content-Type: application/json' \
-d '{"agentId":"<agent_id>"}'
curl -X POST ${API_URL}/slack/create-app \
-H 'Cookie: <session_without_tokens>' \
-H 'Content-Type: application/json' \
-d '{"agentId":"<agent_id>"}'
tooling.tokens.rotatetooling.tokens.rotate endpoint returns new token, refresh_token, iat, and exp.
# Simulate expired token in DB:
mongosh --eval 'db.users.updateOne(
{email:"qa-agent@pazi.ai"},
{$set:{"slackConfigTokens.expiresAt": new Date("2020-01-01")}}
)'
# Then try app creation โ should auto-refresh
curl -X POST ${API_URL}/slack/create-app \
-H 'Cookie: <session_user_A>' \
-H 'Content-Type: application/json' \
-d '{"agentId":"<user_B_agent_id>"}'
url_verification challenge. Events are scoped to the specific user. Cross-user events are rejected.
curl -X POST ${API_URL}/slack/webhook \
-H 'Content-Type: application/json' \
-d '{"type":"url_verification","challenge":"test_challenge_string"}'
member_joined_channel event is received by webhookcurl -X POST ${API_URL}/slack/webhook \
-H 'Content-Type: application/json' \
-d '{"type":"event_callback","event":{"type":"member_joined_channel","user":"U_NEW","channel":"C_AGENT"}}'
curl -X GET ${API_URL}/agents/<agentId>/tools -H 'Cookie: <session>'
/loginxoxe.xoxp-...) and Refresh Token (xoxe-...). The OLD flow (create app from manifest, install, paste bot/app tokens) should NOT appear for new users.
slack.com/oauth/v2/authorize?...slack.com/oauth/v2/authorize?client_id=..., OR a button in the chat says "Authorize in Slack" / "Install to your workspace". Clicking leads to Slack's OAuth consent screen.
type="password" โ values masked. Prevents shoulder-surfing.
#test-agent-channel