'Slack Bolt Verification with Oauth

I've been trying to work through this for a couple of days now. I have a slack app working in development using Slack/Bolt and the built in Express Server. In development, I can successfully go through the installation process, which upon completion, returns the user credentials (auth token, teamId, etc.) and redirects me to my success page. Additionally, my slash commands work as expected.

In production, however, neither the slash command nor the installation process work. The slash command returns a "dispatch_failed" error in Slack and the following verification error in my logs:

error in server logs

The installation process in development starts with my landing page with a add to slack button which takes me to the slack page where I authorize the required scopes. Submitting this form is where my app times out with the a 503 error. I can see the "code" in the URL but it's not being processed. Before tearing the app apart to try a different authentication method, I was hoping someone could identify where I've gone wrong.

Of importance is the auth token. In development, I declare my xbot token when declaring app. I remove it for production and pass in bot tokens from the context variable in event listeners (slash command, etc.). This is how I understood the process from documentation but could be wrong...

const { App, ExpressReceiver } = require('@slack/bolt');
require('dotenv').config({ path: __dirname + '/.env' })
const axios = require('axios')
const request = require('request')
const bodyParser = require('body-parser')
const path = require('path')
const firebase = require("firebase");

//database config here (removed to de-clutter)

const fetchTeam = async (teamId) => {
    try {
        const ref = await database.ref('workspaces')
        ref.orderByChild('team_id').equalTo(teamId).once('value', function (snapshot) {
            return snapshot.val()
        })
    } catch (e) {
        console.log(e);
    }
}

// Create a Bolt Receiver
const receiver = new ExpressReceiver({ signingSecret: process.env.SLACK_SIGNING_SECRET });

const app = new App({
    signingSecret: process.env.SLACK_SIGNING_SECRET,
    clientId: process.env.SLACK_CLIENT_ID,
    clientSecret: process.env.SLACK_CLIENT_SECRET,
    stateSecret: process.env.STATE_SECRET,
    scopes: ['chat:write', 'chat:write:bot', 'im:write', 'commands', 'incoming-webhook', 'users:read', 'users:read.email'],
    // scopes: ['chat:write', 'chat:write:bot', 'channels:history', 'groups:history', 'im:history', 'commands', 'incoming-webhook', 'users:read', 'users:read.email'],
    installationStore: {
        storeInstallation: async (installation) => {
            return await database.ref('workspaces').push({ team_id: installation.team.id, installation })
        },
        fetchInstallation: async (InstallQuery) => {
            return await fetchTeam(InstallationQuery.teamId)
        },
    },
    //removed token for production
    //token: process.env.SLACK_BOT_TOKEN,
    receiver
});

//a bunch of stuff my slack app does here (removed)


receiver.router.get('/slack/oauth_redirect', async (req, res) => {
    var options = {
        uri: 'https://slack.com/api/oauth.v2.access?code='
            + req.query.code +
            '&client_id=' + process.env.SLACK_CLIENT_ID +
            '&client_secret=' + process.env.SLACK_CLIENT_SECRET,
        method: 'GET'
    }
    request(options, async (error, response, body) => {
        var JSONresponse = JSON.parse(body)
        if (!JSONresponse.ok) {
            res.status(500).send("Error: ", JSONresponse)
        } else {
            const newOBJ = {
                team_id: JSONresponse.team.id,
                ...JSONresponse
            }
            console.log(newOBJ);
            await database.ref('workspaces').push(newOBJ)
          
        }
    })
})


receiver.router.post('/', (req, res) => {
    const payload = JSON.parse(req.body.payload)
    res.send(req.data);
});

receiver.router.post('/slack/events', (req, res) => {
    res.send(req.data);
});

receiver.router.post('/actions', (req, res) => {
    res.send(req.data);
});

// Listen for a slash command invocation
app.command('/commandName', async ({ command, ack, say, context }) => {
    await ack();
    try {
        // Call the users.info method using the built-in WebClient
        const result = await app.client.users.info({
            token: context.botToken,
            //in development i use the code below
            // token: process.env.SLACK_BOT_TOKEN,
            user: user
        });
    }
    catch (error) {
        console.error(error);
    }

    await say({
        "blocks": [
            {
                "type": "section",
                "text": {
                    "type": "plain_text",
                    "text": `Hi there 👋,! Here are some ways you can use Slack to get things done with Webstacks:`,
                    "emoji": true
                }
            },
            {
                "type": "actions",
                "elements": [
                    {
                        "type": "button",
                        "text": {
                            "type": "plain_text",
                            "text": "Raise a Request"
                        },
                        "value": "create_request",
                        "action_id": "create_request"
                    },
                    {
                        "type": "button",
                        "text": {
                            "type": "plain_text",
                            "text": "Leave a Review",
                        },
                        "url": "https://kittycats.com"
                    }
                ]
            }
        ]
    })
});

(async () => {
    // Start your app
    await app.start(process.env.PORT || 3000);

    console.log('⚡️ Bolt app is running!');
})();


Solution 1:[1]

double-check your environment vars ! :) I'd overlooked clientSecret having input signingSecret

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1