Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Install and authorize multiple slack app clients with a single bolt js server #2206

Open
KondaHarika opened this issue Aug 15, 2024 · 5 comments
Labels
question M-T: User needs support to use the project

Comments

@KondaHarika
Copy link

KondaHarika commented Aug 15, 2024

I have a scenario where people create slack app and would provide the client credentials.
Generally the installation url provide is like {URL}/slack/install,

but here idea is to pass a query param and identify the respective slack app, hence based on that I will get client data from MongoDB and hence the install process.

Question here is that is this approach possible or do we have any other way to execute this case?

Second, I have been trying execute the above approach so trying to use the express middleware, to set the params required but I am unable to access the middleware here?! Is there anything wrong that Im doing here !?

const { App, ExpressReceiver, LogLevel } = require('@slack/bolt');
const express = require('express');

require('dotenv').config();

const expApp = express();

const oauthRedirect = process.env.SLACK_OAUTHREDIRECT_URL;
const botScopes = process.env.SLACK_BOT_SCOPES ? process.env.SLACK_BOT_SCOPES.split(',') : [];

const expressReceiver = new ExpressReceiver({
    signingSecret: process.env.SLACK_SIGNING_SECRET,
    clientId: process.env.SLACK_CLIENT_ID,
    clientSecret: process.env.SLACK_CLIENT_SECRET,
    stateSecret: "state-secret",
    scopes: botScopes,
    redirectUri: oauthRedirect,
    installerOptions: {
      stateVerification: true,
      redirectUriPath: "/slack/oauth_redirect",
      directInstall: true,
    },
    installationStore: {
        storeInstallation: async (installation) => {
          if (installation.isEnterpriseInstall && installation.enterprise !== undefined) {
            return await appInstall.saveApp(installation.enterprise.id, installation);
          }
          if (installation.team !== undefined) {
            return await appInstall.saveApp(installation.team.id, installation);
          }
          throw new Error('Failed saving installation data to installationStore');
        },
        fetchInstallation: async (installQuery) => {
          if (installQuery.isEnterpriseInstall && installQuery.enterpriseId !== undefined) {
            return await appInstall.getApp(installQuery.enterpriseId);
          }
          if (installQuery.teamId !== undefined) {
            return await appInstall.getApp(installQuery.teamId);
          }
          throw new Error('Failed fetching installation');
        },
        deleteInstallation: async (installQuery) => {
          if (installQuery.isEnterpriseInstall && installQuery.enterpriseId !== undefined) {
            return await appInstall.deleteApp(installQuery.enterpriseId);
          }
          if (installQuery.teamId !== undefined) {
            return await appInstall.deleteApp(installQuery.teamId);
          }
          throw new Error('Failed to delete installation');
        },
      },
  })

const app = new App({
    receiver: expressReceiver,
    // logLevel: LogLevel.DEBUG,
});

expressReceiver.router.get('/api/health', async (req, res) => {
 // health check code
});


function myMiddleware(req, res, next) {
  console.log('Middleware is running');
  next();
}

expApp.use(myMiddleware);

expApp.use('/slack/install', expressReceiver.router);

(async () => {
    try {
        await app.start(process.env.PORT || 4000);
        console.log("Slack Bolt app is running on port", process.env.PORT);
    } catch (error) {
        console.error("Unable to start App", error);
    }
})();
@zimeg zimeg added question M-T: User needs support to use the project and removed untriaged labels Aug 16, 2024
@zimeg zimeg changed the title Able to install and function multiple slack bots with a single bolt js server Install and authorize multiple slack app clients with a single bolt js server Aug 16, 2024
@zimeg
Copy link
Member

zimeg commented Aug 16, 2024

Hey @KondaHarika 👋 This is a super interesting question but I might need more clarification around what you're hoping to achieve.

I have a scenario where people create slack app and would provide the client credentials.

What are the client credentials provided here? I'm wondering mostly if these are associated with the same app ID and signing secrets, just a different client ID and client secret?

If so, deciding this from the installationStore that's part of the App constructor might not be possible. Sharing a few thoughts on this below!

I have been trying execute the above approach so trying to use the express middleware, to set the params required but I am unable to access the middleware here?!

I'm not sure that this path is registering as a router for your app at all - instead it might be the default /slack/install path that is reached, which might make it appear that no middleware is called:

expApp.use('/slack/install', expressReceiver.router);

As for some thoughts- it is my understanding that installationStore is intended for use with a single client, so a more detailed approach might be needed when handling multiple installations for multiple clients.

Instead, installations can be handled with the @slack/oauth package and authorizations with the authorize function.

This might require custom route handling using the router of ExpressReceiver, but should also allow for gathering client credentials from query parameters. Then, installation information can be stored on redirect and fetched during authorization of later incoming events.

Please let me know if this seems like a way to handle installations or if I'm missing some of the nuance with this! I do understand this might be a bit more code, but I'm also hoping it's an alright approach 🙏

@KondaHarika
Copy link
Author

@zimeg, Thanks for explaining.

What are the client credentials provided here? I'm wondering mostly if these are associated with the same app ID and signing secrets, just a different client ID and client secret?

Regarding above, its different app with different app credentials. Basically, the case here is different people will have there own app with different bot name, bot logo etc, but the functionality would be eventually different. Hence I will save the credentials in the DB and provide their installation URL.

@zimeg
Copy link
Member

zimeg commented Aug 16, 2024

@KondaHarika For sure! I'm now wondering when the app functionalities differ 🤔 It sounds like you're wanting to use the same initialization logic and listeners, but with different credentials within the app and different apps and signing secrets altogether?

Could setting up an HTTP router before the app initialization provide enough customization to the routes and request URLs needed for these different installations? I might still be needing more details on the setup you're planning! 💭

Copy link

👋 It looks like this issue has been open for 30 days with no activity. We'll mark this as stale for now, and wait 10 days for an update or for further comment before closing this issue out. If you think this issue needs to be prioritized, please comment to get the thread going again! Maintainers also review issues marked as stale on a regular basis and comment or adjust status if the issue needs to be reprioritized.

@KondaHarika
Copy link
Author

KondaHarika commented Sep 16, 2024

Hey @zimeg , Sorry for the delayed response. I was working on another slack app development. So, coming back to this , I do get your suggestion of using @slack/oauth package and authorizations with the authorize, but i didnt understand

Could setting up an HTTP router before the app initialization provide enough customization to the routes and request URLs needed for these different installations? I might still be needing more details on the setup you're planning! 💭

Also, If am providing the slack credentials dynamically, I was thinking how should I manage the listeners. Currently, I write code for listeners this way

app.message()
app.actions()
app.views()
app.events()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question M-T: User needs support to use the project
Projects
None yet
Development

No branches or pull requests

2 participants