slackapi / bolt-python

A framework to build Slack apps using Python
https://slack.dev/bolt-python/
MIT License
1.02k stars 236 forks source link

Store Installation data in respective Slack org's Infrastructure #1065

Closed rahul-beehyv closed 3 months ago

rahul-beehyv commented 3 months ago

Hello Team,

I'm currently in the process of developing a Slack app using the Python Bolt framework. I've successfully implemented the OAuth2 workflow and completed all necessary steps for installation within a single workspace. Now, our goal is to deploy the app across various organizations.

However, we're opting not to store installation data in a shared Installation Store. Instead, we aim to save all installation data within each client's infrastructure.I'm able to pass a query param to /slack/install URL by which i'm able to save installation in respective client's infrastructure. I've also implemented a custom middleware which can the get same query param value which is being passed to slack workspace and then after appropriate interaction in workspace, a payload is sent to bolt app, where i'm adding it to context as well. I'm having trouble getting this value in find_installation method of Installation Store.Do you have any suggestions or possible workarounds for this?

Reproducible in:

The slack_bolt version

slack-bolt==1.18.1 slack_sdk==3.27.1

Python runtime version

Python 3.10.12

OS info

6.5.0-25-generic GNU/Linux

Steps to reproduce:

None

Expected result:

None

Actual result:

None

Requirements

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.

seratch commented 3 months ago

Hi @rahul-beehyv, thanks for asking the question!

Fetching OAuth installation data via InstallationStore#find_installation(...) is a foundamental design of bolt-python framework, so there is no workaround to avoid using the interface. Thus, for your use case, I'd suggest deploying your app into each client's infrastructure as different Slack apps. This means each app has different app ID, client_id/client_secret, signing secret, and set of OAuth endpoints and Request URLs.

Thinking of an alternative... if you have a front-end proxy server which dispatches event payload requests to each client's infra under the hood, it may work. However, to me, the whole system design this sounds too complicated and it could be hard to operate. For these reasons, I'd suggest running an app per client instead.

I hope this helps.

rahul-beehyv commented 3 months ago

Thanks for the prompt response @seratch! I was hoping that customising the AsyncInstallationStoreAuthorize , AsyncOAuthSettings and passing context value to its find_installation method would solve my issue, but was encountering ERROR:slack_bolt.AsyncMultiTeamsAuthorization:Although the app should be installed into this workspace, the AuthorizeResult (returned value from authorize) for it was not found. I checked where it was being called, and wanted to know if there can be workaround there as well

seratch commented 3 months ago

When your authorize (or its underlying installation store) does not return a relevant AuthorizeResult to a request, the error can arise and that is an expected behavior.

I mean, if your single Bolt app has access to any installation data across those client infrastructure databases in some way, you don't need any workaround (and also, there is no need to customize bolt-python as well). You can just have a custom installation store that encapsulates the access to various on-premise infra databases. Your find_installation should be able to identify which infra to access by enterprise_id and/or team_Id.

If your single Bolt app does not have such access to client databases, there is no way to serve as a single /slack/events endpoint. In this case, your app needs be deployed to each infra and needs to serve as different Request URLs (as I suggested above).

rahul-beehyv commented 3 months ago

Thank you @seratch. Appreciate all your help!

seratch commented 3 months ago

Let me close this issue now, but please feel free to write in whenever you have follow-up questions!

rahul-beehyv commented 3 months ago

Hey @seratch , I'm now trying the fetch installation data in find_installation using enterprise_id , team_id (my app doesn't take actions on behalf of users so no user_id needed)

my app is initialised like this:

installation_store = CustomInstallationStore()
custom_oauth_flow = CustomAsyncOAuthFlow(settings=async_oauth_settings)
app = AsyncApp(name="Bolt Slack App",
               oauth_flow=custom_oauth_flow,
               signing_secret=os.environ.get("SLACK_SIGNING_SECRET", "********************"),
               installation_store=installation_store)

CustomInstallationStore has save installation and find installation methods defined properly

I'm getting a warning when initialising :

WARNING:slack_bolt.AsyncApp:As you gave both installation_store and oauth_settings/auth_flow, the top level one is unused.

and when an incoming request from slack is sent to bolt app,

DEBUG:slack_bolt.AsyncApp:Applying slack_bolt.middleware.authorization.async_multi_teams_authorization.AsyncMultiTeamsAuthorization
DEBUG:oauth.async_outh_settings:No installation data found for enterprise_id: ***** team_id: *******
ERROR:slack_bolt.AsyncMultiTeamsAuthorization:Although the app should be installed into this workspace, the AuthorizeResult (returned value from authorize) for it was not found.

I'm not sure if customer installation store is being used, i can see the data in it after it got installed via /slack/install but its not being used when incoming request from slack workspace posts a payload.

Is there something i'm doing wrong ?

seratch commented 3 months ago

As the warning message indicates, you need to pass the installation_store to oauth_flow instead.

rahul-beehyv commented 3 months ago

Update: Its resolved. I was initialising it incorrectly. Thanks for helping me out!