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

Several questions regarding code and integrating existing solution with bolt-python. #410

Closed
Waszker opened this issue Jul 16, 2021 · 3 comments
Labels
question Further information is requested

Comments

@Waszker
Copy link

Waszker commented Jul 16, 2021

Hi,
I have created this issue in order to clarify some issues and questions I have regarding the code and the Slack integration approach. I have read official Slack integration document available here: https://api.slack.com/start/building/bolt-python, but there is very little explanation in regards to the internal implementation.

I'm trying to implement Slack integration for the Django-based software that handles multiple user accounts. Each account can request installation of the app to individual Slack workspace (using OAuth), and I have based my current implementation on examples available here: https://github.com/slackapi/bolt-python/tree/main/examples/django/oauth_app

  1. What is the difference between SlackBot and SlackInstallation objects?
    This is a very basic question but I just can figure out the differences/relation between those two models. When should the installation, and when the bot should be used? Could you point me to some document describing the differences? I see that those two database models share a lot of fields, could this be streamlined, e.g. installation having multiple bots, etc.?

  2. Why store client_id value in SlackBot database object?
    From what I understand the client_id is a sensitive value. It looks like (looking at the example datastore implementation) it is actually never used after saving it in the database (https://github.com/slackapi/bolt-python/blob/main/examples/django/oauth_app/slack_datastores.py#L100). Could this field be omitted, and not stored within a database?

  3. Why store multiple instances of bot and installation objects in the database?
    I don't understand the idea behind saving new instances of what is essentially the same SlackBot. Would a following code work better, or is there some specific reason multiple instances are required? There could be a unique constraint on (enterprise_id and team_id) pair in the database.

        installation_data = installation.to_dict()
        if is_naive(installation_data["installed_at"]):
            installation_data["installed_at"] = make_aware(installation_data["installed_at"])
        if SlackInstallation.objects.filter(
            team_id=installation.team_id, enterprise_id=installation.enterprise_id,
        ).exists():
            SlackInstallation.objects.filter(
                team_id=installation.team_id, enterprise_id=installation.enterprise_id,
            ).update(**installation_data)
        else:
            SlackInstallation.objects.create(**installation_data)
  1. Is it possible to link SlackBot/SlackInstallation object with an internal user account that initiated the app installation?
    I would like to know which of my users installed bot. I'd like to add foreign key pointing to User model in the SlackBot, and have it filled in during installation process (taking user from django request object). From what I see, the example OAuthFlow object has handle_callback method, but is called with slack request instead of django request object.

  2. Is it possible to request removal of the installed Slack app?
    If the point 4. of this question is not possible, and the user would have to "connect/link" bot from the Slack workspace (e.g. by issuing some command asking for authorization), would it be possible to remove bots that have not been linked after certain period and expire their tokens? I'd like to do this for safety reasons.
    Imagine the following situation:

a) User installs the app in the Slack workspace
b) User invites the bot the channel, but does not authorize it in my app (I have SlackBot with no User object connected in my database)
c) Malicious actions steal bot details
d) I delete bot details after certain time during which the bot has not been linked to the User account in my system (user forgot or was unable to authorize bot in my app)
c) Having the `token` and bot details mailicious code could (potentially) send messages to the channel the bot has been invited to, creating a situation in which user is presented with messages not coming from my system

I would like to ensure that the bot token is invalidated during its removal from my database.

@seratch seratch added the question Further information is requested label Jul 17, 2021
@seratch
Copy link
Member

seratch commented Jul 17, 2021

Hi @Waszker, thanks for asking these questions here!

I hope the following answers are helpful to you (and someone who visits this page in the future).

What is the difference between SlackBot and SlackInstallation objects?
This is a very basic question but I just can figure out the differences/relation between those two models. When should the installation, and when the bot should be used? Could you point me to some document describing the differences?

Reading https://api.slack.com/authentication/oauth-v2 should be helpful to understand the basics of the Slack OAuth in general.

A Slack app installation can provide two types of OAuth access tokens to your app. They are 1) bot token (xoxb- prefixed) per workspace, and 2) user token (xoxp- prefixed) per user in the installed workspace.

In most cases, Slack apps tend to use only bot token scopes. All the end-users in your Slack workspace can use the shared bot's permissions. For instance, using slash commands, shortcuts, modals, message events in channels the bot user is in, and so on can function only with bot token. In other words, bot token scopes can cover most use cases.

