slackapi / node-slack-sdk

Slack Developer Kit for Node.js
https://tools.slack.dev/node-slack-sdk/
MIT License
3.27k stars 662 forks source link

Can we pass metadata as a URL parameter to slack/install endpoint? #1280

Closed AdamInTheOculus closed 3 years ago

AdamInTheOculus commented 3 years ago

Description

I'm using Bolt.js with ExpressReceiver to handle installations via OAuth2 flow. The ExpressReceiver constructor supports an installerOptions object which lets you define a metadata object (see here), but I'm wondering if I can pass dynamically metadata via URL parameter from my website?

Breaking down the Slack install URL here are the following URL parameters:

https://testurl.com/slack/install?client_id=xxx&state=xxx&redirect_uri=xxx&scope=chat:write,chat:write.public,commands,users:read&user_scope=identity.basic&metadata=UNIQUE_ID

What type of issue is this? (place an x in one of the [ ])

Requirements (place an x in each of the [ ])


mwbrooks commented 3 years ago

Hey @AdamInTheOculus 👋🏻 Right now, the slack/install is quite limited, but there is a discussion under https://github.com/slackapi/bolt-js/issues/982 to allow to be more customizable.

I haven't tried this passing customize parameters to the install endpoint, but I can look into whether there is a way to do it. I'll get back to you when I've taken a look!

mwbrooks commented 3 years ago

Hey @AdamInTheOculus, I had a chat with a few of the maintainers. At the moment, it's not possible to pass custom parameters across the slack/install. Those params would be lost during the OAuth flow.

At the moment, the only persistent place to store custom params across the OAuth flow is the state param. In your link above, you noticed that any metadata can be programmatically passed to generateInstallUrl and encoded into the state param. At the end of the OAuth flow, the state param will then be decoded back to the original params.

I think your best approach would be to create a custom route to install your app. The custom route could accept custom params, for example /my/custom/install?param_1=one&param_2=two. The implementation of this route can then pass the custom params into the metadata of generateInstallUrl, so that they are encoded into the state.

If you'd like a working code sample, I can take some time to put one together. But I thought I'd start by explaining the idea, so that you're not waiting :)

AdamInTheOculus commented 3 years ago

Thanks @mwbrooks, I appreciate the quick response!

Does writing a custom install route impact the usage of ExpressReceiver? I'm looking at code and Slack install docs and I'm a bit confused if I can pass a custom install handler to ExpressReceiver, or do I need to use the InstallProvider?

A working code sample would be very much appreciated!

Update: After some thought, it looks like adding a custom route using ExpressReceiver will end up with 2 Slack installation routes - one for default /slack/install as part of ExpressReceiver constructor, and another for the custom endpoint. At first I thought this was an issue, but this shouldn't affect my use case. I'll try out a POC and report back!

AdamInTheOculus commented 3 years ago

Definitely having a case of the Mondays - this was much easier than expected. Here is a POC for a custom installation route.

// Not shown above is initialization of ExpressReceiver ...

expressReceiver.router.get('/slack/custom-install', async (req, res) => {
    scopes = [
        'chat:write',
        'chat:write.public',
        'commands',
        'users:read',
        'channels:read',
        'groups:read',
        'mpim:read',
        'im:read'
    ];

    try {
        const url = await expressReceiver.installer.generateInstallUrl({
            redirectUri: req.query.redirect_uri,
            metadata: { user_id: req.query.user_id },
            scopes: scopes
        });

        res.send(
            `<a href=${url}><img alt=""Add to Slack"" height="40" width="139"
            src="https://platform.slack-edge.com/img/add_to_slack.png"
            srcset="https://platform.slack-edge.com/img/add_to_slack.png 1x,
            https://platform.slack-edge.com/img/add_to_slack@2x.png 2x" /></a>`
        );
    } catch (error) {
        // Handle error
    }
});
mwbrooks commented 3 years ago

Definitely having a case of the Mondays

Hey @AdamInTheOculus, haha, then you and I both are having a slow start to the day!

I'm glad to hear that you've got it working! I should have been more clear, so thanks for reading between the lines. I definitely agree that one drawback is having two routes.

Your code sample looks clean and easy to follow. 👌🏻 Thanks a lot for sharing it! I hope it's useful to other developers looking for an example - and I imagine we'll reference it from time-to-time for future questions!

I'll close this issue, but feel free to follow up if you run into any other blockers!

racinger commented 1 year ago

Hi,

Anyway to achieve that in the slack_bolt python version?

I currently cannot find a way to add metadata.