slackapi/bolt-js

State verification during OAuth process

Closed this issue · 8 comments

Hi, I would like to double check about my understanding of OAuth process and how to validate the state parameter. Based on my understanding, during the OAuth process (https://your-domain/slack/installation), /slack/install URL generates a unique state parameter and appends it to the authorize URL's query string. After user confirms the installation, it would redirects user to my redirect_url. My redirect_url endpoint would validate the state parameter. Is this flow correct?

Since the unique state parameter is generated by /slack/install on the server side, how can I validate the value of state on redirect_url side? I kept getting the error of Invalid state from oauth provider from my redirect_url endpoint.

Your understanding of the use of the state parameter is correct (as per section 4.1 of the OAuth specification).

By default, Bolt uses a 'state store' under the hood to store the state between the redirect and the callback during the OAuth flow. If you take a look at the Authenticating with OAuth Bolt documentation, at the end of the docs is a 'Customizing OAuth defaults' section that can be expanded. In the example in this section, you can see a custom implementation of the state store. It boils down to two methods: generateStateParam and verifyStateParam. Saving and retrieving the generated state is part of the flow here, and can be customized to suit your needs.

The default implementation of the state store is called Clear State Store and its implementation is here, if you are curious.

Thank you for the response. What is the difference between clear state store and file state store? It seems like that clear state store is just to verify whether the state is expired or not. However, the file state store will verify whether the state is expired or not, fetch the value from file, and verify whether the value is the exact match. Why the bolt uses the clear state store over the file state store by default?

Since I need to verify state param on my redirect_url endpoint https://my-domain/slack/oauth/callback, do I still need to verify the state param via verifyStateParam on slack bolt side?

@dt-nwx With regards to the difference between ClearStateStore and FileStateStore, you're indeed right. The file-based solution is more secure than the clear one, but the file-based solution has a downside: it doesn't always work when running multiple app instances because it relies on the local file system. However, if you're operating your app on a single host and you want to use the file-based solution, it should work fine.

Still, you should not turn the validation off. The validation of the installing user's browser cookie data is a more crucial security measure than the StateStore behavior. Please check https://github.com/slackapi/bolt-js/releases/tag/%40slack%2Fbolt%403.11.0 and the links there for more details. With this validation process in place, it would be acceptable to use the ClearStateStore, lacking a persistent layer on the server-side, in terms of OAuth flow security.

@seratch Thank you for the quick response. That makes sense. I have one more question. When does installationStore and storeInstallation be triggered? I have completed the flow of https://my-domain-1/slack/install--> https://my-domain-2/slack/oauth/callback --> oauth.v2.access to receive the success response (the install and redirect domains are different). However, the slack app is installed to user's workspace, but the installation info is not stored. This makes any slack app following requests failed, since it can't locate the installation info. It seems like installationStore and storeInstallation are not triggered on my slack bolt app at https://my-domain-1 server after I calls oauth.v2.access on my domain-2 server.

Is it possible to trigger the installationStore and storeInstallation on https://my-domain-1 after domain-2 server callsoauth.v2.access?

How to store installationStore and storeInstallation? Do I need to manually store the installation info on https://my-domain-2 after my domain-2 server callsoauth.v2.access? What installation info I need to store? It seems like oauth.v2.access response is different from installation parameter returned from storeInstallation?

By the way, I have /slack/events, /slack/install, and /slack/oauth_redirect on my slack bolt app.

In this case, you cannot use the built-in local file system based one (also, it's not recommended to use it for production even when it works for you). Instead, your code needs to save the installation data in a database server where your both domains can access to. Also, we recommend serving both /slack/install and /slack/oauth_redirect under a single domain because the browser cookie validation needs to access the cookie data within the same domain.

Checking the following documents may be helpful too:

@seratch I am still very confused. Just a little bit background of what I want to achieve here. My webApp serving on domain 1 wants user with userId 123456 to install the slack app via https://my-domain-2/slack/install. During the slack OAuth process, I want to link webApp's userId 123456 with user's slack userId 78901. How can I achieve that? Is there a way I can just pass webApp's userId 123456 to https://my-domain-2/slack/install and enable slack app to store the linked info during the OAuth process?

I am reading this github issue. #1453 (comment). const tenantId = '1111'; What is this tenantId referring to? How can I pass webapp's userId 123456 to tenantId during beforeRedirection process?

To connect your web app's login session and Slack installation flow, you can simply do both Slack OAuth flow and your web app authentication within the same web browser session. You can use browser cookies to remember who the installing user is. That's why the "tenantId" in the comment is set as a cookie value. The value can be "yourServiceUserId" or whatever for you. The term "tenant" came from the context of the question in the thread.

Thank you!