On the other hand, if your app requests to grant user token scopes (=granting your app to do something on behalf of a user), your app will receive user tokens in addition to a bot token. From an end-user's perspective, the user allows the app to post messages as the user, access private channels, etc by giving a user token.

SlackInstallation and Bolt's interface Installation represent the oauth.v2.access data your app receives here: https://api.slack.com/authentication/oauth-v2#exchanging This means that the data model can have both bot token and user token, plus each row is associated with the user who installed the app.

Th reason why the example app has SlackBot is that, even when all the user tokens are revoked, the bot token in the workspace should still be alive. Likewise, even after the bot token revocation, your app still needs user tokens in some use cases. Refer to the built-in revocation handlers to learn what should be done when revocation/uninstallation events happen.

I see that those two database models share a lot of fields, could this be streamlined, e.g. installation having multiple bots, etc.?

For the above reason, we have two types of database tables to cover the mentioned use cases adequately. But if your app does not need to have both, having a single database table for simplicity is also good to go. As recommendations from this project, we don't hesitate having duplicated properties in those two tables. It makes easier to delete data in either of the tables than having relationships in the revocation scenario.

e.g. installation having multiple bots, etc.?

As I mentioned above, you can have only one bot per workspace in installations. This means that, even if 100 users in the workspace installed the same app with bot token scopes, only one bot and its token will be available.

Why store client_id value in SlackBot database object?

Having the column in a database enables developers to share the same database tables for multiple Slack apps. If that's not your use case, you can remove the column safely.

It looks like (looking at the example datastore implementation) it is actually never used after saving it in the database

No, it saves the property this way:

i["client_id"] = self.client_id

As you mentioned, indeed, the database model does not set the value to Installation/Bot in the finder methods. This is because Bolt's App instance is available per client_id (Slack App ID, in other words).

Why store multiple instances of bot and installation objects in the database? I don't understand the idea behind saving new instances of what is essentially the same SlackBot.

As the default way, we recommend saving all the installation history data with timestamps. Having all-time data can make your troubleshooting / technical investigations in production system operations easier.

For instance, your app may need more scopes in the future (say, You app starts with only chat:write, and then need others like files:write). In this scenario, reinstalling the app can be required to use the app's new functionalities. This means that there can be multiple versions of the app installations. Having only the latest data may be also acceptable to properly manage this specific pattern, but more complex situations may need complete historical data to figure out what's happening.

Would a following code work better, or is there some specific reason multiple instances are required? There could be a unique constraint on (enterprise_id and team_id) pair in the database.

If you prefer updating a single database row, that's also the option. It would be a valid choice particularly if you want simplicity in design or have to manage millions/billions of installation events.

Is it possible to link SlackBot/SlackInstallation object with an internal user account that initiated the app installation?
I would like to know which of my users installed bot. I'd like to add foreign key pointing to User model in the SlackBot, and have it filled in during installation process (taking user from django request object).

I would recommend connecting to SlackInstallation in this case. A bot is associated with the workspace, not a user. After an installation, who installed the bot into the workspace does not matter.

From what I see, the example OAuthFlow object has handle_callback method, but is called with slack request instead of django request object.

If you are talking abou the callback_options, the callbacks are supposed to be called at the end of the OAuth flow. Refer to "Customizing OAuth defaults" in the document (it needs a click to display the part) https://slack.dev/bolt-python/concepts#authenticating-oauth The callbacks should be useful for this purpose.

Is it possible to request removal of the installed Slack app?

You can use the following APIs as necessary:

Let us know if you have some follow-ups here 👋

If you may have many more questions in development and you are happy to join the Slack Community workspace, I can help you there more easily. Joining channels like #tool-bolt, #lang-python, and #slack-api would be recommended.

@seratch
Copy link
Member

seratch commented Jul 31, 2021

👋 Hi @Waszker, let us know if you have anything further to ask/discuss here. We'll close this issue after waiting for your response for a few more days.

@seratch seratch closed this as completed Aug 6, 2021
@Waszker
Copy link
Author

Waszker commented Aug 6, 2021

@seratch ooops sorry about lack of response from me - I didn't notice the mention. Your answers were really detailed and sufficient for me - thank you very much for taking your time and answering them. 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants